From a1682a646a89b44009b40392d7f6e68250abd39e Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 6 Sep 2017 11:22:21 +0800 Subject: [PATCH 1/2] ALSA: PCM: volume API implementation Introduced a new helper function snd_pcm_add_volume_ctls() to create control elements representing the volume for each PCM (sub)stream. This patch also squashes the logic implemented by below change- ASoC: Fix freed memory access of pcm stream kctl Consider sound card instantiate fails due to audrx init failure. In such case, all dais/ctls are de-registered and freed. But as part of it, access to unregistered ctls for pcm_chmap and similar controls result in crash. Ctls are freed at disconnection but the disconnect is called only when it was registered. CRs-Fixed: 1038054 Change-Id: Ief8817b4ec000c058d46aa021977b7c6003c0011 Signed-off-by: Laxminath Kasam Signed-off-by: Damir Didjusto Signed-off-by: Banajit Goswami Signed-off-by: Sudheer Papothi Signed-off-by: Meng Wang --- include/sound/pcm.h | 29 +++++++++++++ sound/core/pcm.c | 6 +++ sound/core/pcm_lib.c | 97 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index cf563e968513..167cf3e2dc45 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -501,6 +501,9 @@ struct snd_pcm_str { #endif #endif struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */ +#ifdef CONFIG_AUDIO_QGKI + struct snd_kcontrol *vol_kctl; /* volume controls */ +#endif struct device dev; }; @@ -1417,6 +1420,32 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format) return 1ULL << (__force int) pcm_format; } +#ifdef CONFIG_AUDIO_QGKI +/* + * PCM Volume control API + */ +/* array element of volume */ +struct snd_pcm_volume_elem { + int volume; +}; + +/* pp information; retrieved via snd_kcontrol_chip() */ +struct snd_pcm_volume { + struct snd_pcm *pcm; /* assigned PCM instance */ + int stream; /* PLAYBACK or CAPTURE */ + struct snd_kcontrol *kctl; + const struct snd_pcm_volume_elem *volume; + int max_length; + void *private_data; /* optional: private data pointer */ +}; + +int snd_pcm_add_volume_ctls(struct snd_pcm *pcm, int stream, + const struct snd_pcm_volume_elem *volume, + int max_length, + unsigned long private_value, + struct snd_pcm_volume **info_ret); +#endif + /* printk helpers */ #define pcm_err(pcm, fmt, args...) \ dev_err((pcm)->card->dev, fmt, ##args) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 15d4ff893aba..86062c96644f 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -816,6 +816,12 @@ static void free_chmap(struct snd_pcm_str *pstr) snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl); pstr->chmap_kctl = NULL; } +#ifdef CONFIG_AUDIO_QGKI + if (pstr->vol_kctl) { + snd_ctl_remove(pstr->pcm->card, pstr->vol_kctl); + pstr->vol_kctl = NULL; + } +#endif } static void snd_pcm_free_stream(struct snd_pcm_str * pstr) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 2236b5e0c1f2..6718b08e1977 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -30,6 +30,10 @@ #define trace_applptr(substream, prev, curr) #endif +#ifdef CONFIG_AUDIO_QGKI +#define STRING_LENGTH_OF_INT 12 +#endif + static int fill_silence_frames(struct snd_pcm_substream *substream, snd_pcm_uframes_t off, snd_pcm_uframes_t frames); @@ -2400,6 +2404,26 @@ static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol) kfree(info); } +#ifdef CONFIG_AUDIO_QGKI +static int pcm_volume_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x2000; + return 0; +} + +static void pcm_volume_ctl_private_free(struct snd_kcontrol *kcontrol) +{ + struct snd_pcm_volume *info = snd_kcontrol_chip(kcontrol); + + info->pcm->streams[info->stream].vol_kctl = NULL; + kfree(info); +} +#endif + /** * snd_pcm_add_chmap_ctls - create channel-mapping control elements * @pcm: the assigned PCM instance @@ -2461,3 +2485,76 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, return 0; } EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls); + +#ifdef CONFIG_AUDIO_QGKI +/** + * snd_pcm_add_volume_ctls - create volume control elements + * @pcm: the assigned PCM instance + * @stream: stream direction + * @max_length: the max length of the volume parameter of stream + * @private_value: the value passed to each kcontrol's private_value field + * @info_ret: store struct snd_pcm_volume instance if non-NULL + * + * Create volume control elements assigned to the given PCM stream(s). + * Returns zero if succeed, or a negative error value. + */ +int snd_pcm_add_volume_ctls(struct snd_pcm *pcm, int stream, + const struct snd_pcm_volume_elem *volume, + int max_length, + unsigned long private_value, + struct snd_pcm_volume **info_ret) +{ + struct snd_pcm_volume *info; + struct snd_kcontrol_new knew = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = pcm_volume_ctl_info, + }; + int err; + int size; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->pcm = pcm; + info->stream = stream; + info->volume = volume; + info->max_length = max_length; + size = sizeof("Playback ") + sizeof(" Volume") + + STRING_LENGTH_OF_INT*sizeof(char) + 1; + knew.name = kzalloc(size, GFP_KERNEL); + if (!knew.name) { + kfree(info); + return -ENOMEM; + } + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + snprintf((char *)knew.name, size, "%s %d %s", + "Playback", pcm->device, "Volume"); + else + snprintf((char *)knew.name, size, "%s %d %s", + "Capture", pcm->device, "Volume"); + knew.device = pcm->device; + knew.count = pcm->streams[stream].substream_count; + knew.private_value = private_value; + info->kctl = snd_ctl_new1(&knew, info); + if (!info->kctl) { + kfree(info); + kfree(knew.name); + return -ENOMEM; + } + info->kctl->private_free = pcm_volume_ctl_private_free; + err = snd_ctl_add(pcm->card, info->kctl); + if (err < 0) { + kfree(info); + kfree(knew.name); + return -ENOMEM; + } + pcm->streams[stream].vol_kctl = info->kctl; + if (info_ret) + *info_ret = info; + kfree(knew.name); + return 0; +} +EXPORT_SYMBOL(snd_pcm_add_volume_ctls); +#endif From 8625c90a01f85be6dff058d922954adc49bf5e0a Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 6 Sep 2017 15:43:32 +0800 Subject: [PATCH 2/2] ALSA: PCM: User control API implementation Introduced a new helper function snd_pcm_add_usr_ctls() to create control elements representing the user control for each PCM (sub)stream. Change-Id: I9f69470ff2c5d6df8d1fdc2e1c2caf198076076e Signed-off-by: Jayasena Sangaraboina Signed-off-by: Banajit Goswami Signed-off-by: Sudheer Papothi Signed-off-by: Meng Wang --- include/sound/pcm.h | 26 ++++++++++++- sound/core/pcm.c | 4 ++ sound/core/pcm_lib.c | 93 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 167cf3e2dc45..9576a47f2baf 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -503,6 +503,7 @@ struct snd_pcm_str { struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */ #ifdef CONFIG_AUDIO_QGKI struct snd_kcontrol *vol_kctl; /* volume controls */ + struct snd_kcontrol *usr_kctl; /* user controls */ #endif struct device dev; }; @@ -1444,8 +1445,31 @@ int snd_pcm_add_volume_ctls(struct snd_pcm *pcm, int stream, int max_length, unsigned long private_value, struct snd_pcm_volume **info_ret); -#endif +/* + * PCM User control API + */ +/* array element of usr elem */ +struct snd_pcm_usr_elem { + int val[128]; +}; + +/* pp information; retrieved via snd_kcontrol_chip() */ +struct snd_pcm_usr { + struct snd_pcm *pcm; /* assigned PCM instance */ + int stream; /* PLAYBACK or CAPTURE */ + struct snd_kcontrol *kctl; + const struct snd_pcm_usr_elem *usr; + int max_length; + void *private_data; /* optional: private data pointer */ +}; + +int snd_pcm_add_usr_ctls(struct snd_pcm *pcm, int stream, + const struct snd_pcm_usr_elem *usr, + int max_length, int max_control_str_len, + unsigned long private_value, + struct snd_pcm_usr **info_ret); +#endif /* printk helpers */ #define pcm_err(pcm, fmt, args...) \ dev_err((pcm)->card->dev, fmt, ##args) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 86062c96644f..021127fa9b46 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -821,6 +821,10 @@ static void free_chmap(struct snd_pcm_str *pstr) snd_ctl_remove(pstr->pcm->card, pstr->vol_kctl); pstr->vol_kctl = NULL; } + if (pstr->usr_kctl) { + snd_ctl_remove(pstr->pcm->card, pstr->usr_kctl); + pstr->usr_kctl = NULL; + } #endif } diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 6718b08e1977..19ff7832b071 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -32,6 +32,7 @@ #ifdef CONFIG_AUDIO_QGKI #define STRING_LENGTH_OF_INT 12 +#define MAX_USR_CTRL_CNT 128 #endif static int fill_silence_frames(struct snd_pcm_substream *substream, @@ -2557,4 +2558,96 @@ int snd_pcm_add_volume_ctls(struct snd_pcm *pcm, int stream, return 0; } EXPORT_SYMBOL(snd_pcm_add_volume_ctls); + +static int pcm_usr_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = MAX_USR_CTRL_CNT; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = INT_MAX; + return 0; +} + +static void pcm_usr_ctl_private_free(struct snd_kcontrol *kcontrol) +{ + struct snd_pcm_usr *info = snd_kcontrol_chip(kcontrol); + + info->pcm->streams[info->stream].usr_kctl = NULL; + kfree(info); +} + +/** + * snd_pcm_add_usr_ctls - create user control elements + * @pcm: the assigned PCM instance + * @stream: stream direction + * @max_length: the max length of the user parameter of stream + * @private_value: the value passed to each kcontrol's private_value field + * @info_ret: store struct snd_pcm_usr instance if non-NULL + * + * Create usr control elements assigned to the given PCM stream(s). + * Returns zero if succeed, or a negative error value. + */ +int snd_pcm_add_usr_ctls(struct snd_pcm *pcm, int stream, + const struct snd_pcm_usr_elem *usr, + int max_length, int max_kctrl_str_len, + unsigned long private_value, + struct snd_pcm_usr **info_ret) +{ + struct snd_pcm_usr *info; + struct snd_kcontrol_new knew = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = pcm_usr_ctl_info, + }; + int err; + char *buf; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pcm = pcm; + info->stream = stream; + info->usr = usr; + info->max_length = max_length; + buf = kzalloc(max_kctrl_str_len, GFP_KERNEL); + if (!buf) { + pr_err("%s: buffer allocation failed\n", __func__); + kfree(info); + return -ENOMEM; + } + knew.name = buf; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + snprintf(buf, max_kctrl_str_len, "%s %d %s", + "Playback", pcm->device, "User kcontrol"); + else + snprintf(buf, max_kctrl_str_len, "%s %d %s", + "Capture", pcm->device, "User kcontrol"); + knew.device = pcm->device; + knew.count = pcm->streams[stream].substream_count; + knew.private_value = private_value; + info->kctl = snd_ctl_new1(&knew, info); + if (!info->kctl) { + kfree(info); + kfree(knew.name); + pr_err("%s: snd_ctl_new failed\n", __func__); + return -ENOMEM; + } + info->kctl->private_free = pcm_usr_ctl_private_free; + err = snd_ctl_add(pcm->card, info->kctl); + if (err < 0) { + kfree(info); + kfree(knew.name); + pr_err("%s: snd_ctl_add failed:%d\n", __func__, + err); + return -ENOMEM; + } + pcm->streams[stream].usr_kctl = info->kctl; + if (info_ret) + *info_ret = info; + kfree(knew.name); + return 0; +} +EXPORT_SYMBOL(snd_pcm_add_usr_ctls); #endif