From mboxrd@z Thu Jan 1 00:00:00 1970 From: Takashi Iwai Subject: Re: [PATCH 08/13] topology: Add DAPM object parser Date: Mon, 29 Jun 2015 20:43:41 +0200 Message-ID: References: <1435595578-6899-1-git-send-email-liam.r.girdwood@linux.intel.com> <1435595578-6899-8-git-send-email-liam.r.girdwood@linux.intel.com> Mime-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: Received: from mx2.suse.de (cantor2.suse.de [195.135.220.15]) by alsa0.perex.cz (Postfix) with ESMTP id B42ED2654CD for ; Mon, 29 Jun 2015 20:43:41 +0200 (CEST) In-Reply-To: <1435595578-6899-8-git-send-email-liam.r.girdwood@linux.intel.com> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org To: Liam Girdwood Cc: alsa-devel@alsa-project.org, Mark Brown List-Id: alsa-devel@alsa-project.org At Mon, 29 Jun 2015 17:32:53 +0100, Liam Girdwood wrote: > > Parse DAPM objects including widgets and graph elements. > > Signed-off-by: Liam Girdwood > --- > src/topology/dapm.c | 560 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 560 insertions(+) > create mode 100644 src/topology/dapm.c > > diff --git a/src/topology/dapm.c b/src/topology/dapm.c > new file mode 100644 > index 0000000..fafe3cb > --- /dev/null > +++ b/src/topology/dapm.c > @@ -0,0 +1,560 @@ > +/* > + Copyright(c) 2014-2015 Intel Corporation > + All rights reserved. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of version 2 of the GNU General Public License as > + published by the Free Software Foundation. > + > + 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 > + General Public License for more details. > + > + Authors: Mengdong Lin > + Yao Jin > + Liam Girdwood > +*/ > + > +#include "list.h" > +#include "tplg_local.h" > + > +/* mapping of widget text names to types */ > +static const struct map_elem widget_map[] = { > + {"input", SND_SOC_TPLG_DAPM_INPUT}, > + {"output", SND_SOC_TPLG_DAPM_OUTPUT}, > + {"mux", SND_SOC_TPLG_DAPM_MUX}, > + {"mixer", SND_SOC_TPLG_DAPM_MIXER}, > + {"pga", SND_SOC_TPLG_DAPM_PGA}, > + {"out_drv", SND_SOC_TPLG_DAPM_OUT_DRV}, > + {"adc", SND_SOC_TPLG_DAPM_ADC}, > + {"dac", SND_SOC_TPLG_DAPM_DAC}, > + {"switch", SND_SOC_TPLG_DAPM_SWITCH}, > + {"pre", SND_SOC_TPLG_DAPM_PRE}, > + {"post", SND_SOC_TPLG_DAPM_POST}, > + {"aif_in", SND_SOC_TPLG_DAPM_AIF_IN}, > + {"aif_out", SND_SOC_TPLG_DAPM_AIF_OUT}, > + {"dai_in", SND_SOC_TPLG_DAPM_DAI_IN}, > + {"dai_out", SND_SOC_TPLG_DAPM_DAI_OUT}, > + {"dai_link", SND_SOC_TPLG_DAPM_DAI_LINK}, > +}; > + > +/* mapping of widget kcontrol text names to types */ > +static const struct map_elem widget_control_map[] = { > + {"volsw", SND_SOC_TPLG_DAPM_CTL_VOLSW}, > + {"enum_double", SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE}, > + {"enum_virt", SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT}, > + {"enum_value", SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE}, > +}; > + > +static int lookup_widget(const char *w) > +{ > + unsigned int i; > + > + for (i = 0; i < ARRAY_SIZE(widget_map); i++) { > + if (strcmp(widget_map[i].name, w) == 0) > + return widget_map[i].id; > + } > + > + return -EINVAL; > +} > + > +static int tplg_parse_dapm_mixers(snd_config_t *cfg, struct tplg_elem *elem) > +{ > + snd_config_iterator_t i, next; > + snd_config_t *n; > + const char *value = NULL; > + > + tplg_dbg(" DAPM Mixer Controls: %s\n", elem->id); > + > + snd_config_for_each(i, next, cfg) { > + n = snd_config_iterator_entry(i); > + > + /* get value */ > + if (snd_config_get_string(n, &value) < 0) > + continue; > + > + tplg_ref_add(elem, PARSER_TYPE_MIXER, value); > + tplg_dbg("\t\t %s\n", value); > + } > + > + return 0; > +} > + > +static int tplg_parse_dapm_enums(snd_config_t *cfg, struct tplg_elem *elem) > +{ > + snd_config_iterator_t i, next; > + snd_config_t *n; > + const char *value = NULL; > + > + tplg_dbg(" DAPM Enum Controls: %s\n", elem->id); > + > + snd_config_for_each(i, next, cfg) { > + n = snd_config_iterator_entry(i); > + > + /* get value */ > + if (snd_config_get_string(n, &value) < 0) > + continue; > + > + tplg_ref_add(elem, PARSER_TYPE_ENUM, value); > + tplg_dbg("\t\t %s\n", value); > + } > + > + return 0; > +} > + > +/* move referenced controls to the widget */ > +static int copy_dapm_control(struct tplg_elem *elem, struct tplg_elem *ref) > +{ > + struct snd_soc_tplg_dapm_widget *widget = elem->widget; > + struct snd_soc_tplg_mixer_control *mixer_ctrl = ref->mixer_ctrl; > + struct snd_soc_tplg_enum_control *enum_ctrl = ref->enum_ctrl; > + > + tplg_dbg("Control '%s' used by '%s'\n", ref->id, elem->id); > + tplg_dbg("\tparent size: %d + %d -> %d, priv size -> %d\n", > + elem->size, ref->size, elem->size + ref->size, > + widget->priv.size); > + > + widget = realloc(widget, elem->size + ref->size); > + if (!widget) > + return -ENOMEM; > + > + elem->widget = widget; > + > + /* copy new widget at the end */ > + if (ref->type == PARSER_TYPE_MIXER) > + memcpy((void*)widget + elem->size, mixer_ctrl, ref->size); > + else if (ref->type == PARSER_TYPE_ENUM) > + memcpy((void*)widget + elem->size, enum_ctrl, ref->size); > + > + elem->size += ref->size; > + widget->num_kcontrols++; > + ref->compound_elem = 1; > + return 0; > +} > + > +/* check referenced controls for a widget */ > +static int tplg_build_widget(snd_tplg_t *tplg, > + struct tplg_elem *elem) > +{ > + struct tplg_ref *ref; > + struct list_head *base, *pos, *npos; > + int err = 0; > + > + base = &elem->ref_list; > + > + /* for each ref in this control elem */ > + list_for_each_safe(pos, npos, base) { > + > + ref = list_entry(pos, struct tplg_ref, list); > + if (ref->id == NULL || ref->elem) > + continue; > + > + switch (ref->type) { > + case PARSER_TYPE_MIXER: > + ref->elem = tplg_elem_lookup(&tplg->mixer_list, > + ref->id, PARSER_TYPE_MIXER); > + if (ref->elem) > + err = copy_dapm_control(elem, ref->elem); > + break; > + > + case PARSER_TYPE_ENUM: > + ref->elem = tplg_elem_lookup(&tplg->enum_list, > + ref->id, PARSER_TYPE_ENUM); > + if (ref->elem) > + err = copy_dapm_control(elem, ref->elem); > + break; > + > + case PARSER_TYPE_DATA: > + ref->elem = tplg_elem_lookup(&tplg->pdata_list, > + ref->id, PARSER_TYPE_DATA); > + if (ref->elem) > + err = tplg_copy_data(elem, ref->elem); > + break; > + default: > + break; > + } > + > + if (!ref->elem) { > + fprintf(stderr, "error: cannot find control '%s'" > + " referenced by widget '%s'\n", > + ref->id, elem->id); > + return -EINVAL; > + } > + > + if (err < 0) > + return err; > + } > + > + return 0; > +} > + > +int tplg_build_widgets(snd_tplg_t *tplg) > +{ > + > + struct list_head *base, *pos, *npos; > + struct tplg_elem *elem; > + int err; > + > + base = &tplg->widget_list; > + list_for_each_safe(pos, npos, base) { > + > + elem = list_entry(pos, struct tplg_elem, list); > + if (!elem->widget || elem->type != PARSER_TYPE_DAPM_WIDGET) { > + fprintf(stderr, "error: invalid widget '%s'\n", > + elem->id); > + return -EINVAL; > + } > + > + err = tplg_build_widget(tplg, elem); > + if (err < 0) > + return err; > + } > + > + return 0; > +} > + > +int tplg_build_routes(snd_tplg_t *tplg) > +{ > + struct list_head *base, *pos, *npos; > + struct tplg_elem *elem; > + struct snd_soc_tplg_dapm_graph_elem *route; > + > + base = &tplg->route_list; > + > + list_for_each_safe(pos, npos, base) { > + elem = list_entry(pos, struct tplg_elem, list); > + > + if (!elem->route || elem->type != PARSER_TYPE_DAPM_GRAPH) { > + fprintf(stderr, "error: invalid route '%s'\n", > + elem->id); > + return -EINVAL; > + } > + > + route = elem->route; > + tplg_dbg("\nCheck route: sink '%s', control '%s', source '%s'\n", > + route->sink, route->control, route->source); > + > + /* validate sink */ > + if (strlen(route->sink) <= 0) { > + fprintf(stderr, "error: no sink\n"); > + return -EINVAL; > + > + } > + if (!tplg_elem_lookup(&tplg->widget_list, route->sink, > + PARSER_TYPE_DAPM_WIDGET)) { > + fprintf(stderr, "warning: undefined sink widget/stream '%s'\n", > + route->sink); > + } > + > + /* validate control name */ > + if (strlen(route->control)) { > + if (!tplg_elem_lookup(&tplg->mixer_list, > + route->control, PARSER_TYPE_MIXER) && > + !tplg_elem_lookup(&tplg->enum_list, > + route->control, PARSER_TYPE_ENUM)) { > + fprintf(stderr, "warning: Undefined mixer/enum control '%s'\n", > + route->control); > + } > + } > + > + /* validate source */ > + if (strlen(route->source) <= 0) { > + fprintf(stderr, "error: no source\n"); > + return -EINVAL; > + > + } > + if (!tplg_elem_lookup(&tplg->widget_list, route->source, > + PARSER_TYPE_DAPM_WIDGET)) { > + fprintf(stderr, "warning: Undefined source widget/stream '%s'\n", > + route->source); > + } > + } > + > + return 0; > +} > + > +#define LINE_SIZE 1024 > + > +/* line is defined as '"source, control, sink"' */ > +static int tplg_parse_line(const char *text, > + struct snd_soc_tplg_dapm_graph_elem *line) > +{ > + char buf[LINE_SIZE]; > + unsigned int len, i; > + const char *source = NULL, *sink = NULL, *control = NULL; > + > + strncpy(buf, text, LINE_SIZE); strncpy() doesn't terminate with NUL-character. You have to put manually buf[LINE_SIZE - 1] = 0; after strncpy() call. Unfortunately there is no strlcpy() in user-space... Takashi > + > + len = strlen(buf); > + if (len <= 2) { > + fprintf(stderr, "error: invalid route \"%s\"\n", buf); > + return -EINVAL; > + } > + > + /* find first , */ > + for (i = 1; i < len; i++) { > + if (buf[i] == ',') > + goto second; > + } > + fprintf(stderr, "error: invalid route \"%s\"\n", buf); > + return -EINVAL; > + > +second: > + /* find second , */ > + sink = buf; > + control = &buf[i + 2]; > + buf[i] = 0; > + > + for (; i < len; i++) { > + if (buf[i] == ',') > + goto done; > + } > + > + fprintf(stderr, "error: invalid route \"%s\"\n", buf); > + return -EINVAL; > + > +done: > + buf[i] = 0; > + source = &buf[i + 2]; > + > + strcpy(line->source, source); > + strcpy(line->control, control); > + strcpy(line->sink, sink); > + return 0; > +} > + > + > +static int tplg_parse_routes(snd_tplg_t *tplg, snd_config_t *cfg) > +{ > + snd_config_iterator_t i, next; > + snd_config_t *n; > + struct tplg_elem *elem; > + struct snd_soc_tplg_dapm_graph_elem *line = NULL; > + int err; > + > + snd_config_for_each(i, next, cfg) { > + const char *val; > + > + n = snd_config_iterator_entry(i); > + if (snd_config_get_string(n, &val) < 0) > + continue; > + > + elem = tplg_elem_new(); > + if (!elem) > + return -ENOMEM; > + > + list_add_tail(&elem->list, &tplg->route_list); > + strcpy(elem->id, "line"); > + elem->type = PARSER_TYPE_DAPM_GRAPH; > + elem->size = sizeof(*line); > + > + line = calloc(1, sizeof(*line)); > + if (!line) > + return -ENOMEM; > + > + elem->route = line; > + > + err = tplg_parse_line(val, line); > + if (err < 0) > + return err; > + > + tplg_dbg("route: sink '%s', control '%s', source '%s'\n", > + line->sink, line->control, line->source); > + } > + > + return 0; > +} > + > +int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, > + void *private ATTRIBUTE_UNUSED) > +{ > + snd_config_iterator_t i, next; > + snd_config_t *n; > + int err; > + const char *graph_id; > + > + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { > + fprintf(stderr, "error: compound is expected for dapm graph definition\n"); > + return -EINVAL; > + } > + > + snd_config_get_id(cfg, &graph_id); > + > + snd_config_for_each(i, next, cfg) { > + const char *id; > + > + n = snd_config_iterator_entry(i); > + if (snd_config_get_id(n, &id) < 0) { > + continue; > + } > + > + if (strcmp(id, "lines") == 0) { > + err = tplg_parse_routes(tplg, n); > + if (err < 0) { > + fprintf(stderr, "error: failed to parse dapm graph %s\n", > + graph_id); > + return err; > + } > + continue; > + } > + } > + > + return 0; > +} > + > +/* DAPM Widget. > + * > + * SectionWidget."widget name" { > + * > + * index "1" > + * type "aif_in" > + * no_pm "true" > + * shift "0" > + * invert "1 > + * mixer "name" > + * enum "name" > + * data "name" > + * } > + */ > +int tplg_parse_dapm_widget(snd_tplg_t *tplg, > + snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) > +{ > + struct snd_soc_tplg_dapm_widget *widget; > + struct tplg_elem *elem; > + snd_config_iterator_t i, next; > + snd_config_t *n; > + const char *id, *val = NULL; > + int widget_type, err; > + > + elem = tplg_elem_new_common(tplg, cfg, PARSER_TYPE_DAPM_WIDGET); > + if (!elem) > + return -ENOMEM; > + > + tplg_dbg(" Widget: %s\n", elem->id); > + > + widget = elem->widget; > + strncpy(widget->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); > + widget->size = elem->size; > + > + snd_config_for_each(i, next, cfg) { > + > + n = snd_config_iterator_entry(i); > + if (snd_config_get_id(n, &id) < 0) > + continue; > + > + /* skip comments */ > + if (strcmp(id, "comment") == 0) > + continue; > + if (id[0] == '#') > + continue; > + > + if (strcmp(id, "index") == 0) { > + if (snd_config_get_string(n, &val) < 0) > + return -EINVAL; > + > + elem->index = atoi(val); > + tplg_dbg("\t%s: %d\n", id, elem->index); > + continue; > + } > + > + if (strcmp(id, "type") == 0) { > + if (snd_config_get_string(n, &val) < 0) > + return -EINVAL; > + > + widget_type = lookup_widget(val); > + if (widget_type < 0){ > + fprintf(stderr, "Widget '%s': Unsupported widget type %s\n", > + elem->id, val); > + return -EINVAL; > + } > + > + widget->id = widget_type; > + tplg_dbg("\t%s: %s\n", id, val); > + continue; > + } > + > + if (strcmp(id, "no_pm") == 0) { > + if (snd_config_get_string(n, &val) < 0) > + return -EINVAL; > + > + if (strcmp(val, "true") == 0) > + widget->reg = -1; > + > + tplg_dbg("\t%s: %s\n", id, val); > + continue; > + } > + > + if (strcmp(id, "shift") == 0) { > + if (snd_config_get_string(n, &val) < 0) > + return -EINVAL; > + > + widget->shift = atoi(val); > + tplg_dbg("\t%s: %d\n", id, widget->shift); > + continue; > + } > + > + if (strcmp(id, "invert") == 0) { > + if (snd_config_get_string(n, &val) < 0) > + return -EINVAL; > + > + widget->invert = atoi(val); > + tplg_dbg("\t%s: %d\n", id, widget->invert); > + continue; > + } > + > + if (strcmp(id, "subseq") == 0) { > + if (snd_config_get_string(n, &val) < 0) > + return -EINVAL; > + > + widget->subseq= atoi(val); > + tplg_dbg("\t%s: %d\n", id, widget->subseq); > + continue; > + } > + > + if (strcmp(id, "event_type") == 0) { > + if (snd_config_get_string(n, &val) < 0) > + return -EINVAL; > + > + widget->event_type = atoi(val); > + tplg_dbg("\t%s: %d\n", id, widget->event_type); > + continue; > + } > + > + if (strcmp(id, "event_flags") == 0) { > + if (snd_config_get_string(n, &val) < 0) > + return -EINVAL; > + > + widget->event_flags = atoi(val); > + tplg_dbg("\t%s: %d\n", id, widget->event_flags); > + continue; > + } > + > + if (strcmp(id, "enum") == 0) { > + err = tplg_parse_dapm_enums(n, elem); > + if (err < 0) > + return err; > + > + continue; > + } > + > + if (strcmp(id, "mixer") == 0) { > + err = tplg_parse_dapm_mixers(n, elem); > + if (err < 0) > + return err; > + > + continue; > + } > + > + if (strcmp(id, "data") == 0) { > + if (snd_config_get_string(n, &val) < 0) > + return -EINVAL; > + > + tplg_ref_add(elem, PARSER_TYPE_DATA, val); > + tplg_dbg("\t%s: %s\n", id, val); > + continue; > + } > + } > + > + return 0; > +} > -- > 2.1.4 >