mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-10 03:45:42 +01:00
d999c267d3
The pre-processor converts the Topology2.0 objects into the relevant sections by looking for attributes defined in the template config for the section and reading the attribute values from the object instance config. The structure struct build_function_map contains the mapping of the build function to use for each object based on the type and name for the class that the object belongs to. The manifest object is the simplest with no attributes. So, the build function simply creates a new Section called SectionManifest which will be populated with the data section in the following patches. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
981 lines
24 KiB
C
981 lines
24 KiB
C
/*
|
|
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 <assert.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <alsa/input.h>
|
|
#include <alsa/output.h>
|
|
#include <alsa/conf.h>
|
|
#include <alsa/error.h>
|
|
#include "gettext.h"
|
|
#include "topology.h"
|
|
#include "pre-processor.h"
|
|
|
|
static int tplg_create_config_template(struct tplg_pre_processor *tplg_pp,
|
|
snd_config_t **template,
|
|
const struct config_template_items *items)
|
|
{
|
|
snd_config_t *top, *child;
|
|
int ret, i;
|
|
|
|
ret = snd_config_make(&top, "template", SND_CONFIG_TYPE_COMPOUND);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* add integer configs */
|
|
if (items->int_config_ids)
|
|
for (i = 0; i < MAX_CONFIGS_IN_TEMPLATE; i++)
|
|
if (items->int_config_ids[i]) {
|
|
ret = tplg_config_make_add(&child, items->int_config_ids[i],
|
|
SND_CONFIG_TYPE_INTEGER, top);
|
|
if (ret < 0)
|
|
goto err;
|
|
}
|
|
|
|
/* add string configs */
|
|
if (items->string_config_ids)
|
|
for (i = 0; i < MAX_CONFIGS_IN_TEMPLATE; i++)
|
|
if (items->string_config_ids[i]) {
|
|
ret = tplg_config_make_add(&child, items->string_config_ids[i],
|
|
SND_CONFIG_TYPE_STRING, top);
|
|
if (ret < 0)
|
|
goto err;
|
|
}
|
|
|
|
/* add compound configs */
|
|
if (items->compound_config_ids)
|
|
for (i = 0; i < MAX_CONFIGS_IN_TEMPLATE; i++) {
|
|
if (items->compound_config_ids[i]) {
|
|
ret = tplg_config_make_add(&child, items->compound_config_ids[i],
|
|
SND_CONFIG_TYPE_COMPOUND, top);
|
|
if (ret < 0)
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
err:
|
|
if (ret < 0) {
|
|
snd_config_delete(top);
|
|
return ret;
|
|
}
|
|
|
|
*template = top;
|
|
return ret;
|
|
}
|
|
|
|
static void tplg_attribute_print_valid_values(snd_config_t *valid_values, const char *name)
|
|
{
|
|
snd_config_iterator_t i, next;
|
|
snd_config_t *n;
|
|
|
|
SNDERR("valid values for attribute %s are:\n", name);
|
|
|
|
snd_config_for_each(i, next, valid_values) {
|
|
const char *s, *id;
|
|
|
|
n = snd_config_iterator_entry(i);
|
|
if (snd_config_get_id(n, &id) < 0)
|
|
continue;
|
|
|
|
if (snd_config_get_string(n, &s) < 0)
|
|
continue;
|
|
|
|
SNDERR("%s", s);
|
|
}
|
|
}
|
|
|
|
/* check is attribute value belongs in the set of valid values */
|
|
static bool tplg_is_attribute_valid_value(snd_config_t *valid_values, const char *value)
|
|
{
|
|
snd_config_iterator_t i, next;
|
|
snd_config_t *n;
|
|
|
|
snd_config_for_each(i, next, valid_values) {
|
|
const char *s, *id;
|
|
|
|
n = snd_config_iterator_entry(i);
|
|
if (snd_config_get_id(n, &id) < 0)
|
|
continue;
|
|
|
|
if (snd_config_get_string(n, &s) < 0)
|
|
continue;
|
|
|
|
if (!strcmp(value, s))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* check if attribute value passes the min/max value constraints */
|
|
static bool tplg_object_is_attribute_min_max_valid(snd_config_t *attr, snd_config_t *obj_attr,
|
|
bool min_check)
|
|
{
|
|
snd_config_type_t type = snd_config_get_type(obj_attr);
|
|
snd_config_t *valid;
|
|
const char *attr_name;
|
|
int ret;
|
|
|
|
if (snd_config_get_id(attr, &attr_name) < 0)
|
|
return false;
|
|
|
|
if (min_check) {
|
|
ret = snd_config_search(attr, "constraints.min", &valid);
|
|
if (ret < 0)
|
|
return true;
|
|
} else {
|
|
ret = snd_config_search(attr, "constraints.max", &valid);
|
|
if (ret < 0)
|
|
return true;
|
|
}
|
|
|
|
switch(type) {
|
|
case SND_CONFIG_TYPE_INTEGER:
|
|
{
|
|
long v, m;
|
|
|
|
if (snd_config_get_integer(valid, &m) < 0)
|
|
return true;
|
|
|
|
if (snd_config_get_integer(obj_attr, &v) < 0)
|
|
return false;
|
|
|
|
if (min_check) {
|
|
if (v < m) {
|
|
SNDERR("attribute '%s' value: %ld is less than min value: %d\n",
|
|
attr_name, v, m);
|
|
return false;
|
|
}
|
|
} else {
|
|
if (v > m) {
|
|
SNDERR("attribute '%s' value: %ld is greater than max value: %d\n",
|
|
attr_name, v, m);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
case SND_CONFIG_TYPE_INTEGER64:
|
|
{
|
|
long long v;
|
|
long m;
|
|
|
|
if (snd_config_get_integer(valid, &m) < 0)
|
|
return true;
|
|
|
|
if (snd_config_get_integer64(obj_attr, &v) < 0)
|
|
return false;
|
|
|
|
if (min_check) {
|
|
if (v < m) {
|
|
SNDERR("attribute '%s' value: %ld is less than min value: %d\n",
|
|
attr_name, v, m);
|
|
return false;
|
|
}
|
|
} else {
|
|
if (v > m) {
|
|
SNDERR("attribute '%s' value: %ld is greater than max value: %d\n",
|
|
attr_name, v, m);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* check for min/max and valid value constraints */
|
|
static bool tplg_object_is_attribute_valid(struct tplg_pre_processor *tplg_pp,
|
|
snd_config_t *attr, snd_config_t *object)
|
|
{
|
|
snd_config_iterator_t i, next;
|
|
snd_config_t *valid, *obj_attr, *n;
|
|
snd_config_type_t type;
|
|
const char *attr_name, *obj_value;
|
|
int ret;
|
|
|
|
if (snd_config_get_id(attr, &attr_name) < 0)
|
|
return false;
|
|
|
|
ret = snd_config_search(object, attr_name, &obj_attr);
|
|
if (ret < 0) {
|
|
SNDERR("attr %s not found \n", attr_name);
|
|
return false;
|
|
}
|
|
type = snd_config_get_type(obj_attr);
|
|
|
|
/* check if attribute has valid values */
|
|
ret = snd_config_search(attr, "constraints.valid_values", &valid);
|
|
if (ret < 0)
|
|
goto min_max_check;
|
|
|
|
switch(type) {
|
|
case SND_CONFIG_TYPE_STRING:
|
|
if (snd_config_get_string(obj_attr, &obj_value) < 0)
|
|
return false;
|
|
if (!tplg_is_attribute_valid_value(valid, obj_value)) {
|
|
tplg_attribute_print_valid_values(valid, attr_name);
|
|
return false;
|
|
}
|
|
return true;
|
|
case SND_CONFIG_TYPE_COMPOUND:
|
|
snd_config_for_each(i, next, obj_attr) {
|
|
const char *s, *id;
|
|
|
|
n = snd_config_iterator_entry(i);
|
|
if (snd_config_get_id(n, &id) < 0)
|
|
continue;
|
|
|
|
if (snd_config_get_string(n, &s) < 0)
|
|
continue;
|
|
|
|
if (!tplg_is_attribute_valid_value(valid, s)) {
|
|
tplg_attribute_print_valid_values(valid, attr_name);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
|
|
min_max_check:
|
|
if (!tplg_object_is_attribute_min_max_valid(attr, obj_attr, true))
|
|
return false;
|
|
|
|
return tplg_object_is_attribute_min_max_valid(attr, obj_attr, false);
|
|
}
|
|
|
|
/* get object's name attribute value */
|
|
const char *tplg_object_get_name(struct tplg_pre_processor *tplg_pp,
|
|
snd_config_t *object)
|
|
{
|
|
snd_config_t *cfg;
|
|
const char *name;
|
|
int ret;
|
|
|
|
ret = snd_config_search(object, "name", &cfg);
|
|
if (ret < 0)
|
|
return NULL;
|
|
|
|
ret = snd_config_get_string(cfg, &name);
|
|
if (ret < 0)
|
|
return NULL;
|
|
|
|
return name;
|
|
}
|
|
|
|
/* look up the instance of object in a config */
|
|
static snd_config_t *tplg_object_lookup_in_config(struct tplg_pre_processor *tplg_pp,
|
|
snd_config_t *class, const char *type,
|
|
const char *class_name, const char *id)
|
|
{
|
|
snd_config_t *obj_cfg = NULL;
|
|
char *config_id;
|
|
|
|
config_id = tplg_snprintf("Object.%s.%s.%s", type, class_name, id);
|
|
if (!config_id)
|
|
return NULL;
|
|
|
|
snd_config_search(class, config_id, &obj_cfg);
|
|
free(config_id);
|
|
return obj_cfg;
|
|
}
|
|
|
|
/* search for all template configs in the source config and copy them to the destination */
|
|
static int tplg_object_add_attributes(snd_config_t *dst, snd_config_t *template,
|
|
snd_config_t *src)
|
|
{
|
|
snd_config_iterator_t i, next;
|
|
snd_config_t *n;
|
|
int ret;
|
|
|
|
snd_config_for_each(i, next, template) {
|
|
snd_config_t *attr, *new;
|
|
const char *id;
|
|
|
|
n = snd_config_iterator_entry(i);
|
|
if (snd_config_get_id(n, &id) < 0)
|
|
continue;
|
|
|
|
ret = snd_config_search(src, id, &attr);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
/* skip if attribute is already set */
|
|
ret = snd_config_search(dst, id, &new);
|
|
if (ret >= 0)
|
|
continue;
|
|
|
|
ret = snd_config_copy(&new, attr);
|
|
if (ret < 0) {
|
|
SNDERR("failed to copy attribute %s\n", id);
|
|
return ret;
|
|
}
|
|
|
|
ret = snd_config_add(dst, new);
|
|
if (ret < 0) {
|
|
snd_config_delete(new);
|
|
SNDERR("failed to add attribute %s\n", id);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct build_function_map *tplg_object_get_map(struct tplg_pre_processor *tplg_pp,
|
|
snd_config_t *obj);
|
|
|
|
/*
|
|
* Function to create a new "section" config based on the template. The new config will be
|
|
* added to the output_cfg or the top_config input parameter.
|
|
*/
|
|
int tplg_build_object_from_template(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
|
|
snd_config_t **wtop, snd_config_t *top_config,
|
|
bool skip_name)
|
|
{
|
|
snd_config_t *top, *template, *obj;
|
|
const struct build_function_map *map;
|
|
const char *object_name;
|
|
int ret;
|
|
|
|
/* look up object map */
|
|
map = tplg_object_get_map(tplg_pp, obj_cfg);
|
|
if (!map) {
|
|
SNDERR("unknown object type or class name\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
obj = tplg_object_get_instance_config(tplg_pp, obj_cfg);
|
|
|
|
/* look up or create the corresponding section config for object */
|
|
if (!top_config)
|
|
top_config = tplg_pp->output_cfg;
|
|
|
|
ret = snd_config_search(top_config, map->section_name, &top);
|
|
if (ret < 0) {
|
|
ret = tplg_config_make_add(&top, map->section_name, SND_CONFIG_TYPE_COMPOUND,
|
|
top_config);
|
|
if (ret < 0) {
|
|
SNDERR("Error creating %s config\n", map->section_name);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* get object name */
|
|
object_name = tplg_object_get_name(tplg_pp, obj);
|
|
if (!object_name) {
|
|
ret = snd_config_get_id(obj, &object_name);
|
|
if (ret < 0) {
|
|
SNDERR("Invalid ID for %s\n", map->section_name);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
tplg_pp_debug("Building object: '%s' ...", object_name);
|
|
|
|
/* create and add new object config with name, if needed */
|
|
if (skip_name) {
|
|
*wtop = top;
|
|
} else {
|
|
*wtop = tplg_find_config(top, object_name);
|
|
if (!(*wtop)) {
|
|
ret = tplg_config_make_add(wtop, object_name, SND_CONFIG_TYPE_COMPOUND,
|
|
top);
|
|
if (ret < 0) {
|
|
SNDERR("Error creating config for %s\n", object_name);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* create template config */
|
|
if (!map->template_items)
|
|
return 0;
|
|
|
|
ret = tplg_create_config_template(tplg_pp, &template, map->template_items);
|
|
if (ret < 0) {
|
|
SNDERR("Error creating template config for %s\n", object_name);
|
|
return ret;
|
|
}
|
|
|
|
/* update section config based on template and the attribute values in the object */
|
|
ret = tplg_object_add_attributes(*wtop, template, obj);
|
|
snd_config_delete(template);
|
|
if (ret < 0)
|
|
SNDERR("Error adding attributes for object '%s'\n", object_name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tplg_build_generic_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
|
|
snd_config_t *parent)
|
|
{
|
|
snd_config_t *wtop;
|
|
|
|
return tplg_build_object_from_template(tplg_pp, obj_cfg, &wtop, NULL, false);
|
|
}
|
|
|
|
const struct build_function_map object_build_map[] = {
|
|
{"Base", "manifest", "SectionManifest", &tplg_build_generic_object, NULL},
|
|
};
|
|
|
|
static const struct build_function_map *tplg_object_get_map(struct tplg_pre_processor *tplg_pp,
|
|
snd_config_t *obj)
|
|
{
|
|
snd_config_iterator_t first;
|
|
snd_config_t *class;
|
|
const char *class_type, *class_name;
|
|
unsigned int i;
|
|
|
|
first = snd_config_iterator_first(obj);
|
|
class = snd_config_iterator_entry(first);
|
|
|
|
if (snd_config_get_id(class, &class_name) < 0)
|
|
return NULL;
|
|
|
|
if (snd_config_get_id(obj, &class_type) < 0)
|
|
return NULL;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(object_build_map); i++) {
|
|
if (!strcmp(class_type, "Widget") &&
|
|
!strcmp(object_build_map[i].class_type, "Widget"))
|
|
return &object_build_map[i];
|
|
|
|
if (!strcmp(class_type, "Dai") &&
|
|
!strcmp(object_build_map[i].class_type, "Dai"))
|
|
return &object_build_map[i];
|
|
|
|
/* for other type objects, also match the object class_name */
|
|
if (!strcmp(class_type, object_build_map[i].class_type) &&
|
|
!strcmp(object_build_map[i].class_name, class_name))
|
|
return &object_build_map[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* return 1 if attribute not found in search_config, 0 on success and negative value on error */
|
|
static int tplg_object_copy_and_add_param(struct tplg_pre_processor *tplg_pp,
|
|
snd_config_t *obj,
|
|
snd_config_t *attr_cfg,
|
|
snd_config_t *search_config)
|
|
{
|
|
snd_config_t *attr, *new;
|
|
const char *id, *search_id;
|
|
int ret;
|
|
|
|
if (snd_config_get_id(attr_cfg, &id) < 0)
|
|
return 0;
|
|
|
|
if (snd_config_get_id(search_config, &search_id) < 0)
|
|
return 0;
|
|
|
|
/* copy object value */
|
|
ret = snd_config_search(search_config, id, &attr);
|
|
if (ret < 0)
|
|
return 1;
|
|
|
|
ret = snd_config_copy(&new, attr);
|
|
if (ret < 0) {
|
|
SNDERR("error copying attribute '%s' value from %s\n", id, search_id);
|
|
return ret;
|
|
}
|
|
|
|
ret = snd_config_add(obj, new);
|
|
if (ret < 0) {
|
|
snd_config_delete(new);
|
|
SNDERR("error adding attribute '%s' value to %s\n", id, search_id);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Attribute values for an object can be set in one of the following in order of
|
|
* precedence:
|
|
* 1. Value set in object instance
|
|
* 2. Default value set in the object's class definition
|
|
* 3. Inherited value from the parent object
|
|
* 4. Value set in the object instance embedded in the parent object
|
|
* 5. Value set in the object instance embedded in the parent class definition
|
|
*/
|
|
static int tplg_object_update(struct tplg_pre_processor *tplg_pp, snd_config_t *obj,
|
|
snd_config_t *parent)
|
|
{
|
|
snd_config_iterator_t i, next;
|
|
snd_config_t *n, *cfg, *args;
|
|
snd_config_t *obj_cfg, *class_cfg, *parent_obj;
|
|
const char *obj_id, *class_name, *class_type;
|
|
int ret;
|
|
|
|
class_cfg = tplg_class_lookup(tplg_pp, obj);
|
|
if (!class_cfg)
|
|
return -EINVAL;
|
|
|
|
/* find config for class attributes */
|
|
ret = snd_config_search(class_cfg, "DefineAttribute", &args);
|
|
if (ret < 0)
|
|
return 0;
|
|
|
|
if (snd_config_get_id(obj, &class_type) < 0)
|
|
return 0;
|
|
|
|
if (snd_config_get_id(class_cfg, &class_name) < 0)
|
|
return 0;
|
|
|
|
/* get obj cfg */
|
|
obj_cfg = tplg_object_get_instance_config(tplg_pp, obj);
|
|
if (snd_config_get_id(obj_cfg, &obj_id) < 0)
|
|
return 0;
|
|
|
|
/* copy and add attributes */
|
|
snd_config_for_each(i, next, args) {
|
|
snd_config_t *attr;
|
|
const char *id;
|
|
|
|
n = snd_config_iterator_entry(i);
|
|
if (snd_config_get_id(n, &id) < 0)
|
|
continue;
|
|
|
|
if (tplg_class_is_attribute_unique(id, class_cfg))
|
|
continue;
|
|
|
|
if (tplg_class_is_attribute_immutable(id, class_cfg))
|
|
goto class;
|
|
|
|
/* check if attribute value is set in the object */
|
|
ret = snd_config_search(obj_cfg, id, &attr);
|
|
if (ret < 0)
|
|
goto class;
|
|
goto validate;
|
|
class:
|
|
/* search for attributes value in class */
|
|
ret = tplg_object_copy_and_add_param(tplg_pp, obj_cfg, n, class_cfg);
|
|
if (ret == 1) {
|
|
if (tplg_class_is_attribute_immutable(id, class_cfg)) {
|
|
SNDERR("Immutable attribute %s not set in class %s\n",
|
|
id, class_name);
|
|
return -EINVAL;
|
|
}
|
|
goto parent;
|
|
}
|
|
else if (ret < 0)
|
|
return ret;
|
|
goto validate;
|
|
parent:
|
|
/* search for attribute value in parent */
|
|
if (!parent)
|
|
goto parent_object;
|
|
|
|
/* get parent obj cfg */
|
|
parent_obj = tplg_object_get_instance_config(tplg_pp, parent);
|
|
if (!parent_obj)
|
|
goto parent_object;
|
|
|
|
ret = tplg_object_copy_and_add_param(tplg_pp, obj_cfg, n, parent_obj);
|
|
if (ret == 1)
|
|
goto parent_object;
|
|
else if (ret < 0)
|
|
return ret;
|
|
goto validate;
|
|
parent_object:
|
|
if (!parent)
|
|
goto parent_class;
|
|
|
|
cfg = tplg_object_lookup_in_config(tplg_pp, parent_obj, class_type,
|
|
class_name, obj_id);
|
|
if (!cfg)
|
|
goto parent_class;
|
|
|
|
ret = tplg_object_copy_and_add_param(tplg_pp, obj_cfg, n, cfg);
|
|
if (ret == 1)
|
|
goto parent_class;
|
|
else if (ret < 0)
|
|
return ret;
|
|
goto validate;
|
|
parent_class:
|
|
if (!parent)
|
|
goto check;
|
|
|
|
cfg = tplg_class_lookup(tplg_pp, parent);
|
|
if (!cfg)
|
|
return -EINVAL;
|
|
|
|
cfg = tplg_object_lookup_in_config(tplg_pp, cfg, class_type,
|
|
class_name, obj_id);
|
|
if (!cfg)
|
|
goto check;
|
|
|
|
ret = tplg_object_copy_and_add_param(tplg_pp, obj_cfg, n, cfg);
|
|
if (ret == 1)
|
|
goto check;
|
|
else if (ret < 0)
|
|
return ret;
|
|
goto validate;
|
|
check:
|
|
if (tplg_class_is_attribute_mandatory(id, class_cfg)) {
|
|
SNDERR("Mandatory attribute %s not set for class %s\n", id, class_name);
|
|
return -EINVAL;
|
|
}
|
|
continue;
|
|
validate:
|
|
if (!tplg_object_is_attribute_valid(tplg_pp, n, obj_cfg))
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tplg_construct_object_name(struct tplg_pre_processor *tplg_pp, snd_config_t *obj,
|
|
snd_config_t *class_cfg)
|
|
{
|
|
snd_config_iterator_t i, next;
|
|
snd_config_t *args, *n;
|
|
const char *id, *class_id, *obj_id, *s;
|
|
char *new_name;
|
|
int ret;
|
|
|
|
/* find config for class constructor attributes. Nothing to do if not defined */
|
|
ret = snd_config_search(class_cfg, "attributes.constructor", &args);
|
|
if (ret < 0)
|
|
return 0;
|
|
|
|
/* set class name as the name prefix for the object */
|
|
snd_config_get_id(obj, &obj_id);
|
|
snd_config_get_id(class_cfg, &class_id);
|
|
new_name = strdup(class_id);
|
|
if (!new_name)
|
|
return -ENOMEM;
|
|
|
|
/* iterate through all class arguments and set object name */
|
|
snd_config_for_each(i, next, args) {
|
|
snd_config_t *arg;
|
|
char *arg_value, *temp;
|
|
|
|
n = snd_config_iterator_entry(i);
|
|
|
|
if (snd_config_get_id(n, &id) < 0) {
|
|
SNDERR("Invalid ID for constructor argument\n");
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
if (snd_config_get_string(n, &s) < 0) {
|
|
SNDERR("Invalid value for constructor argument\n");
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
/* find and replace with value set in object */
|
|
ret = snd_config_search(obj, s, &arg);
|
|
if (ret < 0) {
|
|
SNDERR("Argument %s not set for object '%s.%s'\n", s, class_id, obj_id);
|
|
ret = -ENOENT;
|
|
goto err;
|
|
}
|
|
|
|
/* concat arg value to object name. arg types must be either integer or string */
|
|
switch (snd_config_get_type(arg)) {
|
|
case SND_CONFIG_TYPE_INTEGER:
|
|
{
|
|
long v;
|
|
ret = snd_config_get_integer(arg, &v);
|
|
assert(ret >= 0);
|
|
|
|
arg_value = tplg_snprintf("%ld", v);
|
|
if (!arg_value) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
break;
|
|
}
|
|
case SND_CONFIG_TYPE_STRING:
|
|
{
|
|
const char *s;
|
|
|
|
ret = snd_config_get_string(arg, &s);
|
|
assert(ret >= 0);
|
|
|
|
arg_value = strdup(s);
|
|
if (!arg_value) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
SNDERR("Argument '%s' in object '%s.%s' is not an integer or a string\n",
|
|
s, class_id, obj_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* alloc and concat arg value to the name */
|
|
temp = tplg_snprintf("%s.%s", new_name, arg_value);
|
|
if (!temp) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
free(new_name);
|
|
new_name = temp;
|
|
free(arg_value);
|
|
}
|
|
|
|
ret = snd_config_set_id(obj, new_name);
|
|
err:
|
|
free(new_name);
|
|
return ret;
|
|
}
|
|
|
|
/* set the attribute value by type */
|
|
static int tplg_set_attribute_value(snd_config_t *attr, const char *value)
|
|
{
|
|
int err;
|
|
snd_config_type_t type = snd_config_get_type(attr);
|
|
|
|
switch (type) {
|
|
case SND_CONFIG_TYPE_INTEGER:
|
|
{
|
|
long v;
|
|
|
|
v = strtol(value, NULL, 10);
|
|
err = snd_config_set_integer(attr, v);
|
|
assert(err >= 0);
|
|
break;
|
|
}
|
|
case SND_CONFIG_TYPE_INTEGER64:
|
|
{
|
|
long long v;
|
|
|
|
v = strtoll(value, NULL, 10);
|
|
err = snd_config_set_integer64(attr, v);
|
|
assert(err >= 0);
|
|
break;
|
|
}
|
|
case SND_CONFIG_TYPE_STRING:
|
|
{
|
|
err = snd_config_set_string(attr, value);
|
|
assert(err >= 0);
|
|
break;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Find the unique attribute in the class definition and set its value and type.
|
|
* Only string or integer types are allowed for unique values.
|
|
*/
|
|
static int tplg_object_set_unique_attribute(struct tplg_pre_processor *tplg_pp,
|
|
snd_config_t *obj, snd_config_t *class_cfg,
|
|
const char *id)
|
|
{
|
|
snd_config_t *unique_attr, *new;
|
|
const char *unique_name, *class_id;
|
|
int ret;
|
|
|
|
if (snd_config_get_id(class_cfg, &class_id) < 0)
|
|
return 0;
|
|
|
|
/* find config for class unique attribute */
|
|
unique_name = tplg_class_get_unique_attribute_name(tplg_pp, class_cfg);
|
|
if (!unique_name)
|
|
return -ENOENT;
|
|
|
|
/* find the unique attribute definition in the class */
|
|
unique_attr = tplg_class_find_attribute_by_name(tplg_pp, class_cfg, unique_name);
|
|
if (!unique_attr)
|
|
return -ENOENT;
|
|
|
|
/* override value if unique attribute is set in the object instance */
|
|
ret = snd_config_search(obj, unique_name, &new);
|
|
if (ret < 0) {
|
|
ret = snd_config_make(&new, unique_name,
|
|
tplg_class_get_attribute_type(tplg_pp, unique_attr));
|
|
if (ret < 0) {
|
|
SNDERR("error creating new attribute cfg for object %s\n", id);
|
|
return ret;
|
|
}
|
|
ret = snd_config_add(obj, new);
|
|
if (ret < 0) {
|
|
SNDERR("error adding new attribute cfg for object %s\n", id);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = tplg_set_attribute_value(new, id);
|
|
if (ret < 0) {
|
|
SNDERR("error setting unique attribute cfg for object %s\n", id);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Helper function to get object instance config which is 2 nodes down from class_type config.
|
|
* ex: Get the pointer to the config node with ID "0" from the input config Widget.pga.0 {}
|
|
*/
|
|
snd_config_t *tplg_object_get_instance_config(struct tplg_pre_processor *tplg_pp,
|
|
snd_config_t *class_type)
|
|
{
|
|
snd_config_iterator_t first;
|
|
snd_config_t *cfg;
|
|
|
|
first = snd_config_iterator_first(class_type);
|
|
cfg = snd_config_iterator_entry(first);
|
|
first = snd_config_iterator_first(cfg);
|
|
return snd_config_iterator_entry(first);
|
|
}
|
|
|
|
/* build object config */
|
|
static int tplg_build_object(struct tplg_pre_processor *tplg_pp, snd_config_t *new_obj,
|
|
snd_config_t *parent)
|
|
{
|
|
snd_config_t *obj_local, *class_cfg;
|
|
const struct build_function_map *map;
|
|
build_func builder;
|
|
const char *id, *class_id;
|
|
int ret;
|
|
|
|
obj_local = tplg_object_get_instance_config(tplg_pp, new_obj);
|
|
if (!obj_local)
|
|
return -EINVAL;
|
|
|
|
class_cfg = tplg_class_lookup(tplg_pp, new_obj);
|
|
if (!class_cfg)
|
|
return -EINVAL;
|
|
|
|
if (snd_config_get_id(obj_local, &id) < 0)
|
|
return 0;
|
|
|
|
if (snd_config_get_id(class_cfg, &class_id) < 0)
|
|
return 0;
|
|
|
|
/* set unique attribute value */
|
|
ret = tplg_object_set_unique_attribute(tplg_pp, obj_local, class_cfg, id);
|
|
if (ret < 0) {
|
|
SNDERR("error setting unique attribute value for '%s.%s'\n", class_id, id);
|
|
return ret;
|
|
}
|
|
|
|
/* update object attributes and validate them */
|
|
ret = tplg_object_update(tplg_pp, new_obj, parent);
|
|
if (ret < 0) {
|
|
SNDERR("Failed to update attributes for object '%s.%s'\n", class_id, id);
|
|
return ret;
|
|
}
|
|
|
|
/* construct object name using class constructor */
|
|
ret = tplg_construct_object_name(tplg_pp, obj_local, class_cfg);
|
|
if (ret < 0) {
|
|
SNDERR("Failed to construct object name for %s\n", id);
|
|
return ret;
|
|
}
|
|
|
|
tplg_pp_config_debug(tplg_pp, obj_local);
|
|
|
|
/* nothing to do if object is not supported */
|
|
map = tplg_object_get_map(tplg_pp, new_obj);
|
|
if (!map)
|
|
return 0;
|
|
|
|
/* build the object and save the sections to the output config */
|
|
builder = map->builder;
|
|
return builder(tplg_pp, new_obj, parent);
|
|
}
|
|
|
|
/* create top-level topology objects */
|
|
int tplg_pre_process_objects(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg,
|
|
snd_config_t *parent)
|
|
{
|
|
snd_config_iterator_t i, next, i2, next2;
|
|
snd_config_t *n, *n2, *_obj_type, *_obj_class, *_obj;
|
|
const char *id, *class_type, *class_name;
|
|
int ret;
|
|
|
|
if (snd_config_get_id(cfg, &class_type) < 0)
|
|
return 0;
|
|
|
|
/* create all objects of the same type and class */
|
|
snd_config_for_each(i, next, cfg) {
|
|
n = snd_config_iterator_entry(i);
|
|
if (snd_config_get_id(n, &class_name) < 0)
|
|
continue;
|
|
snd_config_for_each(i2, next2, n) {
|
|
n2 = snd_config_iterator_entry(i2);
|
|
if (snd_config_get_id(n2, &id) < 0) {
|
|
SNDERR("Invalid id for object\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* create a temp config for object with class type as the root node */
|
|
ret = snd_config_make(&_obj_type, class_type, SND_CONFIG_TYPE_COMPOUND);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = snd_config_make(&_obj_class, class_name, SND_CONFIG_TYPE_COMPOUND);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
ret = snd_config_add(_obj_type, _obj_class);
|
|
if (ret < 0) {
|
|
snd_config_delete(_obj_class);
|
|
goto err;
|
|
}
|
|
|
|
ret = snd_config_copy(&_obj, n2);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
ret = snd_config_add(_obj_class, _obj);
|
|
if (ret < 0) {
|
|
snd_config_delete(_obj);
|
|
goto err;
|
|
}
|
|
|
|
/* Build the object now */
|
|
ret = tplg_build_object(tplg_pp, _obj_type, parent);
|
|
if (ret < 0)
|
|
SNDERR("Error building object %s.%s.%s\n",
|
|
class_type, class_name, id);
|
|
err:
|
|
snd_config_delete(_obj_type);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|