📄 control.c
字号:
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; extra_size = 0; switch (info.type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: private_size = sizeof(char); if (info.count > 128) return -EINVAL; break; 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) return -EINVAL; break; case SNDRV_CTL_ELEM_TYPE_ENUMERATED: private_size = sizeof(unsigned int); if (info.count > 128) return -EINVAL; if (info.value.enumerated.items > 1024) return -EINVAL; extra_size = info.value.enumerated.items * 64; 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 sndrv_aes_iec958); if (info.count != 1) return -EINVAL; break; default: return -EINVAL; } private_size *= info.count; if (private_size > 1024 * 1024) return -EINVAL; dimen_size = 0; if (!(info.access & SNDRV_CTL_ELEM_ACCESS_DINDIRECT)) for (idx = 0; idx < 4 && info.dimen.d[idx]; idx++) dimen_size += sizeof(unsigned short); ue = kcalloc(1, sizeof(struct user_element) + dimen_size + private_size + extra_size, GFP_KERNEL); if (ue == NULL) return -ENOMEM; ue->type = info.type; ue->elem_count = info.count; if (!(info.access & SNDRV_CTL_ELEM_ACCESS_DINDIRECT)) { for (idx = 0; idx < 4 && info.dimen.d[idx]; idx++) ue->dimen[idx] = info.dimen.d[idx]; ue->dimen_count = dimen_size / sizeof(unsigned short); } ue->elem_data = (char *)ue + sizeof(ue) + dimen_size; ue->elem_data_size = private_size; if (extra_size) { ue->priv_data = (char *)ue + sizeof(ue) + dimen_size + private_size; ue->priv_data_size = extra_size; if (ue->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { if (copy_from_user(ue->priv_data, *(char **)info.value.enumerated.name, extra_size)) return -EFAULT; ue->u.enumerated.items = info.value.enumerated.items; } } kctl.private_free = snd_ctl_elem_user_free; _kctl = snd_ctl_new(&kctl, access); if (_kctl == NULL) { kfree(_kctl->private_data); 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) { snd_ctl_free_one(_kctl); return err; } return 0;}static int snd_ctl_elem_remove(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id){ snd_ctl_elem_id_t id; if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT; return snd_ctl_remove_unlocked_id(file, &id);}static int snd_ctl_subscribe_events(snd_ctl_file_t *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;}#ifdef CONFIG_PM/* * change the power state */static int snd_ctl_set_power_state(snd_card_t *card, unsigned int power_state){ switch (power_state) { case SNDRV_CTL_POWER_D0: case SNDRV_CTL_POWER_D1: case SNDRV_CTL_POWER_D2: if (card->power_state != power_state) /* FIXME: pass the correct state value */ card->pm_resume(card, 0); break; case SNDRV_CTL_POWER_D3hot: case SNDRV_CTL_POWER_D3cold: if (card->power_state != power_state) /* FIXME: pass the correct state value */ card->pm_suspend(card, 0); break; default: return -EINVAL; } return 0;}#endifstatic inline int _snd_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ snd_ctl_file_t *ctl; snd_card_t *card; struct list_head *list; snd_kctl_ioctl_t *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(ctl->card, argp); case SNDRV_CTL_IOCTL_ELEM_INFO: return snd_ctl_elem_info(ctl, argp); case SNDRV_CTL_IOCTL_ELEM_READ: return snd_ctl_elem_read(ctl->card, argp); case SNDRV_CTL_IOCTL_ELEM_WRITE: return snd_ctl_elem_write(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(ctl, argp, 0); case SNDRV_CTL_IOCTL_ELEM_REPLACE: return snd_ctl_elem_add(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_POWER: if (get_user(err, ip)) return -EFAULT; if (!capable(CAP_SYS_ADMIN)) return -EPERM;#ifdef CONFIG_PM if (card->pm_suspend && card->pm_resume) { snd_power_lock(card); err = snd_ctl_set_power_state(card, err); snd_power_unlock(card); } else#endif err = -ENOPROTOOPT; return err; 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(list, &snd_control_ioctls) { p = list_entry(list, snd_kctl_ioctl_t, list); err = p->fioctl(card, ctl, cmd, arg); if (err != -ENOIOCTLCMD) { up_read(&snd_ioctl_rwsem); return err; } } up_read(&snd_ioctl_rwsem); snd_printd("unknown ioctl = 0x%x\n", cmd); return -ENOTTY;}/* FIXME: need to unlock BKL to allow preemption */static int snd_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int err; unlock_kernel(); err = _snd_ctl_ioctl(inode, file, cmd, arg); lock_kernel(); return err;}static ssize_t snd_ctl_read(struct file *file, char __user *buffer, size_t count, loff_t * offset){ snd_ctl_file_t *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(snd_ctl_event_t)) return -EINVAL; spin_lock_irq(&ctl->read_lock); while (count >= sizeof(snd_ctl_event_t)) { snd_ctl_event_t ev; snd_kctl_event_t *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 result > 0 ? result : -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(snd_ctl_event_t))) { err = -EFAULT; goto __end; } spin_lock_irq(&ctl->read_lock); buffer += sizeof(snd_ctl_event_t); count -= sizeof(snd_ctl_event_t); result += sizeof(snd_ctl_event_t); } __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; snd_ctl_file_t *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. */int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn){ snd_kctl_ioctl_t *pn; pn = kcalloc(1, sizeof(snd_kctl_ioctl_t), GFP_KERNEL); if (pn == NULL) return -ENOMEM; pn->fioctl = fcn; down_write(&snd_ioctl_rwsem); list_add_tail(&pn->list, &snd_control_ioctls); up_write(&snd_ioctl_rwsem); return 0;}/* * de-register the device-specific control-ioctls. */int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn){ struct list_head *list; snd_kctl_ioctl_t *p; snd_runtime_check(fcn != NULL, return -EINVAL); down_write(&snd_ioctl_rwsem); list_for_each(list, &snd_control_ioctls) { p = list_entry(list, snd_kctl_ioctl_t, 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;}static int snd_ctl_fasync(int fd, struct file * file, int on){ snd_ctl_file_t *ctl; int err; ctl = file->private_data; err = fasync_helper(fd, file, on, &ctl->fasync); if (err < 0) return err; return 0;}/* * INIT PART */static 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, .ioctl = snd_ctl_ioctl, .fasync = snd_ctl_fasync,};static snd_minor_t snd_ctl_reg ={ .comment = "ctl", .f_ops = &snd_ctl_f_ops,};/* * registration of the control device: * called from init.c */int snd_ctl_register(snd_card_t *card){ 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, 0, &snd_ctl_reg, name)) < 0) return err; return 0;}/* * disconnection of the control device: * called from init.c */int snd_ctl_disconnect(snd_card_t *card){ struct list_head *flist; snd_ctl_file_t *ctl; down_read(&card->controls_rwsem); list_for_each(flist, &card->ctl_files) { ctl = snd_ctl_file(flist); wake_up(&ctl->change_sleep); kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); } up_read(&card->controls_rwsem); return 0;}/* * de-registration of the control device: * called from init.c */int snd_ctl_unregister(snd_card_t *card){ int err, cardnum; snd_kcontrol_t *control; snd_assert(card != NULL, return -ENXIO); cardnum = card->number; snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, card, 0)) < 0) return err; 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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -