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_value1(struct ucm_value * val)403 static void uc_mgr_free_value1(struct ucm_value *val)
404 {
405 free(val->name);
406 free(val->data);
407 list_del(&val->list);
408 free(val);
409 }
410
uc_mgr_free_value(struct list_head * base)411 void uc_mgr_free_value(struct list_head *base)
412 {
413 struct list_head *pos, *npos;
414 struct ucm_value *val;
415
416 list_for_each_safe(pos, npos, base) {
417 val = list_entry(pos, struct ucm_value, list);
418 uc_mgr_free_value1(val);
419 }
420 }
421
uc_mgr_free_dev_list(struct dev_list * dev_list)422 void uc_mgr_free_dev_list(struct dev_list *dev_list)
423 {
424 struct list_head *pos, *npos;
425 struct dev_list_node *dlist;
426
427 list_for_each_safe(pos, npos, &dev_list->list) {
428 dlist = list_entry(pos, struct dev_list_node, list);
429 free(dlist->name);
430 list_del(&dlist->list);
431 free(dlist);
432 }
433 }
434
uc_mgr_put_to_dev_list(struct dev_list * dev_list,const char * name)435 int uc_mgr_put_to_dev_list(struct dev_list *dev_list, const char *name)
436 {
437 struct list_head *pos;
438 struct dev_list_node *dlist;
439 char *n;
440
441 list_for_each(pos, &dev_list->list) {
442 dlist = list_entry(pos, struct dev_list_node, list);
443 if (strcmp(dlist->name, name) == 0)
444 return 0;
445 }
446
447 dlist = calloc(1, sizeof(*dlist));
448 if (dlist == NULL)
449 return -ENOMEM;
450 n = strdup(name);
451 if (n == NULL) {
452 free(dlist);
453 return -ENOMEM;
454 }
455 dlist->name = n;
456 list_add(&dlist->list, &dev_list->list);
457 return 0;
458 }
459
uc_mgr_rename_in_dev_list(struct dev_list * dev_list,const char * src,const char * dst)460 int uc_mgr_rename_in_dev_list(struct dev_list *dev_list, const char *src,
461 const char *dst)
462 {
463 struct list_head *pos;
464 struct dev_list_node *dlist;
465 char *dst1;
466
467 list_for_each(pos, &dev_list->list) {
468 dlist = list_entry(pos, struct dev_list_node, list);
469 if (strcmp(dlist->name, src) == 0) {
470 dst1 = strdup(dst);
471 if (dst1 == NULL)
472 return -ENOMEM;
473 free(dlist->name);
474 dlist->name = dst1;
475 return 0;
476 }
477 }
478 return -ENODEV;
479 }
480
uc_mgr_remove_from_dev_list(struct dev_list * dev_list,const char * name)481 int uc_mgr_remove_from_dev_list(struct dev_list *dev_list, const char *name)
482 {
483 struct list_head *pos;
484 struct dev_list_node *dlist;
485
486 list_for_each(pos, &dev_list->list) {
487 dlist = list_entry(pos, struct dev_list_node, list);
488 if (strcmp(dlist->name, name) == 0) {
489 free(dlist->name);
490 list_del(&dlist->list);
491 free(dlist);
492 return 0;
493 }
494 }
495 return -ENODEV;
496 }
497
uc_mgr_free_sequence_element(struct sequence_element * seq)498 void uc_mgr_free_sequence_element(struct sequence_element *seq)
499 {
500 if (seq == NULL)
501 return;
502 switch (seq->type) {
503 case SEQUENCE_ELEMENT_TYPE_CDEV:
504 free(seq->data.cdev);
505 break;
506 case SEQUENCE_ELEMENT_TYPE_CSET:
507 case SEQUENCE_ELEMENT_TYPE_CSET_NEW:
508 case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
509 case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
510 case SEQUENCE_ELEMENT_TYPE_CTL_REMOVE:
511 free(seq->data.cset);
512 break;
513 case SEQUENCE_ELEMENT_TYPE_SYSSET:
514 free(seq->data.sysw);
515 break;
516 case SEQUENCE_ELEMENT_TYPE_EXEC:
517 case SEQUENCE_ELEMENT_TYPE_SHELL:
518 free(seq->data.exec);
519 break;
520 case SEQUENCE_ELEMENT_TYPE_CFGSAVE:
521 free(seq->data.cfgsave);
522 break;
523 case SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ:
524 case SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ:
525 free(seq->data.device);
526 break;
527 default:
528 break;
529 }
530 free(seq);
531 }
532
uc_mgr_free_sequence(struct list_head * base)533 void uc_mgr_free_sequence(struct list_head *base)
534 {
535 struct list_head *pos, *npos;
536 struct sequence_element *seq;
537
538 list_for_each_safe(pos, npos, base) {
539 seq = list_entry(pos, struct sequence_element, list);
540 list_del(&seq->list);
541 uc_mgr_free_sequence_element(seq);
542 }
543 }
544
uc_mgr_free_transition_element(struct transition_sequence * tseq)545 void uc_mgr_free_transition_element(struct transition_sequence *tseq)
546 {
547 free(tseq->name);
548 uc_mgr_free_sequence(&tseq->transition_list);
549 free(tseq);
550 }
551
uc_mgr_free_transition(struct list_head * base)552 void uc_mgr_free_transition(struct list_head *base)
553 {
554 struct list_head *pos, *npos;
555 struct transition_sequence *tseq;
556
557 list_for_each_safe(pos, npos, base) {
558 tseq = list_entry(pos, struct transition_sequence, list);
559 list_del(&tseq->list);
560 uc_mgr_free_transition_element(tseq);
561 }
562 }
563
uc_mgr_free_dev_name_list(struct list_head * base)564 void uc_mgr_free_dev_name_list(struct list_head *base)
565 {
566 struct list_head *pos, *npos;
567 struct ucm_dev_name *dev;
568
569 list_for_each_safe(pos, npos, base) {
570 dev = list_entry(pos, struct ucm_dev_name, list);
571 list_del(&dev->list);
572 free(dev->name1);
573 free(dev->name2);
574 free(dev);
575 }
576 }
577
uc_mgr_free_modifier(struct list_head * base)578 void uc_mgr_free_modifier(struct list_head *base)
579 {
580 struct list_head *pos, *npos;
581 struct use_case_modifier *mod;
582
583 list_for_each_safe(pos, npos, base) {
584 mod = list_entry(pos, struct use_case_modifier, list);
585 free(mod->name);
586 free(mod->comment);
587 uc_mgr_free_sequence(&mod->enable_list);
588 uc_mgr_free_sequence(&mod->disable_list);
589 uc_mgr_free_transition(&mod->transition_list);
590 uc_mgr_free_dev_list(&mod->dev_list);
591 uc_mgr_free_value(&mod->value_list);
592 list_del(&mod->list);
593 free(mod);
594 }
595 }
596
uc_mgr_free_device(struct use_case_device * dev)597 void uc_mgr_free_device(struct use_case_device *dev)
598 {
599 free(dev->name);
600 free(dev->comment);
601 uc_mgr_free_sequence(&dev->enable_list);
602 uc_mgr_free_sequence(&dev->disable_list);
603 uc_mgr_free_transition(&dev->transition_list);
604 uc_mgr_free_dev_list(&dev->dev_list);
605 uc_mgr_free_value(&dev->value_list);
606 list_del(&dev->list);
607 free(dev);
608 }
609
uc_mgr_free_device_list(struct list_head * base)610 void uc_mgr_free_device_list(struct list_head *base)
611 {
612 struct list_head *pos, *npos;
613 struct use_case_device *dev;
614
615 list_for_each_safe(pos, npos, base) {
616 dev = list_entry(pos, struct use_case_device, list);
617 uc_mgr_free_device(dev);
618 }
619 }
620
uc_mgr_rename_device(struct use_case_verb * verb,const char * src,const char * dst)621 int uc_mgr_rename_device(struct use_case_verb *verb, const char *src,
622 const char *dst)
623 {
624 struct use_case_device *device;
625 struct list_head *pos, *npos;
626 char *dst1;
627
628 /* no errors when device is not found */
629 list_for_each_safe(pos, npos, &verb->device_list) {
630 device = list_entry(pos, struct use_case_device, list);
631 if (strcmp(device->name, src) == 0) {
632 dst1 = strdup(dst);
633 if (dst1 == NULL)
634 return -ENOMEM;
635 free(device->name);
636 device->name = dst1;
637 continue;
638 }
639 uc_mgr_rename_in_dev_list(&device->dev_list, src, dst);
640 }
641 return 0;
642 }
643
uc_mgr_remove_device(struct use_case_verb * verb,const char * name)644 int uc_mgr_remove_device(struct use_case_verb *verb, const char *name)
645 {
646 struct use_case_device *device;
647 struct list_head *pos, *npos;
648 int err, found = 0;
649
650 list_for_each_safe(pos, npos, &verb->device_list) {
651 device = list_entry(pos, struct use_case_device, list);
652 if (strcmp(device->name, name) == 0) {
653 uc_mgr_free_device(device);
654 found++;
655 continue;
656 }
657 err = uc_mgr_remove_from_dev_list(&device->dev_list, name);
658 if (err < 0 && err != -ENODEV)
659 return err;
660 if (err == 0)
661 found++;
662 }
663 return found == 0 ? -ENODEV : 0;
664 }
665
uc_mgr_get_variable(snd_use_case_mgr_t * uc_mgr,const char * name)666 const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, const char *name)
667 {
668 struct list_head *pos;
669 struct ucm_value *value;
670
671 list_for_each(pos, &uc_mgr->variable_list) {
672 value = list_entry(pos, struct ucm_value, list);
673 if (strcmp(value->name, name) == 0)
674 return value->data;
675 }
676 return NULL;
677 }
678
uc_mgr_set_variable(snd_use_case_mgr_t * uc_mgr,const char * name,const char * val)679 int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name,
680 const char *val)
681 {
682 struct list_head *pos;
683 struct ucm_value *curr;
684 char *val2;
685
686 list_for_each(pos, &uc_mgr->variable_list) {
687 curr = list_entry(pos, struct ucm_value, list);
688 if (strcmp(curr->name, name) == 0) {
689 val2 = strdup(val);
690 if (val2 == NULL)
691 return -ENOMEM;
692 free(curr->data);
693 curr->data = val2;
694 return 0;
695 }
696 }
697
698 curr = calloc(1, sizeof(struct ucm_value));
699 if (curr == NULL)
700 return -ENOMEM;
701 curr->name = strdup(name);
702 if (curr->name == NULL) {
703 free(curr);
704 return -ENOMEM;
705 }
706 curr->data = strdup(val);
707 if (curr->data == NULL) {
708 free(curr->name);
709 free(curr);
710 return -ENOMEM;
711 }
712 list_add_tail(&curr->list, &uc_mgr->variable_list);
713 return 0;
714 }
715
uc_mgr_delete_variable(snd_use_case_mgr_t * uc_mgr,const char * name)716 int uc_mgr_delete_variable(snd_use_case_mgr_t *uc_mgr, const char *name)
717 {
718 struct list_head *pos;
719 struct ucm_value *curr;
720
721 list_for_each(pos, &uc_mgr->variable_list) {
722 curr = list_entry(pos, struct ucm_value, list);
723 if (strcmp(curr->name, name) == 0) {
724 uc_mgr_free_value1(curr);
725 return 0;
726 }
727 }
728
729 return -ENOENT;
730 }
731
uc_mgr_free_verb(snd_use_case_mgr_t * uc_mgr)732 void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
733 {
734 struct list_head *pos, *npos;
735 struct use_case_verb *verb;
736
737 if (uc_mgr->local_config) {
738 snd_config_delete(uc_mgr->local_config);
739 uc_mgr->local_config = NULL;
740 }
741 if (uc_mgr->macros) {
742 snd_config_delete(uc_mgr->macros);
743 uc_mgr->macros = NULL;
744 }
745 list_for_each_safe(pos, npos, &uc_mgr->verb_list) {
746 verb = list_entry(pos, struct use_case_verb, list);
747 free(verb->name);
748 free(verb->comment);
749 uc_mgr_free_sequence(&verb->enable_list);
750 uc_mgr_free_sequence(&verb->disable_list);
751 uc_mgr_free_transition(&verb->transition_list);
752 uc_mgr_free_value(&verb->value_list);
753 uc_mgr_free_device_list(&verb->device_list);
754 uc_mgr_free_device_list(&verb->cmpt_device_list);
755 uc_mgr_free_modifier(&verb->modifier_list);
756 uc_mgr_free_dev_name_list(&verb->rename_list);
757 uc_mgr_free_dev_name_list(&verb->remove_list);
758 list_del(&verb->list);
759 free(verb);
760 }
761 uc_mgr_free_sequence(&uc_mgr->fixedboot_list);
762 uc_mgr_free_sequence(&uc_mgr->boot_list);
763 uc_mgr_free_sequence(&uc_mgr->default_list);
764 uc_mgr_free_value(&uc_mgr->value_list);
765 uc_mgr_free_value(&uc_mgr->variable_list);
766 free(uc_mgr->comment);
767 free(uc_mgr->conf_dir_name);
768 free(uc_mgr->conf_file_name);
769 uc_mgr->comment = NULL;
770 uc_mgr->conf_dir_name = NULL;
771 uc_mgr->conf_file_name = NULL;
772 uc_mgr->active_verb = NULL;
773 INIT_LIST_HEAD(&uc_mgr->active_devices);
774 INIT_LIST_HEAD(&uc_mgr->active_modifiers);
775 }
776
uc_mgr_free(snd_use_case_mgr_t * uc_mgr)777 void uc_mgr_free(snd_use_case_mgr_t *uc_mgr)
778 {
779 uc_mgr_free_verb(uc_mgr);
780 uc_mgr_free_ctl_list(uc_mgr);
781 free(uc_mgr->card_name);
782 free(uc_mgr);
783 }
784
785 /*
786 * UCM card list stuff
787 */
788
789 static pthread_mutex_t ucm_cards_mutex = PTHREAD_MUTEX_INITIALIZER;
790 static LIST_HEAD(ucm_cards);
791 static unsigned int ucm_card_assign;
792
uc_mgr_card_find(unsigned int card_number)793 static snd_use_case_mgr_t *uc_mgr_card_find(unsigned int card_number)
794 {
795 struct list_head *pos;
796 snd_use_case_mgr_t *uc_mgr;
797
798 list_for_each(pos, &ucm_cards) {
799 uc_mgr = list_entry(pos, snd_use_case_mgr_t, cards_list);
800 if (uc_mgr->ucm_card_number == card_number)
801 return uc_mgr;
802 }
803 return NULL;
804 }
805
uc_mgr_card_open(snd_use_case_mgr_t * uc_mgr)806 int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr)
807 {
808 unsigned int prev;
809
810 pthread_mutex_lock(&ucm_cards_mutex);
811 prev = ucm_card_assign++;
812 while (uc_mgr_card_find(ucm_card_assign)) {
813 ucm_card_assign++;
814 ucm_card_assign &= 0xffff;
815 /* avoid zero card instance number */
816 if (ucm_card_assign == 0)
817 ucm_card_assign++;
818 if (ucm_card_assign == prev) {
819 pthread_mutex_unlock(&ucm_cards_mutex);
820 return -ENOMEM;
821 }
822 }
823 uc_mgr->ucm_card_number = ucm_card_assign;
824 list_add(&uc_mgr->cards_list, &ucm_cards);
825 pthread_mutex_unlock(&ucm_cards_mutex);
826 return 0;
827 }
828
uc_mgr_card_close(snd_use_case_mgr_t * uc_mgr)829 void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr)
830 {
831 pthread_mutex_lock(&ucm_cards_mutex);
832 list_del(&uc_mgr->cards_list);
833 pthread_mutex_unlock(&ucm_cards_mutex);
834 }
835
836 /**
837 * \brief Get library configuration based on the private ALSA device name
838 * \param name[in] ALSA device name
839 * \retval config A configuration tree or NULL
840 *
841 * The returned configuration (non-NULL) should be unreferenced using
842 * snd_config_unref() call.
843 */
uc_mgr_alibcfg_by_device(snd_config_t ** top,const char * name)844 const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name)
845 {
846 char buf[5];
847 long card_num;
848 snd_config_t *config;
849 snd_use_case_mgr_t *uc_mgr;
850 int err;
851
852 if (strncmp(name, "_ucm", 4) || strlen(name) < 12 || name[8] != '.')
853 return NULL;
854 strncpy(buf, name + 4, 4);
855 buf[4] = '\0';
856 err = safe_strtol_base(buf, &card_num, 16);
857 if (err < 0 || card_num < 0 || card_num > 0xffff)
858 return NULL;
859 config = NULL;
860 pthread_mutex_lock(&ucm_cards_mutex);
861 uc_mgr = uc_mgr_card_find(card_num);
862 /* non-empty configs are accepted only */
863 if (uc_mgr_has_local_config(uc_mgr)) {
864 config = uc_mgr->local_config;
865 snd_config_ref(config);
866 }
867 pthread_mutex_unlock(&ucm_cards_mutex);
868 if (!config)
869 return NULL;
870 *top = config;
871 return name + 9;
872 }
873