/* Copyright(c) 2021 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called LICENSE.GPL. */ #include #include #include #include "topology.h" #include "pre-processor.h" int tplg_build_base_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent, bool skip_name) { snd_config_t *top, *parent_obj, *cfg, *dest; const char *parent_name; /* find parent section config */ top = tplg_object_get_section(tplg_pp, parent); if (!top) return -EINVAL; parent_obj = tplg_object_get_instance_config(tplg_pp, parent); /* get parent name */ parent_name = tplg_object_get_name(tplg_pp, parent_obj); if (!parent_name) return 0; /* find parent config with name */ dest = tplg_find_config(top, parent_name); if (!dest) { SNDERR("Cannot find parent config %s\n", parent_name); return -EINVAL; } /* build config from template and add to parent */ return tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, dest, skip_name); } int tplg_build_scale_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent) { return tplg_build_base_object(tplg_pp, obj_cfg, parent, true); } int tplg_build_ops_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent) { return tplg_build_base_object(tplg_pp, obj_cfg, parent, false); } int tplg_build_channel_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent) { return tplg_build_base_object(tplg_pp, obj_cfg, parent, false); } int tplg_build_text_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent) { snd_config_t *cfg; const char *name; int ret; cfg = tplg_object_get_instance_config(tplg_pp, obj_cfg); name = tplg_object_get_name(tplg_pp, cfg); if (!name) return -EINVAL; ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false); if (ret < 0) return ret; return tplg_parent_update(tplg_pp, parent, "texts", name); } int tplg_build_tlv_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent) { snd_config_t *cfg; const char *name; int ret; cfg = tplg_object_get_instance_config(tplg_pp, obj_cfg); name = tplg_object_get_name(tplg_pp, cfg); if (!name) return -EINVAL; ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false); if (ret < 0) return ret; return tplg_parent_update(tplg_pp, parent, "tlv", name); } static int tplg_build_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent, char *type) { snd_config_t *cfg, *obj; const char *name; int ret; obj = tplg_object_get_instance_config(tplg_pp, obj_cfg); /* get control name */ ret = snd_config_search(obj, "name", &cfg); if (ret < 0) return 0; ret = snd_config_get_string(cfg, &name); if (ret < 0) return ret; ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false); if (ret < 0) return ret; ret = tplg_add_object_data(tplg_pp, obj_cfg, cfg, NULL); if (ret < 0) SNDERR("Failed to add data section for %s\n", name); return tplg_parent_update(tplg_pp, parent, type, name); } int tplg_build_mixer_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent) { return tplg_build_control(tplg_pp, obj_cfg, parent, "mixer"); } int tplg_build_bytes_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent) { return tplg_build_control(tplg_pp, obj_cfg, parent, "bytes"); } int tplg_build_enum_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent) { return tplg_build_control(tplg_pp, obj_cfg, parent, "enum"); } /* * Widget names for pipeline endpoints can be of the following type: * "class. 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, '.'); if (!args) { SNDERR("Error getting class name for %s\n", string); return -EINVAL; } 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); ret = -EINVAL; goto err; } 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", sink_widget_name, control, src_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; }