📄 control.c
字号:
/* * Routines for driver control interface * Copyright (c) by Jaroslav Kysela <perex@perex.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/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 32struct 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);#endifstatic 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->prefer_pcm_subdevice = -1; ctl->prefer_rawmidi_subdevice = -1; 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){ unsigned long flags; struct snd_kctl_event *cread; spin_lock_irqsave(&ctl->read_lock, flags); while (!list_empty(&ctl->events)) { cread = snd_kctl_event(ctl->events.next); list_del(&cread->list); kfree(cread); } spin_unlock_irqrestore(&ctl->read_lock, flags);}static int snd_ctl_release(struct inode *inode, struct file *file){ unsigned long flags; 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_entry(control, &card->controls, 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 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_entry(ctl, &card->ctl_files, list) { if (!ctl->subscribed) continue; spin_lock_irqsave(&ctl->read_lock, flags); list_for_each_entry(ev, &ctl->events, list) { 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. */static 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;}/** * 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| SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put; kctl.tlv.p = ncontrol->tlv.p; 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 snd_kcontrol *kctl; list_for_each_entry(kctl, &card->controls, 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 snd_kcontrol *kctl; snd_assert(card != NULL && numid != 0, return NULL); list_for_each_entry(kctl, &card->controls, list) { if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid) return kctl; } return NULL;}EXPORT_SYMBOL(snd_ctl_find_numid);/** * snd_ctl_find_id - find the control instance with the given id * @card: the card instance
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -