{
return tplg_build_control(tplg_pp, obj_cfg, parent, "bytes");
}
+
+/*
+ * Widget names for pipeline endpoints can be of the following type:
+ * "class.<constructor args separated by .> ex: pga.0.1, buffer.1.1 etc
+ * Optionally, the index argument for a widget can be omitted and will be substituted with
+ * the index from the route: ex: pga..0, host..playback etc
+ */
+static int tplg_pp_get_widget_name(struct tplg_pre_processor *tplg_pp,
+ const char *string, long index, char **widget)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *temp_cfg, *child, *class_cfg, *n;
+ char *class_name, *args, *widget_name;
+ int ret;
+
+ /* get class name */
+ args = strchr(string, '.');
+ class_name = calloc(1, strlen(string) - strlen(args) + 1);
+ if (!class_name)
+ return -ENOMEM;
+
+ snprintf(class_name, strlen(string) - strlen(args) + 1, "%s", string);
+
+ /* create config with Widget class type */
+ ret = snd_config_make(&temp_cfg, "Widget", SND_CONFIG_TYPE_COMPOUND);
+ if (ret < 0) {
+ free(class_name);
+ return ret;
+ }
+
+ /* create config with class name and add it to the Widget config */
+ ret = tplg_config_make_add(&child, class_name, SND_CONFIG_TYPE_COMPOUND, temp_cfg);
+ if (ret < 0) {
+ free(class_name);
+ return ret;
+ }
+
+ /* get class definition for widget */
+ class_cfg = tplg_class_lookup(tplg_pp, temp_cfg);
+ snd_config_delete(temp_cfg);
+ if (!class_cfg) {
+ free(class_name);
+ return -EINVAL;
+ }
+
+ /* get constructor for class */
+ ret = snd_config_search(class_cfg, "attributes.constructor", &temp_cfg);
+ if (ret < 0) {
+ SNDERR("No arguments in class for widget %s\n", string);
+ free(class_name);
+ return ret;
+ }
+
+ widget_name = strdup(class_name);
+ free(class_name);
+ if (!widget_name)
+ return -ENOMEM;
+
+ /* construct widget name using the constructor argument values */
+ snd_config_for_each(i, next, temp_cfg) {
+ const char *id;
+ char *arg, *remaining, *temp;
+
+ n = snd_config_iterator_entry(i);
+ if (snd_config_get_string(n, &id) < 0)
+ continue;
+
+ if (!args) {
+ SNDERR("insufficient arugments for widget %s\n", string);
+ return -EINVAL;
+ }
+
+ remaining = strchr(args + 1, '.');
+ if (remaining) {
+ arg = calloc(1, strlen(args + 1) - strlen(remaining) + 1);
+ if (!arg) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ snprintf(arg, strlen(args + 1) - strlen(remaining) + 1, "%s", args + 1);
+ } else {
+ arg = calloc(1, strlen(args + 1) + 1);
+ if (!arg) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ snprintf(arg, strlen(args + 1) + 1, "%s", args + 1);
+ }
+
+ /* if no index provided, substitue with route index */
+ if (!strcmp(arg, "") && !strcmp(id, "index")) {
+ free(arg);
+ arg = tplg_snprintf("%ld", index);
+ if (!arg) {
+ ret = -ENOMEM;
+ free(arg);
+ goto err;
+ }
+ }
+
+ temp = tplg_snprintf("%s.%s", widget_name, arg);
+ if (!temp) {
+ ret = -ENOMEM;
+ free(arg);
+ goto err;
+ }
+
+ free(widget_name);
+ widget_name = temp;
+ free(arg);
+ if (remaining)
+ args = remaining;
+ else
+ args = NULL;
+ }
+
+ *widget = widget_name;
+ return 0;
+
+err:
+ free(widget_name);
+ return ret;
+}
+
+int tplg_build_dapm_route_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
+ snd_config_t *parent)
+{
+ snd_config_t *top, *obj, *cfg, *route, *child, *parent_obj;
+ const char *name, *wname;
+ const char *parent_name = "Endpoint";
+ char *src_widget_name, *sink_widget_name, *line_str, *route_name;
+ const char *control = "";
+ long index = 0;
+ int ret;
+
+ obj = tplg_object_get_instance_config(tplg_pp, obj_cfg);
+
+ ret = snd_config_get_id(obj, &name);
+ if (ret < 0)
+ return -EINVAL;
+
+ /* endpoint connections at the top-level conf have no parent */
+ if (parent) {
+ parent_obj = tplg_object_get_instance_config(tplg_pp, parent);
+
+ ret = snd_config_get_id(parent_obj, &parent_name);
+ if (ret < 0)
+ return -EINVAL;
+ }
+
+ tplg_pp_debug("Building DAPM route object: '%s' ...", name);
+
+ ret = snd_config_search(tplg_pp->output_cfg, "SectionGraph", &top);
+ if (ret < 0) {
+ ret = tplg_config_make_add(&top, "SectionGraph",
+ SND_CONFIG_TYPE_COMPOUND, tplg_pp->output_cfg);
+ if (ret < 0) {
+ SNDERR("Error creating 'SectionGraph' config\n");
+ return ret;
+ }
+ }
+
+ /* get route index */
+ ret = snd_config_search(obj, "index", &cfg);
+ if (ret >= 0) {
+ ret = snd_config_get_integer(cfg, &index);
+ if (ret < 0) {
+ SNDERR("Invalid index route %s\n", name);
+ return ret;
+ }
+ }
+
+ /* get source widget name */
+ ret = snd_config_search(obj, "source", &cfg);
+ if (ret < 0) {
+ SNDERR("No source for route %s\n", name);
+ return ret;
+ }
+
+ ret = snd_config_get_string(cfg, &wname);
+ if (ret < 0) {
+ SNDERR("Invalid name for source in route %s\n", name);
+ return ret;
+ }
+
+ ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &src_widget_name);
+ if (ret < 0) {
+ SNDERR("error getting widget name for %s\n", wname);
+ return ret;
+ }
+
+ /* get sink widget name */
+ ret = snd_config_search(obj, "sink", &cfg);
+ if (ret < 0) {
+ SNDERR("No sink for route %s\n", name);
+ free(src_widget_name);
+ return ret;
+ }
+
+ ret = snd_config_get_string(cfg, &wname);
+ if (ret < 0) {
+ SNDERR("Invalid name for sink in route %s\n", name);
+ free(src_widget_name);
+ return ret;
+ }
+
+ ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &sink_widget_name);
+ if (ret < 0) {
+ SNDERR("error getting widget name for %s\n", wname);
+ free(src_widget_name);
+ return ret;
+ }
+
+ /* get control name */
+ ret = snd_config_search(obj, "control", &cfg);
+ if (ret >= 0) {
+ ret = snd_config_get_string(cfg, &control);
+ if (ret < 0) {
+ SNDERR("Invalid control name for route %s\n", name);
+ goto err;
+ }
+ }
+
+ /* add route */
+ route_name = tplg_snprintf("%s.%s", parent_name, name);
+ if (!route_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = snd_config_make(&route, route_name, SND_CONFIG_TYPE_COMPOUND);
+ free(route_name);
+ if (ret < 0) {
+ SNDERR("Error creating route config for %s %d\n", name, ret);
+ goto err;
+ }
+
+ ret = snd_config_add(top, route);
+ if (ret < 0) {
+ SNDERR("Error adding route config for %s %d\n", name, ret);
+ goto err;
+ }
+
+ /* add index */
+ ret = tplg_config_make_add(&child, "index", SND_CONFIG_TYPE_INTEGER, route);
+ if (ret < 0) {
+ SNDERR("Error creating index config for %s\n", name);
+ goto err;
+ }
+
+ ret = snd_config_set_integer(child, index);
+ if (ret < 0) {
+ SNDERR("Error setting index config for %s\n", name);
+ goto err;
+ }
+
+ /* add lines */
+ ret = tplg_config_make_add(&cfg, "lines", SND_CONFIG_TYPE_COMPOUND, route);
+ if (ret < 0) {
+ SNDERR("Error creating lines config for %s\n", name);
+ goto err;
+ }
+
+ /* add route string */
+ ret = tplg_config_make_add(&child, "0", SND_CONFIG_TYPE_STRING, cfg);
+ if (ret < 0) {
+ SNDERR("Error creating lines config for %s\n", name);
+ goto err;
+ }
+
+ line_str = tplg_snprintf("%s, %s, %s", src_widget_name, control, sink_widget_name);
+ if (!line_str) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* set the line string */
+ ret = snd_config_set_string(child, line_str);
+ free(line_str);
+ if (ret < 0)
+ SNDERR("Error creating lines config for %s\n", name);
+err:
+ free(src_widget_name);
+ free(sink_widget_name);
+ return ret;
+}