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