📄 control.c
字号:
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;}/** * 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). */snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid){ struct list_head *list; snd_kcontrol_t *kctl; snd_runtime_check(card != NULL && numid != 0, return NULL); list_for_each(list, &card->controls) { kctl = snd_kcontrol(list); if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid) return kctl; } return NULL;}/** * snd_ctl_find_id - find the control instance with the given id * @card: the card instance * @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). */snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id){ struct list_head *list; snd_kcontrol_t *kctl; snd_runtime_check(card != NULL && id != NULL, return NULL); if (id->numid != 0) return snd_ctl_find_numid(card, id->numid); list_for_each(list, &card->controls) { kctl = snd_kcontrol(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;}static int snd_ctl_card_info(snd_card_t * card, snd_ctl_file_t * ctl, unsigned int cmd, void __user *arg){ snd_ctl_card_info_t info; memset(&info, 0, sizeof(info)); 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(snd_ctl_card_info_t))) return -EFAULT; return 0;}static int snd_ctl_elem_list(snd_card_t *card, snd_ctl_elem_list_t __user *_list){ struct list_head *plist; snd_ctl_elem_list_t list; snd_kcontrol_t *kctl; snd_ctl_elem_id_t *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(snd_ctl_elem_id_t)); 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(snd_ctl_elem_id_t))) { 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(snd_ctl_file_t *ctl, snd_ctl_elem_info_t __user *_info){ snd_card_t *card = ctl->card; snd_ctl_elem_info_t info; snd_kcontrol_t *kctl; snd_kcontrol_volatile_t *vd; unsigned int index_offset; int result; if (copy_from_user(&info, _info, sizeof(info))) return -EFAULT; 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); if (result >= 0) if (copy_to_user(_info, &info, sizeof(info))) return -EFAULT; return result;}static int snd_ctl_elem_read(snd_card_t *card, snd_ctl_elem_value_t __user *_control){ snd_ctl_elem_value_t *control; snd_kcontrol_t *kctl; snd_kcontrol_volatile_t *vd; unsigned int index_offset; int result, indirect; control = kmalloc(sizeof(*control), GFP_KERNEL); if (control == NULL) return -ENOMEM; if (copy_from_user(control, _control, sizeof(*control))) return -EFAULT; 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); if (result >= 0) if (copy_to_user(_control, control, sizeof(*control))) return -EFAULT; kfree(control); return result;}static int snd_ctl_elem_write(snd_ctl_file_t *file, snd_ctl_elem_value_t __user *_control){ snd_card_t *card = file->card; snd_ctl_elem_value_t *control; snd_kcontrol_t *kctl; snd_kcontrol_volatile_t *vd; unsigned int index_offset; int result, indirect; control = kmalloc(sizeof(*control), GFP_KERNEL); if (control == NULL) return -ENOMEM; if (copy_from_user(control, _control, sizeof(*control))) return -EFAULT; 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 || (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); result = 0; goto __unlocked; } } } up_read(&card->controls_rwsem); __unlocked: if (result >= 0) if (copy_to_user(_control, control, sizeof(*control))) return -EFAULT; kfree(control); return result;}static int snd_ctl_elem_lock(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id){ snd_card_t *card = file->card; snd_ctl_elem_id_t id; snd_kcontrol_t *kctl; snd_kcontrol_volatile_t *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(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id){ snd_card_t *card = file->card; snd_ctl_elem_id_t id; snd_kcontrol_t *kctl; snd_kcontrol_volatile_t *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 { enum sndrv_ctl_elem_type type; /* element type */ unsigned int elem_count; /* count of elements */ union { struct { unsigned int items; } enumerated; } u; void *elem_data; /* element data */ unsigned long elem_data_size; /* size of element data in bytes */ void *priv_data; /* private data (like strings for enumerated type) */ unsigned long priv_data_size; /* size of private data in bytes */ unsigned short dimen_count; /* count of dimensions */ unsigned short dimen[0]; /* array of dimensions */};static int snd_ctl_elem_user_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ struct user_element *ue = kcontrol->private_data; uinfo->type = ue->type; uinfo->count = ue->elem_count; if (ue->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { uinfo->value.enumerated.items = ue->u.enumerated.items; if (uinfo->value.enumerated.item >= ue->u.enumerated.items) uinfo->value.enumerated.item = 0; strlcpy(uinfo->value.enumerated.name, (char *)ue->priv_data + uinfo->value.enumerated.item * 64, 64); } return 0;}static int snd_ctl_elem_user_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * 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(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ int change; struct user_element *ue = kcontrol->private_data; change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size); memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); return !!change;}static void snd_ctl_elem_user_free(snd_kcontrol_t * kcontrol){ kfree(kcontrol->private_data);}static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_info, int replace){ snd_card_t *card = file->card; snd_ctl_elem_info_t info; snd_kcontrol_t kctl, *_kctl; unsigned int access; long private_size, dimen_size, extra_size; struct user_element *ue; int idx, err; if (copy_from_user(&info, _info, sizeof(info))) return -EFAULT; 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_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); if (access & (SNDRV_CTL_ELEM_ACCESS_DINDIRECT | SNDRV_CTL_ELEM_ACCESS_INDIRECT)) return -EINVAL; info.id.numid = 0; memset(&kctl, 0, sizeof(kctl)); down_write(&card->controls_rwsem); if (!!((_kctl = snd_ctl_find_id(card, &info.id)) != NULL) ^ replace) { up_write(&card->controls_rwsem); return !replace ? -EBUSY : -ENOENT; } if (replace) { err = snd_ctl_remove(card, _kctl); if (err < 0) { up_write(&card->controls_rwsem); return err; } } up_write(&card->controls_rwsem); memcpy(&kctl.id, &info.id, sizeof(info.id));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -