1 /*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15 *
16 * Support for the verb/device/modifier core logic and API,
17 * command line tool and file parser was kindly sponsored by
18 * Texas Instruments Inc.
19 * Support for multiple active modifiers and devices,
20 * transition sequences, multiple client access and user defined use
21 * cases was kindly sponsored by Wolfson Microelectronics PLC.
22 *
23 * Copyright (C) 2008-2010 SlimLogic Ltd
24 * Copyright (C) 2010 Wolfson Microelectronics PLC
25 * Copyright (C) 2010 Texas Instruments Inc.
26 * Copyright (C) 2010 Red Hat Inc.
27 * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
28 * Stefan Schmidt <stefan@slimlogic.co.uk>
29 * Justin Xu <justinx@slimlogic.co.uk>
30 * Jaroslav Kysela <perex@perex.cz>
31 */
32
33 #include "ucm_local.h"
34
uc_mgr_error(const char * fmt,...)35 void uc_mgr_error(const char *fmt,...)
36 {
37 va_list va;
38 va_start(va, fmt);
39 fprintf(stderr, "ucm: ");
40 vfprintf(stderr, fmt, va);
41 va_end(va);
42 }
43
uc_mgr_stdout(const char * fmt,...)44 void uc_mgr_stdout(const char *fmt,...)
45 {
46 va_list va;
47 va_start(va, fmt);
48 vfprintf(stdout, fmt, va);
49 va_end(va);
50 }
51
uc_mgr_sysfs_root(void)52 const char *uc_mgr_sysfs_root(void)
53 {
54 const char *e = getenv("SYSFS_PATH");
55 if (e == NULL)
56 return "/sys";
57 if (*e == '\0')
58 uc_error("no sysfs root!");
59 return e;
60 }
61
uc_mgr_get_master_ctl(snd_use_case_mgr_t * uc_mgr)62 struct ctl_list *uc_mgr_get_master_ctl(snd_use_case_mgr_t *uc_mgr)
63 {
64 struct list_head *pos;
65 struct ctl_list *ctl_list = NULL, *ctl_list2;
66
67 list_for_each(pos, &uc_mgr->ctl_list) {
68 ctl_list2 = list_entry(pos, struct ctl_list, list);
69 if (ctl_list2->slave)
70 continue;
71 if (ctl_list) {
72 uc_error("multiple control device names were found!");
73 return NULL;
74 }
75 ctl_list = ctl_list2;
76 }
77 return ctl_list;
78 }
79
uc_mgr_get_ctl_by_card(snd_use_case_mgr_t * uc_mgr,int card)80 struct ctl_list *uc_mgr_get_ctl_by_card(snd_use_case_mgr_t *uc_mgr, int card)
81 {
82 struct ctl_list *ctl_list;
83 char cname[32];
84 int err;
85
86 sprintf(cname, "hw:%d", card);
87 err = uc_mgr_open_ctl(uc_mgr, &ctl_list, cname, 1);
88 if (err < 0)
89 return NULL;
90 return ctl_list;
91 }
92
uc_mgr_get_ctl_by_name(snd_use_case_mgr_t * uc_mgr,const char * name,int idx)93 struct ctl_list *uc_mgr_get_ctl_by_name(snd_use_case_mgr_t *uc_mgr, const char *name, int idx)
94 {
95 struct list_head *pos;
96 struct ctl_list *ctl_list;
97 const char *s;
98 int idx2, card;
99
100 idx2 = idx;
101 list_for_each(pos, &uc_mgr->ctl_list) {
102 ctl_list = list_entry(pos, struct ctl_list, list);
103 s = snd_ctl_card_info_get_name(ctl_list->ctl_info);
104 if (s == NULL)
105 continue;
106 if (strcmp(s, name) == 0) {
107 if (idx2 == 0)
108 return ctl_list;
109 idx2--;
110 }
111 }
112
113 idx2 = idx;
114 card = -1;
115 if (snd_card_next(&card) < 0 || card < 0)
116 return NULL;
117
118 while (card >= 0) {
119 ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, card);
120 if (ctl_list == NULL)
121 continue; /* really? */
122 s = snd_ctl_card_info_get_name(ctl_list->ctl_info);
123 if (s && strcmp(s, name) == 0) {
124 if (idx2 == 0)
125 return ctl_list;
126 idx2--;
127 }
128 if (snd_card_next(&card) < 0)
129 break;
130 }
131
132 return NULL;
133 }
134
uc_mgr_get_ctl(snd_use_case_mgr_t * uc_mgr)135 snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr)
136 {
137 struct ctl_list *ctl_list;
138
139 ctl_list = uc_mgr_get_master_ctl(uc_mgr);
140 if (ctl_list)
141 return ctl_list->ctl;
142 return NULL;
143 }
144
uc_mgr_free_ctl(struct ctl_list * ctl_list)145 static void uc_mgr_free_ctl(struct ctl_list *ctl_list)
146 {
147 struct list_head *pos, *npos;
148 struct ctl_dev *ctl_dev;
149
150 list_for_each_safe(pos, npos, &ctl_list->dev_list) {
151 ctl_dev = list_entry(pos, struct ctl_dev, list);
152 free(ctl_dev->device);
153 free(ctl_dev);
154 }
155 snd_ctl_card_info_free(ctl_list->ctl_info);
156 free(ctl_list);
157 }
158
uc_mgr_free_ctl_list(snd_use_case_mgr_t * uc_mgr)159 void uc_mgr_free_ctl_list(snd_use_case_mgr_t *uc_mgr)
160 {
161 struct list_head *pos, *npos;
162 struct ctl_list *ctl_list;
163
164 list_for_each_safe(pos, npos, &uc_mgr->ctl_list) {
165 ctl_list = list_entry(pos, struct ctl_list, list);
166 snd_ctl_close(ctl_list->ctl);
167 list_del(&ctl_list->list);
168 uc_mgr_free_ctl(ctl_list);
169 }
170 }
171
uc_mgr_ctl_add_dev(struct ctl_list * ctl_list,const char * device)172 static int uc_mgr_ctl_add_dev(struct ctl_list *ctl_list, const char *device)
173 {
174 struct list_head *pos;
175 struct ctl_dev *ctl_dev;
176
177 /* skip duplicates */
178 list_for_each(pos, &ctl_list->dev_list) {
179 ctl_dev = list_entry(pos, struct ctl_dev, list);
180 if (strcmp(ctl_dev->device, device) == 0)
181 return 0;
182 }
183
184 /* allocate new device name */
185 ctl_dev = malloc(sizeof(*ctl_dev));
186 if (ctl_dev == NULL)
187 return -ENOMEM;
188 ctl_dev->device = strdup(device);
189 if (ctl_dev->device == NULL) {
190 free(ctl_dev);
191 return -ENOMEM;
192 }
193 list_add_tail(&ctl_dev->list, &ctl_list->dev_list);
194 return 0;
195 }
196
uc_mgr_ctl_add(snd_use_case_mgr_t * uc_mgr,struct ctl_list ** ctl_list,snd_ctl_t * ctl,int card,snd_ctl_card_info_t * info,const char * device,int slave)197 static int uc_mgr_ctl_add(snd_use_case_mgr_t *uc_mgr,
198 struct ctl_list **ctl_list,
199 snd_ctl_t *ctl, int card,
200 snd_ctl_card_info_t *info,
201 const char *device,
202 int slave)
203 {
204 struct ctl_list *cl = NULL;
205 const char *id = snd_ctl_card_info_get_id(info);
206 char dev[MAX_CARD_LONG_NAME];
207 int err, hit = 0;
208
209 if (id == NULL || id[0] == '\0')
210 return -ENOENT;
211 if (!(*ctl_list)) {
212 cl = malloc(sizeof(*cl));
213 if (cl == NULL)
214 return -ENOMEM;
215 INIT_LIST_HEAD(&cl->dev_list);
216 cl->ctl = ctl;
217 if (snd_ctl_card_info_malloc(&cl->ctl_info) < 0) {
218 free(cl);
219 return -ENOMEM;
220 }
221 snd_ctl_card_info_copy(cl->ctl_info, info);
222 cl->slave = slave;
223 *ctl_list = cl;
224 } else {
225 if (!slave)
226 (*ctl_list)->slave = slave;
227 }
228 if (card >= 0) {
229 snprintf(dev, sizeof(dev), "hw:%d", card);
230 hit |= !!(device && (strcmp(dev, device) == 0));
231 err = uc_mgr_ctl_add_dev(*ctl_list, dev);
232 if (err < 0)
233 goto __nomem;
234 }
235 snprintf(dev, sizeof(dev), "hw:%s", id);
236 hit |= !!(device && (strcmp(dev, device) == 0));
237 err = uc_mgr_ctl_add_dev(*ctl_list, dev);
238 if (err < 0)
239 goto __nomem;
240 /* the UCM name not based on the card name / id */
241 if (!hit && device) {
242 err = uc_mgr_ctl_add_dev(*ctl_list, device);
243 if (err < 0)
244 goto __nomem;
245 }
246
247 list_add_tail(&(*ctl_list)->list, &uc_mgr->ctl_list);
248 return 0;
249
250 __nomem:
251 if (*ctl_list == cl) {
252 uc_mgr_free_ctl(cl);
253 *ctl_list = NULL;
254 }
255 return -ENOMEM;
256 }
257
uc_mgr_open_ctl(snd_use_case_mgr_t * uc_mgr,struct ctl_list ** ctll,const char * device,int slave)258 int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr,
259 struct ctl_list **ctll,
260 const char *device,
261 int slave)
262 {
263 struct list_head *pos1, *pos2;
264 snd_ctl_t *ctl;
265 struct ctl_list *ctl_list;
266 struct ctl_dev *ctl_dev;
267 snd_ctl_card_info_t *info;
268 const char *id;
269 int err, card, ucm_group, ucm_offset;
270
271 snd_ctl_card_info_alloca(&info);
272
273 ucm_group = _snd_is_ucm_device(device);
274 ucm_offset = ucm_group ? 8 : 0;
275
276 /* cache lookup */
277 list_for_each(pos1, &uc_mgr->ctl_list) {
278 ctl_list = list_entry(pos1, struct ctl_list, list);
279 if (ctl_list->ucm_group != ucm_group)
280 continue;
281 list_for_each(pos2, &ctl_list->dev_list) {
282 ctl_dev = list_entry(pos2, struct ctl_dev, list);
283 if (strcmp(ctl_dev->device, device + ucm_offset) == 0) {
284 *ctll = ctl_list;
285 if (!slave)
286 ctl_list->slave = 0;
287 return 0;
288 }
289 }
290 }
291
292 err = snd_ctl_open(&ctl, device, 0);
293 if (err < 0)
294 return err;
295
296 id = NULL;
297 err = snd_ctl_card_info(ctl, info);
298 if (err == 0)
299 id = snd_ctl_card_info_get_id(info);
300 if (err < 0 || id == NULL || id[0] == '\0') {
301 uc_error("control hardware info (%s): %s", device, snd_strerror(err));
302 snd_ctl_close(ctl);
303 return err >= 0 ? -EINVAL : err;
304 }
305
306 /* insert to cache, if just name differs */
307 list_for_each(pos1, &uc_mgr->ctl_list) {
308 ctl_list = list_entry(pos1, struct ctl_list, list);
309 if (ctl_list->ucm_group != ucm_group)
310 continue;
311 if (strcmp(id, snd_ctl_card_info_get_id(ctl_list->ctl_info)) == 0) {
312 card = snd_card_get_index(id);
313 err = uc_mgr_ctl_add(uc_mgr, &ctl_list, ctl, card, info, device + ucm_offset, slave);
314 if (err < 0)
315 goto __nomem;
316 snd_ctl_close(ctl);
317 ctl_list->ucm_group = ucm_group;
318 *ctll = ctl_list;
319 return 0;
320 }
321 }
322
323 ctl_list = NULL;
324 err = uc_mgr_ctl_add(uc_mgr, &ctl_list, ctl, -1, info, device + ucm_offset, slave);
325 if (err < 0)
326 goto __nomem;
327
328 ctl_list->ucm_group = ucm_group;
329 *ctll = ctl_list;
330 return 0;
331
332 __nomem:
333 snd_ctl_close(ctl);
334 return -ENOMEM;
335 }
336
uc_mgr_config_dir(int format)337 const char *uc_mgr_config_dir(int format)
338 {
339 const char *path;
340
341 if (format >= 2) {
342 path = getenv(ALSA_CONFIG_UCM2_VAR);
343 if (!path || path[0] == '\0')
344 path = ALSA_CONFIG_DIR "/ucm2";
345 } else {
346 path = getenv(ALSA_CONFIG_UCM_VAR);
347 if (!path || path[0] == '\0')
348 path = ALSA_CONFIG_DIR "/ucm";
349 }
350 return path;
351 }
352
uc_mgr_config_load_into(int format,const char * file,snd_config_t * top)353 int uc_mgr_config_load_into(int format, const char *file, snd_config_t *top)
354 {
355 FILE *fp;
356 snd_input_t *in;
357 const char *default_paths[2];
358 int err;
359
360 fp = fopen(file, "r");
361 if (!fp) {
362 err = -errno;
363 __err_open:
364 uc_error("could not open configuration file %s", file);
365 return err;
366 }
367 err = snd_input_stdio_attach(&in, fp, 1);
368 if (err < 0)
369 goto __err_open;
370
371 default_paths[0] = uc_mgr_config_dir(format);
372 default_paths[1] = NULL;
373 err = _snd_config_load_with_include(top, in, 0, default_paths);
374 if (err < 0) {
375 uc_error("could not load configuration file %s", file);
376 if (in)
377 snd_input_close(in);
378 return err;
379 }
380 err = snd_input_close(in);
381 if (err < 0)
382 return err;
383 return 0;
384 }
385
uc_mgr_config_load(int format,const char * file,snd_config_t ** cfg)386 int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
387 {
388 snd_config_t *top;
389 int err;
390
391 err = snd_config_top(&top);
392 if (err < 0)
393 return err;
394 err = uc_mgr_config_load_into(format, file, top);
395 if (err < 0) {
396 snd_config_delete(top);
397 return err;
398 }
399 *cfg = top;
400 return 0;
401 }
402
uc_mgr_free_value(struct list_head * base)403 void uc_mgr_free_value(struct list_head *base)
404 {
405 struct list_head *pos, *npos;
406 struct ucm_value *val;
407
408 list_for_each_safe(pos, npos, base) {
409 val = list_entry(pos, struct ucm_value, list);
410 free(val->name);
411 free(val->data);
412 list_del(&val->list);
413 free(val);
414 }
415 }
416
uc_mgr_free_dev_list(struct dev_list * dev_list)417 void uc_mgr_free_dev_list(struct dev_list *dev_list)
418 {
419 struct list_head *pos, *npos;
420 struct dev_list_node *dlist;
421
422 list_for_each_safe(pos, npos, &dev_list->list) {
423 dlist = list_entry(pos, struct dev_list_node, list);
424 free(dlist->name);
425 list_del(&dlist->list);
426 free(dlist);
427 }
428 }
429
uc_mgr_put_to_dev_list(struct dev_list * dev_list,const char * name)430 int uc_mgr_put_to_dev_list(struct dev_list *dev_list, const char *name)
431 {
432 struct list_head *pos;
433 struct dev_list_node *dlist;
434 char *n;
435
436 list_for_each(pos, &dev_list->list) {
437 dlist = list_entry(pos, struct dev_list_node, list);
438 if (strcmp(dlist->name, name) == 0)
439 return 0;
440 }
441
442 dlist = calloc(1, sizeof(*dlist));
443 if (dlist == NULL)
444 return -ENOMEM;
445 n = strdup(name);
446 if (n == NULL) {
447 free(dlist);
448 return -ENOMEM;
449 }
450 dlist->name = n;
451 list_add(&dlist->list, &dev_list->list);
452 return 0;
453 }
454
uc_mgr_rename_in_dev_list(struct dev_list * dev_list,const char * src,const char * dst)455 int uc_mgr_rename_in_dev_list(struct dev_list *dev_list, const char *src,
456 const char *dst)
457 {
458 struct list_head *pos;
459 struct dev_list_node *dlist;
460 char *dst1;
461
462 list_for_each(pos, &dev_list->list) {
463 dlist = list_entry(pos, struct dev_list_node, list);
464 if (strcmp(dlist->name, src) == 0) {
465 dst1 = strdup(dst);
466 if (dst1 == NULL)
467 return -ENOMEM;
468 free(dlist->name);
469 dlist->name = dst1;
470 return 0;
471 }
472 }
473 return -ENODEV;
474 }
475
uc_mgr_remove_from_dev_list(struct dev_list * dev_list,const char * name)476 int uc_mgr_remove_from_dev_list(struct dev_list *dev_list, const char *name)
477 {
478 struct list_head *pos;
479 struct dev_list_node *dlist;
480
481 list_for_each(pos, &dev_list->list) {
482 dlist = list_entry(pos, struct dev_list_node, list);
483 if (strcmp(dlist->name, name) == 0) {
484 free(dlist->name);
485 list_del(&dlist->list);
486 free(dlist);
487 return 0;
488 }
489 }
490 return -ENODEV;
491 }
492
uc_mgr_free_sequence_element(struct sequence_element * seq)493 void uc_mgr_free_sequence_element(struct sequence_element *seq)
494 {
495 if (seq == NULL)
496 return;
497 switch (seq->type) {
498 case SEQUENCE_ELEMENT_TYPE_CDEV:
499 free(seq->data.cdev);
500 break;
501 case SEQUENCE_ELEMENT_TYPE_CSET:
502 case SEQUENCE_ELEMENT_TYPE_CSET_NEW:
503 case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
504 case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
505 case SEQUENCE_ELEMENT_TYPE_CTL_REMOVE:
506 free(seq->data.cset);
507 break;
508 case SEQUENCE_ELEMENT_TYPE_SYSSET:
509 free(seq->data.sysw);
510 break;
511 case SEQUENCE_ELEMENT_TYPE_EXEC:
512 case SEQUENCE_ELEMENT_TYPE_SHELL:
513 free(seq->data.exec);
514 break;
515 case SEQUENCE_ELEMENT_TYPE_CFGSAVE:
516 free(seq->data.cfgsave);
517 break;
518 default:
519 break;
520 }
521 free(seq);
522 }
523
uc_mgr_free_sequence(struct list_head * base)524 void uc_mgr_free_sequence(struct list_head *base)
525 {
526 struct list_head *pos, *npos;
527 struct sequence_element *seq;
528
529 list_for_each_safe(pos, npos, base) {
530 seq = list_entry(pos, struct sequence_element, list);
531 list_del(&seq->list);
532 uc_mgr_free_sequence_element(seq);
533 }
534 }
535
uc_mgr_free_transition_element(struct transition_sequence * tseq)536 void uc_mgr_free_transition_element(struct transition_sequence *tseq)
537 {
538 free(tseq->name);
539 uc_mgr_free_sequence(&tseq->transition_list);
540 free(tseq);
541 }
542
uc_mgr_free_transition(struct list_head * base)543 void uc_mgr_free_transition(struct list_head *base)
544 {
545 struct list_head *pos, *npos;
546 struct transition_sequence *tseq;
547
548 list_for_each_safe(pos, npos, base) {
549 tseq = list_entry(pos, struct transition_sequence, list);
550 list_del(&tseq->list);
551 uc_mgr_free_transition_element(tseq);
552 }
553 }
554
uc_mgr_free_dev_name_list(struct list_head * base)555 void uc_mgr_free_dev_name_list(struct list_head *base)
556 {
557 struct list_head *pos, *npos;
558 struct ucm_dev_name *dev;
559
560 list_for_each_safe(pos, npos, base) {
561 dev = list_entry(pos, struct ucm_dev_name, list);
562 list_del(&dev->list);
563 free(dev->name1);
564 free(dev->name2);
565 free(dev);
566 }
567 }
568
uc_mgr_free_modifier(struct list_head * base)569 void uc_mgr_free_modifier(struct list_head *base)
570 {
571 struct list_head *pos, *npos;
572 struct use_case_modifier *mod;
573
574 list_for_each_safe(pos, npos, base) {
575 mod = list_entry(pos, struct use_case_modifier, list);
576 free(mod->name);
577 free(mod->comment);
578 uc_mgr_free_sequence(&mod->enable_list);
579 uc_mgr_free_sequence(&mod->disable_list);
580 uc_mgr_free_transition(&mod->transition_list);
581 uc_mgr_free_dev_list(&mod->dev_list);
582 uc_mgr_free_value(&mod->value_list);
583 list_del(&mod->list);
584 free(mod);
585 }
586 }
587
uc_mgr_free_device(struct use_case_device * dev)588 void uc_mgr_free_device(struct use_case_device *dev)
589 {
590 free(dev->name);
591 free(dev->comment);
592 uc_mgr_free_sequence(&dev->enable_list);
593 uc_mgr_free_sequence(&dev->disable_list);
594 uc_mgr_free_transition(&dev->transition_list);
595 uc_mgr_free_dev_list(&dev->dev_list);
596 uc_mgr_free_value(&dev->value_list);
597 list_del(&dev->list);
598 free(dev);
599 }
600
uc_mgr_free_device_list(struct list_head * base)601 void uc_mgr_free_device_list(struct list_head *base)
602 {
603 struct list_head *pos, *npos;
604 struct use_case_device *dev;
605
606 list_for_each_safe(pos, npos, base) {
607 dev = list_entry(pos, struct use_case_device, list);
608 uc_mgr_free_device(dev);
609 }
610 }
611
uc_mgr_rename_device(struct use_case_verb * verb,const char * src,const char * dst)612 int uc_mgr_rename_device(struct use_case_verb *verb, const char *src,
613 const char *dst)
614 {
615 struct use_case_device *device;
616 struct list_head *pos, *npos;
617 char *dst1;
618
619 /* no errors when device is not found */
620 list_for_each_safe(pos, npos, &verb->device_list) {
621 device = list_entry(pos, struct use_case_device, list);
622 if (strcmp(device->name, src) == 0) {
623 dst1 = strdup(dst);
624 if (dst1 == NULL)
625 return -ENOMEM;
626 free(device->name);
627 device->name = dst1;
628 continue;
629 }
630 uc_mgr_rename_in_dev_list(&device->dev_list, src, dst);
631 }
632 return 0;
633 }
634
uc_mgr_remove_device(struct use_case_verb * verb,const char * name)635 int uc_mgr_remove_device(struct use_case_verb *verb, const char *name)
636 {
637 struct use_case_device *device;
638 struct list_head *pos, *npos;
639 int err, found = 0;
640
641 list_for_each_safe(pos, npos, &verb->device_list) {
642 device = list_entry(pos, struct use_case_device, list);
643 if (strcmp(device->name, name) == 0) {
644 uc_mgr_free_device(device);
645 found++;
646 continue;
647 }
648 err = uc_mgr_remove_from_dev_list(&device->dev_list, name);
649 if (err < 0 && err != -ENODEV)
650 return err;
651 if (err == 0)
652 found++;
653 }
654 return found == 0 ? -ENODEV : 0;
655 }
656
uc_mgr_get_variable(snd_use_case_mgr_t * uc_mgr,const char * name)657 const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, const char *name)
658 {
659 struct list_head *pos;
660 struct ucm_value *value;
661
662 list_for_each(pos, &uc_mgr->variable_list) {
663 value = list_entry(pos, struct ucm_value, list);
664 if (strcmp(value->name, name) == 0)
665 return value->data;
666 }
667 return NULL;
668 }
669
uc_mgr_set_variable(snd_use_case_mgr_t * uc_mgr,const char * name,const char * val)670 int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name,
671 const char *val)
672 {
673 struct list_head *pos;
674 struct ucm_value *curr;
675 char *val2;
676
677 list_for_each(pos, &uc_mgr->variable_list) {
678 curr = list_entry(pos, struct ucm_value, list);
679 if (strcmp(curr->name, name) == 0) {
680 val2 = strdup(val);
681 if (val2 == NULL)
682 return -ENOMEM;
683 free(curr->data);
684 curr->data = val2;
685 return 0;
686 }
687 }
688
689 curr = calloc(1, sizeof(struct ucm_value));
690 if (curr == NULL)
691 return -ENOMEM;
692 curr->name = strdup(name);
693 if (curr->name == NULL) {
694 free(curr);
695 return -ENOMEM;
696 }
697 curr->data = strdup(val);
698 if (curr->data == NULL) {
699 free(curr->name);
700 free(curr);
701 return -ENOMEM;
702 }
703 list_add_tail(&curr->list, &uc_mgr->variable_list);
704 return 0;
705 }
706
uc_mgr_free_verb(snd_use_case_mgr_t * uc_mgr)707 void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
708 {
709 struct list_head *pos, *npos;
710 struct use_case_verb *verb;
711
712 list_for_each_safe(pos, npos, &uc_mgr->verb_list) {
713 verb = list_entry(pos, struct use_case_verb, list);
714 free(verb->name);
715 free(verb->comment);
716 uc_mgr_free_sequence(&verb->enable_list);
717 uc_mgr_free_sequence(&verb->disable_list);
718 uc_mgr_free_transition(&verb->transition_list);
719 uc_mgr_free_value(&verb->value_list);
720 uc_mgr_free_device_list(&verb->device_list);
721 uc_mgr_free_device_list(&verb->cmpt_device_list);
722 uc_mgr_free_modifier(&verb->modifier_list);
723 uc_mgr_free_dev_name_list(&verb->rename_list);
724 uc_mgr_free_dev_name_list(&verb->remove_list);
725 list_del(&verb->list);
726 free(verb);
727 }
728 uc_mgr_free_sequence(&uc_mgr->fixedboot_list);
729 uc_mgr_free_sequence(&uc_mgr->boot_list);
730 uc_mgr_free_sequence(&uc_mgr->default_list);
731 uc_mgr_free_value(&uc_mgr->value_list);
732 uc_mgr_free_value(&uc_mgr->variable_list);
733 free(uc_mgr->comment);
734 free(uc_mgr->conf_dir_name);
735 free(uc_mgr->conf_file_name);
736 uc_mgr->comment = NULL;
737 uc_mgr->conf_dir_name = NULL;
738 uc_mgr->conf_file_name = NULL;
739 uc_mgr->active_verb = NULL;
740 INIT_LIST_HEAD(&uc_mgr->active_devices);
741 INIT_LIST_HEAD(&uc_mgr->active_modifiers);
742 }
743
uc_mgr_free(snd_use_case_mgr_t * uc_mgr)744 void uc_mgr_free(snd_use_case_mgr_t *uc_mgr)
745 {
746 snd_config_delete(uc_mgr->local_config);
747 uc_mgr_free_verb(uc_mgr);
748 uc_mgr_free_ctl_list(uc_mgr);
749 free(uc_mgr->card_name);
750 free(uc_mgr);
751 }
752
753 /*
754 * UCM card list stuff
755 */
756
757 static pthread_mutex_t ucm_cards_mutex = PTHREAD_MUTEX_INITIALIZER;
758 static LIST_HEAD(ucm_cards);
759 static unsigned int ucm_card_assign;
760
uc_mgr_card_find(unsigned int card_number)761 static snd_use_case_mgr_t *uc_mgr_card_find(unsigned int card_number)
762 {
763 struct list_head *pos;
764 snd_use_case_mgr_t *uc_mgr;
765
766 list_for_each(pos, &ucm_cards) {
767 uc_mgr = list_entry(pos, snd_use_case_mgr_t, cards_list);
768 if (uc_mgr->ucm_card_number == card_number)
769 return uc_mgr;
770 }
771 return NULL;
772 }
773
uc_mgr_card_open(snd_use_case_mgr_t * uc_mgr)774 int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr)
775 {
776 unsigned int prev;
777
778 pthread_mutex_lock(&ucm_cards_mutex);
779 prev = ucm_card_assign++;
780 while (uc_mgr_card_find(ucm_card_assign)) {
781 ucm_card_assign++;
782 ucm_card_assign &= 0xffff;
783 /* avoid zero card instance number */
784 if (ucm_card_assign == 0)
785 ucm_card_assign++;
786 if (ucm_card_assign == prev) {
787 pthread_mutex_unlock(&ucm_cards_mutex);
788 return -ENOMEM;
789 }
790 }
791 uc_mgr->ucm_card_number = ucm_card_assign;
792 list_add(&uc_mgr->cards_list, &ucm_cards);
793 pthread_mutex_unlock(&ucm_cards_mutex);
794 return 0;
795 }
796
uc_mgr_card_close(snd_use_case_mgr_t * uc_mgr)797 void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr)
798 {
799 pthread_mutex_lock(&ucm_cards_mutex);
800 list_del(&uc_mgr->cards_list);
801 pthread_mutex_unlock(&ucm_cards_mutex);
802 }
803
804 /**
805 * \brief Get library configuration based on the private ALSA device name
806 * \param name[in] ALSA device name
807 * \retval config A configuration tree or NULL
808 *
809 * The returned configuration (non-NULL) should be unreferenced using
810 * snd_config_unref() call.
811 */
uc_mgr_alibcfg_by_device(snd_config_t ** top,const char * name)812 const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name)
813 {
814 char buf[5];
815 long card_num;
816 snd_config_t *config;
817 snd_use_case_mgr_t *uc_mgr;
818 int err;
819
820 if (strncmp(name, "_ucm", 4) || strlen(name) < 12 || name[8] != '.')
821 return NULL;
822 strncpy(buf, name + 4, 4);
823 buf[4] = '\0';
824 err = safe_strtol_base(buf, &card_num, 16);
825 if (err < 0 || card_num < 0 || card_num > 0xffff)
826 return NULL;
827 config = NULL;
828 pthread_mutex_lock(&ucm_cards_mutex);
829 uc_mgr = uc_mgr_card_find(card_num);
830 /* non-empty configs are accepted only */
831 if (uc_mgr_has_local_config(uc_mgr)) {
832 config = uc_mgr->local_config;
833 snd_config_ref(config);
834 }
835 pthread_mutex_unlock(&ucm_cards_mutex);
836 if (!config)
837 return NULL;
838 *top = config;
839 return name + 9;
840 }
841