1/* 2 Copyright(c) 2021 Intel Corporation 3 All rights reserved. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of version 2 of the GNU General Public License as 7 published by the Free Software Foundation. 8 9 This program is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; if not, write to the Free Software 16 Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 17 The full GNU General Public License is included in this distribution 18 in the file called LICENSE.GPL. 19*/ 20#include <errno.h> 21#include <stdio.h> 22#include <alsa/asoundlib.h> 23#include "topology.h" 24#include "pre-processor.h" 25 26int tplg_build_base_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 27 snd_config_t *parent, bool skip_name) 28{ 29 snd_config_t *top, *parent_obj, *cfg, *dest; 30 const char *parent_name; 31 32 /* find parent section config */ 33 top = tplg_object_get_section(tplg_pp, parent); 34 if (!top) 35 return -EINVAL; 36 37 parent_obj = tplg_object_get_instance_config(tplg_pp, parent); 38 39 /* get parent name */ 40 parent_name = tplg_object_get_name(tplg_pp, parent_obj); 41 if (!parent_name) 42 return 0; 43 44 /* find parent config with name */ 45 dest = tplg_find_config(top, parent_name); 46 if (!dest) { 47 SNDERR("Cannot find parent config %s\n", parent_name); 48 return -EINVAL; 49 } 50 51 /* build config from template and add to parent */ 52 return tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, dest, skip_name); 53} 54 55int tplg_build_scale_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 56 snd_config_t *parent) 57{ 58 return tplg_build_base_object(tplg_pp, obj_cfg, parent, true); 59} 60 61int tplg_build_ops_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 62 snd_config_t *parent) 63{ 64 return tplg_build_base_object(tplg_pp, obj_cfg, parent, false); 65} 66 67int tplg_build_channel_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 68 snd_config_t *parent) 69{ 70 return tplg_build_base_object(tplg_pp, obj_cfg, parent, false); 71} 72 73int tplg_build_text_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 74 snd_config_t *parent) 75{ 76 snd_config_t *cfg; 77 const char *name; 78 int ret; 79 80 cfg = tplg_object_get_instance_config(tplg_pp, obj_cfg); 81 82 name = tplg_object_get_name(tplg_pp, cfg); 83 if (!name) 84 return -EINVAL; 85 86 ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false); 87 if (ret < 0) 88 return ret; 89 90 return tplg_parent_update(tplg_pp, parent, "texts", name); 91} 92 93int tplg_build_tlv_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 94 snd_config_t *parent) 95{ 96 snd_config_t *cfg; 97 const char *name; 98 int ret; 99 100 cfg = tplg_object_get_instance_config(tplg_pp, obj_cfg); 101 102 name = tplg_object_get_name(tplg_pp, cfg); 103 if (!name) 104 return -EINVAL; 105 106 ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false); 107 if (ret < 0) 108 return ret; 109 110 return tplg_parent_update(tplg_pp, parent, "tlv", name); 111} 112 113static int tplg_build_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 114 snd_config_t *parent, char *type) 115{ 116 snd_config_t *cfg, *obj; 117 const char *name; 118 int ret; 119 120 obj = tplg_object_get_instance_config(tplg_pp, obj_cfg); 121 122 /* get control name */ 123 ret = snd_config_search(obj, "name", &cfg); 124 if (ret < 0) 125 return 0; 126 127 ret = snd_config_get_string(cfg, &name); 128 if (ret < 0) 129 return ret; 130 131 ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false); 132 if (ret < 0) 133 return ret; 134 135 ret = tplg_add_object_data(tplg_pp, obj_cfg, cfg, NULL); 136 if (ret < 0) 137 SNDERR("Failed to add data section for %s\n", name); 138 139 return tplg_parent_update(tplg_pp, parent, type, name); 140} 141 142int tplg_build_mixer_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 143 snd_config_t *parent) 144{ 145 return tplg_build_control(tplg_pp, obj_cfg, parent, "mixer"); 146} 147 148int tplg_build_bytes_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 149 snd_config_t *parent) 150{ 151 return tplg_build_control(tplg_pp, obj_cfg, parent, "bytes"); 152} 153 154int tplg_build_enum_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 155 snd_config_t *parent) 156{ 157 return tplg_build_control(tplg_pp, obj_cfg, parent, "enum"); 158} 159 160/* 161 * Widget names for pipeline endpoints can be of the following type: 162 * "class.<constructor args separated by .> ex: pga.0.1, buffer.1.1 etc 163 * Optionally, the index argument for a widget can be omitted and will be substituted with 164 * the index from the route: ex: pga..0, host..playback etc 165 */ 166static int tplg_pp_get_widget_name(struct tplg_pre_processor *tplg_pp, 167 const char *string, long index, char **widget) 168{ 169 snd_config_iterator_t i, next; 170 snd_config_t *temp_cfg, *child, *class_cfg, *n; 171 char *class_name, *args, *widget_name; 172 int ret; 173 174 /* get class name */ 175 args = strchr(string, '.'); 176 if (!args) { 177 SNDERR("Error getting class name for %s\n", string); 178 return -EINVAL; 179 } 180 181 class_name = calloc(1, strlen(string) - strlen(args) + 1); 182 if (!class_name) 183 return -ENOMEM; 184 185 snprintf(class_name, strlen(string) - strlen(args) + 1, "%s", string); 186 187 /* create config with Widget class type */ 188 ret = snd_config_make(&temp_cfg, "Widget", SND_CONFIG_TYPE_COMPOUND); 189 if (ret < 0) { 190 free(class_name); 191 return ret; 192 } 193 194 /* create config with class name and add it to the Widget config */ 195 ret = tplg_config_make_add(&child, class_name, SND_CONFIG_TYPE_COMPOUND, temp_cfg); 196 if (ret < 0) { 197 free(class_name); 198 return ret; 199 } 200 201 /* get class definition for widget */ 202 class_cfg = tplg_class_lookup(tplg_pp, temp_cfg); 203 snd_config_delete(temp_cfg); 204 if (!class_cfg) { 205 free(class_name); 206 return -EINVAL; 207 } 208 209 /* get constructor for class */ 210 ret = snd_config_search(class_cfg, "attributes.constructor", &temp_cfg); 211 if (ret < 0) { 212 SNDERR("No arguments in class for widget %s\n", string); 213 free(class_name); 214 return ret; 215 } 216 217 widget_name = strdup(class_name); 218 free(class_name); 219 if (!widget_name) 220 return -ENOMEM; 221 222 /* construct widget name using the constructor argument values */ 223 snd_config_for_each(i, next, temp_cfg) { 224 const char *id; 225 char *arg, *remaining, *temp; 226 227 n = snd_config_iterator_entry(i); 228 if (snd_config_get_string(n, &id) < 0) 229 continue; 230 231 if (!args) { 232 SNDERR("insufficient arugments for widget %s\n", string); 233 ret = -EINVAL; 234 goto err; 235 } 236 237 remaining = strchr(args + 1, '.'); 238 if (remaining) { 239 arg = calloc(1, strlen(args + 1) - strlen(remaining) + 1); 240 if (!arg) { 241 ret = -ENOMEM; 242 goto err; 243 } 244 snprintf(arg, strlen(args + 1) - strlen(remaining) + 1, "%s", args + 1); 245 } else { 246 arg = calloc(1, strlen(args + 1) + 1); 247 if (!arg) { 248 ret = -ENOMEM; 249 goto err; 250 } 251 252 snprintf(arg, strlen(args + 1) + 1, "%s", args + 1); 253 } 254 255 /* if no index provided, substitue with route index */ 256 if (!strcmp(arg, "") && !strcmp(id, "index")) { 257 free(arg); 258 arg = tplg_snprintf("%ld", index); 259 if (!arg) { 260 ret = -ENOMEM; 261 free(arg); 262 goto err; 263 } 264 } 265 266 temp = tplg_snprintf("%s.%s", widget_name, arg); 267 if (!temp) { 268 ret = -ENOMEM; 269 free(arg); 270 goto err; 271 } 272 273 free(widget_name); 274 widget_name = temp; 275 free(arg); 276 if (remaining) 277 args = remaining; 278 else 279 args = NULL; 280 } 281 282 *widget = widget_name; 283 return 0; 284 285err: 286 free(widget_name); 287 return ret; 288} 289 290int tplg_build_dapm_route_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, 291 snd_config_t *parent) 292{ 293 snd_config_t *top, *obj, *cfg, *route, *child, *parent_obj; 294 const char *name, *wname; 295 const char *parent_name = "Endpoint"; 296 char *src_widget_name, *sink_widget_name, *line_str, *route_name; 297 const char *control = ""; 298 long index = 0; 299 int ret; 300 301 obj = tplg_object_get_instance_config(tplg_pp, obj_cfg); 302 303 ret = snd_config_get_id(obj, &name); 304 if (ret < 0) 305 return -EINVAL; 306 307 /* endpoint connections at the top-level conf have no parent */ 308 if (parent) { 309 parent_obj = tplg_object_get_instance_config(tplg_pp, parent); 310 311 ret = snd_config_get_id(parent_obj, &parent_name); 312 if (ret < 0) 313 return -EINVAL; 314 } 315 316 tplg_pp_debug("Building DAPM route object: '%s' ...", name); 317 318 ret = snd_config_search(tplg_pp->output_cfg, "SectionGraph", &top); 319 if (ret < 0) { 320 ret = tplg_config_make_add(&top, "SectionGraph", 321 SND_CONFIG_TYPE_COMPOUND, tplg_pp->output_cfg); 322 if (ret < 0) { 323 SNDERR("Error creating 'SectionGraph' config\n"); 324 return ret; 325 } 326 } 327 328 /* get route index */ 329 ret = snd_config_search(obj, "index", &cfg); 330 if (ret >= 0) { 331 ret = snd_config_get_integer(cfg, &index); 332 if (ret < 0) { 333 SNDERR("Invalid index route %s\n", name); 334 return ret; 335 } 336 } 337 338 /* get source widget name */ 339 ret = snd_config_search(obj, "source", &cfg); 340 if (ret < 0) { 341 SNDERR("No source for route %s\n", name); 342 return ret; 343 } 344 345 ret = snd_config_get_string(cfg, &wname); 346 if (ret < 0) { 347 SNDERR("Invalid name for source in route %s\n", name); 348 return ret; 349 } 350 351 ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &src_widget_name); 352 if (ret < 0) { 353 SNDERR("error getting widget name for %s\n", wname); 354 return ret; 355 } 356 357 /* get sink widget name */ 358 ret = snd_config_search(obj, "sink", &cfg); 359 if (ret < 0) { 360 SNDERR("No sink for route %s\n", name); 361 free(src_widget_name); 362 return ret; 363 } 364 365 ret = snd_config_get_string(cfg, &wname); 366 if (ret < 0) { 367 SNDERR("Invalid name for sink in route %s\n", name); 368 free(src_widget_name); 369 return ret; 370 } 371 372 ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &sink_widget_name); 373 if (ret < 0) { 374 SNDERR("error getting widget name for %s\n", wname); 375 free(src_widget_name); 376 return ret; 377 } 378 379 /* get control name */ 380 ret = snd_config_search(obj, "control", &cfg); 381 if (ret >= 0) { 382 ret = snd_config_get_string(cfg, &control); 383 if (ret < 0) { 384 SNDERR("Invalid control name for route %s\n", name); 385 goto err; 386 } 387 } 388 389 /* add route */ 390 route_name = tplg_snprintf("%s.%s", parent_name, name); 391 if (!route_name) { 392 ret = -ENOMEM; 393 goto err; 394 } 395 396 ret = snd_config_make(&route, route_name, SND_CONFIG_TYPE_COMPOUND); 397 free(route_name); 398 if (ret < 0) { 399 SNDERR("Error creating route config for %s %d\n", name, ret); 400 goto err; 401 } 402 403 ret = snd_config_add(top, route); 404 if (ret < 0) { 405 SNDERR("Error adding route config for %s %d\n", name, ret); 406 goto err; 407 } 408 409 /* add index */ 410 ret = tplg_config_make_add(&child, "index", SND_CONFIG_TYPE_INTEGER, route); 411 if (ret < 0) { 412 SNDERR("Error creating index config for %s\n", name); 413 goto err; 414 } 415 416 ret = snd_config_set_integer(child, index); 417 if (ret < 0) { 418 SNDERR("Error setting index config for %s\n", name); 419 goto err; 420 } 421 422 /* add lines */ 423 ret = tplg_config_make_add(&cfg, "lines", SND_CONFIG_TYPE_COMPOUND, route); 424 if (ret < 0) { 425 SNDERR("Error creating lines config for %s\n", name); 426 goto err; 427 } 428 429 /* add route string */ 430 ret = tplg_config_make_add(&child, "0", SND_CONFIG_TYPE_STRING, cfg); 431 if (ret < 0) { 432 SNDERR("Error creating lines config for %s\n", name); 433 goto err; 434 } 435 436 line_str = tplg_snprintf("%s, %s, %s", sink_widget_name, control, src_widget_name); 437 if (!line_str) { 438 ret = -ENOMEM; 439 goto err; 440 } 441 442 /* set the line string */ 443 ret = snd_config_set_string(child, line_str); 444 free(line_str); 445 if (ret < 0) 446 SNDERR("Error creating lines config for %s\n", name); 447err: 448 free(src_widget_name); 449 free(sink_widget_name); 450 return ret; 451} 452