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
22 struct tplg_table tplg_table[] = {
23 {
24 .name = "manifest",
25 .id = "SectionManifest",
26 .loff = offsetof(snd_tplg_t, manifest_list),
27 .type = SND_TPLG_TYPE_MANIFEST,
28 .tsoc = SND_SOC_TPLG_TYPE_MANIFEST,
29 .size = sizeof(struct snd_soc_tplg_manifest),
30 .enew = 1,
31 .parse = tplg_parse_manifest_data,
32 .save = tplg_save_manifest_data,
33 .decod = tplg_decode_manifest_data,
34 },
35 {
36 .name = "control mixer",
37 .id = "SectionControlMixer",
38 .loff = offsetof(snd_tplg_t, mixer_list),
39 .type = SND_TPLG_TYPE_MIXER,
40 .tsoc = SND_SOC_TPLG_TYPE_MIXER,
41 .size = sizeof(struct snd_soc_tplg_mixer_control),
42 .build = 1,
43 .enew = 1,
44 .parse = tplg_parse_control_mixer,
45 .save = tplg_save_control_mixer,
46 .decod = tplg_decode_control_mixer,
47 },
48 {
49 .name = "control enum",
50 .id = "SectionControlEnum",
51 .loff = offsetof(snd_tplg_t, enum_list),
52 .type = SND_TPLG_TYPE_ENUM,
53 .tsoc = SND_SOC_TPLG_TYPE_ENUM,
54 .size = sizeof(struct snd_soc_tplg_enum_control),
55 .build = 1,
56 .enew = 1,
57 .parse = tplg_parse_control_enum,
58 .save = tplg_save_control_enum,
59 .decod = tplg_decode_control_enum,
60 },
61 {
62 .name = "control extended (bytes)",
63 .id = "SectionControlBytes",
64 .loff = offsetof(snd_tplg_t, bytes_ext_list),
65 .type = SND_TPLG_TYPE_BYTES,
66 .tsoc = SND_SOC_TPLG_TYPE_BYTES,
67 .size = sizeof(struct snd_soc_tplg_bytes_control),
68 .build = 1,
69 .enew = 1,
70 .parse = tplg_parse_control_bytes,
71 .save = tplg_save_control_bytes,
72 .decod = tplg_decode_control_bytes,
73 },
74 {
75 .name = "dapm widget",
76 .id = "SectionWidget",
77 .loff = offsetof(snd_tplg_t, widget_list),
78 .type = SND_TPLG_TYPE_DAPM_WIDGET,
79 .tsoc = SND_SOC_TPLG_TYPE_DAPM_WIDGET,
80 .size = sizeof(struct snd_soc_tplg_dapm_widget),
81 .build = 1,
82 .enew = 1,
83 .parse = tplg_parse_dapm_widget,
84 .save = tplg_save_dapm_widget,
85 .decod = tplg_decode_dapm_widget,
86 },
87 {
88 .name = "pcm",
89 .id = "SectionPCM",
90 .loff = offsetof(snd_tplg_t, pcm_list),
91 .type = SND_TPLG_TYPE_PCM,
92 .tsoc = SND_SOC_TPLG_TYPE_PCM,
93 .size = sizeof(struct snd_soc_tplg_pcm),
94 .build = 1,
95 .enew = 1,
96 .parse = tplg_parse_pcm,
97 .save = tplg_save_pcm,
98 .decod = tplg_decode_pcm,
99 },
100 {
101 .name = "physical dai",
102 .id = "SectionDAI",
103 .loff = offsetof(snd_tplg_t, dai_list),
104 .type = SND_TPLG_TYPE_DAI,
105 .tsoc = SND_SOC_TPLG_TYPE_DAI,
106 .size = sizeof(struct snd_soc_tplg_dai),
107 .build = 1,
108 .enew = 1,
109 .parse = tplg_parse_dai,
110 .save = tplg_save_dai,
111 .decod = tplg_decode_dai,
112 },
113 {
114 .name = "be",
115 .id = "SectionBE",
116 .id2 = "SectionLink",
117 .loff = offsetof(snd_tplg_t, be_list),
118 .type = SND_TPLG_TYPE_BE,
119 .tsoc = SND_SOC_TPLG_TYPE_BACKEND_LINK,
120 .size = sizeof(struct snd_soc_tplg_link_config),
121 .build = 1,
122 .enew = 1,
123 .parse = tplg_parse_link,
124 .save = tplg_save_link,
125 .decod = tplg_decode_link,
126 },
127 {
128 .name = "cc",
129 .id = "SectionCC",
130 .loff = offsetof(snd_tplg_t, cc_list),
131 .type = SND_TPLG_TYPE_CC,
132 .tsoc = SND_SOC_TPLG_TYPE_CODEC_LINK,
133 .size = sizeof(struct snd_soc_tplg_link_config),
134 .build = 1,
135 .enew = 1,
136 .parse = tplg_parse_cc,
137 .save = tplg_save_cc,
138 .decod = tplg_decode_cc,
139 },
140 {
141 .name = "route (dapm graph)",
142 .id = "SectionGraph",
143 .loff = offsetof(snd_tplg_t, route_list),
144 .type = SND_TPLG_TYPE_DAPM_GRAPH,
145 .tsoc = SND_SOC_TPLG_TYPE_DAPM_GRAPH,
146 .build = 1,
147 .parse = tplg_parse_dapm_graph,
148 .gsave = tplg_save_dapm_graph,
149 .decod = tplg_decode_dapm_graph,
150 },
151 {
152 .name = "private data",
153 .id = "SectionData",
154 .loff = offsetof(snd_tplg_t, pdata_list),
155 .type = SND_TPLG_TYPE_DATA,
156 .tsoc = SND_SOC_TPLG_TYPE_PDATA,
157 .build = 1,
158 .enew = 1,
159 .parse = tplg_parse_data,
160 .save = tplg_save_data,
161 .decod = tplg_decode_data,
162 },
163 {
164 .name = "text",
165 .id = "SectionText",
166 .loff = offsetof(snd_tplg_t, text_list),
167 .type = SND_TPLG_TYPE_TEXT,
168 .size = sizeof(struct tplg_texts),
169 .enew = 1,
170 .parse = tplg_parse_text,
171 .save = tplg_save_text,
172 },
173 {
174 .name = "tlv",
175 .id = "SectionTLV",
176 .loff = offsetof(snd_tplg_t, tlv_list),
177 .type = SND_TPLG_TYPE_TLV,
178 .size = sizeof(struct snd_soc_tplg_ctl_tlv),
179 .enew = 1,
180 .parse = tplg_parse_tlv,
181 .save = tplg_save_tlv,
182 },
183 {
184 .name = "stream config",
185 .loff = offsetof(snd_tplg_t, pcm_config_list),
186 .type = SND_TPLG_TYPE_STREAM_CONFIG,
187 .size = sizeof(struct snd_soc_tplg_stream),
188 .enew = 1,
189 },
190 {
191 .name = "stream capabilities",
192 .id = "SectionPCMCapabilities",
193 .loff = offsetof(snd_tplg_t, pcm_caps_list),
194 .type = SND_TPLG_TYPE_STREAM_CAPS,
195 .size = sizeof(struct snd_soc_tplg_stream_caps),
196 .enew = 1,
197 .parse = tplg_parse_stream_caps,
198 .save = tplg_save_stream_caps,
199 },
200 {
201 .name = "token",
202 .id = "SectionVendorTokens",
203 .loff = offsetof(snd_tplg_t, token_list),
204 .type = SND_TPLG_TYPE_TOKEN,
205 .enew = 1,
206 .parse = tplg_parse_tokens,
207 .save = tplg_save_tokens,
208 },
209 {
210 .name = "tuple",
211 .id = "SectionVendorTuples",
212 .loff = offsetof(snd_tplg_t, tuple_list),
213 .type = SND_TPLG_TYPE_TUPLE,
214 .free = tplg_free_tuples,
215 .enew = 1,
216 .parse = tplg_parse_tuples,
217 .save = tplg_save_tuples,
218 },
219 {
220 .name = "hw config",
221 .id = "SectionHWConfig",
222 .loff = offsetof(snd_tplg_t, hw_cfg_list),
223 .type = SND_TPLG_TYPE_HW_CONFIG,
224 .size = sizeof(struct snd_soc_tplg_hw_config),
225 .enew = 1,
226 .parse = tplg_parse_hw_config,
227 .save = tplg_save_hw_config,
228 }
229 };
230
231 unsigned int tplg_table_items = ARRAY_SIZE(tplg_table);
232
tplg_get_type(int asoc_type)233 int tplg_get_type(int asoc_type)
234 {
235 unsigned int index;
236
237 for (index = 0; index < tplg_table_items; index++)
238 if (tplg_table[index].tsoc == asoc_type)
239 return tplg_table[index].type;
240 SNDERR("uknown asoc type %d", asoc_type);
241 return -EINVAL;
242 }
243
tplg_ref_add(struct tplg_elem * elem,int type,const char * id)244 int tplg_ref_add(struct tplg_elem *elem, int type, const char* id)
245 {
246 struct tplg_ref *ref;
247
248 ref = calloc(1, sizeof(*ref));
249 if (!ref)
250 return -ENOMEM;
251
252 strncpy(ref->id, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
253 ref->id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
254 ref->type = type;
255
256 list_add_tail(&ref->list, &elem->ref_list);
257 return 0;
258 }
259
260 /* directly add a reference elem */
tplg_ref_add_elem(struct tplg_elem * elem,struct tplg_elem * elem_ref)261 int tplg_ref_add_elem(struct tplg_elem *elem, struct tplg_elem *elem_ref)
262 {
263 struct tplg_ref *ref;
264
265 ref = calloc(1, sizeof(*ref));
266 if (!ref)
267 return -ENOMEM;
268
269 ref->type = elem_ref->type;
270 ref->elem = elem_ref;
271 snd_strlcpy(ref->id, elem_ref->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
272
273 list_add_tail(&ref->list, &elem->ref_list);
274 return 0;
275 }
276
tplg_ref_free_list(struct list_head * base)277 void tplg_ref_free_list(struct list_head *base)
278 {
279 struct list_head *pos, *npos;
280 struct tplg_ref *ref;
281
282 list_for_each_safe(pos, npos, base) {
283 ref = list_entry(pos, struct tplg_ref, list);
284 list_del(&ref->list);
285 free(ref);
286 }
287 }
288
tplg_elem_new(void)289 struct tplg_elem *tplg_elem_new(void)
290 {
291 struct tplg_elem *elem;
292
293 elem = calloc(1, sizeof(*elem));
294 if (!elem)
295 return NULL;
296
297 INIT_LIST_HEAD(&elem->ref_list);
298 return elem;
299 }
300
tplg_elem_free(struct tplg_elem * elem)301 void tplg_elem_free(struct tplg_elem *elem)
302 {
303 list_del(&elem->list);
304
305 tplg_ref_free_list(&elem->ref_list);
306
307 /* free struct snd_tplg_ object,
308 * the union pointers share the same address
309 */
310 if (elem->obj) {
311 if (elem->free)
312 elem->free(elem->obj);
313
314 free(elem->obj);
315 }
316
317 free(elem);
318 }
319
tplg_elem_free_list(struct list_head * base)320 void tplg_elem_free_list(struct list_head *base)
321 {
322 struct list_head *pos, *npos;
323 struct tplg_elem *elem;
324
325 list_for_each_safe(pos, npos, base) {
326 elem = list_entry(pos, struct tplg_elem, list);
327 tplg_elem_free(elem);
328 }
329 }
330
tplg_elem_lookup(struct list_head * base,const char * id,unsigned int type,int index)331 struct tplg_elem *tplg_elem_lookup(struct list_head *base, const char* id,
332 unsigned int type, int index)
333 {
334 struct list_head *pos;
335 struct tplg_elem *elem;
336
337 if (!base || !id)
338 return NULL;
339
340 list_for_each(pos, base) {
341
342 elem = list_entry(pos, struct tplg_elem, list);
343
344 if (!strcmp(elem->id, id) && elem->type == type)
345 return elem;
346 /* SND_TPLG_INDEX_ALL is the default value "0" and applicable
347 for all use cases */
348 if ((index != SND_TPLG_INDEX_ALL)
349 && (elem->index > index))
350 break;
351 }
352
353 return NULL;
354 }
355
356 /* find an element by type */
tplg_elem_type_lookup(snd_tplg_t * tplg,enum snd_tplg_type type)357 struct tplg_elem *tplg_elem_type_lookup(snd_tplg_t *tplg,
358 enum snd_tplg_type type)
359 {
360 struct tplg_table *tptr;
361 struct list_head *pos, *list;
362 struct tplg_elem *elem;
363 unsigned int index;
364
365 for (index = 0; index < tplg_table_items; index++) {
366 tptr = &tplg_table[index];
367 if (!tptr->enew)
368 continue;
369 if ((int)type != tptr->type)
370 continue;
371 break;
372 }
373 if (index >= tplg_table_items)
374 return NULL;
375
376 list = (struct list_head *)((void *)tplg + tptr->loff);
377
378 /* return only first element */
379 list_for_each(pos, list) {
380 elem = list_entry(pos, struct tplg_elem, list);
381 return elem;
382 }
383 return NULL;
384 }
385
386 /* insert a new element into list in the ascending order of index value */
tplg_elem_insert(struct tplg_elem * elem_p,struct list_head * list)387 void tplg_elem_insert(struct tplg_elem *elem_p, struct list_head *list)
388 {
389 struct list_head *pos, *p = &(elem_p->list);
390 struct tplg_elem *elem;
391
392 list_for_each(pos, list) {
393 elem = list_entry(pos, struct tplg_elem, list);
394 if (elem_p->index < elem->index)
395 break;
396 }
397 /* insert item before pos */
398 list_insert(p, pos->prev, pos);
399 }
400
401 /* create a new common element and object */
tplg_elem_new_common(snd_tplg_t * tplg,snd_config_t * cfg,const char * name,enum snd_tplg_type type)402 struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg,
403 snd_config_t *cfg,
404 const char *name,
405 enum snd_tplg_type type)
406 {
407 struct tplg_table *tptr;
408 struct tplg_elem *elem;
409 struct list_head *list;
410 const char *id;
411 int obj_size = 0;
412 unsigned index;
413 void *obj;
414 snd_config_iterator_t i, next;
415 snd_config_t *n;
416
417 if (!cfg && !name)
418 return NULL;
419
420 elem = tplg_elem_new();
421 if (!elem)
422 return NULL;
423
424 /* do we get name from cfg */
425 if (cfg) {
426 snd_config_get_id(cfg, &id);
427 snd_strlcpy(elem->id, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
428 elem->id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
429 /* as we insert new elem based on the index value, move index
430 parsing here */
431 snd_config_for_each(i, next, cfg) {
432 n = snd_config_iterator_entry(i);
433 if (snd_config_get_id(n, &id))
434 continue;
435 if (strcmp(id, "index") == 0) {
436 if (tplg_get_integer(n, &elem->index, 0)) {
437 free(elem);
438 return NULL;
439 }
440 if (elem->index < 0) {
441 free(elem);
442 return NULL;
443 }
444 }
445 }
446 } else if (name != NULL)
447 snd_strlcpy(elem->id, name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
448
449 for (index = 0; index < tplg_table_items; index++) {
450 tptr = &tplg_table[index];
451 if (!tptr->enew)
452 continue;
453 if ((int)type != tptr->type)
454 continue;
455 break;
456 }
457 if (index >= tplg_table_items) {
458 free(elem);
459 return NULL;
460 }
461
462 list = (struct list_head *)((void *)tplg + tptr->loff);
463 tplg_elem_insert(elem, list);
464 obj_size = tptr->size;
465 elem->free = tptr->free;
466 elem->table = tptr;
467
468 /* create new object too if required */
469 if (obj_size > 0) {
470 obj = calloc(1, obj_size);
471 if (obj == NULL) {
472 free(elem);
473 return NULL;
474 }
475
476 elem->obj = obj;
477 elem->size = obj_size;
478 }
479
480 elem->type = type;
481 return elem;
482 }
483
484 #ifndef DOC_HIDDEN
485 struct tplg_alloc {
486 struct list_head list;
487 void *data[0];
488 };
489 #endif /* DOC_HIDDEN */
490
tplg_calloc(struct list_head * heap,size_t size)491 void *tplg_calloc(struct list_head *heap, size_t size)
492 {
493 struct tplg_alloc *a;
494
495 a = calloc(1, sizeof(*a) + size);
496 if (a == NULL)
497 return NULL;
498 list_add_tail(&a->list, heap);
499 return a->data;
500 }
501
tplg_free(struct list_head * heap)502 void tplg_free(struct list_head *heap)
503 {
504 struct list_head *pos, *npos;
505 struct tplg_alloc *a;
506
507 list_for_each_safe(pos, npos, heap) {
508 a = list_entry(pos, struct tplg_alloc, list);
509 list_del(&a->list);
510 free(a);
511 }
512 }
513