Skip to content

Commit

Permalink
tools: plugin: ctl: Add support for bytes control
Browse files Browse the repository at this point in the history
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 <ranjani.sridharan@linux.intel.com>
  • Loading branch information
ranj063 committed Sep 18, 2024
1 parent 03715b2 commit d5ea317
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 17 deletions.
16 changes: 15 additions & 1 deletion tools/plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
140 changes: 125 additions & 15 deletions tools/plugin/alsaplug/ctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down
32 changes: 31 additions & 1 deletion tools/plugin/alsaplug/tplg.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <assert.h>
#include <errno.h>
#include <dlfcn.h>
#include <kernel/header.h>

#include <ipc4/error_status.h>

Expand Down Expand Up @@ -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++;

Expand All @@ -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;
Expand Down
13 changes: 13 additions & 0 deletions tools/plugin/alsaplug/tplg_ctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
42 changes: 42 additions & 0 deletions tools/plugin/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
6 changes: 6 additions & 0 deletions tools/plugin/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <mqueue.h>
#include <semaphore.h>
#include <alsa/asoundlib.h>
#include <ipc/control.h>
#include <tplg_parser/topology.h>

/* temporary - current MAXLEN is not define in UAPI header - fix pending */
#ifndef SNDRV_CTL_ELEM_ID_NAME_MAXLEN
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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

0 comments on commit d5ea317

Please sign in to comment.