Merge branch 'topic/seq-cleanup' into for-next
This commit is contained in:
commit
6176fadce2
|
@ -25,29 +25,26 @@
|
|||
* registered device information
|
||||
*/
|
||||
|
||||
#define ID_LEN 32
|
||||
|
||||
/* status flag */
|
||||
#define SNDRV_SEQ_DEVICE_FREE 0
|
||||
#define SNDRV_SEQ_DEVICE_REGISTERED 1
|
||||
|
||||
struct snd_seq_device {
|
||||
/* device info */
|
||||
struct snd_card *card; /* sound card */
|
||||
int device; /* device number */
|
||||
char id[ID_LEN]; /* driver id */
|
||||
const char *id; /* driver id */
|
||||
char name[80]; /* device name */
|
||||
int argsize; /* size of the argument */
|
||||
void *driver_data; /* private data for driver */
|
||||
int status; /* flag - read only */
|
||||
void *private_data; /* private data for the caller */
|
||||
void (*private_free)(struct snd_seq_device *device);
|
||||
struct list_head list; /* link to next device */
|
||||
struct device dev;
|
||||
};
|
||||
|
||||
#define to_seq_dev(_dev) \
|
||||
container_of(_dev, struct snd_seq_device, dev)
|
||||
|
||||
/* sequencer driver */
|
||||
|
||||
/* driver operators
|
||||
* init_device:
|
||||
* probe:
|
||||
* Initialize the device with given parameters.
|
||||
* Typically,
|
||||
* 1. call snd_hwdep_new
|
||||
|
@ -55,25 +52,40 @@ struct snd_seq_device {
|
|||
* 3. call snd_hwdep_register
|
||||
* 4. store the instance to dev->driver_data pointer.
|
||||
*
|
||||
* free_device:
|
||||
* remove:
|
||||
* Release the private data.
|
||||
* Typically, call snd_device_free(dev->card, dev->driver_data)
|
||||
*/
|
||||
struct snd_seq_dev_ops {
|
||||
int (*init_device)(struct snd_seq_device *dev);
|
||||
int (*free_device)(struct snd_seq_device *dev);
|
||||
struct snd_seq_driver {
|
||||
struct device_driver driver;
|
||||
char *id;
|
||||
int argsize;
|
||||
};
|
||||
|
||||
#define to_seq_drv(_drv) \
|
||||
container_of(_drv, struct snd_seq_driver, driver)
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
#ifdef CONFIG_MODULES
|
||||
void snd_seq_device_load_drivers(void);
|
||||
int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result);
|
||||
int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize);
|
||||
int snd_seq_device_unregister_driver(char *id);
|
||||
#else
|
||||
#define snd_seq_device_load_drivers()
|
||||
#endif
|
||||
int snd_seq_device_new(struct snd_card *card, int device, const char *id,
|
||||
int argsize, struct snd_seq_device **result);
|
||||
|
||||
#define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(struct snd_seq_device))
|
||||
|
||||
int __must_check __snd_seq_driver_register(struct snd_seq_driver *drv,
|
||||
struct module *mod);
|
||||
#define snd_seq_driver_register(drv) \
|
||||
__snd_seq_driver_register(drv, THIS_MODULE)
|
||||
void snd_seq_driver_unregister(struct snd_seq_driver *drv);
|
||||
|
||||
#define module_snd_seq_driver(drv) \
|
||||
module_driver(drv, snd_seq_driver_register, snd_seq_driver_unregister)
|
||||
|
||||
/*
|
||||
* id strings for generic devices
|
||||
|
|
|
@ -99,13 +99,9 @@ int snd_seq_event_port_attach(int client, struct snd_seq_port_callback *pcbp,
|
|||
int snd_seq_event_port_detach(int client, int port);
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
void snd_seq_autoload_lock(void);
|
||||
void snd_seq_autoload_unlock(void);
|
||||
void snd_seq_autoload_init(void);
|
||||
#define snd_seq_autoload_exit() snd_seq_autoload_lock()
|
||||
void snd_seq_autoload_exit(void);
|
||||
#else
|
||||
#define snd_seq_autoload_lock()
|
||||
#define snd_seq_autoload_unlock()
|
||||
#define snd_seq_autoload_init()
|
||||
#define snd_seq_autoload_exit()
|
||||
#endif
|
||||
|
|
|
@ -65,15 +65,20 @@ static unsigned int odev_poll(struct file *file, poll_table * wait);
|
|||
* module interface
|
||||
*/
|
||||
|
||||
static struct snd_seq_driver seq_oss_synth_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.probe = snd_seq_oss_synth_probe,
|
||||
.remove = snd_seq_oss_synth_remove,
|
||||
},
|
||||
.id = SNDRV_SEQ_DEV_ID_OSS,
|
||||
.argsize = sizeof(struct snd_seq_oss_reg),
|
||||
};
|
||||
|
||||
static int __init alsa_seq_oss_init(void)
|
||||
{
|
||||
int rc;
|
||||
static struct snd_seq_dev_ops ops = {
|
||||
snd_seq_oss_synth_register,
|
||||
snd_seq_oss_synth_unregister,
|
||||
};
|
||||
|
||||
snd_seq_autoload_lock();
|
||||
if ((rc = register_device()) < 0)
|
||||
goto error;
|
||||
if ((rc = register_proc()) < 0) {
|
||||
|
@ -86,8 +91,8 @@ static int __init alsa_seq_oss_init(void)
|
|||
goto error;
|
||||
}
|
||||
|
||||
if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops,
|
||||
sizeof(struct snd_seq_oss_reg))) < 0) {
|
||||
rc = snd_seq_driver_register(&seq_oss_synth_driver);
|
||||
if (rc < 0) {
|
||||
snd_seq_oss_delete_client();
|
||||
unregister_proc();
|
||||
unregister_device();
|
||||
|
@ -98,13 +103,12 @@ static int __init alsa_seq_oss_init(void)
|
|||
snd_seq_oss_synth_init();
|
||||
|
||||
error:
|
||||
snd_seq_autoload_unlock();
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_oss_exit(void)
|
||||
{
|
||||
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS);
|
||||
snd_seq_driver_unregister(&seq_oss_synth_driver);
|
||||
snd_seq_oss_delete_client();
|
||||
unregister_proc();
|
||||
unregister_device();
|
||||
|
|
|
@ -98,8 +98,9 @@ snd_seq_oss_synth_init(void)
|
|||
* registration of the synth device
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_synth_register(struct snd_seq_device *dev)
|
||||
snd_seq_oss_synth_probe(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
int i;
|
||||
struct seq_oss_synth *rec;
|
||||
struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
|
||||
|
@ -149,8 +150,9 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
|
|||
|
||||
|
||||
int
|
||||
snd_seq_oss_synth_unregister(struct snd_seq_device *dev)
|
||||
snd_seq_oss_synth_remove(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
int index;
|
||||
struct seq_oss_synth *rec = dev->driver_data;
|
||||
unsigned long flags;
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
#include <sound/seq_device.h>
|
||||
|
||||
void snd_seq_oss_synth_init(void);
|
||||
int snd_seq_oss_synth_register(struct snd_seq_device *dev);
|
||||
int snd_seq_oss_synth_unregister(struct snd_seq_device *dev);
|
||||
int snd_seq_oss_synth_probe(struct device *dev);
|
||||
int snd_seq_oss_synth_remove(struct device *dev);
|
||||
void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp);
|
||||
void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp);
|
||||
void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/core.h>
|
||||
|
@ -51,74 +52,44 @@ MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
|
|||
MODULE_DESCRIPTION("ALSA sequencer device management");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* driver state */
|
||||
#define DRIVER_EMPTY 0
|
||||
#define DRIVER_LOADED (1<<0)
|
||||
#define DRIVER_REQUESTED (1<<1)
|
||||
#define DRIVER_LOCKED (1<<2)
|
||||
#define DRIVER_REQUESTING (1<<3)
|
||||
/*
|
||||
* bus definition
|
||||
*/
|
||||
static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct snd_seq_device *sdev = to_seq_dev(dev);
|
||||
struct snd_seq_driver *sdrv = to_seq_drv(drv);
|
||||
|
||||
struct ops_list {
|
||||
char id[ID_LEN]; /* driver id */
|
||||
int driver; /* driver state */
|
||||
int used; /* reference counter */
|
||||
int argsize; /* argument size */
|
||||
return strcmp(sdrv->id, sdev->id) == 0 &&
|
||||
sdrv->argsize == sdev->argsize;
|
||||
}
|
||||
|
||||
/* operators */
|
||||
struct snd_seq_dev_ops ops;
|
||||
|
||||
/* registered 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 mutex reg_mutex;
|
||||
|
||||
struct list_head list; /* next driver */
|
||||
static struct bus_type snd_seq_bus_type = {
|
||||
.name = "snd_seq",
|
||||
.match = snd_seq_bus_match,
|
||||
};
|
||||
|
||||
|
||||
static LIST_HEAD(opslist);
|
||||
static int num_ops;
|
||||
static DEFINE_MUTEX(ops_mutex);
|
||||
/*
|
||||
* proc interface -- just for compatibility
|
||||
*/
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static struct snd_info_entry *info_entry;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static int snd_seq_device_free(struct snd_seq_device *dev);
|
||||
static int snd_seq_device_dev_free(struct snd_device *device);
|
||||
static int snd_seq_device_dev_register(struct snd_device *device);
|
||||
static int snd_seq_device_dev_disconnect(struct snd_device *device);
|
||||
static int print_dev_info(struct device *dev, void *data)
|
||||
{
|
||||
struct snd_seq_device *sdev = to_seq_dev(dev);
|
||||
struct snd_info_buffer *buffer = data;
|
||||
|
||||
static int init_device(struct snd_seq_device *dev, struct ops_list *ops);
|
||||
static int free_device(struct snd_seq_device *dev, struct ops_list *ops);
|
||||
static struct ops_list *find_driver(char *id, int create_if_empty);
|
||||
static struct ops_list *create_driver(char *id);
|
||||
static void unlock_driver(struct ops_list *ops);
|
||||
static void remove_drivers(void);
|
||||
snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
|
||||
dev->driver ? "loaded" : "empty",
|
||||
dev->driver ? 1 : 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* show all drivers and their status
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void snd_seq_device_info(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct ops_list *ops;
|
||||
|
||||
mutex_lock(&ops_mutex);
|
||||
list_for_each_entry(ops, &opslist, 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);
|
||||
}
|
||||
mutex_unlock(&ops_mutex);
|
||||
bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -127,64 +98,32 @@ static void snd_seq_device_info(struct snd_info_entry *entry,
|
|||
*/
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
/* avoid auto-loading during module_init() */
|
||||
/* flag to block auto-loading */
|
||||
static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
|
||||
void snd_seq_autoload_lock(void)
|
||||
|
||||
static int request_seq_drv(struct device *dev, void *data)
|
||||
{
|
||||
atomic_inc(&snd_seq_in_init);
|
||||
struct snd_seq_device *sdev = to_seq_dev(dev);
|
||||
|
||||
if (!dev->driver)
|
||||
request_module("snd-%s", sdev->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_seq_autoload_unlock(void)
|
||||
{
|
||||
atomic_dec(&snd_seq_in_init);
|
||||
}
|
||||
|
||||
static void autoload_drivers(void)
|
||||
static void autoload_drivers(struct work_struct *work)
|
||||
{
|
||||
/* avoid reentrance */
|
||||
if (atomic_inc_return(&snd_seq_in_init) == 1) {
|
||||
struct ops_list *ops;
|
||||
|
||||
mutex_lock(&ops_mutex);
|
||||
list_for_each_entry(ops, &opslist, list) {
|
||||
if ((ops->driver & DRIVER_REQUESTING) &&
|
||||
!(ops->driver & DRIVER_REQUESTED)) {
|
||||
ops->used++;
|
||||
mutex_unlock(&ops_mutex);
|
||||
ops->driver |= DRIVER_REQUESTED;
|
||||
request_module("snd-%s", ops->id);
|
||||
mutex_lock(&ops_mutex);
|
||||
ops->used--;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ops_mutex);
|
||||
}
|
||||
if (atomic_inc_return(&snd_seq_in_init) == 1)
|
||||
bus_for_each_dev(&snd_seq_bus_type, NULL, NULL,
|
||||
request_seq_drv);
|
||||
atomic_dec(&snd_seq_in_init);
|
||||
}
|
||||
|
||||
static void call_autoload(struct work_struct *work)
|
||||
{
|
||||
autoload_drivers();
|
||||
}
|
||||
|
||||
static DECLARE_WORK(autoload_work, call_autoload);
|
||||
|
||||
static void try_autoload(struct ops_list *ops)
|
||||
{
|
||||
if (!ops->driver) {
|
||||
ops->driver |= DRIVER_REQUESTING;
|
||||
schedule_work(&autoload_work);
|
||||
}
|
||||
}
|
||||
static DECLARE_WORK(autoload_work, autoload_drivers);
|
||||
|
||||
static void queue_autoload_drivers(void)
|
||||
{
|
||||
struct ops_list *ops;
|
||||
|
||||
mutex_lock(&ops_mutex);
|
||||
list_for_each_entry(ops, &opslist, list)
|
||||
try_autoload(ops);
|
||||
mutex_unlock(&ops_mutex);
|
||||
schedule_work(&autoload_work);
|
||||
}
|
||||
|
||||
void snd_seq_autoload_init(void)
|
||||
|
@ -195,16 +134,63 @@ void snd_seq_autoload_init(void)
|
|||
queue_autoload_drivers();
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
#define try_autoload(ops) /* NOP */
|
||||
#endif
|
||||
EXPORT_SYMBOL(snd_seq_autoload_init);
|
||||
|
||||
void snd_seq_autoload_exit(void)
|
||||
{
|
||||
atomic_inc(&snd_seq_in_init);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_seq_autoload_exit);
|
||||
|
||||
void snd_seq_device_load_drivers(void)
|
||||
{
|
||||
#ifdef CONFIG_MODULES
|
||||
queue_autoload_drivers();
|
||||
flush_work(&autoload_work);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_seq_device_load_drivers);
|
||||
#else
|
||||
#define queue_autoload_drivers() /* NOP */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* device management
|
||||
*/
|
||||
static int snd_seq_device_dev_free(struct snd_device *device)
|
||||
{
|
||||
struct snd_seq_device *dev = device->device_data;
|
||||
|
||||
put_device(&dev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_device_dev_register(struct snd_device *device)
|
||||
{
|
||||
struct snd_seq_device *dev = device->device_data;
|
||||
int err;
|
||||
|
||||
err = device_add(&dev->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (!dev->dev.driver)
|
||||
queue_autoload_drivers();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_device_dev_disconnect(struct snd_device *device)
|
||||
{
|
||||
struct snd_seq_device *dev = device->device_data;
|
||||
|
||||
device_del(&dev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_seq_dev_release(struct device *dev)
|
||||
{
|
||||
struct snd_seq_device *sdev = to_seq_dev(dev);
|
||||
|
||||
if (sdev->private_free)
|
||||
sdev->private_free(sdev);
|
||||
kfree(sdev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -214,11 +200,10 @@ void snd_seq_device_load_drivers(void)
|
|||
* id = id of driver
|
||||
* result = return pointer (NULL allowed if unnecessary)
|
||||
*/
|
||||
int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
|
||||
struct snd_seq_device **result)
|
||||
int snd_seq_device_new(struct snd_card *card, int device, const char *id,
|
||||
int argsize, struct snd_seq_device **result)
|
||||
{
|
||||
struct snd_seq_device *dev;
|
||||
struct ops_list *ops;
|
||||
int err;
|
||||
static struct snd_device_ops dops = {
|
||||
.dev_free = snd_seq_device_dev_free,
|
||||
|
@ -232,347 +217,60 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
|
|||
if (snd_BUG_ON(!id))
|
||||
return -EINVAL;
|
||||
|
||||
ops = find_driver(id, 1);
|
||||
if (ops == NULL)
|
||||
dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
|
||||
if (!dev)
|
||||
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->id = id;
|
||||
dev->argsize = argsize;
|
||||
dev->status = SNDRV_SEQ_DEVICE_FREE;
|
||||
|
||||
device_initialize(&dev->dev);
|
||||
dev->dev.parent = &card->card_dev;
|
||||
dev->dev.bus = &snd_seq_bus_type;
|
||||
dev->dev.release = snd_seq_dev_release;
|
||||
dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
|
||||
|
||||
/* add this device to the list */
|
||||
mutex_lock(&ops->reg_mutex);
|
||||
list_add_tail(&dev->list, &ops->dev_list);
|
||||
ops->num_devices++;
|
||||
mutex_unlock(&ops->reg_mutex);
|
||||
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
|
||||
snd_seq_device_free(dev);
|
||||
err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
|
||||
if (err < 0) {
|
||||
put_device(&dev->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
try_autoload(ops);
|
||||
unlock_driver(ops);
|
||||
|
||||
if (result)
|
||||
*result = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_seq_device_new);
|
||||
|
||||
/*
|
||||
* free the existing device
|
||||
* driver registration
|
||||
*/
|
||||
static int snd_seq_device_free(struct snd_seq_device *dev)
|
||||
int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
|
||||
{
|
||||
struct ops_list *ops;
|
||||
|
||||
if (snd_BUG_ON(!dev))
|
||||
if (WARN_ON(!drv->driver.name || !drv->id))
|
||||
return -EINVAL;
|
||||
|
||||
ops = find_driver(dev->id, 0);
|
||||
if (ops == NULL)
|
||||
return -ENXIO;
|
||||
|
||||
/* remove the device from the list */
|
||||
mutex_lock(&ops->reg_mutex);
|
||||
list_del(&dev->list);
|
||||
ops->num_devices--;
|
||||
mutex_unlock(&ops->reg_mutex);
|
||||
|
||||
free_device(dev, ops);
|
||||
if (dev->private_free)
|
||||
dev->private_free(dev);
|
||||
kfree(dev);
|
||||
|
||||
unlock_driver(ops);
|
||||
|
||||
return 0;
|
||||
drv->driver.bus = &snd_seq_bus_type;
|
||||
drv->driver.owner = mod;
|
||||
return driver_register(&drv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
|
||||
|
||||
static int snd_seq_device_dev_free(struct snd_device *device)
|
||||
void snd_seq_driver_unregister(struct snd_seq_driver *drv)
|
||||
{
|
||||
struct snd_seq_device *dev = device->device_data;
|
||||
return snd_seq_device_free(dev);
|
||||
driver_unregister(&drv->driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* register the device
|
||||
*/
|
||||
static int snd_seq_device_dev_register(struct snd_device *device)
|
||||
{
|
||||
struct snd_seq_device *dev = device->device_data;
|
||||
struct ops_list *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(struct snd_device *device)
|
||||
{
|
||||
struct snd_seq_device *dev = device->device_data;
|
||||
struct ops_list *ops;
|
||||
|
||||
ops = find_driver(dev->id, 0);
|
||||
if (ops == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
free_device(dev, ops);
|
||||
|
||||
unlock_driver(ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* register device driver
|
||||
* id = driver id
|
||||
* entry = driver operators - duplicated to each instance
|
||||
*/
|
||||
int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
|
||||
int argsize)
|
||||
{
|
||||
struct ops_list *ops;
|
||||
struct snd_seq_device *dev;
|
||||
|
||||
if (id == NULL || entry == NULL ||
|
||||
entry->init_device == NULL || entry->free_device == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ops = find_driver(id, 1);
|
||||
if (ops == NULL)
|
||||
return -ENOMEM;
|
||||
if (ops->driver & DRIVER_LOADED) {
|
||||
pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id);
|
||||
unlock_driver(ops);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
mutex_lock(&ops->reg_mutex);
|
||||
/* copy driver operators */
|
||||
ops->ops = *entry;
|
||||
ops->driver |= DRIVER_LOADED;
|
||||
ops->argsize = argsize;
|
||||
|
||||
/* initialize existing devices if necessary */
|
||||
list_for_each_entry(dev, &ops->dev_list, list) {
|
||||
init_device(dev, ops);
|
||||
}
|
||||
mutex_unlock(&ops->reg_mutex);
|
||||
|
||||
unlock_driver(ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* create driver record
|
||||
*/
|
||||
static struct ops_list * create_driver(char *id)
|
||||
{
|
||||
struct ops_list *ops;
|
||||
|
||||
ops = kzalloc(sizeof(*ops), GFP_KERNEL);
|
||||
if (ops == NULL)
|
||||
return ops;
|
||||
|
||||
/* set up driver entry */
|
||||
strlcpy(ops->id, id, sizeof(ops->id));
|
||||
mutex_init(&ops->reg_mutex);
|
||||
/*
|
||||
* The ->reg_mutex locking rules are per-driver, so we create
|
||||
* separate per-driver lock classes:
|
||||
*/
|
||||
lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id);
|
||||
|
||||
ops->driver = DRIVER_EMPTY;
|
||||
INIT_LIST_HEAD(&ops->dev_list);
|
||||
/* lock this instance */
|
||||
ops->used = 1;
|
||||
|
||||
/* register driver entry */
|
||||
mutex_lock(&ops_mutex);
|
||||
list_add_tail(&ops->list, &opslist);
|
||||
num_ops++;
|
||||
mutex_unlock(&ops_mutex);
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* unregister the specified driver
|
||||
*/
|
||||
int snd_seq_device_unregister_driver(char *id)
|
||||
{
|
||||
struct ops_list *ops;
|
||||
struct snd_seq_device *dev;
|
||||
|
||||
ops = find_driver(id, 0);
|
||||
if (ops == NULL)
|
||||
return -ENXIO;
|
||||
if (! (ops->driver & DRIVER_LOADED) ||
|
||||
(ops->driver & DRIVER_LOCKED)) {
|
||||
pr_err("ALSA: seq: 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 */
|
||||
mutex_lock(&ops->reg_mutex);
|
||||
ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
|
||||
list_for_each_entry(dev, &ops->dev_list, list) {
|
||||
free_device(dev, ops);
|
||||
}
|
||||
|
||||
ops->driver = 0;
|
||||
if (ops->num_init_devices > 0)
|
||||
pr_err("ALSA: seq: free_driver: init_devices > 0!! (%d)\n",
|
||||
ops->num_init_devices);
|
||||
mutex_unlock(&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;
|
||||
|
||||
mutex_lock(&ops_mutex);
|
||||
head = opslist.next;
|
||||
while (head != &opslist) {
|
||||
struct ops_list *ops = list_entry(head, struct ops_list, 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;
|
||||
}
|
||||
mutex_unlock(&ops_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize the device - call init_device operator
|
||||
*/
|
||||
static int init_device(struct snd_seq_device *dev, struct ops_list *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) {
|
||||
pr_err("ALSA: seq: 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 {
|
||||
pr_err("ALSA: seq: init_device failed: %s: %s\n",
|
||||
dev->name, dev->id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* release the device - call free_device operator
|
||||
*/
|
||||
static int free_device(struct snd_seq_device *dev, struct ops_list *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) {
|
||||
pr_err("ALSA: seq: 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 {
|
||||
pr_err("ALSA: seq: free_device failed: %s: %s\n",
|
||||
dev->name, dev->id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* find the matching driver with given id
|
||||
*/
|
||||
static struct ops_list * find_driver(char *id, int create_if_empty)
|
||||
{
|
||||
struct ops_list *ops;
|
||||
|
||||
mutex_lock(&ops_mutex);
|
||||
list_for_each_entry(ops, &opslist, list) {
|
||||
if (strcmp(ops->id, id) == 0) {
|
||||
ops->used++;
|
||||
mutex_unlock(&ops_mutex);
|
||||
return ops;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ops_mutex);
|
||||
if (create_if_empty)
|
||||
return create_driver(id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void unlock_driver(struct ops_list *ops)
|
||||
{
|
||||
mutex_lock(&ops_mutex);
|
||||
ops->used--;
|
||||
mutex_unlock(&ops_mutex);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
|
||||
|
||||
/*
|
||||
* module part
|
||||
*/
|
||||
|
||||
static int __init alsa_seq_device_init(void)
|
||||
static int __init seq_dev_proc_init(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
|
||||
|
@ -589,28 +287,29 @@ static int __init alsa_seq_device_init(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init alsa_seq_device_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bus_register(&snd_seq_bus_type);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = seq_dev_proc_init();
|
||||
if (err < 0)
|
||||
bus_unregister(&snd_seq_bus_type);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_device_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_MODULES
|
||||
cancel_work_sync(&autoload_work);
|
||||
#endif
|
||||
remove_drivers();
|
||||
#ifdef CONFIG_PROC_FS
|
||||
snd_info_free_entry(info_entry);
|
||||
#endif
|
||||
if (num_ops)
|
||||
pr_err("ALSA: seq: drivers not released (%d)\n", num_ops);
|
||||
bus_unregister(&snd_seq_bus_type);
|
||||
}
|
||||
|
||||
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_MODULES
|
||||
EXPORT_SYMBOL(snd_seq_autoload_init);
|
||||
EXPORT_SYMBOL(snd_seq_autoload_lock);
|
||||
EXPORT_SYMBOL(snd_seq_autoload_unlock);
|
||||
#endif
|
||||
|
|
|
@ -214,11 +214,7 @@ delete_client(void)
|
|||
|
||||
static int __init alsa_seq_dummy_init(void)
|
||||
{
|
||||
int err;
|
||||
snd_seq_autoload_lock();
|
||||
err = register_client();
|
||||
snd_seq_autoload_unlock();
|
||||
return err;
|
||||
return register_client();
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_dummy_exit(void)
|
||||
|
|
|
@ -273,8 +273,9 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
|
|||
|
||||
/* register new midi synth port */
|
||||
static int
|
||||
snd_seq_midisynth_register_port(struct snd_seq_device *dev)
|
||||
snd_seq_midisynth_probe(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
struct seq_midisynth_client *client;
|
||||
struct seq_midisynth *msynth, *ms;
|
||||
struct snd_seq_port_info *port;
|
||||
|
@ -427,8 +428,9 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
|
|||
|
||||
/* release midi synth port */
|
||||
static int
|
||||
snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
|
||||
snd_seq_midisynth_remove(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
struct seq_midisynth_client *client;
|
||||
struct seq_midisynth *msynth;
|
||||
struct snd_card *card = dev->card;
|
||||
|
@ -457,24 +459,14 @@ snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_seq_driver seq_midisynth_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.probe = snd_seq_midisynth_probe,
|
||||
.remove = snd_seq_midisynth_remove,
|
||||
},
|
||||
.id = SNDRV_SEQ_DEV_ID_MIDISYNTH,
|
||||
.argsize = 0,
|
||||
};
|
||||
|
||||
static int __init alsa_seq_midi_init(void)
|
||||
{
|
||||
static struct snd_seq_dev_ops ops = {
|
||||
snd_seq_midisynth_register_port,
|
||||
snd_seq_midisynth_unregister_port,
|
||||
};
|
||||
memset(&synths, 0, sizeof(synths));
|
||||
snd_seq_autoload_lock();
|
||||
snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
|
||||
snd_seq_autoload_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_midi_exit(void)
|
||||
{
|
||||
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
|
||||
}
|
||||
|
||||
module_init(alsa_seq_midi_init)
|
||||
module_exit(alsa_seq_midi_exit)
|
||||
module_snd_seq_driver(seq_midisynth_driver);
|
||||
|
|
|
@ -216,8 +216,9 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
|
|||
|
||||
/* ------------------------------ */
|
||||
|
||||
static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
|
||||
static int snd_opl3_seq_probe(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
struct snd_opl3 *opl3;
|
||||
int client, err;
|
||||
char name[32];
|
||||
|
@ -257,8 +258,9 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
|
||||
static int snd_opl3_seq_remove(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
struct snd_opl3 *opl3;
|
||||
|
||||
opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
|
||||
|
@ -275,22 +277,14 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init alsa_opl3_seq_init(void)
|
||||
{
|
||||
static struct snd_seq_dev_ops ops =
|
||||
{
|
||||
snd_opl3_seq_new_device,
|
||||
snd_opl3_seq_delete_device
|
||||
};
|
||||
static struct snd_seq_driver opl3_seq_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.probe = snd_opl3_seq_probe,
|
||||
.remove = snd_opl3_seq_remove,
|
||||
},
|
||||
.id = SNDRV_SEQ_DEV_ID_OPL3,
|
||||
.argsize = sizeof(struct snd_opl3 *),
|
||||
};
|
||||
|
||||
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops,
|
||||
sizeof(struct snd_opl3 *));
|
||||
}
|
||||
|
||||
static void __exit alsa_opl3_seq_exit(void)
|
||||
{
|
||||
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3);
|
||||
}
|
||||
|
||||
module_init(alsa_opl3_seq_init)
|
||||
module_exit(alsa_opl3_seq_exit)
|
||||
module_snd_seq_driver(opl3_seq_driver);
|
||||
|
|
|
@ -124,8 +124,9 @@ static void snd_opl4_seq_free_port(void *private_data)
|
|||
snd_midi_channel_free_set(opl4->chset);
|
||||
}
|
||||
|
||||
static int snd_opl4_seq_new_device(struct snd_seq_device *dev)
|
||||
static int snd_opl4_seq_probe(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
struct snd_opl4 *opl4;
|
||||
int client;
|
||||
struct snd_seq_port_callback pcallbacks;
|
||||
|
@ -180,8 +181,9 @@ static int snd_opl4_seq_new_device(struct snd_seq_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int snd_opl4_seq_delete_device(struct snd_seq_device *dev)
|
||||
static int snd_opl4_seq_remove(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
struct snd_opl4 *opl4;
|
||||
|
||||
opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
|
||||
|
@ -195,21 +197,14 @@ static int snd_opl4_seq_delete_device(struct snd_seq_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init alsa_opl4_synth_init(void)
|
||||
{
|
||||
static struct snd_seq_dev_ops ops = {
|
||||
snd_opl4_seq_new_device,
|
||||
snd_opl4_seq_delete_device
|
||||
};
|
||||
static struct snd_seq_driver opl4_seq_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.probe = snd_opl4_seq_probe,
|
||||
.remove = snd_opl4_seq_remove,
|
||||
},
|
||||
.id = SNDRV_SEQ_DEV_ID_OPL4,
|
||||
.argsize = sizeof(struct snd_opl4 *),
|
||||
};
|
||||
|
||||
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL4, &ops,
|
||||
sizeof(struct snd_opl4 *));
|
||||
}
|
||||
|
||||
static void __exit alsa_opl4_synth_exit(void)
|
||||
{
|
||||
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL4);
|
||||
}
|
||||
|
||||
module_init(alsa_opl4_synth_init)
|
||||
module_exit(alsa_opl4_synth_exit)
|
||||
module_snd_seq_driver(opl4_seq_driver);
|
||||
|
|
|
@ -34,8 +34,9 @@ MODULE_LICENSE("GPL");
|
|||
/*
|
||||
* create a new hardware dependent device for Emu8000
|
||||
*/
|
||||
static int snd_emu8000_new_device(struct snd_seq_device *dev)
|
||||
static int snd_emu8000_probe(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
struct snd_emu8000 *hw;
|
||||
struct snd_emux *emu;
|
||||
|
||||
|
@ -93,8 +94,9 @@ static int snd_emu8000_new_device(struct snd_seq_device *dev)
|
|||
/*
|
||||
* free all resources
|
||||
*/
|
||||
static int snd_emu8000_delete_device(struct snd_seq_device *dev)
|
||||
static int snd_emu8000_remove(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
struct snd_emu8000 *hw;
|
||||
|
||||
if (dev->driver_data == NULL)
|
||||
|
@ -114,21 +116,14 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev)
|
|||
* INIT part
|
||||
*/
|
||||
|
||||
static int __init alsa_emu8000_init(void)
|
||||
{
|
||||
static struct snd_seq_driver emu8000_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.probe = snd_emu8000_probe,
|
||||
.remove = snd_emu8000_remove,
|
||||
},
|
||||
.id = SNDRV_SEQ_DEV_ID_EMU8000,
|
||||
.argsize = sizeof(struct snd_emu8000 *),
|
||||
};
|
||||
|
||||
static struct snd_seq_dev_ops ops = {
|
||||
snd_emu8000_new_device,
|
||||
snd_emu8000_delete_device,
|
||||
};
|
||||
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops,
|
||||
sizeof(struct snd_emu8000*));
|
||||
}
|
||||
|
||||
static void __exit alsa_emu8000_exit(void)
|
||||
{
|
||||
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000);
|
||||
}
|
||||
|
||||
module_init(alsa_emu8000_init)
|
||||
module_exit(alsa_emu8000_exit)
|
||||
module_snd_seq_driver(emu8000_driver);
|
||||
|
|
|
@ -29,8 +29,9 @@ MODULE_LICENSE("GPL");
|
|||
/*
|
||||
* create a new hardware dependent device for Emu10k1
|
||||
*/
|
||||
static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
|
||||
static int snd_emu10k1_synth_probe(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
struct snd_emux *emux;
|
||||
struct snd_emu10k1 *hw;
|
||||
struct snd_emu10k1_synth_arg *arg;
|
||||
|
@ -79,8 +80,9 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
|
||||
static int snd_emu10k1_synth_remove(struct device *_dev)
|
||||
{
|
||||
struct snd_seq_device *dev = to_seq_dev(_dev);
|
||||
struct snd_emux *emux;
|
||||
struct snd_emu10k1 *hw;
|
||||
unsigned long flags;
|
||||
|
@ -104,21 +106,14 @@ static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
|
|||
* INIT part
|
||||
*/
|
||||
|
||||
static int __init alsa_emu10k1_synth_init(void)
|
||||
{
|
||||
static struct snd_seq_driver emu10k1_synth_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.probe = snd_emu10k1_synth_probe,
|
||||
.remove = snd_emu10k1_synth_remove,
|
||||
},
|
||||
.id = SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
|
||||
.argsize = sizeof(struct snd_emu10k1_synth_arg),
|
||||
};
|
||||
|
||||
static struct snd_seq_dev_ops ops = {
|
||||
snd_emu10k1_synth_new_device,
|
||||
snd_emu10k1_synth_delete_device,
|
||||
};
|
||||
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops,
|
||||
sizeof(struct snd_emu10k1_synth_arg));
|
||||
}
|
||||
|
||||
static void __exit alsa_emu10k1_synth_exit(void)
|
||||
{
|
||||
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH);
|
||||
}
|
||||
|
||||
module_init(alsa_emu10k1_synth_init)
|
||||
module_exit(alsa_emu10k1_synth_exit)
|
||||
module_snd_seq_driver(emu10k1_synth_driver);
|
||||
|
|
Loading…
Reference in New Issue