From 3eac8de3f51b3567c4ba6139736b59027da56285 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 13 Mar 2023 13:03:38 +0200 Subject: ASoC: SOF: ipc3-control: Rename snd_sof_refresh_control() Rename the snd_sof_refresh_control() to sof_ipc3_refresh_control() to follow the function naming convention for IPC specific code. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20230313110344.16644-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-control.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 217ac5501a98..4e9d04124145 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -104,7 +104,7 @@ unlock: return ret; } -static void snd_sof_refresh_control(struct snd_sof_control *scontrol) +static void sof_ipc3_refresh_control(struct snd_sof_control *scontrol) { struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; @@ -138,7 +138,7 @@ static int sof_ipc3_volume_get(struct snd_sof_control *scontrol, unsigned int channels = scontrol->num_channels; unsigned int i; - snd_sof_refresh_control(scontrol); + sof_ipc3_refresh_control(scontrol); /* read back each channel */ for (i = 0; i < channels; i++) @@ -189,7 +189,7 @@ static int sof_ipc3_switch_get(struct snd_sof_control *scontrol, unsigned int channels = scontrol->num_channels; unsigned int i; - snd_sof_refresh_control(scontrol); + sof_ipc3_refresh_control(scontrol); /* read back each channel */ for (i = 0; i < channels; i++) @@ -237,7 +237,7 @@ static int sof_ipc3_enum_get(struct snd_sof_control *scontrol, unsigned int channels = scontrol->num_channels; unsigned int i; - snd_sof_refresh_control(scontrol); + sof_ipc3_refresh_control(scontrol); /* read back each channel */ for (i = 0; i < channels; i++) @@ -286,7 +286,7 @@ static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol, struct sof_abi_hdr *data = cdata->data; size_t size; - snd_sof_refresh_control(scontrol); + sof_ipc3_refresh_control(scontrol); if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", @@ -352,7 +352,7 @@ static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, struct snd_ctl_tlv header; size_t data_size; - snd_sof_refresh_control(scontrol); + sof_ipc3_refresh_control(scontrol); /* * Decrement the limit by ext bytes header size to -- cgit v1.2.3 From 76fc628aebdb452e3c620d9ff5c5e9448d316754 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 13 Mar 2023 13:03:39 +0200 Subject: ASoC: SOF: ipc3-control: Merge functions to handle bytes_ext get variants The code for bytes_ext_get and bytes_ext_volatile_get is identical with the only difference is that in case of volatile_get we refresh the data from the DSP before returning it to user space. Convert the callbacks to a simple wrapper for the same function. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20230313110344.16644-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-control.c | 80 +++++++++++++------------------------------- 1 file changed, 23 insertions(+), 57 deletions(-) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 4e9d04124145..ad040e7bb850 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -343,55 +343,6 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, return 0; } -static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, - const unsigned int __user *binary_data, unsigned int size) -{ - struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct snd_soc_component *scomp = scontrol->scomp; - struct snd_ctl_tlv header; - size_t data_size; - - sof_ipc3_refresh_control(scontrol); - - /* - * Decrement the limit by ext bytes header size to - * ensure the user space buffer is not exceeded. - */ - if (size < sizeof(struct snd_ctl_tlv)) - return -ENOSPC; - - size -= sizeof(struct snd_ctl_tlv); - - /* set the ABI header values */ - cdata->data->magic = SOF_ABI_MAGIC; - cdata->data->abi = SOF_ABI_VERSION; - - /* check data size doesn't exceed max coming from topology */ - if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { - dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", - cdata->data->size, - scontrol->max_size - sizeof(struct sof_abi_hdr)); - return -EINVAL; - } - - data_size = cdata->data->size + sizeof(struct sof_abi_hdr); - - /* make sure we don't exceed size provided by user space for data */ - if (data_size > size) - return -ENOSPC; - - header.numid = cdata->cmd; - header.length = data_size; - if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) - return -EFAULT; - - if (copy_to_user(tlvd->tlv, cdata->data, data_size)) - return -EFAULT; - - return 0; -} - static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, const unsigned int __user *binary_data, unsigned int size) @@ -457,16 +408,15 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, return 0; } -static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, - const unsigned int __user *binary_data, - unsigned int size) +static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, + unsigned int size, bool from_dsp) { struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; struct snd_ctl_tlv header; size_t data_size; - int ret; /* * Decrement the limit by ext bytes header size to @@ -482,9 +432,12 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, cdata->data->abi = SOF_ABI_VERSION; /* get all the component data from DSP */ - ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true); - if (ret < 0) - return ret; + if (from_dsp) { + int ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true); + + if (ret < 0) + return ret; + } /* check data size doesn't exceed max coming from topology */ if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { @@ -508,7 +461,20 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, if (copy_to_user(tlvd->tlv, cdata->data, data_size)) return -EFAULT; - return ret; + return 0; +} + +static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, unsigned int size) +{ + return _sof_ipc3_bytes_ext_get(scontrol, binary_data, size, false); +} + +static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, + unsigned int size) +{ + return _sof_ipc3_bytes_ext_get(scontrol, binary_data, size, true); } static void snd_sof_update_control(struct snd_sof_control *scontrol, -- cgit v1.2.3 From 2e4ef6f4798c1d2951dd7bb3ae5f0d41ec3d31e8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 13 Mar 2023 13:03:40 +0200 Subject: ASoC: SOF: uapi: header: Convert sof_abi_hdr comments to kernel style Replace the comments for sof_abi_hdr to kernel style. Signed-off-by: Peter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Jaska Uimonen Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20230313110344.16644-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/header.h | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/include/uapi/sound/sof/header.h b/include/uapi/sound/sof/header.h index e9bba93a5399..e53b62b9e2c5 100644 --- a/include/uapi/sound/sof/header.h +++ b/include/uapi/sound/sof/header.h @@ -11,19 +11,25 @@ #include -/* - * Header for all non IPC ABI data. +/** + * struct sof_abi_hdr - Header for all non IPC ABI data. + * @magic: Magic number for validation: 0x00464F53 ('S', 'O', 'F', '\0') + * @type: Component specific type + * @size: The size in bytes of the data, excluding this struct + * @abi: SOF ABI version + * @reserved: Reserved for future use + * @data: Component data - opaque to core * * Identifies data type, size and ABI. * Used by any bespoke component data structures or binary blobs. */ struct sof_abi_hdr { - __u32 magic; /**< 'S', 'O', 'F', '\0' */ - __u32 type; /**< component specific type */ - __u32 size; /**< size in bytes of data excl. this struct */ - __u32 abi; /**< SOF ABI version */ - __u32 reserved[4]; /**< reserved for future use */ - __u32 data[]; /**< Component data - opaque to core */ + __u32 magic; + __u32 type; + __u32 size; + __u32 abi; + __u32 reserved[4]; + __u32 data[]; } __packed; #define SOF_MANIFEST_DATA_TYPE_NHLT 1 -- cgit v1.2.3 From ea4a4e82f625ae451175a2a74779776b006d25a1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 13 Mar 2023 13:03:41 +0200 Subject: ASoC: SOF: uapi: header: Update sof_abi_hdr doc for IPC4 use With IPC4 the sof_abit_hdr is only used between user space (and in topology) and kernel. The same abi header is used with small differencies like different magic number and the type field have slightly different name, but similar function in IPC4 (param_id). Update the kernel documentation to highlight the differences. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Jaska Uimonen Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20230313110344.16644-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/abi.h | 2 ++ include/uapi/sound/sof/header.h | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h index 3566630ca965..45c657c3919e 100644 --- a/include/uapi/sound/sof/abi.h +++ b/include/uapi/sound/sof/abi.h @@ -60,5 +60,7 @@ /* SOF ABI magic number "SOF\0". */ #define SOF_ABI_MAGIC 0x00464F53 +/* SOF IPC4 ABI magic number "SOF4". */ +#define SOF_IPC4_ABI_MAGIC 0x34464F53 #endif diff --git a/include/uapi/sound/sof/header.h b/include/uapi/sound/sof/header.h index e53b62b9e2c5..cb3c1ace69e3 100644 --- a/include/uapi/sound/sof/header.h +++ b/include/uapi/sound/sof/header.h @@ -13,10 +13,15 @@ /** * struct sof_abi_hdr - Header for all non IPC ABI data. - * @magic: Magic number for validation: 0x00464F53 ('S', 'O', 'F', '\0') - * @type: Component specific type + * @magic: Magic number for validation + * for IPC3 data: 0x00464F53 ('S', 'O', 'F', '\0') + * for IPC4 data: 0x34464F53 ('S', 'O', 'F', '4') + * @type: module specific parameter + * for IPC3: Component specific type + * for IPC4: parameter ID (param_id) of the data * @size: The size in bytes of the data, excluding this struct - * @abi: SOF ABI version + * @abi: SOF ABI version. The version is valid in scope of the 'magic', IPC3 and + * IPC4 ABI version numbers have no relationship. * @reserved: Reserved for future use * @data: Component data - opaque to core * -- cgit v1.2.3 From dc47ef4f8f6426cd625eb4057e174541e7c23ccd Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Mon, 13 Mar 2023 13:03:42 +0200 Subject: ASoC: SOF: ipc4-control: set_volume_data only applies to VOLSW family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure sof_ipc4_set_volume_data() is only called for the SND_SOC_TPLG_CTL_VOLSW, SND_SOC_TPLG_CTL_VOLSW_SX and SND_SOC_TPLG_CTL_VOLSW_XR_SX info_type. Signed-off-by: Libin Yang Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20230313110344.16644-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-control.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 67bd2233fd9a..c978325db416 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -186,15 +186,25 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s struct snd_sof_control *scontrol; int ret; - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { - ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false); - if (ret < 0) { - dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n", - __func__, scontrol->comp_id, swidget->widget->name); - return ret; + switch (scontrol->info_type) { + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + ret = sof_ipc4_set_volume_data(sdev, swidget, + scontrol, false); + if (ret < 0) { + dev_err(sdev->dev, "kcontrol %d set up failed for widget %s\n", + scontrol->comp_id, swidget->widget->name); + return ret; + } + break; + default: + break; } } + } return 0; } -- cgit v1.2.3 From a382082ff74b036944cbc5b6ad29b65f633acd3a Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Mon, 13 Mar 2023 13:03:43 +0200 Subject: ASoC: SOF: ipc4-topology: Add support for TPLG_CTL_BYTES MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add byte type support for IPC4. The bytes controls are used to transfer configuration blobs to/from firmware via large_config messages. Signed-off-by: Libin Yang Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20230313110344.16644-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 67 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index cb75f685e722..d7ce95b9fa0f 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1502,6 +1502,71 @@ static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof return 0; } +static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + struct sof_ipc4_control_data *control_data; + struct sof_ipc4_msg *msg; + int ret; + + if (scontrol->max_size < (sizeof(*control_data) + sizeof(struct sof_abi_hdr))) { + dev_err(sdev->dev, "insufficient size for a bytes control %s: %zu.\n", + scontrol->name, scontrol->max_size); + return -EINVAL; + } + + if (scontrol->priv_size > scontrol->max_size - sizeof(*control_data)) { + dev_err(sdev->dev, "scontrol %s bytes data size %zu exceeds max %zu.\n", + scontrol->name, scontrol->priv_size, + scontrol->max_size - sizeof(*control_data)); + return -EINVAL; + } + + scontrol->size = sizeof(struct sof_ipc4_control_data) + scontrol->priv_size; + + scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL); + if (!scontrol->ipc_control_data) + return -ENOMEM; + + control_data = scontrol->ipc_control_data; + control_data->index = scontrol->index; + if (scontrol->priv_size > 0) { + memcpy(control_data->data, scontrol->priv, scontrol->priv_size); + kfree(scontrol->priv); + scontrol->priv = NULL; + + if (control_data->data->magic != SOF_IPC4_ABI_MAGIC) { + dev_err(sdev->dev, "Wrong ABI magic (%#x) for control: %s\n", + control_data->data->magic, scontrol->name); + ret = -EINVAL; + goto err; + } + + /* TODO: check the ABI version */ + + if (control_data->data->size + sizeof(struct sof_abi_hdr) != + scontrol->priv_size) { + dev_err(sdev->dev, "Control %s conflict in bytes %zu vs. priv size %zu.\n", + scontrol->name, + control_data->data->size + sizeof(struct sof_abi_hdr), + scontrol->priv_size); + ret = -EINVAL; + goto err; + } + } + + msg = &control_data->msg; + msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET); + msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + + return 0; + +err: + kfree(scontrol->ipc_control_data); + scontrol->ipc_control_data = NULL; + return ret; +} + static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) { switch (scontrol->info_type) { @@ -1509,6 +1574,8 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: return sof_ipc4_control_load_volume(sdev, scontrol); + case SND_SOC_TPLG_CTL_BYTES: + return sof_ipc4_control_load_bytes(sdev, scontrol); default: break; } -- cgit v1.2.3 From a062c8899fede2ab5660a817e9b602d3fa280a99 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 13 Mar 2023 13:03:44 +0200 Subject: ASoC: SOF: ipc4-control: Add support for bytes control get and put Add support for bytes control by implementing bytes_get/put and bytes_ext_get/put and blobs with either module init instance or large config type. For module init instance type the put will only update the stored configuration blob and it is going to be taken into use next time the module is (re-)initialized. Large config type of blobs are sent to the firmware whenever the DSP is powered up. Signed-off-by: Peter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Jaska Uimonen Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20230313110344.16644-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-control.c | 248 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 241 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index c978325db416..38c2531e2f09 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -180,11 +180,237 @@ static int sof_ipc4_volume_get(struct snd_sof_control *scontrol, return 0; } +static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, + struct snd_sof_control *scontrol, + bool set, bool lock) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct sof_abi_hdr *data = cdata->data; + struct sof_ipc4_msg *msg = &cdata->msg; + int ret = 0; + + /* Send the new data to the firmware only if it is powered up */ + if (set && !pm_runtime_active(sdev->dev)) + return 0; + + msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type); + + msg->data_ptr = data->data; + msg->data_size = data->size; + + ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock); + if (ret < 0) + dev_err(sdev->dev, "Failed to %s for %s\n", + set ? "set bytes update" : "get bytes", + scontrol->name); + + msg->data_ptr = NULL; + msg->data_size = 0; + + return ret; +} + +static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_abi_hdr *data = cdata->data; + size_t size; + + if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { + dev_err_ratelimited(scomp->dev, + "data max %zu exceeds ucontrol data array size\n", + scontrol->max_size); + return -EINVAL; + } + + /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (data->size > scontrol->max_size - sizeof(*data)) { + dev_err_ratelimited(scomp->dev, + "data size too big %u bytes max is %zu\n", + data->size, scontrol->max_size - sizeof(*data)); + return -EINVAL; + } + + size = data->size + sizeof(*data); + + /* copy from kcontrol */ + memcpy(data, ucontrol->value.bytes.data, size); + + sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); + + return 0; +} + +static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_abi_hdr *data = cdata->data; + size_t size; + + if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { + dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", + scontrol->max_size); + return -EINVAL; + } + + if (data->size > scontrol->max_size - sizeof(*data)) { + dev_err_ratelimited(scomp->dev, + "%u bytes of control data is invalid, max is %zu\n", + data->size, scontrol->max_size - sizeof(*data)); + return -EINVAL; + } + + size = data->size + sizeof(*data); + + /* copy back to kcontrol */ + memcpy(ucontrol->value.bytes.data, data, size); + + return 0; +} + +static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, + unsigned int size) +{ + struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_abi_hdr *data = cdata->data; + struct sof_abi_hdr abi_hdr; + struct snd_ctl_tlv header; + + /* + * The beginning of bytes data contains a header from where + * the length (as bytes) is needed to know the correct copy + * length of data from tlvd->tlv. + */ + if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv))) + return -EFAULT; + + /* make sure TLV info is consistent */ + if (header.length + sizeof(struct snd_ctl_tlv) > size) { + dev_err_ratelimited(scomp->dev, + "Inconsistent TLV, data %d + header %zu > %d\n", + header.length, sizeof(struct snd_ctl_tlv), size); + return -EINVAL; + } + + /* be->max is coming from topology */ + if (header.length > scontrol->max_size) { + dev_err_ratelimited(scomp->dev, + "Bytes data size %d exceeds max %zu\n", + header.length, scontrol->max_size); + return -EINVAL; + } + + /* Verify the ABI header first */ + if (copy_from_user(&abi_hdr, tlvd->tlv, sizeof(abi_hdr))) + return -EFAULT; + + if (abi_hdr.magic != SOF_IPC4_ABI_MAGIC) { + dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", + abi_hdr.magic); + return -EINVAL; + } + + if (abi_hdr.size > scontrol->max_size - sizeof(abi_hdr)) { + dev_err_ratelimited(scomp->dev, + "%u bytes of control data is invalid, max is %zu\n", + abi_hdr.size, scontrol->max_size - sizeof(abi_hdr)); + return -EINVAL; + } + + /* Copy the whole binary data which includes the ABI header and the payload */ + if (copy_from_user(data, tlvd->tlv, header.length)) + return -EFAULT; + + sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); + + return 0; +} + +static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, + unsigned int size, bool from_dsp) +{ + struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_abi_hdr *data = cdata->data; + struct snd_ctl_tlv header; + size_t data_size; + + /* + * Decrement the limit by ext bytes header size to ensure the user space + * buffer is not exceeded. + */ + if (size < sizeof(struct snd_ctl_tlv)) + return -ENOSPC; + + size -= sizeof(struct snd_ctl_tlv); + + /* get all the component data from DSP */ + if (from_dsp) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + int ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, false, true); + + if (ret < 0) + return ret; + + /* Set the ABI magic (if the control is not initialized) */ + data->magic = SOF_IPC4_ABI_MAGIC; + } + + if (data->size > scontrol->max_size - sizeof(*data)) { + dev_err_ratelimited(scomp->dev, + "%u bytes of control data is invalid, max is %zu\n", + data->size, scontrol->max_size - sizeof(*data)); + return -EINVAL; + } + + data_size = data->size + sizeof(struct sof_abi_hdr); + + /* make sure we don't exceed size provided by user space for data */ + if (data_size > size) + return -ENOSPC; + + header.numid = scontrol->comp_id; + header.length = data_size; + + if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) + return -EFAULT; + + if (copy_to_user(tlvd->tlv, data, data_size)) + return -EFAULT; + + return 0; +} + +static int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, + unsigned int size) +{ + return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false); +} + +static int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, + unsigned int size) +{ + return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, true); +} + /* set up all controls for the widget */ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { struct snd_sof_control *scontrol; - int ret; + int ret = 0; list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { @@ -194,11 +420,10 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s case SND_SOC_TPLG_CTL_VOLSW_XR_SX: ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false); - if (ret < 0) { - dev_err(sdev->dev, "kcontrol %d set up failed for widget %s\n", - scontrol->comp_id, swidget->widget->name); - return ret; - } + break; + case SND_SOC_TPLG_CTL_BYTES: + ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, + true, false); break; default: break; @@ -206,7 +431,11 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s } } - return 0; + if (ret < 0) + dev_err(sdev->dev, "kcontrol %d set up failed for widget %s\n", + scontrol->comp_id, swidget->widget->name); + + return ret; } static int @@ -234,6 +463,11 @@ sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_I const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { .volume_put = sof_ipc4_volume_put, .volume_get = sof_ipc4_volume_get, + .bytes_put = sof_ipc4_bytes_put, + .bytes_get = sof_ipc4_bytes_get, + .bytes_ext_put = sof_ipc4_bytes_ext_put, + .bytes_ext_get = sof_ipc4_bytes_ext_get, + .bytes_ext_volatile_get = sof_ipc4_bytes_ext_volatile_get, .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup, .set_up_volume_table = sof_ipc4_set_up_volume_table, }; -- cgit v1.2.3