📄 control.c
字号:
* @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 + -