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

📄 control.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  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 + -