diff options
-rw-r--r-- | sound/core/control.c | 76 |
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) |