/* * Routines for driver control interface * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * * * 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 * */ #include <sound/driver.h>#include <linux/threads.h>#include <linux/interrupt.h>#include <linux/smp_lock.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/time.h>#include <sound/core.h>#include <sound/minors.h>#include <sound/info.h>#include <sound/control.h> /* max number of user-defined controls */#define MAX_USER_CONTROLS 32 struct snd_kctl_ioctl { struct list_head list; /* list of all ioctls */ snd_kctl_ioctl_func_t fioctl;}; static DECLARE_RWSEM(snd_ioctl_rwsem);static LIST_HEAD(snd_control_ioctls);#ifdef CONFIG_COMPATstatic LIST_HEAD(snd_control_compat_ioctls);#endif static int snd_ctl_open(struct inode *inode, struct file *file){ unsigned long flags; struct snd_card *card; struct snd_ctl_file *ctl; int err; card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL); if (!card) { err = -ENODEV; goto __error1; } err = snd_card_file_add(card, file); if (err < 0) { err = -ENODEV; goto __error1; } if (!try_module_get(card->module)) { err = -EFAULT; goto __error2; } ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); if (ctl == NULL) { err = -ENOMEM; goto __error; } INIT_LIST_HEAD(&ctl->events); init_waitqueue_head(&ctl->change_sleep); spin_lock_init(&ctl->read_lock); ctl->card = card; ctl->pid = current->pid; file->private_data = ctl; write_lock_irqsave(&card->ctl_files_rwlock, flags); list_add_tail(&ctl->list, &card->ctl_files); write_unlock_irqrestore(&card->ctl_files_rwlock, flags); return 0; __error: module_put(card->module); __error2: snd_card_file_remove(card, file); __error1: return err;} static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl){ struct snd_kctl_event *cread; spin_lock(&ctl->read_lock); while (!list_empty(&ctl->events)) { cread = snd_kctl_event(ctl->events.next); list_del(&cread->list); kfree(cread); } spin_unlock(&ctl->read_lock);} static int snd_ctl_release(struct inode *inode, struct file *file){ unsigned long flags; struct list_head *list; struct snd_card *card; struct snd_ctl_file *ctl; struct snd_kcontrol *control; unsigned int idx; ctl = file->private_data; fasync_helper(-1, file, 0, &ctl->fasync); file->private_data = NULL; card = ctl->card; write_lock_irqsave(&card->ctl_files_rwlock, flags); list_del(&ctl->list); write_unlock_irqrestore(&card->ctl_files_rwlock, flags); down_write(&card->controls_rwsem); list_for_each(list, &card->controls) { control = snd_kcontrol(list); for (idx = 0; idx < control->count; idx++) if (control->vd[idx].owner == ctl) control->vd[idx].owner = NULL; } up_write(&card->controls_rwsem); snd_ctl_empty_read_queue(ctl); kfree(ctl); module_put(card->module); snd_card_file_remove(card, file); return 0;} void snd_ctl_notify(struct snd_card *card, unsigned int mask, struct snd_ctl_elem_id *id){ unsigned long flags; struct list_head *flist; struct snd_ctl_file *ctl; struct snd_kctl_event *ev; snd_assert(card != NULL && id != NULL, return); read_lock(&card->ctl_files_rwlock);#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) card->mixer_oss_change_count++;#endif list_for_each(flist, &card->ctl_files) { struct list_head *elist; ctl = snd_ctl_file(flist); if (!ctl->subscribed) continue; spin_lock_irqsave(&ctl->read_lock, flags); list_for_each(elist, &ctl->events) { ev = snd_kctl_event(elist); if (ev->id.numid == id->numid) { ev->mask |= mask; goto _found; } } ev = kzalloc(sizeof(*ev), GFP_ATOMIC); if (ev) { ev->id = *id; ev->mask = mask; list_add_tail(&ev->list, &ctl->events); } else { snd_printk(KERN_ERR "No memory available to allocate event\n"); } _found: wake_up(&ctl->change_sleep); spin_unlock_irqrestore(&ctl->read_lock, flags); kill_fasync(&ctl->fasync, SIGIO, POLL_IN); } read_unlock(&card->ctl_files_rwlock);} EXPORT_SYMBOL(snd_ctl_notify); /** * snd_ctl_new - create a control instance from the template * @control: the control template * @access: the default control access * * Allocates a new struct snd_kcontrol instance and copies the given template * to the new instance. It does not copy volatile data (access). * * Returns the pointer of the new instance, or NULL on failure. */struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, unsigned int access){ struct snd_kcontrol *kctl; unsigned int idx; snd_assert(control != NULL, return NULL); snd_assert(control->count > 0, return NULL); kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL); if (kctl == NULL) { snd_printk(KERN_ERR "Cannot allocate control instance\n"); return NULL; } *kctl = *control; for (idx = 0; idx < kctl->count; idx++) kctl->vd[idx].access = access; return kctl;} EXPORT_SYMBOL(snd_ctl_new); /** * snd_ctl_new1 - create a control instance from the template * @ncontrol: the initialization record * @private_data: the private data to set * * Allocates a new struct snd_kcontrol instance and initialize from the given * template. When the access field of ncontrol is 0, it's assumed as * READWRITE access. When the count field is 0, it's assumes as one. * * Returns the pointer of the newly generated instance, or NULL on failure. */struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, void *private_data){ struct snd_kcontrol kctl; unsigned int access; snd_assert(ncontrol != NULL, return NULL); snd_assert(ncontrol->info != NULL, return NULL); memset(&kctl, 0, sizeof(kctl)); kctl.id.iface = ncontrol->iface; kctl.id.device = ncontrol->device; kctl.id.subdevice = ncontrol->subdevice; if (ncontrol->name) strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); kctl.id.index = ncontrol->index; kctl.count = ncontrol->count ? ncontrol->count : 1; access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE| SNDRV_CTL_ELEM_ACCESS_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put; kctl.private_value = ncontrol->private_value; kctl.private_data = private_data; return snd_ctl_new(&kctl, access);} EXPORT_SYMBOL(snd_ctl_new1); /** * snd_ctl_free_one - release the control instance * @kcontrol: the control instance * * Releases the control instance created via snd_ctl_new() * or snd_ctl_new1(). * Don't call this after the control was added to the card. */void snd_ctl_free_one(struct snd_kcontrol *kcontrol){ if (kcontrol) { if (kcontrol->private_free) kcontrol->private_free(kcontrol); kfree(kcontrol); }} EXPORT_SYMBOL(snd_ctl_free_one); static unsigned int snd_ctl_hole_check(struct snd_card *card, unsigned int count){ struct list_head *list; struct snd_kcontrol *kctl; list_for_each(list, &card->controls) { kctl = snd_kcontrol(list); if ((kctl->id.numid <= card->last_numid && kctl->id.numid + kctl->count > card->last_numid) || (kctl->id.numid <= card->last_numid + count - 1 && kctl->id.numid + kctl->count > card->last_numid + count - 1)) return card->last_numid = kctl->id.numid + kctl->count - 1; } return card->last_numid;} static int snd_ctl_find_hole(struct snd_card *card, unsigned int count){ unsigned int last_numid, iter = 100000; last_numid = card->last_numid; while (last_numid != snd_ctl_hole_check(card, count)) { if (--iter == 0) { /* this situation is very unlikely */ snd_printk(KERN_ERR "unable to allocate new control numid\n"); return -ENOMEM; } last_numid = card->last_numid; } return 0;} /** * snd_ctl_add - add the control instance to the card * @card: the card instance * @kcontrol: the control instance to add * * Adds the control instance created via snd_ctl_new() or * snd_ctl_new1() to the given card. Assigns also an unique * numid used for fast search. * * Returns zero if successful, or a negative error code on failure. * * It frees automatically the control which cannot be added. */int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol){ struct snd_ctl_elem_id id; unsigned int idx; int err = -EINVAL; if (! kcontrol) return err; snd_assert(card != NULL, goto error); snd_assert(kcontrol->info != NULL, goto error); id = kcontrol->id; down_write(&card->controls_rwsem); if (snd_ctl_find_id(card, &id)) { up_write(&card->controls_rwsem); snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n", id.iface, id.device, id.subdevice, id.name, id.index); err = -EBUSY; goto error; } if (snd_ctl_find_hole(card, kcontrol->count) < 0) { up_write(&card->controls_rwsem); err = -ENOMEM; goto error; } list_add_tail(&kcontrol->list, &card->controls); card->controls_count += kcontrol->count; kcontrol->id.numid = card->last_numid + 1; card->last_numid += kcontrol->count; up_write(&card->controls_rwsem); for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); return 0; error: snd_ctl_free_one(kcontrol); return err;} EXPORT_SYMBOL(snd_ctl_add); /** * snd_ctl_remove - remove the control from the card and release it * @card: the card instance * @kcontrol: the control instance to remove * * Removes the control from the card and then releases the instance. * You don't need to call snd_ctl_free_one(). You must be in * the write lock - down_write(&card->controls_rwsem). * * Returns 0 if successful, or a negative error code on failure. */int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol){ struct snd_ctl_elem_id id; unsigned int idx; snd_assert(card != NULL && kcontrol != NULL, return -EINVAL); list_del(&kcontrol->list); card->controls_count -= kcontrol->count; id = kcontrol->id; for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id); snd_ctl_free_one(kcontrol); return 0;} EXPORT_SYMBOL(snd_ctl_remove); /** * snd_ctl_remove_id - remove the control of the given id and release it * @card: the card instance * @id: the control id to remove * * Finds the control instance with the given id, removes it from the * card list and releases it. * * Returns 0 if successful, or a negative error code on failure. */int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id){ struct snd_kcontrol *kctl; int ret; down_write(&card->controls_rwsem); kctl = snd_ctl_find_id(card, id); if (kctl == NULL) { up_write(&card->controls_rwsem); return -ENOENT; } ret = snd_ctl_remove(card, kctl); up_write(&card->controls_rwsem); return ret;} EXPORT_SYMBOL(snd_ctl_remove_id); /** * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it * @file: active control handle * @id: the control id to remove * * Finds the control instance with the given id, removes it from the * card list and releases it. * * Returns 0 if successful, or a negative error code on failure. */static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file, struct snd_ctl_elem_id *id){ struct snd_card *card = file->card; struct snd_kcontrol *kctl; int idx, ret; down_write(&card->controls_rwsem); kctl = snd_ctl_find_id(card, id); if (kctl == NULL) { up_write(&card->controls_rwsem); return -ENOENT; } for (idx = 0; idx < kctl->count; idx++) if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) { up_write(&card->controls_rwsem); return -EBUSY; } ret = snd_ctl_remove(card, kctl); up_write(&card->controls_rwsem); return ret;} /** * snd_ctl_rename_id - replace the id of a control on the card * @card: the card instance * @src_id: the old id * @dst_id: the new id * * Finds the control with the old id from the card, and replaces the * id with the new one. * * Returns zero if successful, or a negative error code on failure. */int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id){ struct snd_kcontrol *kctl; down_write(&card->controls_rwsem); kctl = snd_ctl_find_id(card, src_id); if (kctl == NULL) { up_write(&card->controls_rwsem); return -ENOENT; } kctl->id = *dst_id; kctl->id.numid = card->last_numid + 1; card->last_numid += kctl->count; up_write(&card->controls_rwsem); return 0;} EXPORT_SYMBOL(snd_ctl_rename_id); /** * snd_ctl_find_numid - find the control instance with the given number-id * @card: the card instance * @numid: the number-id to search * * Finds the control instance with the given number-id from the card. * * Returns the pointer of the instance if found, or NULL if not. * * The caller must down card->controls_rwsem before calling this function * (if the race condition can happen). */struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid){ struct list_head *list; struct snd_kcontrol *kctl; snd_assert(card != NULL && numid != 0, return NULL); list_for_each(list, &card->controls) { kctl = snd_kcontrol(list); if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)Lines 1–500 of 1401. Load more
/* * Routines for driver control interface * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * * * 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 * */ #include <sound/driver.h> #include <linux/threads.h> #include <linux/interrupt.h> #include <linux/smp_lock.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/time.h> #include <sound/core.h> #include <sound/minors.h> #include <sound/info.h> #include <sound/control.h> /* max number of user-defined controls */ #define MAX_USER_CONTROLS 32 struct snd_kctl_ioctl { struct list_head list; /* list of all ioctls */ snd_kctl_ioctl_func_t fioctl; }; static DECLARE_RWSEM(snd_ioctl_rwsem); static LIST_HEAD(snd_control_ioctls); #ifdef CONFIG_COMPAT static LIST_HEAD(snd_control_compat_ioctls); #endif static int snd_ctl_open(struct inode *inode, struct file *file) { unsigned long flags; struct snd_card *card; struct snd_ctl_file *ctl; int err; card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL); if (!card) { err = -ENODEV; goto __error1; } err = snd_card_file_add(card, file); if (err < 0) { err = -ENODEV; goto __error1; } if (!try_module_get(card->module)) { err = -EFAULT; goto __error2; } ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); if (ctl == NULL) { err = -ENOMEM; goto __error; } INIT_LIST_HEAD(&ctl->events); init_waitqueue_head(&ctl->change_sleep); spin_lock_init(&ctl->read_lock); ctl->card = card; ctl->pid = current->pid; file->private_data = ctl; write_lock_irqsave(&card->ctl_files_rwlock, flags); list_add_tail(&ctl->list, &card->ctl_files); write_unlock_irqrestore(&card->ctl_files_rwlock, flags); return 0; __error: module_put(card->module); __error2: snd_card_file_remove(card, file); __error1: return err; } static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl) { struct snd_kctl_event *cread; spin_lock(&ctl->read_lock); while (!list_empty(&ctl->events)) { cread = snd_kctl_event(ctl->events.next); list_del(&cread->list); kfree(cread); } spin_unlock(&ctl->read_lock); } static int snd_ctl_release(struct inode *inode, struct file *file) { unsigned long flags; struct list_head *list; struct snd_card *card; struct snd_ctl_file *ctl; struct snd_kcontrol *control; unsigned int idx; ctl = file->private_data; fasync_helper(-1, file, 0, &ctl->fasync); file->private_data = NULL; card = ctl->card; write_lock_irqsave(&card->ctl_files_rwlock, flags); list_del(&ctl->list); write_unlock_irqrestore(&card->ctl_files_rwlock, flags); down_write(&card->controls_rwsem); list_for_each(list, &card->controls) { control = snd_kcontrol(list); for (idx = 0; idx < control->count; idx++) if (control->vd[idx].owner == ctl) control->vd[idx].owner = NULL; } up_write(&card->controls_rwsem); snd_ctl_empty_read_queue(ctl); kfree(ctl); module_put(card->module); snd_card_file_remove(card, file); return 0; } void snd_ctl_notify(struct snd_card *card, unsigned int mask, struct snd_ctl_elem_id *id) { unsigned long flags; struct list_head *flist; struct snd_ctl_file *ctl; struct snd_kctl_event *ev; snd_assert(card != NULL && id != NULL, return); read_lock(&card->ctl_files_rwlock); #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) card->mixer_oss_change_count++; #endif list_for_each(flist, &card->ctl_files) { struct list_head *elist; ctl = snd_ctl_file(flist); if (!ctl->subscribed) continue; spin_lock_irqsave(&ctl->read_lock, flags); list_for_each(elist, &ctl->events) { ev = snd_kctl_event(elist); if (ev->id.numid == id->numid) { ev->mask |= mask; goto _found; } } ev = kzalloc(sizeof(*ev), GFP_ATOMIC); if (ev) { ev->id = *id; ev->mask = mask; list_add_tail(&ev->list, &ctl->events); } else { snd_printk(KERN_ERR "No memory available to allocate event\n"); } _found: wake_up(&ctl->change_sleep); spin_unlock_irqrestore(&ctl->read_lock, flags); kill_fasync(&ctl->fasync, SIGIO, POLL_IN); } read_unlock(&card->ctl_files_rwlock); } EXPORT_SYMBOL(snd_ctl_notify); /** * snd_ctl_new - create a control instance from the template * @control: the control template * @access: the default control access * * Allocates a new struct snd_kcontrol instance and copies the given template * to the new instance. It does not copy volatile data (access). * * Returns the pointer of the new instance, or NULL on failure. */ struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, unsigned int access) { struct snd_kcontrol *kctl; unsigned int idx; snd_assert(control != NULL, return NULL); snd_assert(control->count > 0, return NULL); kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL); if (kctl == NULL) { snd_printk(KERN_ERR "Cannot allocate control instance\n"); return NULL; } *kctl = *control; for (idx = 0; idx < kctl->count; idx++) kctl->vd[idx].access = access; return kctl; } EXPORT_SYMBOL(snd_ctl_new); /** * snd_ctl_new1 - create a control instance from the template * @ncontrol: the initialization record * @private_data: the private data to set * * Allocates a new struct snd_kcontrol instance and initialize from the given * template. When the access field of ncontrol is 0, it's assumed as * READWRITE access. When the count field is 0, it's assumes as one. * * Returns the pointer of the newly generated instance, or NULL on failure. */ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, void *private_data) { struct snd_kcontrol kctl; unsigned int access; snd_assert(ncontrol != NULL, return NULL); snd_assert(ncontrol->info != NULL, return NULL); memset(&kctl, 0, sizeof(kctl)); kctl.id.iface = ncontrol->iface; kctl.id.device = ncontrol->device; kctl.id.subdevice = ncontrol->subdevice; if (ncontrol->name) strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); kctl.id.index = ncontrol->index; kctl.count = ncontrol->count ? ncontrol->count : 1; access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE| SNDRV_CTL_ELEM_ACCESS_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put; kctl.private_value = ncontrol->private_value; kctl.private_data = private_data; return snd_ctl_new(&kctl, access); } EXPORT_SYMBOL(snd_ctl_new1); /** * snd_ctl_free_one - release the control instance * @kcontrol: the control instance * * Releases the control instance created via snd_ctl_new() * or snd_ctl_new1(). * Don't call this after the control was added to the card. */ void snd_ctl_free_one(struct snd_kcontrol *kcontrol) { if (kcontrol) { if (kcontrol->private_free) kcontrol->private_free(kcontrol); kfree(kcontrol); } } EXPORT_SYMBOL(snd_ctl_free_one); static unsigned int snd_ctl_hole_check(struct snd_card *card, unsigned int count) { struct list_head *list; struct snd_kcontrol *kctl; list_for_each(list, &card->controls) { kctl = snd_kcontrol(list); if ((kctl->id.numid <= card->last_numid && kctl->id.numid + kctl->count > card->last_numid) || (kctl->id.numid <= card->last_numid + count - 1 && kctl->id.numid + kctl->count > card->last_numid + count - 1)) return card->last_numid = kctl->id.numid + kctl->count - 1; } return card->last_numid; } static int snd_ctl_find_hole(struct snd_card *card, unsigned int count) { unsigned int last_numid, iter = 100000; last_numid = card->last_numid; while (last_numid != snd_ctl_hole_check(card, count)) { if (--iter == 0) { /* this situation is very unlikely */ snd_printk(KERN_ERR "unable to allocate new control numid\n"); return -ENOMEM; } last_numid = card->last_numid; } return 0; } /** * snd_ctl_add - add the control instance to the card * @card: the card instance * @kcontrol: the control instance to add * * Adds the control instance created via snd_ctl_new() or * snd_ctl_new1() to the given card. Assigns also an unique * numid used for fast search. * * Returns zero if successful, or a negative error code on failure. * * It frees automatically the control which cannot be added. */ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) { struct snd_ctl_elem_id id; unsigned int idx; int err = -EINVAL; if (! kcontrol) return err; snd_assert(card != NULL, goto error); snd_assert(kcontrol->info != NULL, goto error); id = kcontrol->id; down_write(&card->controls_rwsem); if (snd_ctl_find_id(card, &id)) { up_write(&card->controls_rwsem); snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n", id.iface, id.device, id.subdevice, id.name, id.index); err = -EBUSY; goto error; } if (snd_ctl_find_hole(card, kcontrol->count) < 0) { up_write(&card->controls_rwsem); err = -ENOMEM; goto error; } list_add_tail(&kcontrol->list, &card->controls); card->controls_count += kcontrol->count; kcontrol->id.numid = card->last_numid + 1; card->last_numid += kcontrol->count; up_write(&card->controls_rwsem); for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); return 0; error: snd_ctl_free_one(kcontrol); return err; } EXPORT_SYMBOL(snd_ctl_add); /** * snd_ctl_remove - remove the control from the card and release it * @card: the card instance * @kcontrol: the control instance to remove * * Removes the control from the card and then releases the instance. * You don't need to call snd_ctl_free_one(). You must be in * the write lock - down_write(&card->controls_rwsem). * * Returns 0 if successful, or a negative error code on failure. */ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol) { struct snd_ctl_elem_id id; unsigned int idx; snd_assert(card != NULL && kcontrol != NULL, return -EINVAL); list_del(&kcontrol->list); card->controls_count -= kcontrol->count; id = kcontrol->id; for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id); snd_ctl_free_one(kcontrol); return 0; } EXPORT_SYMBOL(snd_ctl_remove); /** * snd_ctl_remove_id - remove the control of the given id and release it * @card: the card instance * @id: the control id to remove * * Finds the control instance with the given id, removes it from the * card list and releases it. * * Returns 0 if successful, or a negative error code on failure. */ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id) { struct snd_kcontrol *kctl; int ret; down_write(&card->controls_rwsem); kctl = snd_ctl_find_id(card, id); if (kctl == NULL) { up_write(&card->controls_rwsem); return -ENOENT; } ret = snd_ctl_remove(card, kctl); up_write(&card->controls_rwsem); return ret; } EXPORT_SYMBOL(snd_ctl_remove_id); /** * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it * @file: active control handle * @id: the control id to remove * * Finds the control instance with the given id, removes it from the * card list and releases it. * * Returns 0 if successful, or a negative error code on failure. */ static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file, struct snd_ctl_elem_id *id) { struct snd_card *card = file->card; struct snd_kcontrol *kctl; int idx, ret; down_write(&card->controls_rwsem); kctl = snd_ctl_find_id(card, id); if (kctl == NULL) { up_write(&card->controls_rwsem); return -ENOENT; } for (idx = 0; idx < kctl->count; idx++) if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) { up_write(&card->controls_rwsem); return -EBUSY; } ret = snd_ctl_remove(card, kctl); up_write(&card->controls_rwsem); return ret; } /** * snd_ctl_rename_id - replace the id of a control on the card * @card: the card instance * @src_id: the old id * @dst_id: the new id * * Finds the control with the old id from the card, and replaces the * id with the new one. * * Returns zero if successful, or a negative error code on failure. */ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id) { struct snd_kcontrol *kctl; down_write(&card->controls_rwsem); kctl = snd_ctl_find_id(card, src_id); if (kctl == NULL) { up_write(&card->controls_rwsem); return -ENOENT; } kctl->id = *dst_id; kctl->id.numid = card->last_numid + 1; card->last_numid += kctl->count; up_write(&card->controls_rwsem); return 0; } EXPORT_SYMBOL(snd_ctl_rename_id); /** * snd_ctl_find_numid - find the control instance with the given number-id * @card: the card instance * @numid: the number-id to search * * Finds the control instance with the given number-id from the card. * * Returns the pointer of the instance if found, or NULL if not. * * The caller must down card->controls_rwsem before calling this function * (if the race condition can happen). */ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid) { struct list_head *list; struct snd_kcontrol *kctl; snd_assert(card != NULL && numid != 0, return NULL); list_for_each(list, &card->controls) { kctl = snd_kcontrol(list); if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)