📄 seq_device.c
字号:
/* * ALSA sequencer device management * Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * *---------------------------------------------------------------- * * This device handler separates the card driver module from sequencer * stuff (sequencer core, synth drivers, etc), so that user can avoid * to spend unnecessary resources e.g. if he needs only listening to * MP3s. * * The card (or lowlevel) driver creates a sequencer device entry * via snd_seq_device_new(). This is an entry pointer to communicate * with the sequencer device "driver", which is involved with the * actual part to communicate with the sequencer core. * Each sequencer device entry has an id string and the corresponding * driver with the same id is loaded when required. For example, * lowlevel codes to access emu8000 chip on sbawe card are included in * emu8000-synth module. To activate this module, the hardware * resources like i/o port are passed via snd_seq_device argument. * */#include <sound/driver.h>#include <linux/init.h>#include <sound/core.h>#include <sound/info.h>#include <sound/seq_device.h>#include <sound/seq_kernel.h>#include <sound/initval.h>#include <linux/kmod.h>#include <linux/slab.h>MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");MODULE_DESCRIPTION("ALSA sequencer device management");MODULE_LICENSE("GPL");/* * driver list */typedef struct ops_list ops_list_t;/* driver state */#define DRIVER_EMPTY 0#define DRIVER_LOADED (1<<0)#define DRIVER_REQUESTED (1<<1)#define DRIVER_LOCKED (1<<2)struct ops_list { char id[ID_LEN]; /* driver id */ int driver; /* driver state */ int used; /* reference counter */ int argsize; /* argument size */ /* operators */ snd_seq_dev_ops_t ops; /* registred devices */ struct list_head dev_list; /* list of devices */ int num_devices; /* number of associated devices */ int num_init_devices; /* number of initialized devices */ struct semaphore reg_mutex; struct list_head list; /* next driver */};static LIST_HEAD(opslist);static int num_ops;static DECLARE_MUTEX(ops_mutex);static snd_info_entry_t *info_entry = NULL;/* * prototypes */static int snd_seq_device_free(snd_seq_device_t *dev);static int snd_seq_device_dev_free(snd_device_t *device);static int snd_seq_device_dev_register(snd_device_t *device);static int snd_seq_device_dev_disconnect(snd_device_t *device);static int snd_seq_device_dev_unregister(snd_device_t *device);static int init_device(snd_seq_device_t *dev, ops_list_t *ops);static int free_device(snd_seq_device_t *dev, ops_list_t *ops);static ops_list_t *find_driver(char *id, int create_if_empty);static ops_list_t *create_driver(char *id);static void unlock_driver(ops_list_t *ops);static void remove_drivers(void);/* * show all drivers and their status */static void snd_seq_device_info(snd_info_entry_t *entry, snd_info_buffer_t * buffer){ struct list_head *head; down(&ops_mutex); list_for_each(head, &opslist) { ops_list_t *ops = list_entry(head, ops_list_t, list); snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", ops->id, ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), ops->driver & DRIVER_REQUESTED ? ",requested" : "", ops->driver & DRIVER_LOCKED ? ",locked" : "", ops->num_devices); } up(&ops_mutex); } /* * load all registered drivers (called from seq_clientmgr.c) */#ifdef CONFIG_KMOD/* avoid auto-loading during module_init() */static int snd_seq_in_init;void snd_seq_autoload_lock(void){ snd_seq_in_init++;}void snd_seq_autoload_unlock(void){ snd_seq_in_init--;}#endifvoid snd_seq_device_load_drivers(void){#ifdef CONFIG_KMOD struct list_head *head; /* Calling request_module during module_init() * may cause blocking. */ if (snd_seq_in_init) return; if (! current->fs->root) return; down(&ops_mutex); list_for_each(head, &opslist) { ops_list_t *ops = list_entry(head, ops_list_t, list); if (! (ops->driver & DRIVER_LOADED) && ! (ops->driver & DRIVER_REQUESTED)) { ops->used++; up(&ops_mutex); ops->driver |= DRIVER_REQUESTED; request_module("snd-%s", ops->id); down(&ops_mutex); ops->used--; } } up(&ops_mutex);#endif}/* * register a sequencer device * card = card info (NULL allowed) * device = device number (if any) * id = id of driver * result = return pointer (NULL allowed if unnecessary) */int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize, snd_seq_device_t **result){ snd_seq_device_t *dev; ops_list_t *ops; int err; static snd_device_ops_t dops = { .dev_free = snd_seq_device_dev_free, .dev_register = snd_seq_device_dev_register, .dev_disconnect = snd_seq_device_dev_disconnect, .dev_unregister = snd_seq_device_dev_unregister }; if (result) *result = NULL; snd_assert(id != NULL, return -EINVAL); ops = find_driver(id, 1); if (ops == NULL) return -ENOMEM; dev = kzalloc(sizeof(*dev)*2 + argsize, GFP_KERNEL); if (dev == NULL) { unlock_driver(ops); return -ENOMEM; } /* set up device info */ dev->card = card; dev->device = device; strlcpy(dev->id, id, sizeof(dev->id)); dev->argsize = argsize; dev->status = SNDRV_SEQ_DEVICE_FREE; /* add this device to the list */ down(&ops->reg_mutex); list_add_tail(&dev->list, &ops->dev_list); ops->num_devices++; up(&ops->reg_mutex); unlock_driver(ops); if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { snd_seq_device_free(dev); return err; } if (result) *result = dev; return 0;}/* * free the existing device */static int snd_seq_device_free(snd_seq_device_t *dev){ ops_list_t *ops; snd_assert(dev != NULL, return -EINVAL); ops = find_driver(dev->id, 0); if (ops == NULL) return -ENXIO; /* remove the device from the list */ down(&ops->reg_mutex); list_del(&dev->list); ops->num_devices--; up(&ops->reg_mutex); free_device(dev, ops); if (dev->private_free) dev->private_free(dev); kfree(dev); unlock_driver(ops); return 0;}static int snd_seq_device_dev_free(snd_device_t *device){ snd_seq_device_t *dev = device->device_data; return snd_seq_device_free(dev);}/* * register the device */static int snd_seq_device_dev_register(snd_device_t *device){ snd_seq_device_t *dev = device->device_data; ops_list_t *ops; ops = find_driver(dev->id, 0); if (ops == NULL) return -ENOENT; /* initialize this device if the corresponding driver was * already loaded */ if (ops->driver & DRIVER_LOADED) init_device(dev, ops); unlock_driver(ops); return 0;}/* * disconnect the device */static int snd_seq_device_dev_disconnect(snd_device_t *device){ snd_seq_device_t *dev = device->device_data; ops_list_t *ops; ops = find_driver(dev->id, 0); if (ops == NULL) return -ENOENT; free_device(dev, ops); unlock_driver(ops); return 0;}/* * unregister the existing device */static int snd_seq_device_dev_unregister(snd_device_t *device){ snd_seq_device_t *dev = device->device_data; return snd_seq_device_free(dev);}/* * register device driver * id = driver id * entry = driver operators - duplicated to each instance */int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize){ struct list_head *head; ops_list_t *ops; if (id == NULL || entry == NULL || entry->init_device == NULL || entry->free_device == NULL) return -EINVAL; snd_seq_autoload_lock(); ops = find_driver(id, 1); if (ops == NULL) { snd_seq_autoload_unlock(); return -ENOMEM; } if (ops->driver & DRIVER_LOADED) { snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id); unlock_driver(ops); snd_seq_autoload_unlock(); return -EBUSY; } down(&ops->reg_mutex); /* copy driver operators */ ops->ops = *entry; ops->driver |= DRIVER_LOADED; ops->argsize = argsize; /* initialize existing devices if necessary */ list_for_each(head, &ops->dev_list) { snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list); init_device(dev, ops); } up(&ops->reg_mutex); unlock_driver(ops); snd_seq_autoload_unlock(); return 0;}/* * create driver record */static ops_list_t * create_driver(char *id){ ops_list_t *ops; ops = kmalloc(sizeof(*ops), GFP_KERNEL); if (ops == NULL) return ops; memset(ops, 0, sizeof(*ops)); /* set up driver entry */ strlcpy(ops->id, id, sizeof(ops->id)); init_MUTEX(&ops->reg_mutex); ops->driver = DRIVER_EMPTY; INIT_LIST_HEAD(&ops->dev_list); /* lock this instance */ ops->used = 1; /* register driver entry */ down(&ops_mutex); list_add_tail(&ops->list, &opslist); num_ops++; up(&ops_mutex); return ops;}/* * unregister the specified driver */int snd_seq_device_unregister_driver(char *id){ struct list_head *head; ops_list_t *ops; ops = find_driver(id, 0); if (ops == NULL) return -ENXIO; if (! (ops->driver & DRIVER_LOADED) || (ops->driver & DRIVER_LOCKED)) { snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver); unlock_driver(ops); return -EBUSY; } /* close and release all devices associated with this driver */ down(&ops->reg_mutex); ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ list_for_each(head, &ops->dev_list) { snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list); free_device(dev, ops); } ops->driver = 0; if (ops->num_init_devices > 0) snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices); up(&ops->reg_mutex); unlock_driver(ops); /* remove empty driver entries */ remove_drivers(); return 0;}/* * remove empty driver entries */static void remove_drivers(void){ struct list_head *head; down(&ops_mutex); head = opslist.next; while (head != &opslist) { ops_list_t *ops = list_entry(head, ops_list_t, list); if (! (ops->driver & DRIVER_LOADED) && ops->used == 0 && ops->num_devices == 0) { head = head->next; list_del(&ops->list); kfree(ops); num_ops--; } else head = head->next; } up(&ops_mutex);}/* * initialize the device - call init_device operator */static int init_device(snd_seq_device_t *dev, ops_list_t *ops){ if (! (ops->driver & DRIVER_LOADED)) return 0; /* driver is not loaded yet */ if (dev->status != SNDRV_SEQ_DEVICE_FREE) return 0; /* already initialized */ if (ops->argsize != dev->argsize) { snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); return -EINVAL; } if (ops->ops.init_device(dev) >= 0) { dev->status = SNDRV_SEQ_DEVICE_REGISTERED; ops->num_init_devices++; } else { snd_printk(KERN_ERR "init_device failed: %s: %s\n", dev->name, dev->id); } return 0;}/* * release the device - call free_device operator */static int free_device(snd_seq_device_t *dev, ops_list_t *ops){ int result; if (! (ops->driver & DRIVER_LOADED)) return 0; /* driver is not loaded yet */ if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) return 0; /* not registered */ if (ops->argsize != dev->argsize) { snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); return -EINVAL; } if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { dev->status = SNDRV_SEQ_DEVICE_FREE; dev->driver_data = NULL; ops->num_init_devices--; } else { snd_printk(KERN_ERR "free_device failed: %s: %s\n", dev->name, dev->id); } return 0;}/* * find the matching driver with given id */static ops_list_t * find_driver(char *id, int create_if_empty){ struct list_head *head; down(&ops_mutex); list_for_each(head, &opslist) { ops_list_t *ops = list_entry(head, ops_list_t, list); if (strcmp(ops->id, id) == 0) { ops->used++; up(&ops_mutex); return ops; } } up(&ops_mutex); if (create_if_empty) return create_driver(id); return NULL;}static void unlock_driver(ops_list_t *ops){ down(&ops_mutex); ops->used--; up(&ops_mutex);}/* * module part */static int __init alsa_seq_device_init(void){ info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", snd_seq_root); if (info_entry == NULL) return -ENOMEM; info_entry->content = SNDRV_INFO_CONTENT_TEXT; info_entry->c.text.read_size = 2048; info_entry->c.text.read = snd_seq_device_info; if (snd_info_register(info_entry) < 0) { snd_info_free_entry(info_entry); return -ENOMEM; } return 0;}static void __exit alsa_seq_device_exit(void){ remove_drivers(); snd_info_unregister(info_entry); if (num_ops) snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);}module_init(alsa_seq_device_init)module_exit(alsa_seq_device_exit)EXPORT_SYMBOL(snd_seq_device_load_drivers);EXPORT_SYMBOL(snd_seq_device_new);EXPORT_SYMBOL(snd_seq_device_register_driver);EXPORT_SYMBOL(snd_seq_device_unregister_driver);#ifdef CONFIG_KMODEXPORT_SYMBOL(snd_seq_autoload_lock);EXPORT_SYMBOL(snd_seq_autoload_unlock);#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -