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