1e149ca29SPierre-Louis Bossart // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2c3078f53SLiam Girdwood // 3c3078f53SLiam Girdwood // This file is provided under a dual BSD/GPLv2 license. When using or 4c3078f53SLiam Girdwood // redistributing this file, you may do so under either license. 5c3078f53SLiam Girdwood // 6c3078f53SLiam Girdwood // Copyright(c) 2018 Intel Corporation. All rights reserved. 7c3078f53SLiam Girdwood // 8c3078f53SLiam Girdwood // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 9c3078f53SLiam Girdwood // 10c3078f53SLiam Girdwood 11c3078f53SLiam Girdwood /* Mixer Controls */ 12c3078f53SLiam Girdwood 13c3078f53SLiam Girdwood #include <linux/pm_runtime.h> 145d43001aSJaska Uimonen #include <linux/leds.h> 15c3078f53SLiam Girdwood #include "sof-priv.h" 16ee1e79b7SRanjani Sridharan #include "sof-audio.h" 17c3078f53SLiam Girdwood 185d43001aSJaska Uimonen static void update_mute_led(struct snd_sof_control *scontrol, 195d43001aSJaska Uimonen struct snd_kcontrol *kcontrol, 205d43001aSJaska Uimonen struct snd_ctl_elem_value *ucontrol) 215d43001aSJaska Uimonen { 2249c22696SKai-Heng Feng int temp = 0; 2349c22696SKai-Heng Feng int mask; 245d43001aSJaska Uimonen int i; 255d43001aSJaska Uimonen 265d43001aSJaska Uimonen mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 275d43001aSJaska Uimonen 285d43001aSJaska Uimonen for (i = 0; i < scontrol->num_channels; i++) { 295d43001aSJaska Uimonen if (ucontrol->value.integer.value[i]) { 305d43001aSJaska Uimonen temp |= mask; 315d43001aSJaska Uimonen break; 325d43001aSJaska Uimonen } 335d43001aSJaska Uimonen } 345d43001aSJaska Uimonen 355d43001aSJaska Uimonen if (temp == scontrol->led_ctl.led_value) 365d43001aSJaska Uimonen return; 375d43001aSJaska Uimonen 385d43001aSJaska Uimonen scontrol->led_ctl.led_value = temp; 395d43001aSJaska Uimonen 409899a7a8SYueHaibing #if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) 415d43001aSJaska Uimonen if (!scontrol->led_ctl.direction) 425d43001aSJaska Uimonen ledtrig_audio_set(LED_AUDIO_MUTE, temp ? LED_OFF : LED_ON); 435d43001aSJaska Uimonen else 445d43001aSJaska Uimonen ledtrig_audio_set(LED_AUDIO_MICMUTE, temp ? LED_OFF : LED_ON); 459899a7a8SYueHaibing #endif 465d43001aSJaska Uimonen } 475d43001aSJaska Uimonen 48756bbe42SPeter Ujfalusi static void snd_sof_refresh_control(struct snd_sof_control *scontrol) 49756bbe42SPeter Ujfalusi { 50b5cee8feSRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 51756bbe42SPeter Ujfalusi struct snd_soc_component *scomp = scontrol->scomp; 52756bbe42SPeter Ujfalusi int ret; 53756bbe42SPeter Ujfalusi 54756bbe42SPeter Ujfalusi if (!scontrol->comp_data_dirty) 55756bbe42SPeter Ujfalusi return; 56756bbe42SPeter Ujfalusi 57756bbe42SPeter Ujfalusi if (!pm_runtime_active(scomp->dev)) 58756bbe42SPeter Ujfalusi return; 59756bbe42SPeter Ujfalusi 60756bbe42SPeter Ujfalusi /* set the ABI header values */ 61756bbe42SPeter Ujfalusi cdata->data->magic = SOF_ABI_MAGIC; 62756bbe42SPeter Ujfalusi cdata->data->abi = SOF_ABI_VERSION; 63756bbe42SPeter Ujfalusi 64756bbe42SPeter Ujfalusi /* refresh the component data from DSP */ 65756bbe42SPeter Ujfalusi scontrol->comp_data_dirty = false; 6647d7328fSPeter Ujfalusi ret = snd_sof_ipc_set_get_comp_data(scontrol, false); 67756bbe42SPeter Ujfalusi if (ret < 0) { 68838d04f3SRanjani Sridharan dev_err(scomp->dev, "Failed to get control data: %d\n", ret); 69838d04f3SRanjani Sridharan 70756bbe42SPeter Ujfalusi /* Set the flag to re-try next time to get the data */ 71756bbe42SPeter Ujfalusi scontrol->comp_data_dirty = true; 72756bbe42SPeter Ujfalusi } 73756bbe42SPeter Ujfalusi } 74756bbe42SPeter Ujfalusi 75c3078f53SLiam Girdwood int snd_sof_volume_get(struct snd_kcontrol *kcontrol, 76c3078f53SLiam Girdwood struct snd_ctl_elem_value *ucontrol) 77c3078f53SLiam Girdwood { 78838d04f3SRanjani Sridharan struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; 79c3078f53SLiam Girdwood struct snd_sof_control *scontrol = sm->dobj.private; 80838d04f3SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 81838d04f3SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 82838d04f3SRanjani Sridharan const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; 83c3078f53SLiam Girdwood 84838d04f3SRanjani Sridharan if (tplg_ops->control->volume_get) 85838d04f3SRanjani Sridharan return tplg_ops->control->volume_get(scontrol, ucontrol); 86c3078f53SLiam Girdwood 87c3078f53SLiam Girdwood return 0; 88c3078f53SLiam Girdwood } 89c3078f53SLiam Girdwood 90c3078f53SLiam Girdwood int snd_sof_volume_put(struct snd_kcontrol *kcontrol, 91c3078f53SLiam Girdwood struct snd_ctl_elem_value *ucontrol) 92c3078f53SLiam Girdwood { 93838d04f3SRanjani Sridharan struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; 94c3078f53SLiam Girdwood struct snd_sof_control *scontrol = sm->dobj.private; 95ee1e79b7SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 96838d04f3SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 97838d04f3SRanjani Sridharan const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; 98c3078f53SLiam Girdwood 99838d04f3SRanjani Sridharan if (tplg_ops->control->volume_put) 100838d04f3SRanjani Sridharan return tplg_ops->control->volume_put(scontrol, ucontrol); 101c3078f53SLiam Girdwood 102838d04f3SRanjani Sridharan return false; 103c3078f53SLiam Girdwood } 104c3078f53SLiam Girdwood 105fca18e62SJaska Uimonen int snd_sof_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 106fca18e62SJaska Uimonen { 107fca18e62SJaska Uimonen struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; 108fca18e62SJaska Uimonen struct snd_sof_control *scontrol = sm->dobj.private; 109fca18e62SJaska Uimonen unsigned int channels = scontrol->num_channels; 110fca18e62SJaska Uimonen int platform_max; 111fca18e62SJaska Uimonen 112fca18e62SJaska Uimonen if (!sm->platform_max) 113fca18e62SJaska Uimonen sm->platform_max = sm->max; 114fca18e62SJaska Uimonen platform_max = sm->platform_max; 115fca18e62SJaska Uimonen 116fca18e62SJaska Uimonen if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume")) 117fca18e62SJaska Uimonen uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 118fca18e62SJaska Uimonen else 119fca18e62SJaska Uimonen uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 120fca18e62SJaska Uimonen 121fca18e62SJaska Uimonen uinfo->count = channels; 122fca18e62SJaska Uimonen uinfo->value.integer.min = 0; 123fca18e62SJaska Uimonen uinfo->value.integer.max = platform_max - sm->min; 124fca18e62SJaska Uimonen return 0; 125fca18e62SJaska Uimonen } 126fca18e62SJaska Uimonen 127c3078f53SLiam Girdwood int snd_sof_switch_get(struct snd_kcontrol *kcontrol, 128c3078f53SLiam Girdwood struct snd_ctl_elem_value *ucontrol) 129c3078f53SLiam Girdwood { 130a6668746SRanjani Sridharan struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; 131c3078f53SLiam Girdwood struct snd_sof_control *scontrol = sm->dobj.private; 132a6668746SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 133a6668746SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 134a6668746SRanjani Sridharan const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; 135c3078f53SLiam Girdwood 136a6668746SRanjani Sridharan if (tplg_ops->control->switch_get) 137a6668746SRanjani Sridharan return tplg_ops->control->switch_get(scontrol, ucontrol); 138c3078f53SLiam Girdwood 139c3078f53SLiam Girdwood return 0; 140c3078f53SLiam Girdwood } 141c3078f53SLiam Girdwood 142c3078f53SLiam Girdwood int snd_sof_switch_put(struct snd_kcontrol *kcontrol, 143c3078f53SLiam Girdwood struct snd_ctl_elem_value *ucontrol) 144c3078f53SLiam Girdwood { 145a6668746SRanjani Sridharan struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; 146c3078f53SLiam Girdwood struct snd_sof_control *scontrol = sm->dobj.private; 147ee1e79b7SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 148a6668746SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 149a6668746SRanjani Sridharan const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; 150c3078f53SLiam Girdwood 1515d43001aSJaska Uimonen if (scontrol->led_ctl.use_led) 1525d43001aSJaska Uimonen update_mute_led(scontrol, kcontrol, ucontrol); 1535d43001aSJaska Uimonen 154a6668746SRanjani Sridharan if (tplg_ops->control->switch_put) 155a6668746SRanjani Sridharan return tplg_ops->control->switch_put(scontrol, ucontrol); 156c3078f53SLiam Girdwood 157a6668746SRanjani Sridharan return false; 158c3078f53SLiam Girdwood } 159c3078f53SLiam Girdwood 160c3078f53SLiam Girdwood int snd_sof_enum_get(struct snd_kcontrol *kcontrol, 161c3078f53SLiam Girdwood struct snd_ctl_elem_value *ucontrol) 162c3078f53SLiam Girdwood { 163049307aaSRanjani Sridharan struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; 164c3078f53SLiam Girdwood struct snd_sof_control *scontrol = se->dobj.private; 165049307aaSRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 166049307aaSRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 167049307aaSRanjani Sridharan const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; 168c3078f53SLiam Girdwood 169049307aaSRanjani Sridharan if (tplg_ops->control->enum_get) 170049307aaSRanjani Sridharan return tplg_ops->control->enum_get(scontrol, ucontrol); 171c3078f53SLiam Girdwood 172c3078f53SLiam Girdwood return 0; 173c3078f53SLiam Girdwood } 174c3078f53SLiam Girdwood 175c3078f53SLiam Girdwood int snd_sof_enum_put(struct snd_kcontrol *kcontrol, 176c3078f53SLiam Girdwood struct snd_ctl_elem_value *ucontrol) 177c3078f53SLiam Girdwood { 178049307aaSRanjani Sridharan struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; 179c3078f53SLiam Girdwood struct snd_sof_control *scontrol = se->dobj.private; 180ee1e79b7SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 181049307aaSRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 182049307aaSRanjani Sridharan const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; 183c3078f53SLiam Girdwood 184049307aaSRanjani Sridharan if (tplg_ops->control->enum_put) 185049307aaSRanjani Sridharan return tplg_ops->control->enum_put(scontrol, ucontrol); 186c3078f53SLiam Girdwood 187049307aaSRanjani Sridharan return false; 188c3078f53SLiam Girdwood } 189c3078f53SLiam Girdwood 190c3078f53SLiam Girdwood int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, 191c3078f53SLiam Girdwood struct snd_ctl_elem_value *ucontrol) 192c3078f53SLiam Girdwood { 193*544ac885SRanjani Sridharan struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; 194c3078f53SLiam Girdwood struct snd_sof_control *scontrol = be->dobj.private; 195ee1e79b7SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 196*544ac885SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 197*544ac885SRanjani Sridharan const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; 198c3078f53SLiam Girdwood 199*544ac885SRanjani Sridharan if (tplg_ops->control->bytes_get) 200*544ac885SRanjani Sridharan return tplg_ops->control->bytes_get(scontrol, ucontrol); 201c3078f53SLiam Girdwood 202b9f8e138SGuennadi Liakhovetski return 0; 203c3078f53SLiam Girdwood } 204c3078f53SLiam Girdwood 205c3078f53SLiam Girdwood int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, 206c3078f53SLiam Girdwood struct snd_ctl_elem_value *ucontrol) 207c3078f53SLiam Girdwood { 208*544ac885SRanjani Sridharan struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; 209c3078f53SLiam Girdwood struct snd_sof_control *scontrol = be->dobj.private; 210ee1e79b7SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 211*544ac885SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 212*544ac885SRanjani Sridharan const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; 213c3078f53SLiam Girdwood 214*544ac885SRanjani Sridharan if (tplg_ops->control->bytes_put) 215*544ac885SRanjani Sridharan return tplg_ops->control->bytes_put(scontrol, ucontrol); 216c3078f53SLiam Girdwood 2170c888babSBard Liao return 0; 218c3078f53SLiam Girdwood } 219c3078f53SLiam Girdwood 220c3078f53SLiam Girdwood int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, 221c3078f53SLiam Girdwood const unsigned int __user *binary_data, 222c3078f53SLiam Girdwood unsigned int size) 223c3078f53SLiam Girdwood { 224c3078f53SLiam Girdwood struct soc_bytes_ext *be = 225c3078f53SLiam Girdwood (struct soc_bytes_ext *)kcontrol->private_value; 226c3078f53SLiam Girdwood struct snd_sof_control *scontrol = be->dobj.private; 227ee1e79b7SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 228b5cee8feSRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 229c3078f53SLiam Girdwood struct snd_ctl_tlv header; 230c3078f53SLiam Girdwood const struct snd_ctl_tlv __user *tlvd = 231c3078f53SLiam Girdwood (const struct snd_ctl_tlv __user *)binary_data; 232c3078f53SLiam Girdwood 2332ca21011SPierre-Louis Bossart /* make sure we have at least a header */ 2342ca21011SPierre-Louis Bossart if (size < sizeof(struct snd_ctl_tlv)) 2352ca21011SPierre-Louis Bossart return -EINVAL; 2362ca21011SPierre-Louis Bossart 237c3078f53SLiam Girdwood /* 238c3078f53SLiam Girdwood * The beginning of bytes data contains a header from where 239c3078f53SLiam Girdwood * the length (as bytes) is needed to know the correct copy 240c3078f53SLiam Girdwood * length of data from tlvd->tlv. 241c3078f53SLiam Girdwood */ 2425027fe36SPierre-Louis Bossart if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv))) 243c3078f53SLiam Girdwood return -EFAULT; 244c3078f53SLiam Girdwood 2452ca21011SPierre-Louis Bossart /* make sure TLV info is consistent */ 2462ca21011SPierre-Louis Bossart if (header.length + sizeof(struct snd_ctl_tlv) > size) { 2472ca21011SPierre-Louis Bossart dev_err_ratelimited(scomp->dev, "error: inconsistent TLV, data %d + header %zu > %d\n", 2482ca21011SPierre-Louis Bossart header.length, sizeof(struct snd_ctl_tlv), size); 2492ca21011SPierre-Louis Bossart return -EINVAL; 2502ca21011SPierre-Louis Bossart } 2512ca21011SPierre-Louis Bossart 252c3078f53SLiam Girdwood /* be->max is coming from topology */ 253c3078f53SLiam Girdwood if (header.length > be->max) { 254ee1e79b7SRanjani Sridharan dev_err_ratelimited(scomp->dev, "error: Bytes data size %d exceeds max %d.\n", 255c3078f53SLiam Girdwood header.length, be->max); 256c3078f53SLiam Girdwood return -EINVAL; 257c3078f53SLiam Girdwood } 258c3078f53SLiam Girdwood 259c3078f53SLiam Girdwood /* Check that header id matches the command */ 260dd2fef98SPeter Ujfalusi if (header.numid != cdata->cmd) { 261ee1e79b7SRanjani Sridharan dev_err_ratelimited(scomp->dev, 262c3078f53SLiam Girdwood "error: incorrect numid %d\n", 263c3078f53SLiam Girdwood header.numid); 264c3078f53SLiam Girdwood return -EINVAL; 265c3078f53SLiam Girdwood } 266c3078f53SLiam Girdwood 267c3078f53SLiam Girdwood if (copy_from_user(cdata->data, tlvd->tlv, header.length)) 268c3078f53SLiam Girdwood return -EFAULT; 269c3078f53SLiam Girdwood 270c3078f53SLiam Girdwood if (cdata->data->magic != SOF_ABI_MAGIC) { 271ee1e79b7SRanjani Sridharan dev_err_ratelimited(scomp->dev, 272c3078f53SLiam Girdwood "error: Wrong ABI magic 0x%08x.\n", 273c3078f53SLiam Girdwood cdata->data->magic); 274c3078f53SLiam Girdwood return -EINVAL; 275c3078f53SLiam Girdwood } 276c3078f53SLiam Girdwood 277c3078f53SLiam Girdwood if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { 278ee1e79b7SRanjani Sridharan dev_err_ratelimited(scomp->dev, "error: Incompatible ABI version 0x%08x.\n", 279c3078f53SLiam Girdwood cdata->data->abi); 280c3078f53SLiam Girdwood return -EINVAL; 281c3078f53SLiam Girdwood } 282c3078f53SLiam Girdwood 2830e4ea878SGuennadi Liakhovetski /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ 2845027fe36SPierre-Louis Bossart if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) { 285ee1e79b7SRanjani Sridharan dev_err_ratelimited(scomp->dev, "error: Mismatch in ABI data size (truncated?).\n"); 286c3078f53SLiam Girdwood return -EINVAL; 287c3078f53SLiam Girdwood } 288c3078f53SLiam Girdwood 289c3078f53SLiam Girdwood /* notify DSP of byte control updates */ 290ee1e79b7SRanjani Sridharan if (pm_runtime_active(scomp->dev)) 29147d7328fSPeter Ujfalusi snd_sof_ipc_set_get_comp_data(scontrol, true); 292c3078f53SLiam Girdwood 2930c888babSBard Liao return 0; 294c3078f53SLiam Girdwood } 295c3078f53SLiam Girdwood 296783560d0SDharageswari R int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data, 297783560d0SDharageswari R unsigned int size) 298783560d0SDharageswari R { 299783560d0SDharageswari R struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; 300783560d0SDharageswari R struct snd_sof_control *scontrol = be->dobj.private; 301783560d0SDharageswari R struct snd_soc_component *scomp = scontrol->scomp; 302b5cee8feSRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 303783560d0SDharageswari R struct snd_ctl_tlv header; 304783560d0SDharageswari R struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; 305783560d0SDharageswari R size_t data_size; 306783560d0SDharageswari R int ret; 307783560d0SDharageswari R int err; 308783560d0SDharageswari R 309ec5a9762SPierre-Louis Bossart /* 310ec5a9762SPierre-Louis Bossart * Decrement the limit by ext bytes header size to 311ec5a9762SPierre-Louis Bossart * ensure the user space buffer is not exceeded. 312ec5a9762SPierre-Louis Bossart */ 313ec5a9762SPierre-Louis Bossart if (size < sizeof(struct snd_ctl_tlv)) 314ec5a9762SPierre-Louis Bossart return -ENOSPC; 315ec5a9762SPierre-Louis Bossart size -= sizeof(struct snd_ctl_tlv); 316ec5a9762SPierre-Louis Bossart 317783560d0SDharageswari R ret = pm_runtime_get_sync(scomp->dev); 31899ceec5cSPierre-Louis Bossart if (ret < 0 && ret != -EACCES) { 319783560d0SDharageswari R dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to resume %d\n", ret); 320783560d0SDharageswari R pm_runtime_put_noidle(scomp->dev); 321783560d0SDharageswari R return ret; 322783560d0SDharageswari R } 323783560d0SDharageswari R 324783560d0SDharageswari R /* set the ABI header values */ 325783560d0SDharageswari R cdata->data->magic = SOF_ABI_MAGIC; 326783560d0SDharageswari R cdata->data->abi = SOF_ABI_VERSION; 327783560d0SDharageswari R /* get all the component data from DSP */ 32847d7328fSPeter Ujfalusi ret = snd_sof_ipc_set_get_comp_data(scontrol, false); 329783560d0SDharageswari R if (ret < 0) 330783560d0SDharageswari R goto out; 331783560d0SDharageswari R 332783560d0SDharageswari R /* check data size doesn't exceed max coming from topology */ 3335027fe36SPierre-Louis Bossart if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) { 334783560d0SDharageswari R dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", 335783560d0SDharageswari R cdata->data->size, 3365027fe36SPierre-Louis Bossart be->max - sizeof(struct sof_abi_hdr)); 337783560d0SDharageswari R ret = -EINVAL; 338783560d0SDharageswari R goto out; 339783560d0SDharageswari R } 340783560d0SDharageswari R 3415027fe36SPierre-Louis Bossart data_size = cdata->data->size + sizeof(struct sof_abi_hdr); 342783560d0SDharageswari R 343ec5a9762SPierre-Louis Bossart /* make sure we don't exceed size provided by user space for data */ 344ec5a9762SPierre-Louis Bossart if (data_size > size) { 345ec5a9762SPierre-Louis Bossart ret = -ENOSPC; 346ec5a9762SPierre-Louis Bossart goto out; 347ec5a9762SPierre-Louis Bossart } 348ec5a9762SPierre-Louis Bossart 349dd2fef98SPeter Ujfalusi header.numid = cdata->cmd; 350783560d0SDharageswari R header.length = data_size; 3515027fe36SPierre-Louis Bossart if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) { 352783560d0SDharageswari R ret = -EFAULT; 353783560d0SDharageswari R goto out; 354783560d0SDharageswari R } 355783560d0SDharageswari R 356783560d0SDharageswari R if (copy_to_user(tlvd->tlv, cdata->data, data_size)) 357783560d0SDharageswari R ret = -EFAULT; 358783560d0SDharageswari R out: 359783560d0SDharageswari R pm_runtime_mark_last_busy(scomp->dev); 360783560d0SDharageswari R err = pm_runtime_put_autosuspend(scomp->dev); 361783560d0SDharageswari R if (err < 0) 362783560d0SDharageswari R dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to idle %d\n", err); 363783560d0SDharageswari R 364783560d0SDharageswari R return ret; 365783560d0SDharageswari R } 366783560d0SDharageswari R 367c3078f53SLiam Girdwood int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, 368c3078f53SLiam Girdwood unsigned int __user *binary_data, 369c3078f53SLiam Girdwood unsigned int size) 370c3078f53SLiam Girdwood { 371c3078f53SLiam Girdwood struct soc_bytes_ext *be = 372c3078f53SLiam Girdwood (struct soc_bytes_ext *)kcontrol->private_value; 373c3078f53SLiam Girdwood struct snd_sof_control *scontrol = be->dobj.private; 374ee1e79b7SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 375b5cee8feSRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 376c3078f53SLiam Girdwood struct snd_ctl_tlv header; 377c3078f53SLiam Girdwood struct snd_ctl_tlv __user *tlvd = 378c3078f53SLiam Girdwood (struct snd_ctl_tlv __user *)binary_data; 3790e4ea878SGuennadi Liakhovetski size_t data_size; 380c3078f53SLiam Girdwood 381756bbe42SPeter Ujfalusi snd_sof_refresh_control(scontrol); 382756bbe42SPeter Ujfalusi 383c3078f53SLiam Girdwood /* 384c3078f53SLiam Girdwood * Decrement the limit by ext bytes header size to 385c3078f53SLiam Girdwood * ensure the user space buffer is not exceeded. 386c3078f53SLiam Girdwood */ 3873331bcd6SPierre-Louis Bossart if (size < sizeof(struct snd_ctl_tlv)) 3883331bcd6SPierre-Louis Bossart return -ENOSPC; 3893331bcd6SPierre-Louis Bossart size -= sizeof(struct snd_ctl_tlv); 390c3078f53SLiam Girdwood 391c3078f53SLiam Girdwood /* set the ABI header values */ 392c3078f53SLiam Girdwood cdata->data->magic = SOF_ABI_MAGIC; 393c3078f53SLiam Girdwood cdata->data->abi = SOF_ABI_VERSION; 394c3078f53SLiam Girdwood 395c3078f53SLiam Girdwood /* check data size doesn't exceed max coming from topology */ 3965027fe36SPierre-Louis Bossart if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) { 3970e4ea878SGuennadi Liakhovetski dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", 3980e4ea878SGuennadi Liakhovetski cdata->data->size, 3995027fe36SPierre-Louis Bossart be->max - sizeof(struct sof_abi_hdr)); 400b9f8e138SGuennadi Liakhovetski return -EINVAL; 401c3078f53SLiam Girdwood } 402c3078f53SLiam Girdwood 4035027fe36SPierre-Louis Bossart data_size = cdata->data->size + sizeof(struct sof_abi_hdr); 4040e4ea878SGuennadi Liakhovetski 4053331bcd6SPierre-Louis Bossart /* make sure we don't exceed size provided by user space for data */ 4063331bcd6SPierre-Louis Bossart if (data_size > size) 4073331bcd6SPierre-Louis Bossart return -ENOSPC; 4083331bcd6SPierre-Louis Bossart 409dd2fef98SPeter Ujfalusi header.numid = cdata->cmd; 410c3078f53SLiam Girdwood header.length = data_size; 4115027fe36SPierre-Louis Bossart if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) 412b9f8e138SGuennadi Liakhovetski return -EFAULT; 413c3078f53SLiam Girdwood 414c3078f53SLiam Girdwood if (copy_to_user(tlvd->tlv, cdata->data, data_size)) 415b9f8e138SGuennadi Liakhovetski return -EFAULT; 416c3078f53SLiam Girdwood 417b9f8e138SGuennadi Liakhovetski return 0; 418c3078f53SLiam Girdwood } 419