📄 control.c
字号:
/* * 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>typedef struct _snd_kctl_ioctl { struct list_head list; /* list of all ioctls */ snd_kctl_ioctl_func_t fioctl;} snd_kctl_ioctl_t;#define snd_kctl_ioctl(n) list_entry(n, snd_kctl_ioctl_t, list)static DECLARE_RWSEM(snd_ioctl_rwsem);static LIST_HEAD(snd_control_ioctls);static int snd_ctl_open(struct inode *inode, struct file *file){ int cardnum = SNDRV_MINOR_CARD(iminor(inode)); unsigned long flags; snd_card_t *card; snd_ctl_file_t *ctl; int err; card = snd_cards[cardnum]; 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 = kcalloc(1, 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(snd_ctl_file_t * ctl){ snd_kctl_event_t *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; snd_card_t *card; snd_ctl_file_t *ctl; snd_kcontrol_t *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(snd_card_t *card, unsigned int mask, snd_ctl_elem_id_t *id){ unsigned long flags; struct list_head *flist; snd_ctl_file_t *ctl; snd_kctl_event_t *ev; snd_runtime_check(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 = kcalloc(1, 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);}/** * snd_ctl_new - create a control instance from the template * @control: the control template * @access: the default control access * * Allocates a new snd_kcontrol_t 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. */snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control, unsigned int access){ snd_kcontrol_t *kctl; unsigned int idx; snd_runtime_check(control != NULL, return NULL); snd_runtime_check(control->count > 0, return NULL); kctl = kcalloc(1, sizeof(*kctl) + sizeof(snd_kcontrol_volatile_t) * control->count, GFP_KERNEL); if (kctl == NULL) 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 snd_kcontrol_t 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. */snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data){ snd_kcontrol_t kctl; unsigned int access; snd_runtime_check(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);}/** * 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(snd_kcontrol_t * kcontrol){ if (kcontrol) { if (kcontrol->private_free) kcontrol->private_free(kcontrol); kfree(kcontrol); }}static unsigned int snd_ctl_hole_check(snd_card_t * card, unsigned int count){ struct list_head *list; snd_kcontrol_t *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(snd_card_t * 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(snd_card_t * card, snd_kcontrol_t * kcontrol){ snd_ctl_elem_id_t id; unsigned int idx; snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); snd_assert(kcontrol->info != NULL, return -EINVAL); id = kcontrol->id; down_write(&card->controls_rwsem); if (snd_ctl_find_id(card, &id)) { up_write(&card->controls_rwsem); snd_ctl_free_one(kcontrol); snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n", id.iface, id.device, id.subdevice, id.name, id.index); return -EBUSY; } if (snd_ctl_find_hole(card, kcontrol->count) < 0) { up_write(&card->controls_rwsem); snd_ctl_free_one(kcontrol); return -ENOMEM; } 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;}/** * 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(snd_card_t * card, snd_kcontrol_t * kcontrol){ snd_ctl_elem_id_t id; unsigned int idx; snd_runtime_check(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;}/** * 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(snd_card_t * card, snd_ctl_elem_id_t *id){ snd_kcontrol_t *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;}/** * 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(snd_ctl_file_t * file, snd_ctl_elem_id_t *id){ snd_card_t *card = file->card; snd_kcontrol_t *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(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id){ snd_kcontrol_t *kctl; down_write(&card->controls_rwsem); kctl = snd_ctl_find_id(card, src_id); if (kctl == NULL) { up_write(&card->controls_rwsem);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -