summaryrefslogtreecommitdiffstats
path: root/sound/core
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/control.c76
1 files changed, 46 insertions, 30 deletions
diff --git a/sound/core/control.c b/sound/core/control.c
index 92389000f0df..9e7a4571488b 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1114,43 +1114,59 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
return change;
}
-static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
- int op_flag,
- unsigned int size,
- unsigned int __user *tlv)
+static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
+ unsigned int size)
{
- struct user_element *ue = kcontrol->private_data;
+ struct user_element *ue = kctl->private_data;
+ unsigned int *container;
+ int change;
- if (op_flag == SNDRV_CTL_TLV_OP_WRITE) {
- int change;
- void *new_data;
+ if (size > 1024 * 128) /* sane value */
+ return -EINVAL;
- if (size > 1024 * 128) /* sane value */
- return -EINVAL;
+ container = memdup_user(buf, size);
+ if (IS_ERR(container))
+ return PTR_ERR(container);
- new_data = memdup_user(tlv, size);
- if (IS_ERR(new_data))
- return PTR_ERR(new_data);
- change = ue->tlv_data_size != size;
- if (!change)
- change = memcmp(ue->tlv_data, new_data, size);
- kfree(ue->tlv_data);
- ue->tlv_data = new_data;
- ue->tlv_data_size = size;
-
- return change;
- } else {
- if (!ue->tlv_data_size || !ue->tlv_data)
- return -ENXIO;
+ change = ue->tlv_data_size != size;
+ if (!change)
+ change = memcmp(ue->tlv_data, container, size);
+ if (!change) {
+ kfree(container);
+ return 0;
+ }
- if (size < ue->tlv_data_size)
- return -ENOSPC;
+ kfree(ue->tlv_data);
+ ue->tlv_data = container;
+ ue->tlv_data_size = size;
- if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
- return -EFAULT;
+ return change;
+}
- return 0;
- }
+static int read_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
+ unsigned int size)
+{
+ struct user_element *ue = kctl->private_data;
+
+ if (ue->tlv_data_size == 0 || ue->tlv_data == NULL)
+ return -ENXIO;
+
+ if (size < ue->tlv_data_size)
+ return -ENOSPC;
+
+ if (copy_to_user(buf, ue->tlv_data, ue->tlv_data_size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kctl, int op_flag,
+ unsigned int size, unsigned int __user *buf)
+{
+ if (op_flag == SNDRV_CTL_TLV_OP_WRITE)
+ return replace_user_tlv(kctl, buf, size);
+ else
+ return read_user_tlv(kctl, buf, size);
}
static int snd_ctl_elem_init_enum_names(struct user_element *ue)