Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for byte controls in the SOF plugin #9484

Merged
merged 6 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tools/ctl/ctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,8 @@ static int ctl_set_get(struct ctl_data *ctl_data)
fprintf(stdout, "Retrieving configuration for ");
fprintf(stdout, "device %s control %s.\n",
ctl_data->dev, ctl_data->cname);
/* set the ABI header to pass the param ID */
header_init(ctl_data);
singalsu marked this conversation as resolved.
Show resolved Hide resolved
ctl_data->buffer[BUFFER_SIZE_OFFSET] = ctl_data->ctrl_size;
ret = snd_ctl_elem_tlv_read(ctl_data->ctl, ctl_data->id,
ctl_data->buffer,
Expand Down
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
154 changes: 125 additions & 29 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 @@ -198,20 +197,6 @@ static int plug_ctl_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
return err;
}

static 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)
{
config->primary.r.type = type;
config->primary.r.msg_tgt = SOF_IPC4_MESSAGE_TARGET_MODULE_MSG;
config->primary.r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REQUEST;
config->primary.r.module_id = module_id;
config->primary.r.instance_id = instance_id;

config->extension.r.data_off_size = size;
config->extension.r.large_param_id = param_id;
}

static int plug_ctl_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value)
{
snd_sof_ctl_t *ctl = ext->private_data;
Expand Down Expand Up @@ -550,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
73 changes: 0 additions & 73 deletions tools/plugin/alsaplug/plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,79 +41,6 @@
#include "plugin.h"
#include "common.h"

int plug_mq_cmd_tx_rx(struct plug_mq_desc *ipc_tx, struct plug_mq_desc *ipc_rx,
void *msg, size_t len, void *reply, size_t rlen)
{
struct timespec ts;
ssize_t ipc_size;
char mailbox[IPC3_MAX_MSG_SIZE];
int err;

if (len > IPC3_MAX_MSG_SIZE) {
SNDERR("ipc: message too big %d\n", len);
return -EINVAL;
}
memset(mailbox, 0, IPC3_MAX_MSG_SIZE);
memcpy(mailbox, msg, len);

/* wait for sof-pipe reader to consume data or timeout */
err = clock_gettime(CLOCK_REALTIME, &ts);
if (err == -1) {
SNDERR("ipc: cant get time: %s", strerror(errno));
return -errno;
}

/* IPCs should be read under 10ms */
plug_timespec_add_ms(&ts, 10);

/* now return message completion status */
err = mq_timedsend(ipc_tx->mq, mailbox, IPC3_MAX_MSG_SIZE, 0, &ts);
if (err == -1) {
SNDERR("error: timeout can't send IPC message queue %s : %s\n",
ipc_tx->queue_name, strerror(errno));
return -errno;
}

/* wait for sof-pipe reader to consume data or timeout */
err = clock_gettime(CLOCK_REALTIME, &ts);
if (err == -1) {
SNDERR("ipc: cant get time: %s", strerror(errno));
return -errno;
}

/* IPCs should be processed under 20ms, but wait longer as
* some can take longer especially in valgrind
*/
plug_timespec_add_ms(&ts, 20);

ipc_size = mq_timedreceive(ipc_rx->mq, mailbox, IPC3_MAX_MSG_SIZE, NULL, &ts);
if (ipc_size == -1) {
//fprintf(stderr, "dbg: timeout can't read IPC message queue %s : %s retrying\n",
// ipc->queue_name, strerror(errno));

/* ok, its a long IPC or valgrind, wait longer */
plug_timespec_add_ms(&ts, 800);

ipc_size = mq_timedreceive(ipc_rx->mq, mailbox, IPC3_MAX_MSG_SIZE, NULL, &ts);
if (ipc_size == -1) {
SNDERR("error: timeout can't read IPC message queue %s : %s\n",
ipc_rx->queue_name, strerror(errno));
return -errno;
}

/* needed for valgrind to complete MQ op before next client IPC */
ts.tv_nsec = 20 * 1000 * 1000;
ts.tv_sec = 0;
nanosleep(&ts, NULL);
}

/* do the message work */
if (rlen && reply)
memcpy(reply, mailbox, rlen);

return 0;
}

int plug_mq_cmd(struct plug_mq_desc *ipc, void *msg, size_t len, void *reply, size_t rlen)
{
return plug_mq_cmd_tx_rx(ipc, ipc, msg, len, reply, rlen);
Expand Down
49 changes: 47 additions & 2 deletions 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 @@ -312,7 +313,22 @@ static int plug_new_process(snd_sof_plug_t *plug)

plug_setup_widget_ipc_msg(comp_info);

return 0;
tplg_ctl = calloc(ctx->hdr->payload_size, 1);
if (!tplg_ctl) {
free(comp_info->ipc_payload);
return -ENOMEM;
}

/* set up kcontrols */
ret = tplg_create_controls(ctx, ctx->widget->num_kcontrols,
tplg_ctl, ctx->hdr->payload_size, comp_info);
if (ret < 0) {
SNDERR("failed to create controls for process comp\n");
free(comp_info->ipc_payload);
}

free(tplg_ctl);
ranj063 marked this conversation as resolved.
Show resolved Hide resolved
return ret;
}

static int plug_new_pipeline(snd_sof_plug_t *plug)
Expand Down Expand Up @@ -1090,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 @@ -1108,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
Loading
Loading