/* Copyright(c) 2014-2015 Intel Corporation All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. Authors: Mengdong Lin Yao Jin Liam Girdwood */ #include #include "list.h" #include "tplg_local.h" /* * Parse compound */ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, int (*fcn)(snd_tplg_t *, snd_config_t *, void *), void *private) { const char *id; snd_config_iterator_t i, next; snd_config_t *n; int err = -EINVAL; if (snd_config_get_id(cfg, &id) < 0) return -EINVAL; if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { SNDERR("error: compound type expected for %s", id); return -EINVAL; } /* parse compound */ snd_config_for_each(i, next, cfg) { n = snd_config_iterator_entry(i); if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { SNDERR("error: compound type expected for %s, is %d", id, snd_config_get_type(cfg)); return -EINVAL; } err = fcn(tplg, n, private); if (err < 0) return err; } return err; } static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) { snd_config_iterator_t i, next; snd_config_t *n; const char *id; int err; if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { SNDERR("error: compound type expected at top level"); return -EINVAL; } /* parse topology config sections */ snd_config_for_each(i, next, cfg) { n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) continue; if (strcmp(id, "SectionTLV") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_tlv, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionControlMixer") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_control_mixer, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionControlEnum") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_control_enum, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionControlBytes") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_control_bytes, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionWidget") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_dapm_widget, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionPCMCapabilities") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_stream_caps, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionPCM") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_pcm, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionDAI") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_dai, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionHWConfig") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_hw_config, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionLink") == 0 || strcmp(id, "SectionBE") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_link, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionCC") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_cc, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionGraph") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_dapm_graph, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionText") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_text, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionData") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_data, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionVendorTokens") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_tokens, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionVendorTuples") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_tuples, NULL); if (err < 0) return err; continue; } if (strcmp(id, "SectionManifest") == 0) { err = tplg_parse_compound(tplg, n, tplg_parse_manifest_data, NULL); if (err < 0) return err; continue; } SNDERR("error: unknown section %s\n", id); } return 0; } static int tplg_load_config(const char *file, snd_config_t **cfg) { FILE *fp; snd_input_t *in; snd_config_t *top; int ret; fp = fopen(file, "r"); if (fp == NULL) { SNDERR("error: could not open configuration file %s", file); return -errno; } ret = snd_input_stdio_attach(&in, fp, 1); if (ret < 0) { fclose(fp); SNDERR("error: could not attach stdio %s", file); return ret; } ret = snd_config_top(&top); if (ret < 0) goto err; ret = snd_config_load(top, in); if (ret < 0) { SNDERR("error: could not load configuration file %s", file); goto err_load; } ret = snd_input_close(in); if (ret < 0) { in = NULL; goto err_load; } *cfg = top; return 0; err_load: snd_config_delete(top); err: if (in) snd_input_close(in); return ret; } static int tplg_build_integ(snd_tplg_t *tplg) { int err; err = tplg_build_data(tplg); if (err < 0) return err; err = tplg_build_manifest_data(tplg); if (err < 0) return err; err = tplg_build_controls(tplg); if (err < 0) return err; err = tplg_build_widgets(tplg); if (err < 0) return err; err = tplg_build_pcms(tplg, SND_TPLG_TYPE_PCM); if (err < 0) return err; err = tplg_build_dais(tplg, SND_TPLG_TYPE_DAI); if (err < 0) return err; err = tplg_build_links(tplg, SND_TPLG_TYPE_BE); if (err < 0) return err; err = tplg_build_links(tplg, SND_TPLG_TYPE_CC); if (err < 0) return err; err = tplg_build_routes(tplg); if (err < 0) return err; return err; } int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, const char *outfile) { snd_config_t *cfg = NULL; int err = 0; tplg->out_fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (tplg->out_fd < 0) { SNDERR("error: failed to open %s err %d\n", outfile, -errno); return -errno; } err = tplg_load_config(infile, &cfg); if (err < 0) { SNDERR("error: failed to load topology file %s\n", infile); goto out_close; } err = tplg_parse_config(tplg, cfg); if (err < 0) { SNDERR("error: failed to parse topology\n"); goto out; } err = tplg_build_integ(tplg); if (err < 0) { SNDERR("error: failed to check topology integrity\n"); goto out; } err = tplg_write_data(tplg); if (err < 0) { SNDERR("error: failed to write data %d\n", err); goto out; } out: snd_config_delete(cfg); out_close: close(tplg->out_fd); return err; } int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) { switch (t->type) { case SND_TPLG_TYPE_MIXER: return tplg_add_mixer_object(tplg, t); case SND_TPLG_TYPE_ENUM: return tplg_add_enum_object(tplg, t); case SND_TPLG_TYPE_BYTES: return tplg_add_bytes_object(tplg, t); case SND_TPLG_TYPE_DAPM_WIDGET: return tplg_add_widget_object(tplg, t); case SND_TPLG_TYPE_DAPM_GRAPH: return tplg_add_graph_object(tplg, t); case SND_TPLG_TYPE_PCM: return tplg_add_pcm_object(tplg, t); case SND_TPLG_TYPE_DAI: return tplg_add_dai_object(tplg, t); case SND_TPLG_TYPE_LINK: case SND_TPLG_TYPE_BE: case SND_TPLG_TYPE_CC: return tplg_add_link_object(tplg, t); default: SNDERR("error: invalid object type %d\n", t->type); return -EINVAL; }; } int snd_tplg_build(snd_tplg_t *tplg, const char *outfile) { int err; tplg->out_fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (tplg->out_fd < 0) { SNDERR("error: failed to open %s err %d\n", outfile, -errno); return -errno; } err = tplg_build_integ(tplg); if (err < 0) { SNDERR("error: failed to check topology integrity\n"); goto out; } err = tplg_write_data(tplg); if (err < 0) { SNDERR("error: failed to write data %d\n", err); goto out; } out: close(tplg->out_fd); return err; } int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len) { if (len <= 0) return 0; tplg->manifest.priv.size = len; tplg->manifest_pdata = malloc(len); if (!tplg->manifest_pdata) return -ENOMEM; memcpy(tplg->manifest_pdata, data, len); return 0; } int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version) { tplg->version = version; return 0; } void snd_tplg_verbose(snd_tplg_t *tplg, int verbose) { tplg->verbose = verbose; } static bool is_little_endian(void) { #ifdef __BYTE_ORDER #if __BYTE_ORDER == __LITTLE_ENDIAN return true; #endif #endif return false; } snd_tplg_t *snd_tplg_new(void) { snd_tplg_t *tplg; if (!is_little_endian()) { SNDERR("error: cannot support big-endian machines\n"); return NULL; } tplg = calloc(1, sizeof(snd_tplg_t)); if (!tplg) return NULL; tplg->manifest.size = sizeof(struct snd_soc_tplg_manifest); INIT_LIST_HEAD(&tplg->tlv_list); INIT_LIST_HEAD(&tplg->widget_list); INIT_LIST_HEAD(&tplg->pcm_list); INIT_LIST_HEAD(&tplg->dai_list); INIT_LIST_HEAD(&tplg->be_list); INIT_LIST_HEAD(&tplg->cc_list); INIT_LIST_HEAD(&tplg->route_list); INIT_LIST_HEAD(&tplg->pdata_list); INIT_LIST_HEAD(&tplg->manifest_list); INIT_LIST_HEAD(&tplg->text_list); INIT_LIST_HEAD(&tplg->pcm_config_list); INIT_LIST_HEAD(&tplg->pcm_caps_list); INIT_LIST_HEAD(&tplg->mixer_list); INIT_LIST_HEAD(&tplg->enum_list); INIT_LIST_HEAD(&tplg->bytes_ext_list); INIT_LIST_HEAD(&tplg->token_list); INIT_LIST_HEAD(&tplg->tuple_list); INIT_LIST_HEAD(&tplg->hw_cfg_list); return tplg; } void snd_tplg_free(snd_tplg_t *tplg) { if (tplg->manifest_pdata) free(tplg->manifest_pdata); tplg_elem_free_list(&tplg->tlv_list); tplg_elem_free_list(&tplg->widget_list); tplg_elem_free_list(&tplg->pcm_list); tplg_elem_free_list(&tplg->dai_list); tplg_elem_free_list(&tplg->be_list); tplg_elem_free_list(&tplg->cc_list); tplg_elem_free_list(&tplg->route_list); tplg_elem_free_list(&tplg->pdata_list); tplg_elem_free_list(&tplg->manifest_list); tplg_elem_free_list(&tplg->text_list); tplg_elem_free_list(&tplg->pcm_config_list); tplg_elem_free_list(&tplg->pcm_caps_list); tplg_elem_free_list(&tplg->mixer_list); tplg_elem_free_list(&tplg->enum_list); tplg_elem_free_list(&tplg->bytes_ext_list); tplg_elem_free_list(&tplg->token_list); tplg_elem_free_list(&tplg->tuple_list); tplg_elem_free_list(&tplg->hw_cfg_list); free(tplg); }