Commit 450296f3 authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

ALSA: control: code refactoring TLV ioctl handler

In a design of ALSA control core, execution path bifurcates depending on
target element. When a set with the target element has a handler, it's
called. Else, registered buffer is copied to user space. These two
operations are apparently different.  In current implementation, they're
on the same function with a condition statement. This makes it a bit hard
to understand conditions of each case.

This commit splits codes for these two cases.
Signed-off-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 30d8340b
...@@ -1394,67 +1394,117 @@ static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr) ...@@ -1394,67 +1394,117 @@ static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
return 0; return 0;
} }
static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
struct snd_ctl_tlv __user *_tlv, struct snd_kcontrol *kctl,
int op_flag) struct snd_ctl_elem_id *id,
unsigned int __user *buf, unsigned int size)
{ {
struct snd_card *card = file->card; static const struct {
struct snd_ctl_tlv tlv; int op;
struct snd_kcontrol *kctl; int perm;
struct snd_kcontrol_volatile *vd; } pairs[] = {
unsigned int len; {SNDRV_CTL_TLV_OP_READ, SNDRV_CTL_ELEM_ACCESS_TLV_READ},
{SNDRV_CTL_TLV_OP_WRITE, SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
if (copy_from_user(&tlv, _tlv, sizeof(tlv))) {SNDRV_CTL_TLV_OP_CMD, SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
return -EFAULT; };
if (tlv.length < sizeof(unsigned int) * 2) struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
return -EINVAL; int i;
if (!tlv.numid) int err;
return -EINVAL;
kctl = snd_ctl_find_numid(card, tlv.numid);
if (kctl == NULL)
return -ENOENT;
if (kctl->tlv.p == NULL) /* Check support of the request for this element. */
for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
if (op_flag == pairs[i].op && (vd->access & pairs[i].perm))
break;
}
if (i == ARRAY_SIZE(pairs))
return -ENXIO; return -ENXIO;
vd = &kctl->vd[tlv.numid - kctl->id.numid]; if (kctl->tlv.c == NULL)
if ((op_flag == SNDRV_CTL_TLV_OP_READ &&
(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
(op_flag == SNDRV_CTL_TLV_OP_WRITE &&
(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
(op_flag == SNDRV_CTL_TLV_OP_CMD &&
(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0))
return -ENXIO; return -ENXIO;
if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { /* When locked, this is unavailable. */
int err;
if (vd->owner != NULL && vd->owner != file) if (vd->owner != NULL && vd->owner != file)
return -EPERM; return -EPERM;
err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); err = kctl->tlv.c(kctl, op_flag, size, buf);
if (err < 0) if (err < 0)
return err; return err;
if (err > 0) {
struct snd_ctl_elem_id id = kctl->id; if (err > 0)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &id); snd_ctl_notify(file->card, SNDRV_CTL_EVENT_MASK_TLV, id);
}
} else { return 0;
if (op_flag != SNDRV_CTL_TLV_OP_READ) }
static int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id,
unsigned int __user *buf, unsigned int size)
{
struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
unsigned int len;
if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ))
return -ENXIO; return -ENXIO;
len = kctl->tlv.p[1] + 2 * sizeof(unsigned int); if (kctl->tlv.p == NULL)
if (tlv.length < len) return -ENXIO;
len = sizeof(unsigned int) * 2 + kctl->tlv.p[1];
if (size < len)
return -ENOMEM; return -ENOMEM;
if (copy_to_user(_tlv->tlv, kctl->tlv.p, len)) if (copy_to_user(buf, kctl->tlv.p, len))
return -EFAULT; return -EFAULT;
}
return 0; return 0;
} }
static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
struct snd_ctl_tlv __user *buf,
int op_flag)
{
struct snd_ctl_tlv header;
unsigned int *container;
unsigned int container_size;
struct snd_kcontrol *kctl;
struct snd_ctl_elem_id id;
struct snd_kcontrol_volatile *vd;
if (copy_from_user(&header, buf, sizeof(header)))
return -EFAULT;
/* In design of control core, numerical ID starts at 1. */
if (header.numid == 0)
return -EINVAL;
/* At least, container should include type and length fields. */
if (header.length < sizeof(unsigned int) * 2)
return -EINVAL;
container_size = header.length;
container = buf->tlv;
kctl = snd_ctl_find_numid(file->card, header.numid);
if (kctl == NULL)
return -ENOENT;
/* Calculate index of the element in this set. */
id = kctl->id;
snd_ctl_build_ioff(&id, kctl, header.numid - id.numid);
vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
return call_tlv_handler(file, op_flag, kctl, &id, container,
container_size);
} else {
if (op_flag == SNDRV_CTL_TLV_OP_READ) {
return read_tlv_buf(kctl, &id, container,
container_size);
}
}
/* Not supported. */
return -ENXIO;
}
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{ {
struct snd_ctl_file *ctl; struct snd_ctl_file *ctl;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment