mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-04-13 09:54:04 +00:00
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: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
30d8340b58
commit
450296f305
1 changed files with 99 additions and 49 deletions
|
@ -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[] = {
|
||||||
|
{SNDRV_CTL_TLV_OP_READ, SNDRV_CTL_ELEM_ACCESS_TLV_READ},
|
||||||
|
{SNDRV_CTL_TLV_OP_WRITE, SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
|
||||||
|
{SNDRV_CTL_TLV_OP_CMD, SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
|
||||||
|
};
|
||||||
|
struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
|
||||||
|
int i;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
if (kctl->tlv.c == NULL)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
/* When locked, this is unavailable. */
|
||||||
|
if (vd->owner != NULL && vd->owner != file)
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
err = kctl->tlv.c(kctl, op_flag, size, buf);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (err > 0)
|
||||||
|
snd_ctl_notify(file->card, SNDRV_CTL_EVENT_MASK_TLV, id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
unsigned int len;
|
||||||
|
|
||||||
if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
|
if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ))
|
||||||
return -EFAULT;
|
return -ENXIO;
|
||||||
if (tlv.length < sizeof(unsigned int) * 2)
|
|
||||||
return -EINVAL;
|
|
||||||
if (!tlv.numid)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
kctl = snd_ctl_find_numid(card, tlv.numid);
|
|
||||||
if (kctl == NULL)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
if (kctl->tlv.p == NULL)
|
if (kctl->tlv.p == NULL)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
vd = &kctl->vd[tlv.numid - kctl->id.numid];
|
len = sizeof(unsigned int) * 2 + kctl->tlv.p[1];
|
||||||
if ((op_flag == SNDRV_CTL_TLV_OP_READ &&
|
if (size < len)
|
||||||
(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;
|
|
||||||
|
|
||||||
if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (vd->owner != NULL && vd->owner != file)
|
|
||||||
return -EPERM;
|
|
||||||
|
|
||||||
err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
if (err > 0) {
|
|
||||||
struct snd_ctl_elem_id id = kctl->id;
|
|
||||||
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &id);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (op_flag != SNDRV_CTL_TLV_OP_READ)
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);
|
|
||||||
if (tlv.length < 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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue