// SPDX-License-Identifier: BSD-3-Clause // // Copyright(c) 2019 Intel Corporation. All rights reserved. // // Author: Ranjani Sridharan /* Topology parser */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define MOVE_POINTER_BY_BYTES(p, b) ((typeof(p))((uint8_t *)(p) + (b))) struct sof_process_types { const char *name; enum sof_ipc_process_type type; enum sof_comp_type comp_type; }; static const struct sof_process_types sof_process[] = { {"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR}, {"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR}, {"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT}, {"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB}, {"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR}, {"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX}, {"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX}, {"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK}, }; static enum sof_ipc_process_type find_process(const char *name) { int i; for (i = 0; i < ARRAY_SIZE(sof_process); i++) { if (strcmp(name, sof_process[i].name) == 0) return sof_process[i].type; } return SOF_PROCESS_NONE; } static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) { int i; for (i = 0; i < ARRAY_SIZE(sof_process); i++) { if (sof_process[i].type == type) return sof_process[i].comp_type; } return SOF_COMP_NONE; } bool is_valid_priv_size(size_t size_read, size_t priv_size, struct snd_soc_tplg_vendor_array *array) { size_t arr_size, elem_size, arr_elems_size; arr_size = sizeof(struct snd_soc_tplg_vendor_array); switch (array->type) { case SND_SOC_TPLG_TUPLE_TYPE_UUID: elem_size = sizeof(struct snd_soc_tplg_vendor_uuid_elem); break; case SND_SOC_TPLG_TUPLE_TYPE_STRING: elem_size = sizeof(struct snd_soc_tplg_vendor_string_elem); break; case SND_SOC_TPLG_TUPLE_TYPE_BOOL: case SND_SOC_TPLG_TUPLE_TYPE_BYTE: case SND_SOC_TPLG_TUPLE_TYPE_WORD: case SND_SOC_TPLG_TUPLE_TYPE_SHORT: elem_size = sizeof(struct snd_soc_tplg_vendor_value_elem); break; default: /* This is handled in the further calls */ return true; } arr_elems_size = array->num_elems * elem_size; /* * check if size of data to be read from widget's private data * doesn't exceed private data's size. */ return size_read + arr_size + arr_elems_size <= priv_size; } /* read vendor tuples array from topology */ int tplg_read_array(struct snd_soc_tplg_vendor_array *array, FILE *file) { struct snd_soc_tplg_vendor_uuid_elem uuid; struct snd_soc_tplg_vendor_string_elem string; struct snd_soc_tplg_vendor_value_elem value; int j, ret = 0; size_t size; switch (array->type) { case SND_SOC_TPLG_TUPLE_TYPE_UUID: /* copy uuid elems into array */ for (j = 0; j < array->num_elems; j++) { size = sizeof(struct snd_soc_tplg_vendor_uuid_elem); ret = fread(&uuid, size, 1, file); if (ret != 1) return -EINVAL; memcpy(&array->uuid[j], &uuid, size); } break; case SND_SOC_TPLG_TUPLE_TYPE_STRING: /* copy string elems into array */ for (j = 0; j < array->num_elems; j++) { size = sizeof(struct snd_soc_tplg_vendor_string_elem); ret = fread(&string, size, 1, file); if (ret != 1) return -EINVAL; memcpy(&array->string[j], &string, size); } break; case SND_SOC_TPLG_TUPLE_TYPE_BOOL: case SND_SOC_TPLG_TUPLE_TYPE_BYTE: case SND_SOC_TPLG_TUPLE_TYPE_WORD: case SND_SOC_TPLG_TUPLE_TYPE_SHORT: /* copy value elems into array */ for (j = 0; j < array->num_elems; j++) { size = sizeof(struct snd_soc_tplg_vendor_value_elem); ret = fread(&value, size, 1, file); if (ret != 1) return -EINVAL; memcpy(&array->value[j], &value, size); } break; default: fprintf(stderr, "error: unknown token type %d\n", array->type); return -EINVAL; } return 0; } /* load buffer DAPM widget */ int tplg_load_buffer(int comp_id, int pipeline_id, int size, struct sof_ipc_buffer *buffer, FILE *file) { struct snd_soc_tplg_vendor_array *array; size_t read_size; size_t parsed_size = 0; int ret; /* configure buffer */ buffer->comp.core = 0; buffer->comp.id = comp_id; buffer->comp.pipeline_id = pipeline_id; buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; buffer->comp.type = SOF_COMP_BUFFER; buffer->comp.hdr.size = sizeof(struct sof_ipc_buffer); /* allocate memory for vendor tuple array */ array = (struct snd_soc_tplg_vendor_array *)malloc(size); if (!array) { fprintf(stderr, "error: malloc fail during load_buffer\n"); return -errno; } /* read vendor tokens */ while (parsed_size < size) { read_size = sizeof(struct snd_soc_tplg_vendor_array); ret = fread(array, read_size, 1, file); if (ret != 1) { fprintf(stderr, "error: fread fail during load_buffer\n"); free(array); return -EINVAL; } /* check for array size mismatch */ if (!is_valid_priv_size(parsed_size, size, array)) { fprintf(stderr, "error: load buffer array size mismatch\n"); free(array); return -EINVAL; } ret = tplg_read_array(array, file); if (ret) { fprintf(stderr, "error: read array fail\n"); free(array); return ret; } /* parse buffer comp tokens */ ret = sof_parse_tokens(&buffer->comp, buffer_comp_tokens, ARRAY_SIZE(buffer_comp_tokens), array, array->size); if (ret) { fprintf(stderr, "error: parse buffer comp tokens %d\n", size); free(array); return -EINVAL; } /* parse buffer tokens */ ret = sof_parse_tokens(buffer, buffer_tokens, ARRAY_SIZE(buffer_tokens), array, array->size); if (ret) { fprintf(stderr, "error: parse buffer tokens %d\n", size); free(array); return -EINVAL; } parsed_size += array->size; } free(array); return 0; } int tplg_load_pcm(int comp_id, int pipeline_id, int size, int dir, struct sof_ipc_comp_host *host, FILE *file) { struct snd_soc_tplg_vendor_array *array = NULL; size_t total_array_size = 0, read_size; int ret; /* configure host comp IPC message */ host->comp.hdr.size = sizeof(*host); host->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; host->comp.id = comp_id; host->comp.type = SOF_COMP_HOST; host->comp.pipeline_id = pipeline_id; host->direction = dir; host->config.hdr.size = sizeof(host->config); /* allocate memory for vendor tuple array */ array = (struct snd_soc_tplg_vendor_array *)malloc(size); if (!array) { fprintf(stderr, "error: mem alloc\n"); return -errno; } /* read vendor tokens */ while (total_array_size < size) { read_size = sizeof(struct snd_soc_tplg_vendor_array); ret = fread(array, read_size, 1, file); if (ret != 1) return -EINVAL; /* check for array size mismatch */ if (!is_valid_priv_size(total_array_size, size, array)) { fprintf(stderr, "error: load pcm array size mismatch\n"); free(array); return -EINVAL; } ret = tplg_read_array(array, file); if (ret) { fprintf(stderr, "error: read array fail\n"); free(array); return ret; } /* parse comp tokens */ ret = sof_parse_tokens(&host->config, comp_tokens, ARRAY_SIZE(comp_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse comp tokens %d\n", size); free(array); return -EINVAL; } /* parse pcm tokens */ ret = sof_parse_tokens(host, pcm_tokens, ARRAY_SIZE(comp_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse pcm tokens %d\n", size); free(array); return -EINVAL; } total_array_size += array->size; } free(array); return 0; } /* load dai component */ int tplg_load_dai(int comp_id, int pipeline_id, int size, struct sof_ipc_comp_dai *comp_dai, FILE *file) { struct snd_soc_tplg_vendor_array *array; size_t total_array_size = 0, read_size; int ret; /* configure comp_dai */ comp_dai->comp.hdr.size = sizeof(*comp_dai); comp_dai->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; comp_dai->comp.id = comp_id; comp_dai->comp.type = SOF_COMP_DAI; comp_dai->comp.pipeline_id = pipeline_id; comp_dai->config.hdr.size = sizeof(comp_dai->config); /* allocate memory for vendor tuple array */ array = (struct snd_soc_tplg_vendor_array *)malloc(size); if (!array) { fprintf(stderr, "error: mem alloc\n"); return -errno; } /* read vendor tokens */ while (total_array_size < size) { read_size = sizeof(struct snd_soc_tplg_vendor_array); ret = fread(array, read_size, 1, file); if (ret != 1) { free(array); return -EINVAL; } /* check for array size mismatch */ if (!is_valid_priv_size(total_array_size, size, array)) { fprintf(stderr, "error: load dai array size mismatch\n"); free(array); return -EINVAL; } ret = tplg_read_array(array, file); if (ret) { fprintf(stderr, "error: read array fail\n"); free(array); return ret; } ret = sof_parse_tokens(comp_dai, dai_tokens, ARRAY_SIZE(dai_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse dai tokens failed %d\n", size); free(array); return -EINVAL; } /* parse comp tokens */ ret = sof_parse_tokens(&comp_dai->config, comp_tokens, ARRAY_SIZE(comp_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse filewrite tokens %d\n", size); free(array); return -EINVAL; } total_array_size += array->size; } free(array); return 0; } /* load pda dapm widget */ int tplg_load_pga(int comp_id, int pipeline_id, int size, struct sof_ipc_comp_volume *volume, FILE *file) { struct snd_soc_tplg_vendor_array *array = NULL; size_t total_array_size = 0, read_size; int ret; /* allocate memory for vendor tuple array */ array = (struct snd_soc_tplg_vendor_array *)malloc(size); if (!array) { fprintf(stderr, "error: mem alloc\n"); return -errno; } /* read vendor tokens */ while (total_array_size < size) { read_size = sizeof(struct snd_soc_tplg_vendor_array); ret = fread(array, read_size, 1, file); if (ret != 1) { free(array); return -EINVAL; } /* check for array size mismatch */ if (!is_valid_priv_size(total_array_size, size, array)) { fprintf(stderr, "error: pga array size mismatch\n"); free(array); return -EINVAL; } ret = tplg_read_array(array, file); if (ret) { fprintf(stderr, "error: read array fail\n"); free(array); return ret; } /* parse comp tokens */ ret = sof_parse_tokens(&volume->config, comp_tokens, ARRAY_SIZE(comp_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse pga comp tokens %d\n", size); free(array); return -EINVAL; } /* parse volume tokens */ ret = sof_parse_tokens(volume, volume_tokens, ARRAY_SIZE(volume_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse src tokens %d\n", size); free(array); return -EINVAL; } total_array_size += array->size; } /* configure volume */ volume->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; volume->comp.id = comp_id; volume->comp.hdr.size = sizeof(struct sof_ipc_comp_volume); volume->comp.type = SOF_COMP_VOLUME; volume->comp.pipeline_id = pipeline_id; volume->config.hdr.size = sizeof(struct sof_ipc_comp_config); free(array); return 0; } /* load scheduler dapm widget */ int tplg_load_pipeline(int comp_id, int pipeline_id, int size, struct sof_ipc_pipe_new *pipeline, FILE *file) { struct snd_soc_tplg_vendor_array *array = NULL; size_t total_array_size = 0, read_size; int ret; /* configure pipeline */ pipeline->comp_id = comp_id; pipeline->pipeline_id = pipeline_id; pipeline->hdr.size = sizeof(*pipeline); pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; pipeline->comp_id = comp_id; pipeline->pipeline_id = pipeline_id; /* allocate memory for vendor tuple array */ array = (struct snd_soc_tplg_vendor_array *)malloc(size); if (!array) { fprintf(stderr, "error: mem alloc\n"); return -errno; } /* read vendor arrays */ while (total_array_size < size) { read_size = sizeof(struct snd_soc_tplg_vendor_array); ret = fread(array, read_size, 1, file); if (ret != 1) { free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } /* check for array size mismatch */ if (!is_valid_priv_size(total_array_size, size, array)) { fprintf(stderr, "error: load pipeline array size mismatch\n"); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } ret = tplg_read_array(array, file); if (ret) { fprintf(stderr, "error: read array fail\n"); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } /* parse scheduler tokens */ ret = sof_parse_tokens(pipeline, sched_tokens, ARRAY_SIZE(sched_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse pipeline tokens %d\n", size); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } total_array_size += array->size; /* read next array */ array = MOVE_POINTER_BY_BYTES(array, array->size); } /* point to the start of array so it gets freed properly */ array = MOVE_POINTER_BY_BYTES(array, -total_array_size); free(array); return 0; } int tplg_load_one_control(struct snd_soc_tplg_ctl_hdr **ctl, char **priv_data, FILE *file) { struct snd_soc_tplg_ctl_hdr *ctl_hdr; struct snd_soc_tplg_mixer_control *mixer_ctl = NULL; struct snd_soc_tplg_enum_control *enum_ctl = NULL; struct snd_soc_tplg_bytes_control *bytes_ctl = NULL; size_t rewind_size; size_t read_size; size_t hdr_size; int ret; /* These are set if success */ *ctl = NULL; *priv_data = NULL; /* allocate memory */ hdr_size = sizeof(struct snd_soc_tplg_ctl_hdr); ctl_hdr = (struct snd_soc_tplg_ctl_hdr *)malloc(hdr_size); if (!ctl_hdr) { fprintf(stderr, "error: mem alloc\n"); return -errno; } /* read control header */ ret = fread(ctl_hdr, hdr_size, 1, file); if (ret != 1) { ret = -EINVAL; goto err; } /* load control based on type */ switch (ctl_hdr->ops.info) { case SND_SOC_TPLG_CTL_VOLSW: case SND_SOC_TPLG_CTL_STROBE: case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_DAPM_CTL_VOLSW: /* load mixer type control */ rewind_size = sizeof(struct snd_soc_tplg_ctl_hdr); if (fseek(file, (long)rewind_size * -1, SEEK_CUR)) { ret = -errno; goto err; } read_size = sizeof(struct snd_soc_tplg_mixer_control); mixer_ctl = malloc(read_size); if (!mixer_ctl) { fprintf(stderr, "error: mem alloc\n"); ret = -errno; goto err; } ret = fread(mixer_ctl, read_size, 1, file); if (ret != 1) { ret = -EINVAL; goto err; } /* skip mixer private data */ if (fseek(file, mixer_ctl->priv.size, SEEK_CUR)) { ret = -errno; goto err; } *ctl = (struct snd_soc_tplg_ctl_hdr *)mixer_ctl; break; case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: /* load enum type control */ rewind_size = sizeof(struct snd_soc_tplg_ctl_hdr); if (fseek(file, (long)rewind_size * -1, SEEK_CUR)) { ret = -errno; goto err; } read_size = sizeof(struct snd_soc_tplg_enum_control); enum_ctl = malloc(read_size); if (!enum_ctl) { fprintf(stderr, "error: mem alloc\n"); ret = -errno; goto err; } ret = fread(enum_ctl, read_size, 1, file); if (ret != 1) { ret = -EINVAL; goto err; } /* skip enum private data */ if (fseek(file, enum_ctl->priv.size, SEEK_CUR)) { ret = -errno; goto err; } *ctl = (struct snd_soc_tplg_ctl_hdr *)enum_ctl; break; case SND_SOC_TPLG_CTL_BYTES: /* load bytes type controls */ rewind_size = sizeof(struct snd_soc_tplg_ctl_hdr); if (fseek(file, (long)rewind_size * -1, SEEK_CUR)) { ret = -errno; goto err; } read_size = sizeof(struct snd_soc_tplg_bytes_control); bytes_ctl = malloc(read_size); if (!bytes_ctl) { fprintf(stderr, "error: mem alloc\n"); ret = -errno; goto err; } ret = fread(bytes_ctl, read_size, 1, file); if (ret != 1) { ret = -EINVAL; goto err; } /* Get private data */ read_size = bytes_ctl->priv.size; *priv_data = malloc(read_size); if (!(*priv_data)) { fprintf(stderr, "error: mem alloc\n"); ret = -errno; goto err; } ret = fread(*priv_data, read_size, 1, file); if (ret != 1) { ret = -EINVAL; goto err; } *ctl = (struct snd_soc_tplg_ctl_hdr *)bytes_ctl; break; default: printf("info: control type not supported\n"); return -EINVAL; } free(ctl_hdr); return 0; err: /* free all data */ free(ctl_hdr); free(mixer_ctl); free(enum_ctl); free(bytes_ctl); free(*priv_data); return ret; } /* load dapm widget kcontrols * we don't use controls in the fuzzer atm. * so just skip to the next dapm widget */ int tplg_load_controls(int num_kcontrols, FILE *file) { struct snd_soc_tplg_ctl_hdr *ctl_hdr; struct snd_soc_tplg_mixer_control *mixer_ctl; struct snd_soc_tplg_enum_control *enum_ctl; struct snd_soc_tplg_bytes_control *bytes_ctl; size_t read_size, size; int j, ret = 0; /* allocate memory */ size = sizeof(struct snd_soc_tplg_ctl_hdr); ctl_hdr = (struct snd_soc_tplg_ctl_hdr *)malloc(size); if (!ctl_hdr) { fprintf(stderr, "error: mem alloc\n"); return -errno; } size = sizeof(struct snd_soc_tplg_mixer_control); mixer_ctl = (struct snd_soc_tplg_mixer_control *)malloc(size); if (!mixer_ctl) { fprintf(stderr, "error: mem alloc\n"); free(ctl_hdr); return -errno; } size = sizeof(struct snd_soc_tplg_enum_control); enum_ctl = (struct snd_soc_tplg_enum_control *)malloc(size); if (!enum_ctl) { fprintf(stderr, "error: mem alloc\n"); free(ctl_hdr); free(mixer_ctl); return -errno; } size = sizeof(struct snd_soc_tplg_bytes_control); bytes_ctl = (struct snd_soc_tplg_bytes_control *)malloc(size); if (!bytes_ctl) { fprintf(stderr, "error: mem alloc\n"); free(ctl_hdr); free(mixer_ctl); free(enum_ctl); return -errno; } for (j = 0; j < num_kcontrols; j++) { /* read control header */ read_size = sizeof(struct snd_soc_tplg_ctl_hdr); ret = fread(ctl_hdr, read_size, 1, file); if (ret != 1) { ret = -EINVAL; goto err; } /* load control based on type */ switch (ctl_hdr->ops.info) { case SND_SOC_TPLG_CTL_VOLSW: case SND_SOC_TPLG_CTL_STROBE: case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_DAPM_CTL_VOLSW: /* load mixer type control */ read_size = sizeof(struct snd_soc_tplg_ctl_hdr); if (fseek(file, (long)read_size * -1, SEEK_CUR)) { ret = -errno; goto err; } read_size = sizeof(struct snd_soc_tplg_mixer_control); ret = fread(mixer_ctl, read_size, 1, file); if (ret != 1) { ret = -EINVAL; goto err; } /* skip mixer private data */ if (fseek(file, mixer_ctl->priv.size, SEEK_CUR)) { ret = -errno; goto err; } break; case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: /* load enum type control */ read_size = sizeof(struct snd_soc_tplg_ctl_hdr); if (fseek(file, (long)read_size * -1, SEEK_CUR)) { ret = -errno; goto err; } read_size = sizeof(struct snd_soc_tplg_enum_control); ret = fread(enum_ctl, read_size, 1, file); if (ret != 1) { ret = -EINVAL; goto err; } /* skip enum private data */ if (fseek(file, enum_ctl->priv.size, SEEK_CUR)) { ret = -errno; goto err; } break; case SND_SOC_TPLG_CTL_BYTES: /* load bytes type controls */ read_size = sizeof(struct snd_soc_tplg_ctl_hdr); if (fseek(file, (long)read_size * -1, SEEK_CUR)) { ret = -errno; goto err; } read_size = sizeof(struct snd_soc_tplg_bytes_control); ret = fread(bytes_ctl, read_size, 1, file); if (ret != 1) { ret = -EINVAL; goto err; } /* skip bytes private data */ if (fseek(file, bytes_ctl->priv.size, SEEK_CUR)) { ret = -errno; goto err; } break; default: printf("info: control type not supported\n"); return -EINVAL; } } err: /* free all data */ free(mixer_ctl); free(enum_ctl); free(bytes_ctl); free(ctl_hdr); return ret; } /* load src dapm widget */ int tplg_load_src(int comp_id, int pipeline_id, int size, struct sof_ipc_comp_src *src, FILE *file) { struct snd_soc_tplg_vendor_array *array = NULL; size_t total_array_size = 0, read_size; int ret; /* allocate memory for vendor tuple array */ array = (struct snd_soc_tplg_vendor_array *)malloc(size); if (!array) { fprintf(stderr, "error: mem alloc for src vendor array\n"); return -errno; } /* read vendor tokens */ while (total_array_size < size) { read_size = sizeof(struct snd_soc_tplg_vendor_array); ret = fread(array, read_size, 1, file); if (ret != 1) { free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } /* check for array size mismatch */ if (!is_valid_priv_size(total_array_size, size, array)) { fprintf(stderr, "error: load src array size mismatch\n"); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } ret = tplg_read_array(array, file); if (ret) { fprintf(stderr, "error: read array fail\n"); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return ret; } /* parse comp tokens */ ret = sof_parse_tokens(&src->config, comp_tokens, ARRAY_SIZE(comp_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse src comp_tokens %d\n", size); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } /* parse src tokens */ ret = sof_parse_tokens(src, src_tokens, ARRAY_SIZE(src_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse src tokens %d\n", size); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } total_array_size += array->size; /* read next array */ array = MOVE_POINTER_BY_BYTES(array, array->size); } /* point to the start of array so it gets freed properly */ array = MOVE_POINTER_BY_BYTES(array, -total_array_size); /* configure src */ src->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; src->comp.id = comp_id; src->comp.hdr.size = sizeof(struct sof_ipc_comp_src); src->comp.type = SOF_COMP_SRC; src->comp.pipeline_id = pipeline_id; src->config.hdr.size = sizeof(struct sof_ipc_comp_config); free(array); return 0; } /* load asrc dapm widget */ int tplg_load_asrc(int comp_id, int pipeline_id, int size, struct sof_ipc_comp_asrc *asrc, FILE *file) { struct snd_soc_tplg_vendor_array *array = NULL; size_t total_array_size = 0, read_size; int ret; /* allocate memory for vendor tuple array */ array = (struct snd_soc_tplg_vendor_array *)malloc(size); if (!array) { fprintf(stderr, "error: mem alloc for asrc vendor array\n"); return -errno; } /* read vendor tokens */ while (total_array_size < size) { read_size = sizeof(struct snd_soc_tplg_vendor_array); ret = fread(array, read_size, 1, file); if (ret != 1) { free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } /* check for array size mismatch */ if (!is_valid_priv_size(total_array_size, size, array)) { fprintf(stderr, "error: load asrc array size mismatch\n"); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } ret = tplg_read_array(array, file); if (ret) { fprintf(stderr, "error: read array fail\n"); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return ret; } /* parse comp tokens */ ret = sof_parse_tokens(&asrc->config, comp_tokens, ARRAY_SIZE(comp_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse asrc comp_tokens %d\n", size); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } /* parse asrc tokens */ ret = sof_parse_tokens(asrc, asrc_tokens, ARRAY_SIZE(asrc_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse asrc tokens %d\n", size); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } total_array_size += array->size; /* read next array */ array = MOVE_POINTER_BY_BYTES(array, array->size); } /* point to the start of array so it gets freed properly */ array = MOVE_POINTER_BY_BYTES(array, -total_array_size); /* configure asrc */ asrc->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; asrc->comp.id = comp_id; asrc->comp.hdr.size = sizeof(struct sof_ipc_comp_asrc); asrc->comp.type = SOF_COMP_ASRC; asrc->comp.pipeline_id = pipeline_id; asrc->config.hdr.size = sizeof(struct sof_ipc_comp_config); free(array); return 0; } /* load asrc dapm widget */ int tplg_load_process(int comp_id, int pipeline_id, int size, struct sof_ipc_comp_process *process, FILE *file, struct sof_ipc_comp_ext *comp_ext) { struct snd_soc_tplg_vendor_array *array = NULL; size_t total_array_size = 0; size_t read_size; int ret; /* allocate memory for vendor tuple array */ array = (struct snd_soc_tplg_vendor_array *)malloc(size); if (!array) { fprintf(stderr, "error: mem alloc for asrc vendor array\n"); return -errno; } /* read vendor tokens */ while (total_array_size < size) { read_size = sizeof(struct snd_soc_tplg_vendor_array); ret = fread(array, read_size, 1, file); if (ret != 1) { free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } /* check for array size mismatch */ if (!is_valid_priv_size(total_array_size, size, array)) { fprintf(stderr, "error: load process array size mismatch\n"); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } ret = tplg_read_array(array, file); if (ret) { fprintf(stderr, "error: read array fail\n"); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return ret; } /* parse comp tokens */ ret = sof_parse_tokens(&process->config, comp_tokens, ARRAY_SIZE(comp_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse process comp_tokens %d\n", size); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } /* parse process tokens */ ret = sof_parse_tokens(process, process_tokens, ARRAY_SIZE(process_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse process tokens %d\n", size); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } /* parse uuid tokes */ ret = sof_parse_tokens(comp_ext, comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse comp extended tokens %d\n", size); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } total_array_size += array->size; /* read next array */ array = MOVE_POINTER_BY_BYTES(array, array->size); } /* point to the start of array so it gets freed properly */ array = MOVE_POINTER_BY_BYTES(array, -total_array_size); /* configure asrc */ process->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; process->comp.id = comp_id; process->comp.hdr.size = sizeof(struct sof_ipc_comp_asrc); process->comp.type = find_process_comp_type(process->type); process->comp.pipeline_id = pipeline_id; process->config.hdr.size = sizeof(struct sof_ipc_comp_config); free(array); return 0; } /* load mixer dapm widget */ int tplg_load_mixer(int comp_id, int pipeline_id, int size, struct sof_ipc_comp_mixer *mixer, FILE *file) { struct snd_soc_tplg_vendor_array *array = NULL; size_t total_array_size = 0, read_size; int ret; /* allocate memory for vendor tuple array */ array = (struct snd_soc_tplg_vendor_array *)malloc(size); if (!array) { fprintf(stderr, "error: mem alloc for src vendor array\n"); return -errno; } /* read vendor tokens */ while (total_array_size < size) { read_size = sizeof(struct snd_soc_tplg_vendor_array); ret = fread(array, read_size, 1, file); if (ret != 1) { free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } /* check for array size mismatch */ if (!is_valid_priv_size(total_array_size, size, array)) { fprintf(stderr, "error: load mixer array size mismatch\n"); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } ret = tplg_read_array(array, file); if (ret) { fprintf(stderr, "error: read array fail\n"); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return ret; } /* parse comp tokens */ ret = sof_parse_tokens(&mixer->config, comp_tokens, ARRAY_SIZE(comp_tokens), array, array->size); if (ret != 0) { fprintf(stderr, "error: parse src comp_tokens %d\n", size); free(MOVE_POINTER_BY_BYTES(array, -total_array_size)); return -EINVAL; } total_array_size += array->size; /* read next array */ array = MOVE_POINTER_BY_BYTES(array, array->size); } /* point to the start of array so it gets freed properly */ array = MOVE_POINTER_BY_BYTES(array, -total_array_size); /* configure src */ mixer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; mixer->comp.id = comp_id; mixer->comp.hdr.size = sizeof(struct sof_ipc_comp_src); mixer->comp.type = SOF_COMP_MIXER; mixer->comp.pipeline_id = pipeline_id; mixer->config.hdr.size = sizeof(struct sof_ipc_comp_config); free(array); return 0; } /* load pipeline graph DAPM widget*/ int tplg_load_graph(int num_comps, int pipeline_id, struct comp_info *temp_comp_list, char *pipeline_string, struct sof_ipc_pipe_comp_connect *connection, FILE *file, int route_num, int count) { struct snd_soc_tplg_dapm_graph_elem *graph_elem; char *source = NULL, *sink = NULL; int j, ret = 0; size_t size; /* configure route */ connection->hdr.size = sizeof(*connection); connection->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; /* allocate memory for graph elem */ size = sizeof(struct snd_soc_tplg_dapm_graph_elem); graph_elem = (struct snd_soc_tplg_dapm_graph_elem *)malloc(size); if (!graph_elem) { fprintf(stderr, "error: mem alloc\n"); return -errno; } /* set up component connections */ connection->source_id = -1; connection->sink_id = -1; size = sizeof(struct snd_soc_tplg_dapm_graph_elem); ret = fread(graph_elem, size, 1, file); if (ret != 1) { free(graph_elem); return -EINVAL; } /* look up component id from the component list */ for (j = 0; j < num_comps; j++) { if (strcmp(temp_comp_list[j].name, graph_elem->source) == 0) { connection->source_id = temp_comp_list[j].id; source = graph_elem->source; } if (strcmp(temp_comp_list[j].name, graph_elem->sink) == 0) { connection->sink_id = temp_comp_list[j].id; sink = graph_elem->sink; } } if (!source || !sink) { fprintf(stderr, "%s() error: source=%p, sink=%p\n", __func__, source, sink); free(graph_elem); return -EINVAL; } printf("loading route %s -> %s\n", source, sink); strcat(pipeline_string, graph_elem->source); strcat(pipeline_string, "->"); if (route_num == (count - 1)) { strcat(pipeline_string, graph_elem->sink); strcat(pipeline_string, "\n"); } free(graph_elem); return 0; } /* load dapm widget */ int load_widget(void *dev, int dev_type, struct comp_info *temp_comp_list, int comp_id, int comp_index, int pipeline_id, void *tp, int *sched_id, FILE *file) { struct snd_soc_tplg_dapm_widget *widget; size_t read_size; size_t size; int ret = 0; if (!temp_comp_list) { fprintf(stderr, "load_widget: temp_comp_list argument NULL\n"); return -EINVAL; } /* allocate memory for widget */ size = sizeof(struct snd_soc_tplg_dapm_widget); widget = (struct snd_soc_tplg_dapm_widget *)malloc(size); if (!widget) { fprintf(stderr, "error: mem alloc\n"); return -errno; } /* read widget data */ read_size = sizeof(struct snd_soc_tplg_dapm_widget); ret = fread(widget, read_size, 1, file); if (ret != 1) { ret = -EINVAL; goto exit; } /* * create a list with all widget info * containing mapping between component names and ids * which will be used for setting up component connections */ temp_comp_list[comp_index].id = comp_id; temp_comp_list[comp_index].name = strdup(widget->name); temp_comp_list[comp_index].type = widget->id; temp_comp_list[comp_index].pipeline_id = pipeline_id; printf("debug: loading comp_id %d: widget %s id %d\n", comp_id, widget->name, widget->id); /* load widget based on type */ switch (widget->id) { /* load pga widget */ case(SND_SOC_TPLG_DAPM_PGA): if (load_pga(dev, comp_id, pipeline_id, widget) < 0) { fprintf(stderr, "error: load pga\n"); ret = -EINVAL; goto exit; } break; case(SND_SOC_TPLG_DAPM_AIF_IN): if (load_aif_in_out(dev, comp_id, pipeline_id, widget, SOF_IPC_STREAM_PLAYBACK, tp) < 0) { fprintf(stderr, "error: load AIF IN failed\n"); ret = -EINVAL; goto exit; } break; case(SND_SOC_TPLG_DAPM_AIF_OUT): if (load_aif_in_out(dev, comp_id, pipeline_id, widget, SOF_IPC_STREAM_CAPTURE, tp) < 0) { fprintf(stderr, "error: load AIF OUT failed\n"); ret = -EINVAL; goto exit; } break; case(SND_SOC_TPLG_DAPM_DAI_IN): if (load_dai_in_out(dev, comp_id, pipeline_id, widget, SOF_IPC_STREAM_PLAYBACK, tp) < 0) { fprintf(stderr, "error: load filewrite\n"); ret = -EINVAL; goto exit; } break; case(SND_SOC_TPLG_DAPM_DAI_OUT): if (load_dai_in_out(dev, comp_id, pipeline_id, widget, SOF_IPC_STREAM_CAPTURE, tp) < 0) { fprintf(stderr, "error: load filewrite\n"); ret = -EINVAL; goto exit; } break; case(SND_SOC_TPLG_DAPM_BUFFER): if (load_buffer(dev, comp_id, pipeline_id, widget) < 0) { fprintf(stderr, "error: load buffer\n"); ret = -EINVAL; goto exit; } break; case(SND_SOC_TPLG_DAPM_SCHEDULER): /* find comp id for scheduling comp */ if (dev_type == FUZZER_DEV) *sched_id = find_widget(temp_comp_list, comp_id, widget->sname); if (load_pipeline(dev, comp_id, pipeline_id, widget, *sched_id) < 0) { fprintf(stderr, "error: load pipeline\n"); ret = -EINVAL; goto exit; } break; case(SND_SOC_TPLG_DAPM_SRC): if (load_src(dev, comp_id, pipeline_id, widget, tp) < 0) { fprintf(stderr, "error: load src\n"); ret = -EINVAL; goto exit; } break; case(SND_SOC_TPLG_DAPM_ASRC): if (load_asrc(dev, comp_id, pipeline_id, widget, tp) < 0) { fprintf(stderr, "error: load src\n"); ret = -EINVAL; goto exit; } break; case(SND_SOC_TPLG_DAPM_MIXER): if (load_mixer(dev, comp_id, pipeline_id, widget) < 0) { fprintf(stderr, "error: load mixer\n"); ret = -EINVAL; goto exit; } break; case(SND_SOC_TPLG_DAPM_EFFECT): if (load_process(dev, comp_id, pipeline_id, widget) < 0) { fprintf(stderr, "error: load effect\n"); ret = -EINVAL; goto exit; } break; /* unsupported widgets */ default: if (fseek(file, widget->priv.size, SEEK_CUR)) { fprintf(stderr, "error: fseek unsupported widget\n"); ret = -errno; goto exit; } printf("info: Widget type not supported %d\n", widget->id); ret = tplg_load_controls(widget->num_kcontrols, file); if (ret < 0) { fprintf(stderr, "error: loading controls\n"); goto exit; } break; } ret = 0; exit: /* free allocated widget data */ free(widget); return ret; } /* parse vendor tokens in topology */ int sof_parse_tokens(void *object, const struct sof_topology_token *tokens, int count, struct snd_soc_tplg_vendor_array *array, int priv_size) { int asize; int ret = 0; while (priv_size > 0 && ret == 0) { asize = array->size; /* validate asize */ if (asize < 0) { /* FIXME: A zero-size array makes no sense */ fprintf(stderr, "error: invalid array size 0x%x\n", asize); return -EINVAL; } /* make sure there is enough data before parsing */ priv_size -= asize; if (priv_size < 0) { fprintf(stderr, "error: invalid priv size 0x%x\n", asize); return -EINVAL; } /* call correct parser depending on type */ switch (array->type) { case SND_SOC_TPLG_TUPLE_TYPE_UUID: ret = sof_parse_uuid_tokens(object, tokens, count, array); break; case SND_SOC_TPLG_TUPLE_TYPE_STRING: ret = sof_parse_string_tokens(object, tokens, count, array); break; case SND_SOC_TPLG_TUPLE_TYPE_BOOL: case SND_SOC_TPLG_TUPLE_TYPE_BYTE: case SND_SOC_TPLG_TUPLE_TYPE_WORD: case SND_SOC_TPLG_TUPLE_TYPE_SHORT: ret = sof_parse_word_tokens(object, tokens, count, array); break; default: fprintf(stderr, "error: unknown token type %d\n", array->type); return -EINVAL; } } return ret; } /* parse word tokens */ int sof_parse_word_tokens(void *object, const struct sof_topology_token *tokens, int count, struct snd_soc_tplg_vendor_array *array) { struct snd_soc_tplg_vendor_value_elem *elem; int i, j; if (sizeof(struct snd_soc_tplg_vendor_value_elem) * array->num_elems + sizeof(struct snd_soc_tplg_vendor_array) > array->size) { fprintf(stderr, "error: illegal array number of elements %d\n", array->num_elems); return -EINVAL; } /* parse element by element */ for (i = 0; i < array->num_elems; i++) { elem = &array->value[i]; /* search for token */ for (j = 0; j < count; j++) { /* match token type */ if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_WORD) continue; /* match token id */ if (tokens[j].token != elem->token) continue; /* matched - now load token */ tokens[j].get_token(elem, object, tokens[j].offset, tokens[j].size); } } return 0; } /* parse uuid tokens */ int sof_parse_uuid_tokens(void *object, const struct sof_topology_token *tokens, int count, struct snd_soc_tplg_vendor_array *array) { struct snd_soc_tplg_vendor_uuid_elem *elem; int i, j; if (sizeof(struct snd_soc_tplg_vendor_uuid_elem) * array->num_elems + sizeof(struct snd_soc_tplg_vendor_array) > array->size) { fprintf(stderr, "error: illegal array number of elements %d\n", array->num_elems); return -EINVAL; } /* parse element by element */ for (i = 0; i < array->num_elems; i++) { elem = &array->uuid[i]; /* search for token */ for (j = 0; j < count; j++) { /* match token type */ if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID) continue; /* match token id */ if (tokens[j].token != elem->token) continue; /* matched - now load token */ tokens[j].get_token(elem, object, tokens[j].offset, tokens[j].size); } } return 0; } /* parse string tokens */ int sof_parse_string_tokens(void *object, const struct sof_topology_token *tokens, int count, struct snd_soc_tplg_vendor_array *array) { struct snd_soc_tplg_vendor_string_elem *elem; int i, j; if (sizeof(struct snd_soc_tplg_vendor_string_elem) * array->num_elems + sizeof(struct snd_soc_tplg_vendor_array) > array->size) { fprintf(stderr, "error: illegal array number of elements %d\n", array->num_elems); return -EINVAL; } /* parse element by element */ for (i = 0; i < array->num_elems; i++) { elem = &array->string[i]; /* search for token */ for (j = 0; j < count; j++) { /* match token type */ if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING) continue; /* match token id */ if (tokens[j].token != elem->token) continue; /* matched - now load token */ tokens[j].get_token(elem, object, tokens[j].offset, tokens[j].size); } } return 0; } enum sof_ipc_frame find_format(const char *name) { int i; for (i = 0; i < ARRAY_SIZE(sof_frames); i++) { if (strcmp(name, sof_frames[i].name) == 0) return sof_frames[i].frame; } /* use s32le if nothing is specified */ return SOF_IPC_FRAME_S32_LE; } /* helper functions to get tokens */ int get_token_uint32_t(void *elem, void *object, uint32_t offset, uint32_t size) { struct snd_soc_tplg_vendor_value_elem *velem = elem; uint32_t *val = object + offset; *val = velem->value; return 0; } int get_token_uuid(void *elem, void *object, uint32_t offset, uint32_t size) { struct snd_soc_tplg_vendor_uuid_elem *velem = elem; uint8_t *dst = (uint8_t *)object + offset; memcpy(dst, velem->uuid, UUID_SIZE); return 0; } int get_token_comp_format(void *elem, void *object, uint32_t offset, uint32_t size) { struct snd_soc_tplg_vendor_string_elem *velem = elem; uint32_t *val = object + offset; *val = find_format(velem->string); return 0; } int get_token_dai_type(void *elem, void *object, uint32_t offset, uint32_t size) { struct snd_soc_tplg_vendor_string_elem *velem = elem; uint32_t *val = (uint32_t *)((uint8_t *)object + offset); *val = find_dai(velem->string); return 0; } int get_token_process_type(void *elem, void *object, uint32_t offset, uint32_t size) { struct snd_soc_tplg_vendor_string_elem *velem = elem; uint32_t *val = (uint32_t *)((uint8_t *)object + offset); *val = find_process(velem->string); return 0; }