⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 seq_device.c

📁 linux-2.6.15.6
💻 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 + -