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

📄 control.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
			return -EINVAL;		break;	case SNDRV_CTL_ELEM_TYPE_BYTES:		private_size = sizeof(unsigned char);		if (info->count > 512)			return -EINVAL;		break;	case SNDRV_CTL_ELEM_TYPE_IEC958:		private_size = sizeof(struct snd_aes_iec958);		if (info->count != 1)			return -EINVAL;		break;	default:		return -EINVAL;	}	private_size *= info->count;	ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);	if (ue == NULL)		return -ENOMEM;	ue->info = *info;	ue->info.access = 0;	ue->elem_data = (char *)ue + sizeof(*ue);	ue->elem_data_size = private_size;	kctl.private_free = snd_ctl_elem_user_free;	_kctl = snd_ctl_new(&kctl, access);	if (_kctl == NULL) {		kfree(ue);		return -ENOMEM;	}	_kctl->private_data = ue;	for (idx = 0; idx < _kctl->count; idx++)		_kctl->vd[idx].owner = file;	err = snd_ctl_add(card, _kctl);	if (err < 0)		return err;	down_write(&card->controls_rwsem);	card->user_ctl_count++;	up_write(&card->controls_rwsem);	return 0;}static int snd_ctl_elem_add_user(struct snd_ctl_file *file,				 struct snd_ctl_elem_info __user *_info, int replace){	struct snd_ctl_elem_info info;	if (copy_from_user(&info, _info, sizeof(info)))		return -EFAULT;	return snd_ctl_elem_add(file, &info, replace);}static int snd_ctl_elem_remove(struct snd_ctl_file *file,			       struct snd_ctl_elem_id __user *_id){	struct snd_ctl_elem_id id;	int err;	if (copy_from_user(&id, _id, sizeof(id)))		return -EFAULT;	err = snd_ctl_remove_unlocked_id(file, &id);	if (! err) {		struct snd_card *card = file->card;		down_write(&card->controls_rwsem);		card->user_ctl_count--;		up_write(&card->controls_rwsem);	}	return err;}static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr){	int subscribe;	if (get_user(subscribe, ptr))		return -EFAULT;	if (subscribe < 0) {		subscribe = file->subscribed;		if (put_user(subscribe, ptr))			return -EFAULT;		return 0;	}	if (subscribe) {		file->subscribed = 1;		return 0;	} else if (file->subscribed) {		snd_ctl_empty_read_queue(file);		file->subscribed = 0;	}	return 0;}static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,                             struct snd_ctl_tlv __user *_tlv,                             int op_flag){	struct snd_card *card = file->card;	struct snd_ctl_tlv tlv;	struct snd_kcontrol *kctl;	struct snd_kcontrol_volatile *vd;	unsigned int len;	int err = 0;	if (copy_from_user(&tlv, _tlv, sizeof(tlv)))		return -EFAULT;	if (tlv.length < sizeof(unsigned int) * 3)		return -EINVAL;	down_read(&card->controls_rwsem);	kctl = snd_ctl_find_numid(card, tlv.numid);	if (kctl == NULL) {		err = -ENOENT;		goto __kctl_end;	}	if (kctl->tlv.p == NULL) {		err = -ENXIO;		goto __kctl_end;	}	vd = &kctl->vd[tlv.numid - kctl->id.numid];	if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||	    (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||	    (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {	    	err = -ENXIO;	    	goto __kctl_end;	}	if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {		if (file && vd->owner != NULL && vd->owner != file) {			err = -EPERM;			goto __kctl_end;		}		err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); 		if (err > 0) {			up_read(&card->controls_rwsem);			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);			return 0;		}	} else {		if (op_flag) {			err = -ENXIO;			goto __kctl_end;		}		len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);		if (tlv.length < len) {			err = -ENOMEM;			goto __kctl_end;		}		if (copy_to_user(_tlv->tlv, kctl->tlv.p, len))			err = -EFAULT;	}      __kctl_end:	up_read(&card->controls_rwsem);	return err;}static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg){	struct snd_ctl_file *ctl;	struct snd_card *card;	struct snd_kctl_ioctl *p;	void __user *argp = (void __user *)arg;	int __user *ip = argp;	int err;	ctl = file->private_data;	card = ctl->card;	snd_assert(card != NULL, return -ENXIO);	switch (cmd) {	case SNDRV_CTL_IOCTL_PVERSION:		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;	case SNDRV_CTL_IOCTL_CARD_INFO:		return snd_ctl_card_info(card, ctl, cmd, argp);	case SNDRV_CTL_IOCTL_ELEM_LIST:		return snd_ctl_elem_list(card, argp);	case SNDRV_CTL_IOCTL_ELEM_INFO:		return snd_ctl_elem_info_user(ctl, argp);	case SNDRV_CTL_IOCTL_ELEM_READ:		return snd_ctl_elem_read_user(card, argp);	case SNDRV_CTL_IOCTL_ELEM_WRITE:		return snd_ctl_elem_write_user(ctl, argp);	case SNDRV_CTL_IOCTL_ELEM_LOCK:		return snd_ctl_elem_lock(ctl, argp);	case SNDRV_CTL_IOCTL_ELEM_UNLOCK:		return snd_ctl_elem_unlock(ctl, argp);	case SNDRV_CTL_IOCTL_ELEM_ADD:		return snd_ctl_elem_add_user(ctl, argp, 0);	case SNDRV_CTL_IOCTL_ELEM_REPLACE:		return snd_ctl_elem_add_user(ctl, argp, 1);	case SNDRV_CTL_IOCTL_ELEM_REMOVE:		return snd_ctl_elem_remove(ctl, argp);	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:		return snd_ctl_subscribe_events(ctl, ip);	case SNDRV_CTL_IOCTL_TLV_READ:		return snd_ctl_tlv_ioctl(ctl, argp, 0);	case SNDRV_CTL_IOCTL_TLV_WRITE:		return snd_ctl_tlv_ioctl(ctl, argp, 1);	case SNDRV_CTL_IOCTL_TLV_COMMAND:		return snd_ctl_tlv_ioctl(ctl, argp, -1);	case SNDRV_CTL_IOCTL_POWER:		return -ENOPROTOOPT;	case SNDRV_CTL_IOCTL_POWER_STATE:#ifdef CONFIG_PM		return put_user(card->power_state, ip) ? -EFAULT : 0;#else		return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;#endif	}	down_read(&snd_ioctl_rwsem);	list_for_each_entry(p, &snd_control_ioctls, list) {		err = p->fioctl(card, ctl, cmd, arg);		if (err != -ENOIOCTLCMD) {			up_read(&snd_ioctl_rwsem);			return err;		}	}	up_read(&snd_ioctl_rwsem);	snd_printdd("unknown ioctl = 0x%x\n", cmd);	return -ENOTTY;}static ssize_t snd_ctl_read(struct file *file, char __user *buffer,			    size_t count, loff_t * offset){	struct snd_ctl_file *ctl;	int err = 0;	ssize_t result = 0;	ctl = file->private_data;	snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO);	if (!ctl->subscribed)		return -EBADFD;	if (count < sizeof(struct snd_ctl_event))		return -EINVAL;	spin_lock_irq(&ctl->read_lock);	while (count >= sizeof(struct snd_ctl_event)) {		struct snd_ctl_event ev;		struct snd_kctl_event *kev;		while (list_empty(&ctl->events)) {			wait_queue_t wait;			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {				err = -EAGAIN;				goto __end_lock;			}			init_waitqueue_entry(&wait, current);			add_wait_queue(&ctl->change_sleep, &wait);			set_current_state(TASK_INTERRUPTIBLE);			spin_unlock_irq(&ctl->read_lock);			schedule();			remove_wait_queue(&ctl->change_sleep, &wait);			if (signal_pending(current))				return -ERESTARTSYS;			spin_lock_irq(&ctl->read_lock);		}		kev = snd_kctl_event(ctl->events.next);		ev.type = SNDRV_CTL_EVENT_ELEM;		ev.data.elem.mask = kev->mask;		ev.data.elem.id = kev->id;		list_del(&kev->list);		spin_unlock_irq(&ctl->read_lock);		kfree(kev);		if (copy_to_user(buffer, &ev, sizeof(struct snd_ctl_event))) {			err = -EFAULT;			goto __end;		}		spin_lock_irq(&ctl->read_lock);		buffer += sizeof(struct snd_ctl_event);		count -= sizeof(struct snd_ctl_event);		result += sizeof(struct snd_ctl_event);	}      __end_lock:	spin_unlock_irq(&ctl->read_lock);      __end:      	return result > 0 ? result : err;}static unsigned int snd_ctl_poll(struct file *file, poll_table * wait){	unsigned int mask;	struct snd_ctl_file *ctl;	ctl = file->private_data;	if (!ctl->subscribed)		return 0;	poll_wait(file, &ctl->change_sleep, wait);	mask = 0;	if (!list_empty(&ctl->events))		mask |= POLLIN | POLLRDNORM;	return mask;}/* * register the device-specific control-ioctls. * called from each device manager like pcm.c, hwdep.c, etc. */static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists){	struct snd_kctl_ioctl *pn;	pn = kzalloc(sizeof(struct snd_kctl_ioctl), GFP_KERNEL);	if (pn == NULL)		return -ENOMEM;	pn->fioctl = fcn;	down_write(&snd_ioctl_rwsem);	list_add_tail(&pn->list, lists);	up_write(&snd_ioctl_rwsem);	return 0;}int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn){	return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);}EXPORT_SYMBOL(snd_ctl_register_ioctl);#ifdef CONFIG_COMPATint snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn){	return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);}EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);#endif/* * de-register the device-specific control-ioctls. */static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,				     struct list_head *lists){	struct snd_kctl_ioctl *p;	snd_assert(fcn != NULL, return -EINVAL);	down_write(&snd_ioctl_rwsem);	list_for_each_entry(p, lists, list) {		if (p->fioctl == fcn) {			list_del(&p->list);			up_write(&snd_ioctl_rwsem);			kfree(p);			return 0;		}	}	up_write(&snd_ioctl_rwsem);	snd_BUG();	return -EINVAL;}int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn){	return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);}EXPORT_SYMBOL(snd_ctl_unregister_ioctl);#ifdef CONFIG_COMPATint snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn){	return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);}EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);#endifstatic int snd_ctl_fasync(int fd, struct file * file, int on){	struct snd_ctl_file *ctl;	int err;	ctl = file->private_data;	err = fasync_helper(fd, file, on, &ctl->fasync);	if (err < 0)		return err;	return 0;}/* * ioctl32 compat */#ifdef CONFIG_COMPAT#include "control_compat.c"#else#define snd_ctl_ioctl_compat	NULL#endif/* *  INIT PART */static const struct file_operations snd_ctl_f_ops ={	.owner =	THIS_MODULE,	.read =		snd_ctl_read,	.open =		snd_ctl_open,	.release =	snd_ctl_release,	.poll =		snd_ctl_poll,	.unlocked_ioctl =	snd_ctl_ioctl,	.compat_ioctl =	snd_ctl_ioctl_compat,	.fasync =	snd_ctl_fasync,};/* * registration of the control device */static int snd_ctl_dev_register(struct snd_device *device){	struct snd_card *card = device->device_data;	int err, cardnum;	char name[16];	snd_assert(card != NULL, return -ENXIO);	cardnum = card->number;	snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);	sprintf(name, "controlC%i", cardnum);	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,				       &snd_ctl_f_ops, card, name)) < 0)		return err;	return 0;}/* * disconnection of the control device */static int snd_ctl_dev_disconnect(struct snd_device *device){	struct snd_card *card = device->device_data;	struct snd_ctl_file *ctl;	int err, cardnum;	snd_assert(card != NULL, return -ENXIO);	cardnum = card->number;	snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);	down_read(&card->controls_rwsem);	list_for_each_entry(ctl, &card->ctl_files, list) {		wake_up(&ctl->change_sleep);		kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);	}	up_read(&card->controls_rwsem);	if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,					 card, -1)) < 0)		return err;	return 0;}/* * free all controls */static int snd_ctl_dev_free(struct snd_device *device){	struct snd_card *card = device->device_data;	struct snd_kcontrol *control;	down_write(&card->controls_rwsem);	while (!list_empty(&card->controls)) {		control = snd_kcontrol(card->controls.next);		snd_ctl_remove(card, control);	}	up_write(&card->controls_rwsem);	return 0;}/* * create control core: * called from init.c */int snd_ctl_create(struct snd_card *card){	static struct snd_device_ops ops = {		.dev_free = snd_ctl_dev_free,		.dev_register =	snd_ctl_dev_register,		.dev_disconnect = snd_ctl_dev_disconnect,	};	snd_assert(card != NULL, return -ENXIO);	return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);}/* * Frequently used control callbacks */int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,			      struct snd_ctl_elem_info *uinfo){	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;	uinfo->count = 1;	uinfo->value.integer.min = 0;	uinfo->value.integer.max = 1;	return 0;}EXPORT_SYMBOL(snd_ctl_boolean_mono_info);int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,				struct snd_ctl_elem_info *uinfo){	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;	uinfo->count = 2;	uinfo->value.integer.min = 0;	uinfo->value.integer.max = 1;	return 0;}EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -