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 22struct 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 231unsigned int tplg_table_items = ARRAY_SIZE(tplg_table); 232 233int 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 244int 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 */ 261int 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 277void 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 289struct 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 301void 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 320void 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 331struct 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 */ 357struct 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 */ 387void 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 */ 402struct 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 485struct tplg_alloc { 486 struct list_head list; 487 void *data[0]; 488}; 489#endif /* DOC_HIDDEN */ 490 491void *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 502void 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