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 <sys/stat.h>
21 #include "list.h"
22 #include "tplg_local.h"
23
24 /*
25 * Safe strtol call
26 */
safe_strtol_base(const char * str,long * val,int base)27 int safe_strtol_base(const char *str, long *val, int base)
28 {
29 char *end;
30 long v;
31 if (!*str)
32 return -EINVAL;
33 errno = 0;
34 v = strtol(str, &end, base);
35 if (errno)
36 return -errno;
37 if (*end)
38 return -EINVAL;
39 *val = v;
40 return 0;
41 }
42
43 /*
44 * Get integer value
45 */
tplg_get_integer(snd_config_t * n,int * val,int base)46 int tplg_get_integer(snd_config_t *n, int *val, int base)
47 {
48 const char *str;
49 long lval;
50 int err;
51
52 switch (snd_config_get_type(n)) {
53 case SND_CONFIG_TYPE_INTEGER:
54 err = snd_config_get_integer(n, &lval);
55 if (err < 0)
56 return err;
57 goto __retval;
58 case SND_CONFIG_TYPE_STRING:
59 err = snd_config_get_string(n, &str);
60 if (err < 0)
61 return err;
62 err = safe_strtol_base(str, &lval, base);
63 if (err < 0)
64 return err;
65 goto __retval;
66 default:
67 return -EINVAL;
68 }
69 __retval:
70 if (lval < INT_MIN || lval > INT_MAX)
71 return -ERANGE;
72 *val = lval;
73 return 0;
74 }
75
76 /*
77 * Get unsigned integer value
78 */
tplg_get_unsigned(snd_config_t * n,unsigned * val,int base)79 int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base)
80 {
81 const char *str;
82 long lval;
83 long long llval;
84 unsigned long uval;
85 int err;
86
87 switch (snd_config_get_type(n)) {
88 case SND_CONFIG_TYPE_INTEGER:
89 err = snd_config_get_integer(n, &lval);
90 if (err < 0)
91 return err;
92 if (lval < 0 && lval >= INT_MIN)
93 lval = UINT_MAX + lval + 1;
94 if (lval < 0 || lval > UINT_MAX)
95 return -ERANGE;
96 *val = lval;
97 return err;
98 case SND_CONFIG_TYPE_INTEGER64:
99 err = snd_config_get_integer64(n, &llval);
100 if (err < 0)
101 return err;
102 if (llval < 0 && llval >= INT_MIN)
103 llval = UINT_MAX + llval + 1;
104 if (llval < 0 || llval > UINT_MAX)
105 return -ERANGE;
106 *val = llval;
107 return err;
108 case SND_CONFIG_TYPE_STRING:
109 err = snd_config_get_string(n, &str);
110 if (err < 0)
111 return err;
112 errno = 0;
113 uval = strtoul(str, NULL, base);
114 if (errno == ERANGE && uval == ULONG_MAX)
115 return -ERANGE;
116 if (errno && uval == 0)
117 return -EINVAL;
118 if (uval > UINT_MAX)
119 return -ERANGE;
120 *val = uval;
121 return 0;
122 default:
123 return -EINVAL;
124 }
125 }
126
127 /*
128 * Parse compound
129 */
tplg_parse_compound(snd_tplg_t * tplg,snd_config_t * cfg,int (* fcn)(snd_tplg_t *,snd_config_t *,void *),void * private)130 int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg,
131 int (*fcn)(snd_tplg_t *, snd_config_t *, void *),
132 void *private)
133 {
134 const char *id;
135 snd_config_iterator_t i, next;
136 snd_config_t *n;
137 int err = -EINVAL;
138
139 if (snd_config_get_id(cfg, &id) < 0)
140 return -EINVAL;
141
142 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
143 SNDERR("compound type expected for %s", id);
144 return -EINVAL;
145 }
146
147 /* parse compound */
148 snd_config_for_each(i, next, cfg) {
149 n = snd_config_iterator_entry(i);
150
151 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
152 SNDERR("compound type expected for %s, is %d",
153 id, snd_config_get_type(cfg));
154 return -EINVAL;
155 }
156
157 err = fcn(tplg, n, private);
158 if (err < 0)
159 return err;
160 }
161
162 return err;
163 }
164
tplg_parse_config(snd_tplg_t * tplg,snd_config_t * cfg)165 static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg)
166 {
167 int (*parser)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv);
168 snd_config_iterator_t i, next;
169 snd_config_t *n;
170 const char *id;
171 struct tplg_table *p;
172 unsigned int idx;
173 int err;
174
175 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
176 SNDERR("compound type expected at top level");
177 return -EINVAL;
178 }
179
180 /* parse topology config sections */
181 snd_config_for_each(i, next, cfg) {
182
183 n = snd_config_iterator_entry(i);
184 if (snd_config_get_id(n, &id) < 0)
185 continue;
186
187 parser = NULL;
188 for (idx = 0; idx < tplg_table_items; idx++) {
189 p = &tplg_table[idx];
190 if (p->id && strcmp(id, p->id) == 0) {
191 parser = p->parse;
192 break;
193 }
194 if (p->id2 && strcmp(id, p->id2) == 0) {
195 parser = p->parse;
196 break;
197 }
198 }
199
200 if (parser == NULL) {
201 SNDERR("unknown section %s", id);
202 continue;
203 }
204
205 err = tplg_parse_compound(tplg, n, parser, NULL);
206 if (err < 0)
207 return err;
208 }
209 return 0;
210 }
211
tplg_load_config(snd_tplg_t * tplg,snd_input_t * in)212 static int tplg_load_config(snd_tplg_t *tplg, snd_input_t *in)
213 {
214 snd_config_t *top;
215 int ret;
216
217 ret = snd_config_top(&top);
218 if (ret < 0)
219 return ret;
220
221 ret = snd_config_load(top, in);
222 if (ret < 0) {
223 SNDERR("could not load configuration");
224 snd_config_delete(top);
225 return ret;
226 }
227
228 ret = tplg_parse_config(tplg, top);
229 snd_config_delete(top);
230 if (ret < 0) {
231 SNDERR("failed to parse topology");
232 return ret;
233 }
234
235 return 0;
236 }
237
tplg_build_integ(snd_tplg_t * tplg)238 static int tplg_build_integ(snd_tplg_t *tplg)
239 {
240 int err;
241
242 err = tplg_build_data(tplg);
243 if (err < 0)
244 return err;
245
246 err = tplg_build_manifest_data(tplg);
247 if (err < 0)
248 return err;
249
250 err = tplg_build_controls(tplg);
251 if (err < 0)
252 return err;
253
254 err = tplg_build_widgets(tplg);
255 if (err < 0)
256 return err;
257
258 err = tplg_build_pcms(tplg, SND_TPLG_TYPE_PCM);
259 if (err < 0)
260 return err;
261
262 err = tplg_build_dais(tplg, SND_TPLG_TYPE_DAI);
263 if (err < 0)
264 return err;
265
266 err = tplg_build_links(tplg, SND_TPLG_TYPE_BE);
267 if (err < 0)
268 return err;
269
270 err = tplg_build_links(tplg, SND_TPLG_TYPE_CC);
271 if (err < 0)
272 return err;
273
274 err = tplg_build_routes(tplg);
275 if (err < 0)
276 return err;
277
278 return err;
279 }
280
snd_tplg_load(snd_tplg_t * tplg,const char * buf,size_t size)281 int snd_tplg_load(snd_tplg_t *tplg, const char *buf, size_t size)
282 {
283 snd_input_t *in;
284 int err;
285
286 err = snd_input_buffer_open(&in, buf, size);
287 if (err < 0) {
288 SNDERR("could not create input buffer");
289 return err;
290 }
291
292 err = tplg_load_config(tplg, in);
293 snd_input_close(in);
294 return err;
295 }
296
tplg_build(snd_tplg_t * tplg)297 static int tplg_build(snd_tplg_t *tplg)
298 {
299 int err;
300
301 err = tplg_build_integ(tplg);
302 if (err < 0) {
303 SNDERR("failed to check topology integrity");
304 return err;
305 }
306
307 err = tplg_write_data(tplg);
308 if (err < 0) {
309 SNDERR("failed to write data %d", err);
310 return err;
311 }
312 return 0;
313 }
314
snd_tplg_build_file(snd_tplg_t * tplg,const char * infile,const char * outfile)315 int snd_tplg_build_file(snd_tplg_t *tplg,
316 const char *infile,
317 const char *outfile)
318 {
319 FILE *fp;
320 snd_input_t *in;
321 int err;
322
323 fp = fopen(infile, "r");
324 if (fp == NULL) {
325 SNDERR("could not open configuration file %s", infile);
326 return -errno;
327 }
328
329 err = snd_input_stdio_attach(&in, fp, 1);
330 if (err < 0) {
331 fclose(fp);
332 SNDERR("could not attach stdio %s", infile);
333 return err;
334 }
335
336 err = tplg_load_config(tplg, in);
337 snd_input_close(in);
338 if (err < 0)
339 return err;
340
341 return snd_tplg_build(tplg, outfile);
342 }
343
snd_tplg_add_object(snd_tplg_t * tplg,snd_tplg_obj_template_t * t)344 int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t)
345 {
346 switch (t->type) {
347 case SND_TPLG_TYPE_MIXER:
348 return tplg_add_mixer_object(tplg, t);
349 case SND_TPLG_TYPE_ENUM:
350 return tplg_add_enum_object(tplg, t);
351 case SND_TPLG_TYPE_BYTES:
352 return tplg_add_bytes_object(tplg, t);
353 case SND_TPLG_TYPE_DAPM_WIDGET:
354 return tplg_add_widget_object(tplg, t);
355 case SND_TPLG_TYPE_DAPM_GRAPH:
356 return tplg_add_graph_object(tplg, t);
357 case SND_TPLG_TYPE_PCM:
358 return tplg_add_pcm_object(tplg, t);
359 case SND_TPLG_TYPE_DAI:
360 return tplg_add_dai_object(tplg, t);
361 case SND_TPLG_TYPE_LINK:
362 case SND_TPLG_TYPE_BE:
363 case SND_TPLG_TYPE_CC:
364 return tplg_add_link_object(tplg, t);
365 default:
366 SNDERR("invalid object type %d", t->type);
367 return -EINVAL;
368 };
369 }
370
snd_tplg_build(snd_tplg_t * tplg,const char * outfile)371 int snd_tplg_build(snd_tplg_t *tplg, const char *outfile)
372 {
373 int fd, err;
374 ssize_t r;
375
376 err = tplg_build(tplg);
377 if (err < 0)
378 return err;
379
380 fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
381 if (fd < 0) {
382 SNDERR("failed to open %s err %d", outfile, -errno);
383 return -errno;
384 }
385 r = write(fd, tplg->bin, tplg->bin_size);
386 close(fd);
387 if (r < 0) {
388 err = -errno;
389 SNDERR("write error: %s", strerror(errno));
390 return err;
391 }
392 if ((size_t)r != tplg->bin_size) {
393 SNDERR("partial write (%zd != %zd)", r, tplg->bin_size);
394 return -EIO;
395 }
396 return 0;
397 }
398
snd_tplg_build_bin(snd_tplg_t * tplg,void ** bin,size_t * size)399 int snd_tplg_build_bin(snd_tplg_t *tplg,
400 void **bin, size_t *size)
401 {
402 int err;
403
404 err = tplg_build(tplg);
405 if (err < 0)
406 return err;
407
408 *bin = tplg->bin;
409 *size = tplg->bin_size;
410 tplg->bin = NULL;
411 tplg->bin_size = tplg->bin_pos = 0;
412 return 0;
413 }
414
snd_tplg_set_manifest_data(snd_tplg_t * tplg,const void * data,int len)415 int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len)
416 {
417 struct tplg_elem *elem;
418
419 elem = tplg_elem_type_lookup(tplg, SND_TPLG_TYPE_MANIFEST);
420 if (elem == NULL) {
421 elem = tplg_elem_new_common(tplg, NULL, "manifest",
422 SND_TPLG_TYPE_MANIFEST);
423 if (!elem)
424 return -ENOMEM;
425 tplg->manifest.size = elem->size;
426 }
427
428 if (len <= 0)
429 return 0;
430
431 return tplg_add_data_bytes(tplg, elem, NULL, data, len);
432 }
433
snd_tplg_set_version(snd_tplg_t * tplg,unsigned int version)434 int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version)
435 {
436 tplg->version = version;
437
438 return 0;
439 }
440
snd_tplg_verbose(snd_tplg_t * tplg,int verbose)441 void snd_tplg_verbose(snd_tplg_t *tplg, int verbose)
442 {
443 tplg->verbose = verbose;
444 }
445
is_little_endian(void)446 static bool is_little_endian(void)
447 {
448 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && __SIZEOF_INT__ == 4
449 return true;
450 #endif
451 return false;
452 }
453
snd_tplg_create(int flags)454 snd_tplg_t *snd_tplg_create(int flags)
455 {
456 snd_tplg_t *tplg;
457
458 if (!is_little_endian()) {
459 SNDERR("cannot support big-endian machines");
460 return NULL;
461 }
462
463 tplg = calloc(1, sizeof(snd_tplg_t));
464 if (!tplg)
465 return NULL;
466
467 tplg->verbose = !!(flags & SND_TPLG_CREATE_VERBOSE);
468 tplg->dapm_sort = (flags & SND_TPLG_CREATE_DAPM_NOSORT) == 0;
469
470 tplg->manifest.size = sizeof(struct snd_soc_tplg_manifest);
471
472 INIT_LIST_HEAD(&tplg->tlv_list);
473 INIT_LIST_HEAD(&tplg->widget_list);
474 INIT_LIST_HEAD(&tplg->pcm_list);
475 INIT_LIST_HEAD(&tplg->dai_list);
476 INIT_LIST_HEAD(&tplg->be_list);
477 INIT_LIST_HEAD(&tplg->cc_list);
478 INIT_LIST_HEAD(&tplg->route_list);
479 INIT_LIST_HEAD(&tplg->pdata_list);
480 INIT_LIST_HEAD(&tplg->manifest_list);
481 INIT_LIST_HEAD(&tplg->text_list);
482 INIT_LIST_HEAD(&tplg->pcm_config_list);
483 INIT_LIST_HEAD(&tplg->pcm_caps_list);
484 INIT_LIST_HEAD(&tplg->mixer_list);
485 INIT_LIST_HEAD(&tplg->enum_list);
486 INIT_LIST_HEAD(&tplg->bytes_ext_list);
487 INIT_LIST_HEAD(&tplg->token_list);
488 INIT_LIST_HEAD(&tplg->tuple_list);
489 INIT_LIST_HEAD(&tplg->hw_cfg_list);
490
491 return tplg;
492 }
493
snd_tplg_new(void)494 snd_tplg_t *snd_tplg_new(void)
495 {
496 return snd_tplg_create(0);
497 }
498
snd_tplg_free(snd_tplg_t * tplg)499 void snd_tplg_free(snd_tplg_t *tplg)
500 {
501 free(tplg->bin);
502 free(tplg->manifest_pdata);
503
504 tplg_elem_free_list(&tplg->tlv_list);
505 tplg_elem_free_list(&tplg->widget_list);
506 tplg_elem_free_list(&tplg->pcm_list);
507 tplg_elem_free_list(&tplg->dai_list);
508 tplg_elem_free_list(&tplg->be_list);
509 tplg_elem_free_list(&tplg->cc_list);
510 tplg_elem_free_list(&tplg->route_list);
511 tplg_elem_free_list(&tplg->pdata_list);
512 tplg_elem_free_list(&tplg->manifest_list);
513 tplg_elem_free_list(&tplg->text_list);
514 tplg_elem_free_list(&tplg->pcm_config_list);
515 tplg_elem_free_list(&tplg->pcm_caps_list);
516 tplg_elem_free_list(&tplg->mixer_list);
517 tplg_elem_free_list(&tplg->enum_list);
518 tplg_elem_free_list(&tplg->bytes_ext_list);
519 tplg_elem_free_list(&tplg->token_list);
520 tplg_elem_free_list(&tplg->tuple_list);
521 tplg_elem_free_list(&tplg->hw_cfg_list);
522
523 free(tplg);
524 }
525
snd_tplg_version(void)526 const char *snd_tplg_version(void)
527 {
528 return SND_LIB_VERSION_STR;
529 }
530