From 88cde10fecaf778fef4940ee87c83cdc96dcb654 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 17 Sep 2024 21:03:56 -0700 Subject: [PATCH] tools: plugin: ctl: Add support for bytes control Add a new field in struct plug_shm_ctl to save the bytes data from topology for bytes controls. This will be used to send the kcontrol data to the modules right after they are instantiated. Fix the tlv callback in the control to handle the tlv bytes read/write when using sof-ctl. Signed-off-by: Ranjani Sridharan --- tools/plugin/README.md | 16 +++- tools/plugin/alsaplug/ctl.c | 140 +++++++++++++++++++++++++++---- tools/plugin/alsaplug/tplg.c | 32 ++++++- tools/plugin/alsaplug/tplg_ctl.c | 13 +++ tools/plugin/common.c | 42 ++++++++++ tools/plugin/common.h | 6 ++ 6 files changed, 232 insertions(+), 17 deletions(-) diff --git a/tools/plugin/README.md b/tools/plugin/README.md index 368b0f499377..673cbceb08ff 100644 --- a/tools/plugin/README.md +++ b/tools/plugin/README.md @@ -79,7 +79,21 @@ or ``` amixer -Dsof:plugin cset numid=1 20 ``` -Right now, only volume controls are supported. Support for bytes and enum controls is pending. +Bytes control data can be set using sof-ctl as follows: + +``` +./sof-ctl -Dsof:plugin -n 4 -r -i 4 -p 2 -s ~/data.txt +``` +where -n is the numid of the kcontrol, -i is the IPC version, -p is the param ID and -s specifies +the data in csv format. + +Bytes control data can be read using sof-ctl as follows: +``` +./sof-ctl -Dsof:plugin -n 4 -i 4 -p 2 +``` +where -n is the numid of the kcontrol, -i is the IPC version and -p is the param ID. + +Note: Bytes controls must have tlv_read/tlv_write and tlv_callback access. # Instructions for testing OpenVino noise suppression model with the SOF plugin: 1. Fetch the model from the Open Model zoo repository ex: noise-suppression-poconetlike-0001.xml diff --git a/tools/plugin/alsaplug/ctl.c b/tools/plugin/alsaplug/ctl.c index c235591a5f1f..f45bf4f9b0e1 100644 --- a/tools/plugin/alsaplug/ctl.c +++ b/tools/plugin/alsaplug/ctl.c @@ -145,10 +145,9 @@ static int plug_ctl_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, // TODO: ?? break; case SND_SOC_TPLG_CTL_BYTES: - printf("%s %d\n", __func__, __LINE__); bytes_ctl = (struct snd_soc_tplg_bytes_control *)hdr; *type = SND_CTL_ELEM_TYPE_BYTES; - *count = bytes_ctl->size; // Not sure if size is correct + *count = bytes_ctl->max; break; } @@ -536,36 +535,147 @@ static int plug_ctl_write_enumerated(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, /* * Bytes ops */ +static int plug_ctl_get_bytes_data(snd_sof_ctl_t *ctl, snd_ctl_ext_key_t key, + struct sof_abi_hdr *abi, unsigned int max_bytes) +{ + struct snd_soc_tplg_bytes_control *bytes_ctl = CTL_GET_TPLG_BYTES(ctl, key); + struct ipc4_module_large_config config = {{ 0 }}; + struct ipc4_module_large_config_reply *reply; + char *reply_data, *data; + void *msg; + uint32_t data_size; + int size, reply_data_size; + int err; + + /* configure the IPC message */ + plug_ctl_ipc_message(&config, abi->type, 0, + ctl->glb->ctl[key].module_id, ctl->glb->ctl[key].instance_id, + SOF_IPC4_MOD_LARGE_CONFIG_GET); + + config.extension.r.final_block = 1; + config.extension.r.init_block = 1; + + size = sizeof(config); + msg = calloc(size, 1); + if (!msg) + return -ENOMEM; + + /* + * reply contains both the requested data and the reply status. Allocate enough memory + * for max data + */ + reply_data_size = sizeof(*reply) + sizeof(*data) + bytes_ctl->max; + reply_data = calloc(reply_data_size, 1); + if (!reply_data_size) { + free(msg); + return -ENOMEM; + } + + /* send the IPC message */ + memcpy(msg, &config, sizeof(config)); + err = plug_mq_cmd_tx_rx(&ctl->ipc_tx, &ctl->ipc_rx, + msg, size, reply_data, reply_data_size); + free(msg); + if (err < 0) { + SNDERR("failed to get bytes data for control %s\n", bytes_ctl->hdr.name); + goto out; + } + + reply = (struct ipc4_module_large_config_reply *)reply_data; + if (reply->primary.r.status != IPC4_SUCCESS) { + SNDERR("bytes control %s get failed with status %d\n", + bytes_ctl->hdr.name, reply->primary.r.status); + err = -EINVAL; + goto out; + } + + /* check data sanity */ + data = (char *)(reply_data + sizeof(*reply)); + data_size = reply->extension.r.data_off_size; + if (data_size > bytes_ctl->max) { + SNDERR("received data size %d is larger than max %d for bytes control %s\n", + data_size, bytes_ctl->max, bytes_ctl->hdr.name); + err = -EINVAL; + goto out; + } + + abi->size = data_size; + + if (data_size) + memcpy(abi->data, data, MIN(data_size, max_bytes)); + + err = data_size; +out: + free(reply_data); + return err; +} + static int plug_ctl_read_bytes(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned char *data, size_t max_bytes) { + snd_sof_ctl_t *ctl = ext->private_data; + struct sof_abi_hdr *abi = (struct sof_abi_hdr *)data; + int data_size; + + data_size = plug_ctl_get_bytes_data(ctl, key, abi, max_bytes); + if (data_size < 0) + return data_size; + return 0; } static int plug_ctl_write_bytes(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned char *data, size_t max_bytes) { + snd_sof_ctl_t *ctl = ext->private_data; + struct snd_soc_tplg_bytes_control *bytes_ctl = CTL_GET_TPLG_BYTES(ctl, key); + struct sof_abi_hdr *abi = (struct sof_abi_hdr *)data; + int err; + + /* send IPC with kcontrol data */ + err = plug_send_bytes_data(&ctl->ipc_tx, &ctl->ipc_rx, + ctl->glb->ctl[key].module_id, ctl->glb->ctl[key].instance_id, + abi); + if (err < 0) { + SNDERR("failed to set bytes data for control %s\n", bytes_ctl->hdr.name); + return err; + } + return 0; } -/* - * TLV ops - * - * The format of an array of \a tlv argument is: - * tlv[0]: Type. One of SND_CTL_TLVT_XXX. - * tlv[1]: Length. The length of value in units of byte. - * tlv[2..]: Value. Depending on the type. - */ +/* TLV ops used for TLV bytes control callback */ static int plug_tlv_rw(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int op_flag, unsigned int numid, unsigned int *tlv, unsigned int tlv_size) { snd_sof_ctl_t *ctl = ext->private_data; - struct snd_soc_tplg_ctl_hdr *hdr = CTL_GET_TPLG_HDR(ctl, key); + struct snd_soc_tplg_bytes_control *bytes_ctl = CTL_GET_TPLG_BYTES(ctl, key); + struct sof_abi_hdr *abi = (struct sof_abi_hdr *)(tlv + 2); /* skip TLV header */ + int data_size; + + /* send IPC with kcontrol data if op_flag is > 0 else send IPC to get kcontrol data */ + if (op_flag) { + int err; + + err = plug_send_bytes_data(&ctl->ipc_tx, &ctl->ipc_rx, + ctl->glb->ctl[key].module_id, + ctl->glb->ctl[key].instance_id, abi); + if (err < 0) { + SNDERR("failed to set bytes data for control %s\n", bytes_ctl->hdr.name); + return err; + } + + return 0; + } + + /* read kcontrol data */ + data_size = plug_ctl_get_bytes_data(ctl, key, abi, tlv_size); + if (data_size < 0) + return data_size; - //TODO: alsamixer showing wrong dB scales - tlv[0] = hdr->tlv.type; - tlv[1] = hdr->tlv.size - sizeof(uint32_t) * 2; - memcpy(&tlv[2], hdr->tlv.data, hdr->tlv.size - sizeof(uint32_t) * 2); + /* set data size and numid */ + tlv[0] = numid; + tlv[1] = data_size + sizeof(*abi); return 0; } diff --git a/tools/plugin/alsaplug/tplg.c b/tools/plugin/alsaplug/tplg.c index 2d60bf90921d..f8f229f563d3 100644 --- a/tools/plugin/alsaplug/tplg.c +++ b/tools/plugin/alsaplug/tplg.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -1105,7 +1106,9 @@ static int plug_set_up_route(snd_sof_plug_t *plug, struct tplg_route_info *route static int plug_set_up_widget(snd_sof_plug_t *plug, struct tplg_comp_info *comp_info) { struct tplg_pipeline_info *pipe_info = comp_info->pipe_info; - int ret; + struct plug_shm_glb_state *glb = plug->glb_ctx.addr; + struct plug_shm_ctl *ctl; + int ret, i; pipe_info->usage_count++; @@ -1123,6 +1126,33 @@ static int plug_set_up_widget(snd_sof_plug_t *plug, struct tplg_comp_info *comp_ if (ret < 0) return ret; + /* send kcontrol bytes data */ + for (i = 0; i < glb->num_ctls; i++) { + struct snd_soc_tplg_bytes_control *tplg_bytes; + struct sof_abi_hdr *abi; + int priv_size; + + ctl = &glb->ctl[i]; + + /* send the bytes data from kcontrols associated with current widget */ + if (ctl->module_id != comp_info->module_id || + ctl->instance_id != comp_info->instance_id || + ctl->type != SND_SOC_TPLG_TYPE_BYTES) + continue; + + tplg_bytes = &ctl->bytes_ctl; + priv_size = tplg_bytes->priv.size; + abi = (struct sof_abi_hdr *)ctl->data; + + /* send IPC with kcontrol data */ + ret = plug_send_bytes_data(&plug->ipc_tx, &plug->ipc_rx, + comp_info->module_id, comp_info->instance_id, abi); + if (ret < 0) { + SNDERR("failed to set bytes data for widget %s\n", comp_info->name); + return ret; + } + } + tplg_debug("widget %s set up\n", comp_info->name); return 0; diff --git a/tools/plugin/alsaplug/tplg_ctl.c b/tools/plugin/alsaplug/tplg_ctl.c index 620c730f82a1..6424b54825ba 100644 --- a/tools/plugin/alsaplug/tplg_ctl.c +++ b/tools/plugin/alsaplug/tplg_ctl.c @@ -202,7 +202,20 @@ int plug_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, void *_comp, voi break; } case SND_SOC_TPLG_CTL_BYTES: + { + struct snd_soc_tplg_bytes_control *tplg_bytes = + (struct snd_soc_tplg_bytes_control *)tplg_ctl; + + glb->size += sizeof(struct plug_shm_ctl); + ctl = &glb->ctl[glb->num_ctls++]; + ctl->module_id = comp_info->module_id; + ctl->instance_id = comp_info->instance_id; + ctl->bytes_ctl = *tplg_bytes; + ctl->index = index; + ctl->type = tplg_ctl->type; + memcpy(ctl->data, tplg_bytes->priv.data, tplg_bytes->priv.size); break; + } case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_CTL_STROBE: default: diff --git a/tools/plugin/common.c b/tools/plugin/common.c index 1674dea5f787..46629f061201 100644 --- a/tools/plugin/common.c +++ b/tools/plugin/common.c @@ -264,3 +264,45 @@ void plug_ctl_ipc_message(struct ipc4_module_large_config *config, int param_id, config->extension.r.data_off_size = size; config->extension.r.large_param_id = param_id; } + +int plug_send_bytes_data(struct plug_mq_desc *ipc_tx, struct plug_mq_desc *ipc_rx, + uint32_t module_id, uint32_t instance_id, struct sof_abi_hdr *abi) +{ + struct ipc4_module_large_config config = {{ 0 }}; + struct ipc4_message_reply reply; + void *msg; + int msg_size; + int err; + + /* configure the IPC message */ + plug_ctl_ipc_message(&config, abi->type, abi->size, module_id, instance_id, + SOF_IPC4_MOD_LARGE_CONFIG_SET); + + config.extension.r.final_block = 1; + config.extension.r.init_block = 1; + + /* allocate memory for IPC message */ + msg_size = sizeof(config) + abi->size; + msg = calloc(msg_size, 1); + if (!msg) + return -ENOMEM; + + /* set the IPC message data */ + memcpy(msg, &config, sizeof(config)); + memcpy(msg + sizeof(config), abi->data, abi->size); + + /* send the message and check status */ + err = plug_mq_cmd_tx_rx(ipc_tx, ipc_rx, msg, msg_size, &reply, sizeof(reply)); + free(msg); + if (err < 0) { + SNDERR("failed to send IPC to set bytes data\n"); + return err; + } + + if (reply.primary.r.status != IPC4_SUCCESS) { + SNDERR("IPC failed with status %d\n", reply.primary.r.status); + return -EINVAL; + } + + return 0; +} diff --git a/tools/plugin/common.h b/tools/plugin/common.h index f849676fbcc8..002f3843c3e7 100644 --- a/tools/plugin/common.h +++ b/tools/plugin/common.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include /* temporary - current MAXLEN is not define in UAPI header - fix pending */ #ifndef SNDRV_CTL_ELEM_ID_NAME_MAXLEN @@ -52,6 +54,7 @@ #define SOF_MAGIC "sofpipe" #define MAX_VOLUME_SIZE 120 +#define MAX_DATA_SIZE 512 enum plugin_state { SOF_PLUGIN_STATE_INIT = 0, @@ -67,6 +70,7 @@ struct plug_shm_ctl { unsigned int type; unsigned int volume_table[MAX_VOLUME_SIZE]; unsigned int index; + char data[MAX_DATA_SIZE]; union { struct snd_soc_tplg_mixer_control mixer_ctl; struct snd_soc_tplg_enum_control enum_ctl; @@ -320,5 +324,7 @@ static inline void data_dump(void *vdata, size_t bytes) void plug_ctl_ipc_message(struct ipc4_module_large_config *config, int param_id, size_t size, uint32_t module_id, uint32_t instance_id, uint32_t type); +int plug_send_bytes_data(struct plug_mq_desc *ipc_tx, struct plug_mq_desc *ipc_rx, + uint32_t module_id, uint32_t instance_id, struct sof_abi_hdr *abi); #endif