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