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