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

📄 control.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
 * @id: the id to search * * Finds the control instance with the given 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_id(struct snd_card *card,				     struct snd_ctl_elem_id *id){	struct snd_kcontrol *kctl;	snd_assert(card != NULL && id != NULL, return NULL);	if (id->numid != 0)		return snd_ctl_find_numid(card, id->numid);	list_for_each_entry(kctl, &card->controls, list) {		if (kctl->id.iface != id->iface)			continue;		if (kctl->id.device != id->device)			continue;		if (kctl->id.subdevice != id->subdevice)			continue;		if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))			continue;		if (kctl->id.index > id->index)			continue;		if (kctl->id.index + kctl->count <= id->index)			continue;		return kctl;	}	return NULL;}EXPORT_SYMBOL(snd_ctl_find_id);static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,			     unsigned int cmd, void __user *arg){	struct snd_ctl_card_info *info;	info = kzalloc(sizeof(*info), GFP_KERNEL);	if (! info)		return -ENOMEM;	down_read(&snd_ioctl_rwsem);	info->card = card->number;	strlcpy(info->id, card->id, sizeof(info->id));	strlcpy(info->driver, card->driver, sizeof(info->driver));	strlcpy(info->name, card->shortname, sizeof(info->name));	strlcpy(info->longname, card->longname, sizeof(info->longname));	strlcpy(info->mixername, card->mixername, sizeof(info->mixername));	strlcpy(info->components, card->components, sizeof(info->components));	up_read(&snd_ioctl_rwsem);	if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {		kfree(info);		return -EFAULT;	}	kfree(info);	return 0;}static int snd_ctl_elem_list(struct snd_card *card,			     struct snd_ctl_elem_list __user *_list){	struct list_head *plist;	struct snd_ctl_elem_list list;	struct snd_kcontrol *kctl;	struct snd_ctl_elem_id *dst, *id;	unsigned int offset, space, first, jidx;		if (copy_from_user(&list, _list, sizeof(list)))		return -EFAULT;	offset = list.offset;	space = list.space;	first = 0;	/* try limit maximum space */	if (space > 16384)		return -ENOMEM;	if (space > 0) {		/* allocate temporary buffer for atomic operation */		dst = vmalloc(space * sizeof(struct snd_ctl_elem_id));		if (dst == NULL)			return -ENOMEM;		down_read(&card->controls_rwsem);		list.count = card->controls_count;		plist = card->controls.next;		while (plist != &card->controls) {			if (offset == 0)				break;			kctl = snd_kcontrol(plist);			if (offset < kctl->count)				break;			offset -= kctl->count;			plist = plist->next;		}		list.used = 0;		id = dst;		while (space > 0 && plist != &card->controls) {			kctl = snd_kcontrol(plist);			for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) {				snd_ctl_build_ioff(id, kctl, jidx);				id++;				space--;				list.used++;			}			plist = plist->next;			offset = 0;		}		up_read(&card->controls_rwsem);		if (list.used > 0 &&		    copy_to_user(list.pids, dst,				 list.used * sizeof(struct snd_ctl_elem_id))) {			vfree(dst);			return -EFAULT;		}		vfree(dst);	} else {		down_read(&card->controls_rwsem);		list.count = card->controls_count;		up_read(&card->controls_rwsem);	}	if (copy_to_user(_list, &list, sizeof(list)))		return -EFAULT;	return 0;}static int snd_ctl_elem_info(struct snd_ctl_file *ctl,			     struct snd_ctl_elem_info *info){	struct snd_card *card = ctl->card;	struct snd_kcontrol *kctl;	struct snd_kcontrol_volatile *vd;	unsigned int index_offset;	int result;		down_read(&card->controls_rwsem);	kctl = snd_ctl_find_id(card, &info->id);	if (kctl == NULL) {		up_read(&card->controls_rwsem);		return -ENOENT;	}#ifdef CONFIG_SND_DEBUG	info->access = 0;#endif	result = kctl->info(kctl, info);	if (result >= 0) {		snd_assert(info->access == 0, );		index_offset = snd_ctl_get_ioff(kctl, &info->id);		vd = &kctl->vd[index_offset];		snd_ctl_build_ioff(&info->id, kctl, index_offset);		info->access = vd->access;		if (vd->owner) {			info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;			if (vd->owner == ctl)				info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;			info->owner = vd->owner_pid;		} else {			info->owner = -1;		}	}	up_read(&card->controls_rwsem);	return result;}static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,				  struct snd_ctl_elem_info __user *_info){	struct snd_ctl_elem_info info;	int result;	if (copy_from_user(&info, _info, sizeof(info)))		return -EFAULT;	snd_power_lock(ctl->card);	result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);	if (result >= 0)		result = snd_ctl_elem_info(ctl, &info);	snd_power_unlock(ctl->card);	if (result >= 0)		if (copy_to_user(_info, &info, sizeof(info)))			return -EFAULT;	return result;}int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control){	struct snd_kcontrol *kctl;	struct snd_kcontrol_volatile *vd;	unsigned int index_offset;	int result, indirect;	down_read(&card->controls_rwsem);	kctl = snd_ctl_find_id(card, &control->id);	if (kctl == NULL) {		result = -ENOENT;	} else {		index_offset = snd_ctl_get_ioff(kctl, &control->id);		vd = &kctl->vd[index_offset];		indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;		if (control->indirect != indirect) {			result = -EACCES;		} else {			if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) {				snd_ctl_build_ioff(&control->id, kctl, index_offset);				result = kctl->get(kctl, control);			} else {				result = -EPERM;			}		}	}	up_read(&card->controls_rwsem);	return result;}static int snd_ctl_elem_read_user(struct snd_card *card,				  struct snd_ctl_elem_value __user *_control){	struct snd_ctl_elem_value *control;	int result;		control = kmalloc(sizeof(*control), GFP_KERNEL);	if (control == NULL)		return -ENOMEM;		if (copy_from_user(control, _control, sizeof(*control))) {		kfree(control);		return -EFAULT;	}	snd_power_lock(card);	result = snd_power_wait(card, SNDRV_CTL_POWER_D0);	if (result >= 0)		result = snd_ctl_elem_read(card, control);	snd_power_unlock(card);	if (result >= 0)		if (copy_to_user(_control, control, sizeof(*control)))			result = -EFAULT;	kfree(control);	return result;}int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,		       struct snd_ctl_elem_value *control){	struct snd_kcontrol *kctl;	struct snd_kcontrol_volatile *vd;	unsigned int index_offset;	int result, indirect;	down_read(&card->controls_rwsem);	kctl = snd_ctl_find_id(card, &control->id);	if (kctl == NULL) {		result = -ENOENT;	} else {		index_offset = snd_ctl_get_ioff(kctl, &control->id);		vd = &kctl->vd[index_offset];		indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;		if (control->indirect != indirect) {			result = -EACCES;		} else {			if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||			    kctl->put == NULL ||			    (file && vd->owner != NULL && vd->owner != file)) {				result = -EPERM;			} else {				snd_ctl_build_ioff(&control->id, kctl, index_offset);				result = kctl->put(kctl, control);			}			if (result > 0) {				up_read(&card->controls_rwsem);				snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &control->id);				return 0;			}		}	}	up_read(&card->controls_rwsem);	return result;}static int snd_ctl_elem_write_user(struct snd_ctl_file *file,				   struct snd_ctl_elem_value __user *_control){	struct snd_ctl_elem_value *control;	struct snd_card *card;	int result;	control = kmalloc(sizeof(*control), GFP_KERNEL);	if (control == NULL)		return -ENOMEM;		if (copy_from_user(control, _control, sizeof(*control))) {		kfree(control);		return -EFAULT;	}	card = file->card;	snd_power_lock(card);	result = snd_power_wait(card, SNDRV_CTL_POWER_D0);	if (result >= 0)		result = snd_ctl_elem_write(card, file, control);	snd_power_unlock(card);	if (result >= 0)		if (copy_to_user(_control, control, sizeof(*control)))			result = -EFAULT;	kfree(control);	return result;}static int snd_ctl_elem_lock(struct snd_ctl_file *file,			     struct snd_ctl_elem_id __user *_id){	struct snd_card *card = file->card;	struct snd_ctl_elem_id id;	struct snd_kcontrol *kctl;	struct snd_kcontrol_volatile *vd;	int result;		if (copy_from_user(&id, _id, sizeof(id)))		return -EFAULT;	down_write(&card->controls_rwsem);	kctl = snd_ctl_find_id(card, &id);	if (kctl == NULL) {		result = -ENOENT;	} else {		vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];		if (vd->owner != NULL)			result = -EBUSY;		else {			vd->owner = file;			vd->owner_pid = current->pid;			result = 0;		}	}	up_write(&card->controls_rwsem);	return result;}static int snd_ctl_elem_unlock(struct snd_ctl_file *file,			       struct snd_ctl_elem_id __user *_id){	struct snd_card *card = file->card;	struct snd_ctl_elem_id id;	struct snd_kcontrol *kctl;	struct snd_kcontrol_volatile *vd;	int result;		if (copy_from_user(&id, _id, sizeof(id)))		return -EFAULT;	down_write(&card->controls_rwsem);	kctl = snd_ctl_find_id(card, &id);	if (kctl == NULL) {		result = -ENOENT;	} else {		vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];		if (vd->owner == NULL)			result = -EINVAL;		else if (vd->owner != file)			result = -EPERM;		else {			vd->owner = NULL;			vd->owner_pid = 0;			result = 0;		}	}	up_write(&card->controls_rwsem);	return result;}struct user_element {	struct snd_ctl_elem_info info;	void *elem_data;		/* element data */	unsigned long elem_data_size;	/* size of element data in bytes */	void *tlv_data;			/* TLV data */	unsigned long tlv_data_size;	/* TLV data size */	void *priv_data;		/* private data (like strings for enumerated type) */	unsigned long priv_data_size;	/* size of private data in bytes */};static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,				  struct snd_ctl_elem_info *uinfo){	struct user_element *ue = kcontrol->private_data;	*uinfo = ue->info;	return 0;}static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,				 struct snd_ctl_elem_value *ucontrol){	struct user_element *ue = kcontrol->private_data;	memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size);	return 0;}static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,				 struct snd_ctl_elem_value *ucontrol){	int change;	struct user_element *ue = kcontrol->private_data;		change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;	if (change)		memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);	return change;}static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,				 int op_flag,				 unsigned int size,				 unsigned int __user *tlv){	struct user_element *ue = kcontrol->private_data;	int change = 0;	void *new_data;	if (op_flag > 0) {		if (size > 1024 * 128)	/* sane value */			return -EINVAL;		new_data = kmalloc(size, GFP_KERNEL);		if (new_data == NULL)			return -ENOMEM;		if (copy_from_user(new_data, tlv, size)) {			kfree(new_data);			return -EFAULT;		}		change = ue->tlv_data_size != size;		if (!change)			change = memcmp(ue->tlv_data, new_data, size);		kfree(ue->tlv_data);		ue->tlv_data = new_data;		ue->tlv_data_size = size;	} else {		if (! ue->tlv_data_size || ! ue->tlv_data)			return -ENXIO;		if (size < ue->tlv_data_size)			return -ENOSPC;		if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))			return -EFAULT;	}	return change;}static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol){	struct user_element *ue = kcontrol->private_data;	if (ue->tlv_data)		kfree(ue->tlv_data);	kfree(ue);}static int snd_ctl_elem_add(struct snd_ctl_file *file,			    struct snd_ctl_elem_info *info, int replace){	struct snd_card *card = file->card;	struct snd_kcontrol kctl, *_kctl;	unsigned int access;	long private_size;	struct user_element *ue;	int idx, err;		if (card->user_ctl_count >= MAX_USER_CONTROLS)		return -ENOMEM;	if (info->count > 1024)		return -EINVAL;	access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :		(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|				 SNDRV_CTL_ELEM_ACCESS_INACTIVE|				 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));	info->id.numid = 0;	memset(&kctl, 0, sizeof(kctl));	down_write(&card->controls_rwsem);	_kctl = snd_ctl_find_id(card, &info->id);	err = 0;	if (_kctl) {		if (replace)			err = snd_ctl_remove(card, _kctl);		else			err = -EBUSY;	} else {		if (replace)			err = -ENOENT;	}	up_write(&card->controls_rwsem);	if (err < 0)		return err;	memcpy(&kctl.id, &info->id, sizeof(info->id));	kctl.count = info->owner ? info->owner : 1;	access |= SNDRV_CTL_ELEM_ACCESS_USER;	kctl.info = snd_ctl_elem_user_info;	if (access & SNDRV_CTL_ELEM_ACCESS_READ)		kctl.get = snd_ctl_elem_user_get;	if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)		kctl.put = snd_ctl_elem_user_put;	if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {		kctl.tlv.c = snd_ctl_elem_user_tlv;		access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;	}	switch (info->type) {	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:	case SNDRV_CTL_ELEM_TYPE_INTEGER:		private_size = sizeof(long);		if (info->count > 128)			return -EINVAL;		break;	case SNDRV_CTL_ELEM_TYPE_INTEGER64:		private_size = sizeof(long long);		if (info->count > 64)

⌨️ 快捷键说明

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