Skip to content
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
35 changes: 35 additions & 0 deletions tools/testbench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,41 @@ tools/testbench/build_testbench/install/bin/sof-testbench4 -r 48000 -c 2 -b S32_
sox --encoding signed-integer -L -r 48000 -c 2 -b 32 out.raw out.wav
```

As second example apply in one second intervals bytes control blobs
for IIR equalizer. The impact is easiest to hear with pink noise
signal. Create the control script below and run the following commands
to create the input, run testbench, and convert to wav for examining
the output.

```
#!/bin/sh

# Example test sequence for IIR equalizer, switch other processing off

amixer -c0 cset name='Post Mixer Analog Playback DRC switch' off
amixer -c0 cset name='Post Mixer Analog Playback Volume' 45
sof-ctl -c name='Post Mixer Analog Playback FIR Eq bytes' -s tools/ctl/ipc4/eq_fir/pass.txt
sof-ctl -c name='Post Mixer Analog Playback IIR Eq bytes' -s tools/ctl/ipc4/eq_iir/pass.txt
sleep 1
sof-ctl -c name='Post Mixer Analog Playback IIR Eq bytes' -s tools/ctl/ipc4/eq_iir/loudness.txt
sleep 1
sof-ctl -c name='Post Mixer Analog Playback IIR Eq bytes' -s tools/ctl/ipc4/eq_iir/bandpass.txt
sleep 1
sof-ctl -c name='Post Mixer Analog Playback IIR Eq bytes' -s tools/ctl/ipc4/eq_iir/bassboost.txt
sleep 1
sof-ctl -c name='Post Mixer Analog Playback IIR Eq bytes' -s tools/ctl/ipc4/eq_iir/highpass_50hz_0db_48khz.txt
```

```
sox -n --encoding signed-integer -L -r 48000 -c 2 -b 32 in.raw synth 5 pinknoise norm -20

tools/testbench/build_testbench/install/bin/sof-testbench4 -r 48000 -c 2 -b S32_LE -p 1,2 \
-t tools/build_tools/topology/topology2/production/sof-hda-generic.tplg \
-i in.raw -o out.raw -s controls.sh

sox --encoding signed-integer -L -r 48000 -c 2 -b 32 out.raw out.wav
```

### Run testbench with helper script

The scripts/sof-testbench-helper.sh simplifies the task. See the help
Expand Down
2 changes: 2 additions & 0 deletions tools/testbench/include/testbench/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#define TB_MAX_CTL_NAME_CHARS 128
#define TB_MAX_VOLUME_SIZE 120
#define TB_MAX_BYTES_DATA_SIZE 8192
#define TB_MAX_BLOB_CONTENT_CHARS 32768

/* number of widgets types supported in testbench */
#define TB_NUM_WIDGETS_SUPPORTED 16
Expand Down Expand Up @@ -159,6 +160,7 @@ int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p);
int tb_pipeline_start(struct ipc *ipc, struct pipeline *p);
int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p);
int tb_read_controls(struct testbench_prm *tp, int64_t *sleep_ns);
int tb_set_bytes_control(struct testbench_prm *tp, struct tb_ctl *ctl, uint32_t *data);
int tb_set_enum_control(struct testbench_prm *tp, struct tb_ctl *ctl, char *control_params);
int tb_set_reset_state(struct testbench_prm *tp);
int tb_set_mixer_control(struct testbench_prm *tp, struct tb_ctl *ctl, char *control_params);
Expand Down
146 changes: 144 additions & 2 deletions tools/testbench/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <sof/lib/notifier.h>

#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
Expand Down Expand Up @@ -388,10 +389,141 @@ static int tb_parse_amixer(struct testbench_prm *tp, char *line)
return ret;
}

static int tb_parse_sofctl(struct testbench_prm *tp, char *line)
{
struct tb_ctl *ctl;
uint32_t *blob_bin = NULL;
char *blob_name = NULL;
char *blob_str = NULL;
char *control_name = NULL;
char *end;
char *find_ctl_name_str = "-c name=\"";
char *find_end_str = "\" ";
char *find_set_switch_str = "-s";
char *name_str;
char *rest;
char *token;
int copy_len;
int find_len = strlen(find_ctl_name_str);
int n = 0;
int ret = 0;
FILE *fh;

name_str = strstr(line, find_ctl_name_str);
if (!name_str) {
fprintf(stderr, "error: no control name in script line: %s\n", line);
return -EINVAL;
}

end = strstr(&name_str[find_len], find_end_str);
if (!end) {
fprintf(stderr, "error: no control name end quote in script line: %s\n", line);
return -EINVAL;
}

copy_len = end - name_str - find_len;
control_name = strndup(name_str + find_len, copy_len);
if (!control_name) {
fprintf(stderr, "error: failed to duplicate control name.\n");
return -errno;
}

name_str = strstr(line, find_set_switch_str);
if (!name_str) {
fprintf(stderr, "error: no sof-ctl control set switch in command: %s.\n",
line);
ret = -EINVAL;
goto err;
}

name_str += strlen(find_set_switch_str) + 1;
end = line + strlen(line);
copy_len = end - name_str;
blob_name = strndup(name_str, copy_len);
Copy link

Copilot AI May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider trimming any leading or trailing whitespace from the extracted blob_name before using it to open a file, which can prevent file open failures if the command line has extra spaces.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current released blob files in sof-ctl path do not have whitespace. Maybe need to add later support for quoted paths and filenames to support blanks and special characters with help of some parser lib to avoid to reinvent it but for now I'd not like to to that.

if (!blob_name) {
fprintf(stderr, "error: failed to duplicate blob name.\n");
ret = -errno;
goto err;
}

ctl = tb_find_control_by_name(tp, control_name);
if (!ctl) {
fprintf(stderr, "error: control %s not found in topology.\n", control_name);
ret = -EINVAL;
goto err;
}

if (ctl->type != SND_SOC_TPLG_TYPE_BYTES) {
fprintf(stderr, "error: control %s type %d is not supported.\n",
control_name, ctl->type);
ret = -EINVAL;
goto err;
}

blob_str = malloc(TB_MAX_BLOB_CONTENT_CHARS);
if (!blob_str) {
fprintf(stderr, "error: failed to allocate memory for blob file content.\n");
ret = -ENOMEM;
goto err;
}

blob_bin = malloc(TB_MAX_BYTES_DATA_SIZE);
if (!blob_bin) {
fprintf(stderr, "error: failed to allocate memory for blob data.\n");
ret = -ENOMEM;
goto err;
}

printf("Info: Setting control name '%s' to blob '%s'\n", control_name, blob_name);
fh = fopen(blob_name, "r");
if (!fh) {
fprintf(stderr, "error: could not open file.\n");
ret = -errno;
goto err;
}

end = fgets(blob_str, TB_MAX_BLOB_CONTENT_CHARS, fh);
fclose(fh);
if (!end) {
fprintf(stderr, "error: failed to read data from blob file.\n");
ret = -ENODATA;
goto err;
}

rest = blob_str;
while ((token = strtok_r(rest, ",", &rest))) {
if (n == TB_MAX_BYTES_DATA_SIZE) {
fprintf(stderr, "error: data read exceeds max control data size.\n");
ret = -EINVAL;
goto err;
}

blob_bin[n] = atoi(token);
Copy link

Copilot AI May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using strtol instead of atoi for converting blob file tokens to uint32_t values, to enable robust error checking in case of conversion failures.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@singalsu I assume code is already validating blob_bin[n] here ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this necessary, strtol() could be used to error possible integers over int32_t range. It also supports other numbers than 10-base decimal. There's no such mistakes in computer generated blobs and I don't see security issues from illegal blobs in testbench runtime with user's rights.

n++;
}

Copy link

Copilot AI May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that at least two comma-separated values have been parsed before accessing blob_bin at an offset of 2 to avoid potential out-of-bounds issues. Consider adding a check on the value of 'n' before calling tb_set_bytes_control.

Suggested change
/* Ensure at least two values have been parsed before accessing blob_bin[2]. */
if (n < 2) {
fprintf(stderr, "error: insufficient data in blob file. At least two values are required.\n");
ret = -EINVAL;
goto err;
}

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I'll add this.

if (n < 2) {
fprintf(stderr, "error: at least two values are required in the blob file.\n");
ret = -EINVAL;
goto err;
}

/* Ignore TLV header from beginning. */
ret = tb_set_bytes_control(tp, ctl, &blob_bin[2]);

err:
free(blob_str);
free(blob_bin);
free(blob_name);
free(control_name);
return ret;
}

int tb_read_controls(struct testbench_prm *tp, int64_t *sleep_ns)
{
char *sleep_cmd = "sleep ";
char *amixer_cmd = "amixer ";
char *sofctl_cmd = "sof-ctl ";
char *raw_line;
char *line;
int ret = 0;
Expand All @@ -411,7 +543,7 @@ int tb_read_controls(struct testbench_prm *tp, int64_t *sleep_ns)
if (line[0] == '#' || strlen(line) == 0)
continue;

if (strncmp(line, sleep_cmd, sizeof(*sleep_cmd)) == 0) {
if (strncmp(line, sleep_cmd, strlen(sleep_cmd)) == 0) {
ret = tb_parse_sleep(line, sleep_ns);
if (ret) {
fprintf(stderr, "error: failed parse of sleep command.\n");
Expand All @@ -420,12 +552,22 @@ int tb_read_controls(struct testbench_prm *tp, int64_t *sleep_ns)
break;
}

if (strncmp(line, amixer_cmd, sizeof(*amixer_cmd)) == 0) {
if (strncmp(line, amixer_cmd, strlen(amixer_cmd)) == 0) {
ret = tb_parse_amixer(tp, line);
if (ret) {
fprintf(stderr, "error: failed parse of amixer command.\n");
break;
}
continue;
}

if (strncmp(line, sofctl_cmd, strlen(sofctl_cmd)) == 0) {
ret = tb_parse_sofctl(tp, line);
if (ret) {
fprintf(stderr, "error: failed parse of sof-ctl command.\n");
break;
}
continue;
}
}

Expand Down
5 changes: 5 additions & 0 deletions tools/testbench/utils_ipc3.c
Original file line number Diff line number Diff line change
Expand Up @@ -435,4 +435,9 @@ int tb_set_mixer_control(struct testbench_prm *tp, struct tb_ctl *ctl, char *con
return 0;
}

int tb_set_bytes_control(struct testbench_prm *tp, struct tb_ctl *ctl, uint32_t *data)
{
return 0;
}

#endif /* CONFIG_IPC_MAJOR_3 */
7 changes: 7 additions & 0 deletions tools/testbench/utils_ipc4.c
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,11 @@ int tb_set_mixer_control(struct testbench_prm *tp, struct tb_ctl *ctl, char *con
return ret;
}

int tb_set_bytes_control(struct testbench_prm *tp, struct tb_ctl *ctl, uint32_t *data)
{
return tb_send_bytes_data(&tp->ipc_tx, &tp->ipc_rx,
ctl->module_id, ctl->instance_id,
(struct sof_abi_hdr *)data);
}

#endif /* CONFIG_IPC_MAJOR_4 */
Loading