From 4ed56666b7fc98c750a23b5263350b75e742b534 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 10 Mar 2015 22:13:30 +0900 Subject: ALSA: core: use precomputed table to check userspace control params The parameters can be decided in compile time. This commit adds precomputed table to reduce calculating time. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/control.c | 60 ++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 32 deletions(-) (limited to 'sound/core/control.c') diff --git a/sound/core/control.c b/sound/core/control.c index 35324a8e83c8..0b85cbc27e4d 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1161,6 +1161,23 @@ static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) static int snd_ctl_elem_add(struct snd_ctl_file *file, struct snd_ctl_elem_info *info, int replace) { + /* The capacity of struct snd_ctl_elem_value.value.*/ + static const unsigned int value_sizes[] = { + [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long), + [SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long), + [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int), + [SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char), + [SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958), + [SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long), + }; + static const unsigned int max_value_counts[] = { + [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128, + [SNDRV_CTL_ELEM_TYPE_INTEGER] = 128, + [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128, + [SNDRV_CTL_ELEM_TYPE_BYTES] = 512, + [SNDRV_CTL_ELEM_TYPE_IEC958] = 1, + [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64, + }; struct snd_card *card = file->card; struct snd_kcontrol kctl, *_kctl; unsigned int access; @@ -1168,8 +1185,6 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, struct user_element *ue; int idx, err; - if (info->count < 1) - return -EINVAL; access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| SNDRV_CTL_ELEM_ACCESS_INACTIVE| @@ -1201,37 +1216,18 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, kctl.tlv.c = snd_ctl_elem_user_tlv; access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; } - switch (info->type) { - case SNDRV_CTL_ELEM_TYPE_BOOLEAN: - 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 || info->value.enumerated.items == 0) - return -EINVAL; - 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 snd_aes_iec958); - if (info->count != 1) - return -EINVAL; - break; - default: + + if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN || + info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) return -EINVAL; - } - private_size *= info->count; + if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED && + info->value.enumerated.items == 0) + return -EINVAL; + if (info->count < 1 || + info->count > max_value_counts[info->type]) + return -EINVAL; + + private_size = value_sizes[info->type] * info->count; ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); if (ue == NULL) return -ENOMEM; -- cgit v1.2.3 From 2225e79b9b0370bc179f44756bee809b5e7b4d06 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 10 Mar 2015 22:13:31 +0900 Subject: ALSA: core: reduce stack usage related to snd_ctl_new() The callers of snd_ctl_new() need to have 'struct snd_kcontrol' data, and pass the data as template. Then, the function allocates the structure data again and copy from the template. This is a waste of resources. Especially, the callers use large stack for the template. This commit removes a need of template for the function, thus, changes the prototype of snd_ctl_new(). Furthermore, this commit changes the code of callers, snd_ctl_new1() and snd_ctl_elem_add() for better shape. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/control.c | 213 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 83 deletions(-) (limited to 'sound/core/control.c') diff --git a/sound/core/control.c b/sound/core/control.c index 0b85cbc27e4d..e1d8e0c816f0 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -192,36 +192,43 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, EXPORT_SYMBOL(snd_ctl_notify); /** - * snd_ctl_new - create a control instance from the template - * @control: the control template - * @access: the default control access + * snd_ctl_new - create a new control instance with some elements + * @kctl: the pointer to store new control instance + * @count: the number of elements in this control + * @access: the default access flags for elements in this control + * @file: given when locking these elements * - * Allocates a new struct snd_kcontrol instance and copies the given template - * to the new instance. It does not copy volatile data (access). + * Allocates a memory object for a new control instance. The instance has + * elements as many as the given number (@count). Each element has given + * access permissions (@access). Each element is locked when @file is given. * - * Return: The pointer of the new instance, or %NULL on failure. + * Return: 0 on success, error code on failure */ -static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, - unsigned int access) +static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count, + unsigned int access, struct snd_ctl_file *file) { - struct snd_kcontrol *kctl; + unsigned int size; unsigned int idx; - if (snd_BUG_ON(!control || !control->count)) - return NULL; + if (count == 0 || count > MAX_CONTROL_COUNT) + return -EINVAL; - if (control->count > MAX_CONTROL_COUNT) - return NULL; + size = sizeof(struct snd_kcontrol); + size += sizeof(struct snd_kcontrol_volatile) * count; - kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL); - if (kctl == NULL) { + *kctl = kzalloc(size, GFP_KERNEL); + if (*kctl == NULL) { pr_err("ALSA: Cannot allocate control instance\n"); - return NULL; + return -ENOMEM; } - *kctl = *control; - for (idx = 0; idx < kctl->count; idx++) - kctl->vd[idx].access = access; - return kctl; + + for (idx = 0; idx < count; idx++) { + (*kctl)->vd[idx].access = access; + (*kctl)->vd[idx].owner = file; + } + (*kctl)->count = count; + + return 0; } /** @@ -238,37 +245,53 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, void *private_data) { - struct snd_kcontrol kctl; + struct snd_kcontrol *kctl; + unsigned int count; unsigned int access; + int err; if (snd_BUG_ON(!ncontrol || !ncontrol->info)) return NULL; - memset(&kctl, 0, sizeof(kctl)); - kctl.id.iface = ncontrol->iface; - kctl.id.device = ncontrol->device; - kctl.id.subdevice = ncontrol->subdevice; + + count = ncontrol->count; + if (count == 0) + count = 1; + + access = ncontrol->access; + if (access == 0) + access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); + + err = snd_ctl_new(&kctl, count, access, NULL); + if (err < 0) + return NULL; + + /* The 'numid' member is decided when calling snd_ctl_add(). */ + kctl->id.iface = ncontrol->iface; + kctl->id.device = ncontrol->device; + kctl->id.subdevice = ncontrol->subdevice; if (ncontrol->name) { - strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); - if (strcmp(ncontrol->name, kctl.id.name) != 0) + strlcpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name)); + if (strcmp(ncontrol->name, kctl->id.name) != 0) pr_warn("ALSA: Control name '%s' truncated to '%s'\n", - ncontrol->name, kctl.id.name); + ncontrol->name, kctl->id.name); } - kctl.id.index = ncontrol->index; - kctl.count = ncontrol->count ? ncontrol->count : 1; - access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : - (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| - SNDRV_CTL_ELEM_ACCESS_VOLATILE| - SNDRV_CTL_ELEM_ACCESS_INACTIVE| - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| - SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND| - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); - kctl.info = ncontrol->info; - kctl.get = ncontrol->get; - kctl.put = ncontrol->put; - kctl.tlv.p = ncontrol->tlv.p; - kctl.private_value = ncontrol->private_value; - kctl.private_data = private_data; - return snd_ctl_new(&kctl, access); + kctl->id.index = ncontrol->index; + + kctl->info = ncontrol->info; + kctl->get = ncontrol->get; + kctl->put = ncontrol->put; + kctl->tlv.p = ncontrol->tlv.p; + + kctl->private_value = ncontrol->private_value; + kctl->private_data = private_data; + + return kctl; } EXPORT_SYMBOL(snd_ctl_new1); @@ -1179,44 +1202,48 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64, }; struct snd_card *card = file->card; - struct snd_kcontrol kctl, *_kctl; + struct snd_kcontrol *kctl; + unsigned int count; unsigned int access; long private_size; struct user_element *ue; - int idx, err; - - access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : - (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| - SNDRV_CTL_ELEM_ACCESS_INACTIVE| - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)); - info->id.numid = 0; - memset(&kctl, 0, sizeof(kctl)); + int err; + /* Delete a control to replace them if needed. */ if (replace) { + info->id.numid = 0; err = snd_ctl_remove_user_ctl(file, &info->id); if (err) return err; } - if (card->user_ctl_count >= MAX_USER_CONTROLS) + /* + * The number of userspace controls are counted control by control, + * not element by element. + */ + if (card->user_ctl_count + 1 > MAX_USER_CONTROLS) return -ENOMEM; - memcpy(&kctl.id, &info->id, sizeof(info->id)); - kctl.count = info->owner ? info->owner : 1; - access |= SNDRV_CTL_ELEM_ACCESS_USER; - if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) - kctl.info = snd_ctl_elem_user_enum_info; - else - 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; - if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { - kctl.tlv.c = snd_ctl_elem_user_tlv; + /* Check the number of elements for this userspace control. */ + count = info->owner; + if (count == 0) + count = 1; + + /* Arrange access permissions if needed. */ + access = info->access; + if (access == 0) + access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE); + if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; - } + access |= SNDRV_CTL_ELEM_ACCESS_USER; + /* + * Check information and calculate the size of data specific to + * this userspace control. + */ if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN || info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) return -EINVAL; @@ -1226,11 +1253,27 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (info->count < 1 || info->count > max_value_counts[info->type]) return -EINVAL; - private_size = value_sizes[info->type] * info->count; - ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); - if (ue == NULL) + + /* + * Keep memory object for this userspace control. After passing this + * code block, the instance should be freed by snd_ctl_free_one(). + * + * Note that these elements in this control are locked. + */ + err = snd_ctl_new(&kctl, count, access, file); + if (err < 0) + return err; + kctl->private_data = kzalloc(sizeof(struct user_element) + private_size, + GFP_KERNEL); + if (kctl->private_data == NULL) { + kfree(kctl); return -ENOMEM; + } + kctl->private_free = snd_ctl_elem_user_free; + + /* Set private data for this userspace control. */ + ue = (struct user_element *)kctl->private_data; ue->card = card; ue->info = *info; ue->info.access = 0; @@ -1239,21 +1282,25 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { err = snd_ctl_elem_init_enum_names(ue); if (err < 0) { - kfree(ue); + snd_ctl_free_one(kctl); return err; } } - kctl.private_free = snd_ctl_elem_user_free; - _kctl = snd_ctl_new(&kctl, access); - if (_kctl == NULL) { - kfree(ue->priv_data); - kfree(ue); - return -ENOMEM; - } - _kctl->private_data = ue; - for (idx = 0; idx < _kctl->count; idx++) - _kctl->vd[idx].owner = file; - err = snd_ctl_add(card, _kctl); + + /* Set callback functions. */ + if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) + kctl->info = snd_ctl_elem_user_enum_info; + else + 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; + if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) + kctl->tlv.c = snd_ctl_elem_user_tlv; + + /* This function manage to free the instance on failure. */ + err = snd_ctl_add(card, kctl); if (err < 0) return err; -- cgit v1.2.3 From ec0e9937aaa8b0a4b0633711c4d70d622acd9a7f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2015 15:42:14 +0100 Subject: ALSA: core: Drop superfluous error/debug messages after malloc failures The kernel memory allocators already report the errors when the requested allocation fails, thus we don't need to warn it again in each caller side. Signed-off-by: Takashi Iwai --- sound/core/control.c | 4 +--- sound/core/device.c | 4 +--- sound/core/hwdep.c | 4 +--- sound/core/oss/mixer_oss.c | 4 +--- sound/core/oss/pcm_oss.c | 1 - sound/core/pcm.c | 13 +++---------- sound/core/rawmidi.c | 8 ++------ sound/core/timer.c | 4 +--- 8 files changed, 10 insertions(+), 32 deletions(-) (limited to 'sound/core/control.c') diff --git a/sound/core/control.c b/sound/core/control.c index e1d8e0c816f0..833b223a363a 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -217,10 +217,8 @@ static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count, size += sizeof(struct snd_kcontrol_volatile) * count; *kctl = kzalloc(size, GFP_KERNEL); - if (*kctl == NULL) { - pr_err("ALSA: Cannot allocate control instance\n"); + if (!*kctl) return -ENOMEM; - } for (idx = 0; idx < count; idx++) { (*kctl)->vd[idx].access = access; diff --git a/sound/core/device.c b/sound/core/device.c index 41bec3075ae5..c1a845b42a8b 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -50,10 +50,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, if (snd_BUG_ON(!card || !device_data || !ops)) return -ENXIO; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - dev_err(card->dev, "Cannot allocate device, type=%d\n", type); + if (!dev) return -ENOMEM; - } INIT_LIST_HEAD(&dev->list); dev->card = card; dev->type = type; diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 84244a5143cf..51692c8a39ea 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -378,10 +378,8 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device, if (rhwdep) *rhwdep = NULL; hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL); - if (hwdep == NULL) { - dev_err(card->dev, "hwdep: cannot allocate\n"); + if (!hwdep) return -ENOMEM; - } init_waitqueue_head(&hwdep->open_wait); mutex_init(&hwdep->open_mutex); diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 5e6349f00ecd..056f8e274851 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1212,10 +1212,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, /* not changed */ goto __unlock; tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); - if (! tbl) { - pr_err("ALSA: mixer_oss: no memory\n"); + if (!tbl) goto __unlock; - } tbl->oss_id = ch; tbl->name = kstrdup(str, GFP_KERNEL); if (! tbl->name) { diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 80423a4ccab6..58550cc93f28 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -854,7 +854,6 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) params = kmalloc(sizeof(*params), GFP_KERNEL); sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); if (!sw_params || !params || !sparams) { - pcm_dbg(substream->pcm, "No memory\n"); err = -ENOMEM; goto failure; } diff --git a/sound/core/pcm.c b/sound/core/pcm.c index e9b87465c73d..b25bcf5b8644 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -343,11 +343,8 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, return; info = kmalloc(sizeof(*info), GFP_KERNEL); - if (! info) { - pcm_dbg(substream->pcm, - "snd_pcm_proc_info_read: cannot malloc\n"); + if (!info) return; - } err = snd_pcm_info(substream, info); if (err < 0) { @@ -717,10 +714,8 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) prev = NULL; for (idx = 0, prev = NULL; idx < substream_count; idx++) { substream = kzalloc(sizeof(*substream), GFP_KERNEL); - if (substream == NULL) { - pcm_err(pcm, "Cannot allocate PCM substream\n"); + if (!substream) return -ENOMEM; - } substream->pcm = pcm; substream->pstr = pstr; substream->number = idx; @@ -774,10 +769,8 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device, if (rpcm) *rpcm = NULL; pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); - if (pcm == NULL) { - dev_err(card->dev, "Cannot allocate PCM\n"); + if (!pcm) return -ENOMEM; - } pcm->card = card; pcm->device = device; pcm->internal = internal; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index b5a748596fc4..a7759846fbaa 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1429,10 +1429,8 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi, for (idx = 0; idx < count; idx++) { substream = kzalloc(sizeof(*substream), GFP_KERNEL); - if (substream == NULL) { - rmidi_err(rmidi, "rawmidi: cannot allocate substream\n"); + if (!substream) return -ENOMEM; - } substream->stream = direction; substream->number = idx; substream->rmidi = rmidi; @@ -1479,10 +1477,8 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device, if (rrawmidi) *rrawmidi = NULL; rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL); - if (rmidi == NULL) { - dev_err(card->dev, "rawmidi: cannot allocate\n"); + if (!rmidi) return -ENOMEM; - } rmidi->card = card; rmidi->device = device; mutex_init(&rmidi->open_mutex); diff --git a/sound/core/timer.c b/sound/core/timer.c index 490b489d713d..a9a1a047c521 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -774,10 +774,8 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, if (rtimer) *rtimer = NULL; timer = kzalloc(sizeof(*timer), GFP_KERNEL); - if (timer == NULL) { - pr_err("ALSA: timer: cannot allocate\n"); + if (!timer) return -ENOMEM; - } timer->tmr_class = tid->dev_class; timer->card = card; timer->tmr_device = tid->device; -- cgit v1.2.3 From e79d74ab25339437447478e4dfe2b35c5b560512 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Mar 2015 16:57:51 +0100 Subject: ALSA: control: Fix breakage of user ctl element addition In the commit [2225e79b9b03: 'ALSA: core: reduce stack usage related to snd_ctl_new()'], the id field of the newly added kctl is untouched, thus all attribute like name string remain empty. The fix is just to add the forgotten memcpy of the id field. Fixes: 2225e79b9b03 ('ALSA: core: reduce stack usage related to snd_ctl_new()') Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/control.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/core/control.c') diff --git a/sound/core/control.c b/sound/core/control.c index 54a412af3224..d677c27746e9 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1267,6 +1267,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, err = snd_ctl_new(&kctl, count, access, file); if (err < 0) return err; + memcpy(&kctl->id, &info->id, sizeof(kctl->id)); kctl->private_data = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); if (kctl->private_data == NULL) { -- cgit v1.2.3 From 39d118677baa531cd9ee4c025a34f243746a3d18 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 10 Apr 2015 08:43:00 +0900 Subject: ALSA: ctl: evaluate macro instead of numerical value SNDRV_CTL_TLV_OP_XXX is defined but not used in core code. Instead, raw numerical value is evaluated. This commit replaces these values to these macros for better looking. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/control.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'sound/core/control.c') diff --git a/sound/core/control.c b/sound/core/control.c index d677c27746e9..00fcaa0ca647 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1099,7 +1099,7 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol, int change = 0; void *new_data; - if (op_flag > 0) { + if (op_flag == SNDRV_CTL_TLV_OP_WRITE) { if (size > 1024 * 128) /* sane value */ return -EINVAL; @@ -1381,9 +1381,12 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, goto __kctl_end; } vd = &kctl->vd[tlv.numid - kctl->id.numid]; - if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) || - (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) || - (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) { + 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)) { err = -ENXIO; goto __kctl_end; } @@ -1400,7 +1403,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, return 0; } } else { - if (op_flag) { + if (op_flag != SNDRV_CTL_ELEM_ACCESS_TLV_READ) { err = -ENXIO; goto __kctl_end; } -- cgit v1.2.3 From c78497e010ae62c8abfb33a45d0e0b361218e9bb Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 11 Apr 2015 17:41:02 +0900 Subject: ALSA: ctl: confirm to return all identical information in 'activate' event When event originator doesn't set numerical ID in identical information, the event data includes no numerical ID, thus userspace applications cannot identify the control just by unique ID in event data. This commit fix this bug so as the event data includes all of identical information. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/control.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/core/control.c') diff --git a/sound/core/control.c b/sound/core/control.c index 00fcaa0ca647..90a9e5d9819a 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -578,6 +578,7 @@ error: * * Finds the control instance with the given id, and activate or * inactivate the control together with notification, if changed. + * The given ID data is filled with full information. * * Return: 0 if unchanged, 1 if changed, or a negative error code on failure. */ @@ -607,6 +608,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, goto unlock; vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; } + snd_ctl_build_ioff(id, kctl, index_offset); ret = 1; unlock: up_write(&card->controls_rwsem); -- cgit v1.2.3 From c378c3b03c8d6eef2d2600d0279e2c718d6a0a44 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 11 Apr 2015 17:41:03 +0900 Subject: ALSA: ctl: fix a bug to return no identical information in info operation for userspace controls In operations of SNDRV_CTL_IOCTL_ELEM_INFO, identical information in returned value is cleared. This is not better to userspace application. This commit confirms to return full identical information to the operations. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/control.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound/core/control.c') diff --git a/sound/core/control.c b/sound/core/control.c index 90a9e5d9819a..a750846514dc 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1040,8 +1040,12 @@ static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct user_element *ue = kcontrol->private_data; + unsigned int offset; + offset = snd_ctl_get_ioff(kcontrol, &uinfo->id); *uinfo = ue->info; + snd_ctl_build_ioff(&uinfo->id, kcontrol, offset); + return 0; } @@ -1051,10 +1055,13 @@ static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol, struct user_element *ue = kcontrol->private_data; const char *names; unsigned int item; + unsigned int offset; item = uinfo->value.enumerated.item; + offset = snd_ctl_get_ioff(kcontrol, &uinfo->id); *uinfo = ue->info; + snd_ctl_build_ioff(&uinfo->id, kcontrol, offset); item = min(item, uinfo->value.enumerated.items - 1); uinfo->value.enumerated.item = item; -- cgit v1.2.3 From cab2ed7474bffafd2a68a885e03b85526194abcd Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 11 Apr 2015 17:41:04 +0900 Subject: ALSA: ctl: fill identical information to return value when adding userspace elements currently some members related identical information are not fiiled in returned parameter of SNDRV_CTL_IOCTL_ELEM_ADD. This is not better for userspace application. This commit copies information to returned value. When failing to copy into userspace, the added elements are going to be removed. Then, no applications can lock these elements between adding and removing because these are already locked. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/control.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'sound/core/control.c') diff --git a/sound/core/control.c b/sound/core/control.c index a750846514dc..ccb1ca26a71e 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1214,6 +1214,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, unsigned int access; long private_size; struct user_element *ue; + unsigned int offset; int err; if (!*info->id.name) @@ -1316,6 +1317,15 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, err = snd_ctl_add(card, kctl); if (err < 0) return err; + offset = snd_ctl_get_ioff(kctl, &info->id); + snd_ctl_build_ioff(&info->id, kctl, offset); + /* + * Here we cannot fill any field for the number of elements added by + * this operation because there're no specific fields. The usage of + * 'owner' field for this purpose may cause any bugs to userspace + * applications because the field originally means PID of a process + * which locks the element. + */ down_write(&card->controls_rwsem); card->user_ctl_count++; @@ -1328,9 +1338,19 @@ static int snd_ctl_elem_add_user(struct snd_ctl_file *file, struct snd_ctl_elem_info __user *_info, int replace) { struct snd_ctl_elem_info info; + int err; + if (copy_from_user(&info, _info, sizeof(info))) return -EFAULT; - return snd_ctl_elem_add(file, &info, replace); + err = snd_ctl_elem_add(file, &info, replace); + if (err < 0) + return err; + if (copy_to_user(_info, &info, sizeof(info))) { + snd_ctl_remove_user_ctl(file, &info.id); + return -EFAULT; + } + + return 0; } static int snd_ctl_elem_remove(struct snd_ctl_file *file, -- cgit v1.2.3 From c30cf8cbe55413cd643a0bdd3442d75950caa918 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 12 Apr 2015 09:16:11 +0200 Subject: ALSA: control: Fix a typo of SNDRV_CTL_ELEM_ACCESS_TLV_* with SNDRV_CTL_TLV_OP_* The commit [39d118677baa: ALSA: ctl: evaluate macro instead of numerical value] replaced the numbers with constants, but one place was replaced wrongly with a different type. Fixed now. Fixes: 39d118677baa ('ALSA: ctl: evaluate macro instead of numerical value') Signed-off-by: Takashi Iwai --- sound/core/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/core/control.c') diff --git a/sound/core/control.c b/sound/core/control.c index ccb1ca26a71e..be5b97cd8dc3 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1432,7 +1432,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, return 0; } } else { - if (op_flag != SNDRV_CTL_ELEM_ACCESS_TLV_READ) { + if (op_flag != SNDRV_CTL_TLV_OP_READ) { err = -ENXIO; goto __kctl_end; } -- cgit v1.2.3 From e1c78df1da112f2644058af2425dd5ca3eb1a96a Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 12 Apr 2015 10:12:25 +0900 Subject: ALSA: ctl: fix to handle several elements added by one operation for userspace element An element instance can have several elements with the same feature. Some userspace applications can add such an element instance by add operation with the number of elements. Then, the element instance gets a memory object to keep states of these elements. But the element instance has just one memory object for the elements. This causes the same result to each read/write operations to the different elements. This commit fixes this bug by allocating enough memory objects to the element instance for each of elements. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/core/control.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'sound/core/control.c') diff --git a/sound/core/control.c b/sound/core/control.c index be5b97cd8dc3..196a6fe100ca 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1029,7 +1029,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, struct user_element { struct snd_ctl_elem_info info; struct snd_card *card; - void *elem_data; /* element data */ + char *elem_data; /* element data */ unsigned long elem_data_size; /* size of element data in bytes */ void *tlv_data; /* TLV data */ unsigned long tlv_data_size; /* TLV data size */ @@ -1078,9 +1078,12 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct user_element *ue = kcontrol->private_data; + unsigned int size = ue->elem_data_size; + char *src = ue->elem_data + + snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size; mutex_lock(&ue->card->user_ctl_lock); - memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size); + memcpy(&ucontrol->value, src, size); mutex_unlock(&ue->card->user_ctl_lock); return 0; } @@ -1090,11 +1093,14 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol, { int change; struct user_element *ue = kcontrol->private_data; + unsigned int size = ue->elem_data_size; + char *dst = ue->elem_data + + snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size; mutex_lock(&ue->card->user_ctl_lock); - change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0; + change = memcmp(&ucontrol->value, dst, size) != 0; if (change) - memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); + memcpy(dst, &ucontrol->value, size); mutex_unlock(&ue->card->user_ctl_lock); return change; } @@ -1278,7 +1284,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (err < 0) return err; memcpy(&kctl->id, &info->id, sizeof(kctl->id)); - kctl->private_data = kzalloc(sizeof(struct user_element) + private_size, + kctl->private_data = kzalloc(sizeof(struct user_element) + private_size * count, GFP_KERNEL); if (kctl->private_data == NULL) { kfree(kctl); -- cgit v1.2.3