1 /*
2 Copyright(c) 2014-2015 Intel Corporation
3 All rights reserved.
4
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of
8 the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
14
15 Authors: Mengdong Lin <mengdong.lin@intel.com>
16 Yao Jin <yao.jin@intel.com>
17 Liam Girdwood <liam.r.girdwood@linux.intel.com>
18 */
19
20 #include "list.h"
21 #include "tplg_local.h"
22
23 /* mapping of widget text names to types */
24 static const struct map_elem widget_map[] = {
25 {"input", SND_SOC_TPLG_DAPM_INPUT},
26 {"output", SND_SOC_TPLG_DAPM_OUTPUT},
27 {"mux", SND_SOC_TPLG_DAPM_MUX},
28 {"mixer", SND_SOC_TPLG_DAPM_MIXER},
29 {"pga", SND_SOC_TPLG_DAPM_PGA},
30 {"out_drv", SND_SOC_TPLG_DAPM_OUT_DRV},
31 {"adc", SND_SOC_TPLG_DAPM_ADC},
32 {"dac", SND_SOC_TPLG_DAPM_DAC},
33 {"switch", SND_SOC_TPLG_DAPM_SWITCH},
34 {"pre", SND_SOC_TPLG_DAPM_PRE},
35 {"post", SND_SOC_TPLG_DAPM_POST},
36 {"aif_in", SND_SOC_TPLG_DAPM_AIF_IN},
37 {"aif_out", SND_SOC_TPLG_DAPM_AIF_OUT},
38 {"dai_in", SND_SOC_TPLG_DAPM_DAI_IN},
39 {"dai_out", SND_SOC_TPLG_DAPM_DAI_OUT},
40 {"dai_link", SND_SOC_TPLG_DAPM_DAI_LINK},
41 {"buffer", SND_SOC_TPLG_DAPM_BUFFER},
42 {"scheduler", SND_SOC_TPLG_DAPM_SCHEDULER},
43 {"effect", SND_SOC_TPLG_DAPM_EFFECT},
44 {"siggen", SND_SOC_TPLG_DAPM_SIGGEN},
45 {"src", SND_SOC_TPLG_DAPM_SRC},
46 {"asrc", SND_SOC_TPLG_DAPM_ASRC},
47 {"encoder", SND_SOC_TPLG_DAPM_ENCODER},
48 {"decoder", SND_SOC_TPLG_DAPM_DECODER},
49 };
50
lookup_widget(const char * w)51 static int lookup_widget(const char *w)
52 {
53 unsigned int i;
54
55 for (i = 0; i < ARRAY_SIZE(widget_map); i++) {
56 if (strcmp(widget_map[i].name, w) == 0)
57 return widget_map[i].id;
58 }
59
60 return -EINVAL;
61 }
62
get_widget_name(unsigned int type)63 static const char *get_widget_name(unsigned int type)
64 {
65 unsigned int i;
66
67 for (i = 0; i < ARRAY_SIZE(widget_map); i++) {
68 if ((unsigned int)widget_map[i].id == type)
69 return widget_map[i].name;
70 }
71
72 return NULL;
73 }
74
75 /* move referenced controls to the widget */
copy_dapm_control(struct tplg_elem * elem,struct tplg_elem * ref)76 static int copy_dapm_control(struct tplg_elem *elem, struct tplg_elem *ref)
77 {
78 struct snd_soc_tplg_dapm_widget *widget = elem->widget;
79
80 tplg_dbg("Control '%s' used by '%s'", ref->id, elem->id);
81 tplg_dbg("\tparent size: %d + %d -> %d, priv size -> %d",
82 elem->size, ref->size, elem->size + ref->size,
83 widget->priv.size);
84
85 widget = realloc(widget, elem->size + ref->size);
86 if (!widget)
87 return -ENOMEM;
88
89 elem->widget = widget;
90
91 /* append the control to the end of the widget */
92 memcpy((void*)widget + elem->size, ref->obj, ref->size);
93 elem->size += ref->size;
94
95 widget->num_kcontrols++;
96 ref->compound_elem = 1;
97 return 0;
98 }
99
100 /* check referenced controls for a widget */
tplg_build_widget(snd_tplg_t * tplg,struct tplg_elem * elem)101 static int tplg_build_widget(snd_tplg_t *tplg, struct tplg_elem *elem)
102 {
103 struct tplg_ref *ref;
104 struct list_head *base, *pos;
105 int err = 0;
106
107 base = &elem->ref_list;
108
109 /* A widget's private data sits before the embedded controls.
110 * So merge the private data blocks at first
111 */
112 list_for_each(pos, base) {
113 ref = list_entry(pos, struct tplg_ref, list);
114
115 if (ref->type != SND_TPLG_TYPE_DATA)
116 continue;
117
118 err = tplg_copy_data(tplg, elem, ref);
119 if (err < 0)
120 return err;
121 }
122
123 /* Merge the embedded controls */
124 list_for_each(pos, base) {
125
126 ref = list_entry(pos, struct tplg_ref, list);
127
128 switch (ref->type) {
129 case SND_TPLG_TYPE_MIXER:
130 if (!ref->elem)
131 ref->elem = tplg_elem_lookup(&tplg->mixer_list,
132 ref->id, SND_TPLG_TYPE_MIXER, elem->index);
133 if (ref->elem)
134 err = copy_dapm_control(elem, ref->elem);
135 break;
136
137 case SND_TPLG_TYPE_ENUM:
138 if (!ref->elem)
139 ref->elem = tplg_elem_lookup(&tplg->enum_list,
140 ref->id, SND_TPLG_TYPE_ENUM, elem->index);
141 if (ref->elem)
142 err = copy_dapm_control(elem, ref->elem);
143 break;
144
145 case SND_TPLG_TYPE_BYTES:
146 if (!ref->elem)
147 ref->elem = tplg_elem_lookup(&tplg->bytes_ext_list,
148 ref->id, SND_TPLG_TYPE_BYTES, elem->index);
149 if (ref->elem)
150 err = copy_dapm_control(elem, ref->elem);
151 break;
152
153 default:
154 break;
155 }
156
157 if (!ref->elem) {
158 SNDERR("cannot find '%s' referenced by widget '%s'",
159 ref->id, elem->id);
160 return -EINVAL;
161 }
162
163 if (err < 0)
164 return err;
165 }
166
167 return 0;
168 }
169
tplg_build_widgets(snd_tplg_t * tplg)170 int tplg_build_widgets(snd_tplg_t *tplg)
171 {
172
173 struct list_head *base, *pos;
174 struct tplg_elem *elem;
175 int err;
176
177 base = &tplg->widget_list;
178 list_for_each(pos, base) {
179
180 elem = list_entry(pos, struct tplg_elem, list);
181 if (!elem->widget || elem->type != SND_TPLG_TYPE_DAPM_WIDGET) {
182 SNDERR("invalid widget '%s'", elem->id);
183 return -EINVAL;
184 }
185
186 err = tplg_build_widget(tplg, elem);
187 if (err < 0)
188 return err;
189
190 /* add widget to manifest */
191 tplg->manifest.widget_elems++;
192 }
193
194 return 0;
195 }
196
tplg_build_routes(snd_tplg_t * tplg)197 int tplg_build_routes(snd_tplg_t *tplg)
198 {
199 struct list_head *base, *pos;
200 struct tplg_elem *elem;
201 struct snd_soc_tplg_dapm_graph_elem *route;
202
203 base = &tplg->route_list;
204
205 list_for_each(pos, base) {
206 elem = list_entry(pos, struct tplg_elem, list);
207
208 if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH) {
209 SNDERR("invalid route '%s'", elem->id);
210 return -EINVAL;
211 }
212
213 route = elem->route;
214 tplg_dbg("Check route: sink '%s', control '%s', source '%s'",
215 route->sink, route->control, route->source);
216
217 /* validate sink */
218 if (strlen(route->sink) <= 0) {
219 SNDERR("no sink");
220 return -EINVAL;
221
222 }
223 if (!tplg_elem_lookup(&tplg->widget_list, route->sink,
224 SND_TPLG_TYPE_DAPM_WIDGET, SND_TPLG_INDEX_ALL)) {
225 SNDERR("undefined sink widget/stream '%s'", route->sink);
226 }
227
228 /* validate control name */
229 if (strlen(route->control)) {
230 if (!tplg_elem_lookup(&tplg->mixer_list, route->control,
231 SND_TPLG_TYPE_MIXER, elem->index) &&
232 !tplg_elem_lookup(&tplg->enum_list, route->control,
233 SND_TPLG_TYPE_ENUM, elem->index)) {
234 SNDERR("undefined mixer/enum control '%s'",
235 route->control);
236 }
237 }
238
239 /* validate source */
240 if (strlen(route->source) <= 0) {
241 SNDERR("no source");
242 return -EINVAL;
243
244 }
245 if (!tplg_elem_lookup(&tplg->widget_list, route->source,
246 SND_TPLG_TYPE_DAPM_WIDGET, SND_TPLG_INDEX_ALL)) {
247 SNDERR("undefined source widget/stream '%s'",
248 route->source);
249 }
250
251 /* add graph to manifest */
252 tplg->manifest.graph_elems++;
253 }
254
255 return 0;
256 }
257
tplg_elem_new_route(snd_tplg_t * tplg,int index)258 struct tplg_elem *tplg_elem_new_route(snd_tplg_t *tplg, int index)
259 {
260 struct tplg_elem *elem;
261 struct snd_soc_tplg_dapm_graph_elem *line;
262
263 elem = tplg_elem_new();
264 if (!elem)
265 return NULL;
266
267 elem->index = index;
268 if (tplg->dapm_sort)
269 tplg_elem_insert(elem, &tplg->route_list);
270 else
271 list_add_tail(&elem->list, &tplg->route_list);
272 strcpy(elem->id, "line");
273 elem->type = SND_TPLG_TYPE_DAPM_GRAPH;
274 elem->size = sizeof(*line);
275
276 line = calloc(1, sizeof(*line));
277 if (!line) {
278 tplg_elem_free(elem);
279 return NULL;
280 }
281 elem->route = line;
282
283 return elem;
284 }
285
286 #define LINE_SIZE 1024
287
288 /* line is defined as '"sink, control, source"' */
tplg_parse_line(const char * text,struct snd_soc_tplg_dapm_graph_elem * line)289 static int tplg_parse_line(const char *text,
290 struct snd_soc_tplg_dapm_graph_elem *line)
291 {
292 char buf[LINE_SIZE];
293 unsigned int len, i;
294 const char *source = NULL, *sink = NULL, *control = NULL;
295
296 snd_strlcpy(buf, text, LINE_SIZE);
297
298 len = strlen(buf);
299 if (len <= 2) {
300 SNDERR("invalid route \"%s\"", buf);
301 return -EINVAL;
302 }
303
304 /* find first , */
305 for (i = 1; i < len; i++) {
306 if (buf[i] == ',')
307 goto second;
308 }
309 SNDERR("invalid route \"%s\"", buf);
310 return -EINVAL;
311
312 second:
313 /* find second , */
314 sink = buf;
315 control = &buf[i + 2];
316 buf[i] = 0;
317
318 for (; i < len; i++) {
319 if (buf[i] == ',')
320 goto done;
321 }
322
323 SNDERR("invalid route \"%s\"", buf);
324 return -EINVAL;
325
326 done:
327 buf[i] = 0;
328 source = &buf[i + 2];
329
330 strcpy(line->source, source);
331 strcpy(line->control, control);
332 strcpy(line->sink, sink);
333 return 0;
334 }
335
336
tplg_parse_routes(snd_tplg_t * tplg,snd_config_t * cfg,int index)337 static int tplg_parse_routes(snd_tplg_t *tplg, snd_config_t *cfg, int index)
338 {
339 snd_config_iterator_t i, next;
340 snd_config_t *n;
341 struct tplg_elem *elem;
342 struct snd_soc_tplg_dapm_graph_elem *line;
343 int err;
344
345 snd_config_for_each(i, next, cfg) {
346 const char *val;
347
348 n = snd_config_iterator_entry(i);
349 if (snd_config_get_string(n, &val) < 0)
350 continue;
351
352 elem = tplg_elem_new_route(tplg, index);
353 if (!elem)
354 return -ENOMEM;
355 line = elem->route;
356
357 err = tplg_parse_line(val, line);
358 if (err < 0)
359 return err;
360
361 tplg_dbg("route: sink '%s', control '%s', source '%s'",
362 line->sink, line->control, line->source);
363 }
364
365 return 0;
366 }
367
tplg_parse_dapm_graph(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)368 int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg,
369 void *private ATTRIBUTE_UNUSED)
370 {
371 snd_config_iterator_t i, next;
372 snd_config_t *n;
373 int err;
374 const char *graph_id;
375 int index = -1;
376
377 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
378 SNDERR("compound is expected for dapm graph definition");
379 return -EINVAL;
380 }
381
382 snd_config_get_id(cfg, &graph_id);
383
384 snd_config_for_each(i, next, cfg) {
385 const char *id;
386
387 n = snd_config_iterator_entry(i);
388 if (snd_config_get_id(n, &id) < 0) {
389 continue;
390 }
391
392 if (strcmp(id, "index") == 0) {
393 if (tplg_get_integer(n, &index, 0))
394 return -EINVAL;
395 if (index < 0)
396 return -EINVAL;
397 }
398
399 if (strcmp(id, "lines") == 0) {
400 if (index < 0) {
401 SNDERR("failed to parse dapm graph %s, missing index",
402 graph_id);
403 return -EINVAL;
404 }
405 err = tplg_parse_routes(tplg, n, index);
406 if (err < 0) {
407 SNDERR("failed to parse dapm graph %s",
408 graph_id);
409 return err;
410 }
411 continue;
412 }
413 }
414
415 return 0;
416 }
417
418 /* save DAPM graph */
tplg_save_dapm_graph(snd_tplg_t * tplg,int index,struct tplg_buf * dst,const char * pfx)419 int tplg_save_dapm_graph(snd_tplg_t *tplg, int index,
420 struct tplg_buf *dst, const char *pfx)
421 {
422 struct snd_soc_tplg_dapm_graph_elem *route;
423 struct list_head *pos;
424 struct tplg_elem *elem;
425 int err, first, old_index;
426 unsigned block, count;
427 const char *fmt;
428
429 old_index = -1;
430 block = 0;
431 count = 0;
432 list_for_each(pos, &tplg->route_list) {
433 elem = list_entry(pos, struct tplg_elem, list);
434 if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH)
435 continue;
436 if (index >= 0 && elem->index != index)
437 continue;
438 if (old_index != elem->index) {
439 block++;
440 old_index = elem->index;
441 }
442 count++;
443 }
444 if (count == 0)
445 return 0;
446 if (block < 10) {
447 fmt = "\tset%u {\n";
448 } else if (block < 100) {
449 fmt = "\tset%02u {\n";
450 } else if (block < 1000) {
451 fmt = "\tset%03u {\n";
452 } else {
453 return -EINVAL;
454 }
455 old_index = -1;
456 block = -1;
457 first = 1;
458 err = tplg_save_printf(dst, pfx, "SectionGraph {\n");
459 list_for_each(pos, &tplg->route_list) {
460 elem = list_entry(pos, struct tplg_elem, list);
461 if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH)
462 continue;
463 if (index >= 0 && elem->index != index)
464 continue;
465 if (old_index != elem->index) {
466 if (old_index >= 0) {
467 err = tplg_save_printf(dst, pfx, "\t\t]\n");
468 if (err < 0)
469 return err;
470 err = tplg_save_printf(dst, pfx, "\t}\n");
471 if (err < 0)
472 return err;
473 }
474 old_index = elem->index;
475 block++;
476 first = 1;
477 err = tplg_save_printf(dst, pfx, fmt, block);
478 if (err >= 0)
479 err = tplg_save_printf(dst, pfx, "\t\tindex %u\n",
480 elem->index);
481 if (err < 0)
482 return err;
483 }
484 if (first) {
485 first = 0;
486 err = tplg_save_printf(dst, pfx, "\t\tlines [\n");
487 if (err < 0)
488 return err;
489 }
490 route = elem->route;
491 err = tplg_save_printf(dst, pfx, "\t\t\t'%s, %s, %s'\n",
492 route->sink, route->control,
493 route->source);
494 if (err < 0)
495 return err;
496 }
497
498 if (!first) {
499 if (err >= 0)
500 err = tplg_save_printf(dst, pfx, "\t\t]\n");
501 if (err >= 0)
502 err = tplg_save_printf(dst, pfx, "\t}\n");
503 }
504
505 if (err >= 0)
506 err = tplg_save_printf(dst, pfx, "}\n");
507 return err;
508 }
509
510 /* DAPM Widget */
tplg_parse_dapm_widget(snd_tplg_t * tplg,snd_config_t * cfg,void * private ATTRIBUTE_UNUSED)511 int tplg_parse_dapm_widget(snd_tplg_t *tplg,
512 snd_config_t *cfg, void *private ATTRIBUTE_UNUSED)
513 {
514 struct snd_soc_tplg_dapm_widget *widget;
515 struct tplg_elem *elem;
516 snd_config_iterator_t i, next;
517 snd_config_t *n;
518 const char *id, *val = NULL;
519 int widget_type, err, ival;
520
521 elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAPM_WIDGET);
522 if (!elem)
523 return -ENOMEM;
524
525 tplg_dbg(" Widget: %s", elem->id);
526
527 widget = elem->widget;
528 snd_strlcpy(widget->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
529 widget->size = elem->size;
530
531 snd_config_for_each(i, next, cfg) {
532
533 n = snd_config_iterator_entry(i);
534 if (snd_config_get_id(n, &id) < 0)
535 continue;
536
537 /* skip comments */
538 if (strcmp(id, "comment") == 0)
539 continue;
540 if (id[0] == '#')
541 continue;
542
543 if (strcmp(id, "type") == 0) {
544 if (snd_config_get_string(n, &val) < 0)
545 return -EINVAL;
546
547 widget_type = lookup_widget(val);
548 if (widget_type < 0){
549 SNDERR("widget '%s': Unsupported widget type %s",
550 elem->id, val);
551 return -EINVAL;
552 }
553
554 widget->id = widget_type;
555 tplg_dbg("\t%s: %s", id, val);
556 continue;
557 }
558
559 if (strcmp(id, "stream_name") == 0) {
560 if (snd_config_get_string(n, &val) < 0)
561 return -EINVAL;
562
563 snd_strlcpy(widget->sname, val,
564 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
565 tplg_dbg("\t%s: %s", id, val);
566 continue;
567 }
568
569 if (strcmp(id, "no_pm") == 0) {
570 ival = snd_config_get_bool(n);
571 if (ival < 0)
572 return -EINVAL;
573
574 widget->reg = ival ? -1 : 0;
575
576 tplg_dbg("\t%s: %s", id, val);
577 continue;
578 }
579
580 if (strcmp(id, "shift") == 0) {
581 if (tplg_get_integer(n, &ival, 0))
582 return -EINVAL;
583
584 widget->shift = ival;
585 tplg_dbg("\t%s: %d", id, widget->shift);
586 continue;
587 }
588
589 if (strcmp(id, "reg") == 0) {
590 if (tplg_get_integer(n, &ival, 0))
591 return -EINVAL;
592
593 widget->reg = ival;
594 tplg_dbg("\t%s: %d", id, widget->reg);
595 continue;
596 }
597
598 if (strcmp(id, "invert") == 0) {
599 ival = snd_config_get_bool(n);
600 if (ival < 0)
601 return -EINVAL;
602
603 widget->invert = ival;
604 tplg_dbg("\t%s: %d", id, widget->invert);
605 continue;
606 }
607
608 if (strcmp(id, "subseq") == 0) {
609 if (tplg_get_integer(n, &ival, 0))
610 return -EINVAL;
611
612 widget->subseq = ival;
613 tplg_dbg("\t%s: %d", id, widget->subseq);
614 continue;
615 }
616
617 if (strcmp(id, "event_type") == 0) {
618 if (tplg_get_integer(n, &ival, 0))
619 return -EINVAL;
620
621 widget->event_type = ival;
622 tplg_dbg("\t%s: %d", id, widget->event_type);
623 continue;
624 }
625
626 if (strcmp(id, "event_flags") == 0) {
627 if (tplg_get_integer(n, &ival, 0))
628 return -EINVAL;
629
630 widget->event_flags = ival;
631 tplg_dbg("\t%s: %d", id, widget->event_flags);
632 continue;
633 }
634
635 if (strcmp(id, "enum") == 0) {
636 err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_ENUM);
637 if (err < 0)
638 return err;
639
640 continue;
641 }
642
643 if (strcmp(id, "mixer") == 0) {
644 err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_MIXER);
645 if (err < 0)
646 return err;
647
648 continue;
649 }
650
651 if (strcmp(id, "bytes") == 0) {
652 err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_BYTES);
653 if (err < 0)
654 return err;
655
656 continue;
657 }
658
659 if (strcmp(id, "data") == 0) {
660 err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA);
661 if (err < 0)
662 return err;
663 continue;
664 }
665 }
666
667 return 0;
668 }
669
670 /* save DAPM widget */
tplg_save_dapm_widget(snd_tplg_t * tplg ATTRIBUTE_UNUSED,struct tplg_elem * elem,struct tplg_buf * dst,const char * pfx)671 int tplg_save_dapm_widget(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
672 struct tplg_elem *elem,
673 struct tplg_buf *dst, const char *pfx)
674 {
675 struct snd_soc_tplg_dapm_widget *widget = elem->widget;
676 const char *s;
677 char pfx2[16];
678 int err;
679
680 err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id);
681 if (err >= 0 && elem->index)
682 err = tplg_save_printf(dst, pfx, "\tindex %u\n",
683 elem->index);
684 if (err >= 0) {
685 s = get_widget_name(widget->id);
686 if (s)
687 err = tplg_save_printf(dst, pfx, "\ttype %s\n", s);
688 else
689 err = tplg_save_printf(dst, pfx, "\ttype %u\n",
690 widget->id);
691 }
692 if (err >= 0 && widget->sname[0])
693 err = tplg_save_printf(dst, pfx, "\tstream_name '%s'\n",
694 widget->sname);
695 if (err >= 0 && widget->reg)
696 err = tplg_save_printf(dst, pfx, "\tno_pm 1\n");
697 if (err >= 0 && widget->shift)
698 err = tplg_save_printf(dst, pfx, "\tshift %u\n",
699 widget->shift);
700 if (err >= 0 && widget->invert)
701 err = tplg_save_printf(dst, pfx, "\tinvert %u\n",
702 widget->invert);
703 if (err >= 0 && widget->subseq)
704 err = tplg_save_printf(dst, pfx, "\tsubseq %u\n",
705 widget->subseq);
706 if (err >= 0 && widget->event_type)
707 err = tplg_save_printf(dst, pfx, "\tevent_type %u\n",
708 widget->event_type);
709 if (err >= 0 && widget->event_flags)
710 err = tplg_save_printf(dst, pfx, "\tevent_flags %u\n",
711 widget->event_flags);
712 snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: "");
713 if (err >= 0)
714 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_ENUM,
715 "enum", dst, pfx2);
716 if (err >= 0)
717 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_MIXER,
718 "mixer", dst, pfx2);
719 if (err >= 0)
720 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_BYTES,
721 "bytes", dst, pfx2);
722 if (err >= 0)
723 err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA,
724 "data", dst, pfx2);
725 if (err >= 0)
726 err = tplg_save_printf(dst, pfx, "}\n");
727 return err;
728 }
729
tplg_add_route(snd_tplg_t * tplg,struct snd_tplg_graph_elem * t,int index)730 int tplg_add_route(snd_tplg_t *tplg, struct snd_tplg_graph_elem *t, int index)
731 {
732 struct tplg_elem *elem;
733 struct snd_soc_tplg_dapm_graph_elem *line;
734
735 if (!t->src || !t->sink)
736 return -EINVAL;
737
738 elem = tplg_elem_new_route(tplg, index);
739 if (!elem)
740 return -ENOMEM;
741
742 line = elem->route;
743 snd_strlcpy(line->source, t->src, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
744 if (t->ctl)
745 snd_strlcpy(line->control, t->ctl,
746 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
747 snd_strlcpy(line->sink, t->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
748
749 return 0;
750 }
751
tplg_add_graph_object(snd_tplg_t * tplg,snd_tplg_obj_template_t * t)752 int tplg_add_graph_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
753 {
754 struct snd_tplg_graph_template *gt = t->graph;
755 int i, ret;
756
757 for (i = 0; i < gt->count; i++) {
758 ret = tplg_add_route(tplg, gt->elem + i, t->index);
759 if (ret < 0)
760 return ret;
761 }
762
763 return 0;
764 }
765
tplg_add_widget_object(snd_tplg_t * tplg,snd_tplg_obj_template_t * t)766 int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
767 {
768 struct snd_tplg_widget_template *wt = t->widget;
769 struct snd_soc_tplg_dapm_widget *w;
770 struct tplg_elem *elem;
771 int i, ret = 0;
772
773 tplg_dbg("Widget: %s", wt->name);
774
775 elem = tplg_elem_new_common(tplg, NULL, wt->name,
776 SND_TPLG_TYPE_DAPM_WIDGET);
777 if (!elem)
778 return -ENOMEM;
779
780 /* init new widget */
781 w = elem->widget;
782 w->size = elem->size;
783
784 w->id = wt->id;
785 snd_strlcpy(w->name, wt->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
786 if (wt->sname)
787 snd_strlcpy(w->sname, wt->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
788 w->reg = wt->reg;
789 w->shift = wt->shift;
790 w->mask = wt->mask;
791 w->subseq = wt->subseq;
792 w->invert = wt->invert;
793 w->ignore_suspend = wt->ignore_suspend;
794 w->event_flags = wt->event_flags;
795 w->event_type = wt->event_type;
796
797 /* add private data */
798 if (wt->priv != NULL && wt->priv->size > 0) {
799 ret = tplg_add_data(tplg, elem, wt->priv,
800 sizeof(*wt->priv) + wt->priv->size);
801 if (ret < 0) {
802 tplg_elem_free(elem);
803 return ret;
804 }
805 }
806
807 /* add controls to the widget's reference list */
808 for (i = 0 ; i < wt->num_ctls; i++) {
809 struct snd_tplg_ctl_template *ct = wt->ctl[i];
810 struct tplg_elem *elem_ctl;
811 struct snd_tplg_mixer_template *mt;
812 struct snd_tplg_bytes_template *bt;
813 struct snd_tplg_enum_template *et;
814
815 if (!ct) {
816 tplg_elem_free(elem);
817 return -EINVAL;
818 }
819
820 switch (ct->type) {
821 case SND_SOC_TPLG_TYPE_MIXER:
822 mt = container_of(ct, struct snd_tplg_mixer_template, hdr);
823 ret = tplg_add_mixer(tplg, mt, &elem_ctl);
824 break;
825
826 case SND_SOC_TPLG_TYPE_BYTES:
827 bt = container_of(ct, struct snd_tplg_bytes_template, hdr);
828 ret = tplg_add_bytes(tplg, bt, &elem_ctl);
829 break;
830
831 case SND_SOC_TPLG_TYPE_ENUM:
832 et = container_of(ct, struct snd_tplg_enum_template, hdr);
833 ret = tplg_add_enum(tplg, et, &elem_ctl);
834 break;
835
836 default:
837 SNDERR("widget %s: invalid type %d for ctl %d",
838 wt->name, ct->type, i);
839 ret = -EINVAL;
840 break;
841 }
842
843 if (ret < 0) {
844 tplg_elem_free(elem);
845 return ret;
846 }
847
848 ret = tplg_ref_add_elem(elem, elem_ctl);
849 if (ret < 0) {
850 tplg_elem_free(elem);
851 return ret;
852 }
853 }
854
855 return 0;
856 }
857
858 /* decode dapm widget from the binary input */
tplg_decode_dapm_widget(snd_tplg_t * tplg,size_t pos,struct snd_soc_tplg_hdr * hdr,void * bin,size_t size)859 int tplg_decode_dapm_widget(snd_tplg_t *tplg,
860 size_t pos,
861 struct snd_soc_tplg_hdr *hdr,
862 void *bin, size_t size)
863 {
864 struct list_head heap;
865 struct snd_soc_tplg_dapm_widget *w;
866 snd_tplg_obj_template_t t;
867 struct snd_tplg_widget_template *wt;
868 struct snd_tplg_mixer_template *mt;
869 struct snd_tplg_enum_template *et;
870 struct snd_tplg_bytes_template *bt;
871 struct snd_soc_tplg_ctl_hdr *chdr;
872 struct snd_soc_tplg_mixer_control *mc;
873 struct snd_soc_tplg_enum_control *ec;
874 struct snd_soc_tplg_bytes_control *bc;
875 size_t size2;
876 unsigned int index;
877 int err;
878
879 err = tplg_decode_template(tplg, pos, hdr, &t);
880 if (err < 0)
881 return err;
882
883 next:
884 INIT_LIST_HEAD(&heap);
885 w = bin;
886
887 if (size < sizeof(*w)) {
888 SNDERR("dapm widget: small size %d", size);
889 return -EINVAL;
890 }
891 if (sizeof(*w) != w->size) {
892 SNDERR("dapm widget: unknown element size %d (expected %zd)",
893 w->size, sizeof(*w));
894 return -EINVAL;
895 }
896 if (w->num_kcontrols > 16) {
897 SNDERR("dapm widget: too many kcontrols %d",
898 w->num_kcontrols);
899 return -EINVAL;
900 }
901
902 tplg_log(tplg, 'D', pos, "dapm widget: size %d private size %d kcontrols %d",
903 w->size, w->priv.size, w->num_kcontrols);
904
905 wt = tplg_calloc(&heap, sizeof(*wt) + sizeof(void *) * w->num_kcontrols);
906 if (wt == NULL)
907 return -ENOMEM;
908 wt->id = w->id;
909 wt->name = w->name;
910 wt->sname = w->sname;
911 wt->reg = w->reg;
912 wt->shift = w->shift;
913 wt->mask = w->mask;
914 wt->subseq = w->subseq;
915 wt->invert = w->invert;
916 wt->ignore_suspend = w->ignore_suspend;
917 wt->event_flags = w->event_flags;
918 wt->event_type = w->event_type;
919
920 tplg_log(tplg, 'D', pos, "dapm widget: name '%s' sname '%s'",
921 wt->name, wt->sname);
922
923 if (sizeof(*w) + w->priv.size > size) {
924 SNDERR("dapm widget: wrong private data size %d",
925 w->priv.size);
926 return -EINVAL;
927 }
928
929 tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_dapm_widget, priv),
930 "dapm widget: private start");
931
932 wt->priv = &w->priv;
933 bin += sizeof(*w) + w->priv.size;
934 size -= sizeof(*w) + w->priv.size;
935 pos += sizeof(*w) + w->priv.size;
936
937 for (index = 0; index < w->num_kcontrols; index++) {
938 chdr = bin;
939 switch (chdr->type) {
940 case SND_SOC_TPLG_TYPE_MIXER:
941 mt = tplg_calloc(&heap, sizeof(*mt));
942 if (mt == NULL) {
943 err = -ENOMEM;
944 goto retval;
945 }
946 wt->ctl[index] = (void *)mt;
947 wt->num_ctls++;
948 mc = bin;
949 size2 = mc->size + mc->priv.size;
950 tplg_log(tplg, 'D', pos, "kcontrol mixer size %zd", size2);
951 if (size2 > size) {
952 SNDERR("dapm widget: small mixer size %d",
953 size2);
954 err = -EINVAL;
955 goto retval;
956 }
957 err = tplg_decode_control_mixer1(tplg, &heap, mt, pos,
958 bin, size2);
959 break;
960 case SND_SOC_TPLG_TYPE_ENUM:
961 et = tplg_calloc(&heap, sizeof(*mt));
962 if (et == NULL) {
963 err = -ENOMEM;
964 goto retval;
965 }
966 wt->ctl[index] = (void *)et;
967 wt->num_ctls++;
968 ec = bin;
969 size2 = ec->size + ec->priv.size;
970 tplg_log(tplg, 'D', pos, "kcontrol enum size %zd", size2);
971 if (size2 > size) {
972 SNDERR("dapm widget: small enum size %d",
973 size2);
974 err = -EINVAL;
975 goto retval;
976 }
977 err = tplg_decode_control_enum1(tplg, &heap, et, pos, ec);
978 break;
979 case SND_SOC_TPLG_TYPE_BYTES:
980 bt = tplg_calloc(&heap, sizeof(*bt));
981 if (bt == NULL) {
982 err = -ENOMEM;
983 goto retval;
984 }
985 wt->ctl[index] = (void *)bt;
986 wt->num_ctls++;
987 bc = bin;
988 size2 = bc->size + bc->priv.size;
989 tplg_log(tplg, 'D', pos, "kcontrol bytes size %zd", size2);
990 if (size2 > size) {
991 SNDERR("dapm widget: small bytes size %d",
992 size2);
993 err = -EINVAL;
994 goto retval;
995 }
996 err = tplg_decode_control_bytes1(tplg, bt, pos,
997 bin, size2);
998 break;
999 default:
1000 SNDERR("dapm widget: wrong control type %d",
1001 chdr->type);
1002 err = -EINVAL;
1003 goto retval;
1004 }
1005 if (err < 0)
1006 goto retval;
1007 bin += size2;
1008 size -= size2;
1009 pos += size2;
1010 }
1011
1012 t.widget = wt;
1013 err = snd_tplg_add_object(tplg, &t);
1014 tplg_free(&heap);
1015 if (err < 0)
1016 return err;
1017 if (size > 0)
1018 goto next;
1019 return 0;
1020
1021 retval:
1022 tplg_free(&heap);
1023 return err;
1024 }
1025
1026 /* decode dapm link from the binary input */
tplg_decode_dapm_graph(snd_tplg_t * tplg,size_t pos,struct snd_soc_tplg_hdr * hdr,void * bin,size_t size)1027 int tplg_decode_dapm_graph(snd_tplg_t *tplg,
1028 size_t pos,
1029 struct snd_soc_tplg_hdr *hdr,
1030 void *bin, size_t size)
1031 {
1032 struct snd_soc_tplg_dapm_graph_elem *g;
1033 snd_tplg_obj_template_t t;
1034 struct snd_tplg_graph_template *gt;
1035 struct snd_tplg_graph_elem *ge;
1036 size_t asize;
1037 int err;
1038
1039 err = tplg_decode_template(tplg, pos, hdr, &t);
1040 if (err < 0)
1041 return err;
1042
1043 asize = sizeof(*gt) + (size / sizeof(*g)) * sizeof(*ge);
1044 gt = alloca(asize);
1045 memset(gt, 0, asize);
1046 for (ge = gt->elem; size > 0; ge++) {
1047 g = bin;
1048 if (size < sizeof(*g)) {
1049 SNDERR("dapm graph: small size %d", size);
1050 return -EINVAL;
1051 }
1052 ge->src = g->source;
1053 ge->ctl = g->control;
1054 ge->sink = g->sink;
1055 gt->count++;
1056 tplg_log(tplg, 'D', pos, "dapm graph: src='%s' ctl='%s' sink='%s'",
1057 ge->src, ge->ctl, ge->sink);
1058 bin += sizeof(*g);
1059 size -= sizeof(*g);
1060 pos += sizeof(*g);
1061 }
1062
1063 t.graph = gt;
1064 return snd_tplg_add_object(tplg, &t);
1065 }
1066