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 #include "../control/control_local.h"
35 #include <stdbool.h>
36 #include <ctype.h>
37 #include <stdarg.h>
38 #include <pthread.h>
39 #include <sys/stat.h>
40 #include <sys/wait.h>
41 #include <limits.h>
42
43 /*
44 * misc
45 */
46
47 static int get_value(snd_use_case_mgr_t *uc_mgr,
48 const char *identifier,
49 char **value,
50 const char *mod_dev_name,
51 const char *verb_name,
52 int exact);
53 static int get_value1(snd_use_case_mgr_t *uc_mgr, char **value,
54 struct list_head *value_list, const char *identifier);
55 static int get_value3(snd_use_case_mgr_t *uc_mgr,
56 char **value,
57 const char *identifier,
58 struct list_head *value_list1,
59 struct list_head *value_list2,
60 struct list_head *value_list3);
61
62 static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
63 struct use_case_verb *verb,
64 struct list_head *seq,
65 struct list_head *value_list1,
66 struct list_head *value_list2,
67 struct list_head *value_list3);
68
69 static int execute_component_seq(snd_use_case_mgr_t *uc_mgr,
70 struct component_sequence *cmpt_seq,
71 struct list_head *value_list1,
72 struct list_head *value_list2,
73 struct list_head *value_list3,
74 char *cdev);
75
76 static inline struct use_case_device *
77 find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
78 const char *device_name, int check_supported);
79
check_identifier(const char *identifier, const char *prefix)80 static int check_identifier(const char *identifier, const char *prefix)
81 {
82 int len;
83
84 len = strlen(prefix);
85 if (strncmp(identifier, prefix, len) != 0)
86 return 0;
87
88 if (identifier[len] == 0 || identifier[len] == '/')
89 return 1;
90
91 return 0;
92 }
93
list_count(struct list_head *list)94 static int list_count(struct list_head *list)
95 {
96 struct list_head *pos;
97 int count = 0;
98
99 list_for_each(pos, list) {
100 count += 1;
101 }
102 return count;
103 }
104
alloc_str_list(struct list_head *list, int mult, char **result[])105 static int alloc_str_list(struct list_head *list, int mult, char **result[])
106 {
107 char **res;
108 int cnt;
109
110 cnt = list_count(list) * mult;
111 if (cnt == 0) {
112 *result = NULL;
113 return cnt;
114 }
115 res = calloc(mult, cnt * sizeof(char *));
116 if (res == NULL)
117 return -ENOMEM;
118 *result = res;
119 return cnt;
120 }
121
122 /**
123 * \brief Create an identifier
124 * \param fmt Format (sprintf like)
125 * \param ... Optional arguments for sprintf like format
126 * \return Allocated string identifier or NULL on error
127 */
snd_use_case_identifier(const char *fmt, ...)128 char *snd_use_case_identifier(const char *fmt, ...)
129 {
130 char *str, *res;
131 int size = strlen(fmt) + 512;
132 va_list args;
133
134 str = malloc(size);
135 if (str == NULL)
136 return NULL;
137 va_start(args, fmt);
138 vsnprintf(str, size, fmt, args);
139 va_end(args);
140 str[size-1] = '\0';
141 res = realloc(str, strlen(str) + 1);
142 if (res)
143 return res;
144 return str;
145 }
146
147 /**
148 * \brief Free a string list
149 * \param list The string list to free
150 * \param items Count of strings
151 * \return Zero if success, otherwise a negative error code
152 */
snd_use_case_free_list(const char *list[], int items)153 int snd_use_case_free_list(const char *list[], int items)
154 {
155 int i;
156 if (list == NULL)
157 return 0;
158 for (i = 0; i < items; i++)
159 free((void *)list[i]);
160 free(list);
161 return 0;
162 }
163
read_tlv_file(unsigned int **res, const char *filepath)164 static int read_tlv_file(unsigned int **res,
165 const char *filepath)
166 {
167 int err = 0;
168 int fd;
169 struct stat64 st;
170 size_t sz;
171 ssize_t sz_read;
172 struct snd_ctl_tlv *tlv;
173
174 fd = open(filepath, O_RDONLY);
175 if (fd < 0) {
176 err = -errno;
177 return err;
178 }
179 if (fstat64(fd, &st) == -1) {
180 err = -errno;
181 goto __fail;
182 }
183 sz = st.st_size;
184 if (sz > 16 * 1024 * 1024 || sz < 8 || sz % 4) {
185 uc_error("File size should be less than 16 MB "
186 "and multiple of 4");
187 err = -EINVAL;
188 goto __fail;
189 }
190 *res = malloc(sz);
191 if (res == NULL) {
192 err = -ENOMEM;
193 goto __fail;
194 }
195 sz_read = read(fd, *res, sz);
196 if (sz_read < 0 || (size_t)sz_read != sz) {
197 err = -EIO;
198 free(*res);
199 *res = NULL;
200 }
201 /* Check if the tlv file specifies valid size. */
202 tlv = (struct snd_ctl_tlv *)(*res);
203 if (tlv->length + 2 * sizeof(unsigned int) != sz) {
204 uc_error("Invalid tlv size: %d", tlv->length);
205 err = -EINVAL;
206 free(*res);
207 *res = NULL;
208 }
209
210 __fail:
211 close(fd);
212 return err;
213 }
214
binary_file_parse(snd_ctl_elem_value_t *dst, snd_ctl_elem_info_t *info, const char *filepath)215 static int binary_file_parse(snd_ctl_elem_value_t *dst,
216 snd_ctl_elem_info_t *info,
217 const char *filepath)
218 {
219 int err = 0;
220 int fd;
221 struct stat64 st;
222 size_t sz;
223 ssize_t sz_read;
224 char *res;
225 snd_ctl_elem_type_t type;
226 unsigned int idx, count;
227
228 type = snd_ctl_elem_info_get_type(info);
229 if (type != SND_CTL_ELEM_TYPE_BYTES) {
230 uc_error("only support byte type!");
231 err = -EINVAL;
232 return err;
233 }
234 fd = open(filepath, O_RDONLY);
235 if (fd < 0) {
236 err = -errno;
237 return err;
238 }
239 if (stat64(filepath, &st) == -1) {
240 err = -errno;
241 goto __fail;
242 }
243 sz = st.st_size;
244 count = snd_ctl_elem_info_get_count(info);
245 if (sz != count || sz > sizeof(dst->value.bytes)) {
246 uc_error("invalid parameter size %d!", sz);
247 err = -EINVAL;
248 goto __fail;
249 }
250 res = malloc(sz);
251 if (res == NULL) {
252 err = -ENOMEM;
253 goto __fail;
254 }
255 sz_read = read(fd, res, sz);
256 if (sz_read < 0 || (size_t)sz_read != sz) {
257 err = -errno;
258 goto __fail_read;
259 }
260 for (idx = 0; idx < sz; idx++)
261 snd_ctl_elem_value_set_byte(dst, idx, *(res + idx));
262 __fail_read:
263 free(res);
264 __fail:
265 close(fd);
266 return err;
267 }
268
parse_type(const char *p, const char *prefix, size_t len, snd_ctl_elem_info_t *info)269 static const char *parse_type(const char *p, const char *prefix, size_t len,
270 snd_ctl_elem_info_t *info)
271 {
272 if (strncasecmp(p, prefix, len))
273 return p;
274 p += len;
275 if (info->type != SND_CTL_ELEM_TYPE_NONE)
276 return NULL;
277 if (strncasecmp(p, "bool", sizeof("bool") - 1) == 0)
278 info->type = SND_CTL_ELEM_TYPE_BOOLEAN;
279 else if (strncasecmp(p, "integer64", sizeof("integer64") - 1) == 0)
280 info->type = SND_CTL_ELEM_TYPE_INTEGER64;
281 else if (strncasecmp(p, "int64", sizeof("int64") - 1) == 0)
282 info->type = SND_CTL_ELEM_TYPE_INTEGER64;
283 else if (strncasecmp(p, "int", sizeof("int") - 1) == 0)
284 info->type = SND_CTL_ELEM_TYPE_INTEGER;
285 else if (strncasecmp(p, "enum", sizeof("enum") - 1) == 0)
286 info->type = SND_CTL_ELEM_TYPE_ENUMERATED;
287 else if (strncasecmp(p, "bytes", sizeof("bytes") - 1) == 0)
288 info->type = SND_CTL_ELEM_TYPE_BYTES;
289 else
290 return NULL;
291 while (isalpha(*p))
292 p++;
293 return p;
294 }
295
parse_uint(const char *p, const char *prefix, size_t len, unsigned int min, unsigned int max, unsigned int *rval)296 static const char *parse_uint(const char *p, const char *prefix, size_t len,
297 unsigned int min, unsigned int max, unsigned int *rval)
298 {
299 long v;
300 char *end;
301
302 if (strncasecmp(p, prefix, len))
303 return p;
304 p += len;
305 v = strtol(p, &end, 0);
306 if (*end != '\0' && *end != ' ' && *end != ',') {
307 uc_error("unable to parse '%s'", prefix);
308 return NULL;
309 }
310 if ((unsigned int)v < min || (unsigned int)v > max) {
311 uc_error("value '%s' out of range %u-%u %(%ld)", min, max, v);
312 return NULL;
313 }
314 *rval = v;
315 return end;
316 }
317
parse_labels(const char *p, const char *prefix, size_t len, snd_ctl_elem_info_t *info)318 static const char *parse_labels(const char *p, const char *prefix, size_t len,
319 snd_ctl_elem_info_t *info)
320 {
321 const char *s;
322 char *buf, *bp;
323 size_t l;
324 int c;
325
326 if (info->type != SND_CTL_ELEM_TYPE_ENUMERATED)
327 return NULL;
328 if (strncasecmp(p, prefix, len))
329 return p;
330 p += len;
331 s = p;
332 c = *s;
333 l = 0;
334 if (c == '\'' || c == '\"') {
335 s++;
336 while (*s && *s != c) {
337 s++, l++;
338 }
339 if (*s == c)
340 s++;
341 } else {
342 while (*s && *s != ',')
343 l++;
344 }
345 if (l == 0)
346 return NULL;
347 buf = malloc(l + 1);
348 if (buf == NULL)
349 return NULL;
350 memcpy(buf, p + ((c == '\'' || c == '\"') ? 1 : 0), l);
351 buf[l] = '\0';
352 info->value.enumerated.items = 1;
353 for (bp = buf; *bp; bp++) {
354 if (*bp == ';') {
355 if (bp == buf || bp[1] == ';') {
356 free(buf);
357 return NULL;
358 }
359 info->value.enumerated.items++;
360 *bp = '\0';
361 }
362 }
363 info->value.enumerated.names_ptr = (uintptr_t)buf;
364 info->value.enumerated.names_length = l + 1;
365 return s;
366 }
367
parse_cset_new_info(snd_ctl_elem_info_t *info, const char *s, const char **pos)368 static int parse_cset_new_info(snd_ctl_elem_info_t *info, const char *s, const char **pos)
369 {
370 const char *p = s, *op;
371
372 info->count = 1;
373 while (*s) {
374 op = p;
375 p = parse_type(p, "type=", sizeof("type=") - 1, info);
376 if (p != op)
377 goto next;
378 p = parse_uint(p, "elements=", sizeof("elements=") - 1, 1, 128, (unsigned int *)&info->owner);
379 if (p != op)
380 goto next;
381 p = parse_uint(p, "count=", sizeof("count=") - 1, 1, 128, &info->count);
382 if (p != op)
383 goto next;
384 p = parse_labels(p, "labels=", sizeof("labels=") - 1, info);
385 next:
386 if (p == NULL)
387 goto er;
388 if (*p == ',')
389 p++;
390 if (isspace(*p))
391 break;
392 if (op == p)
393 goto er;
394 }
395 *pos = p;
396 return 0;
397 er:
398 uc_error("unknown syntax '%s'", p);
399 return -EINVAL;
400 }
401
execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type)402 static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type)
403 {
404 const char *pos;
405 int err;
406 snd_ctl_elem_id_t *id;
407 snd_ctl_elem_value_t *value;
408 snd_ctl_elem_info_t *info, *info2 = NULL;
409 unsigned int *res = NULL;
410
411 snd_ctl_elem_id_malloc(&id);
412 snd_ctl_elem_value_malloc(&value);
413 snd_ctl_elem_info_malloc(&info);
414
415 err = __snd_ctl_ascii_elem_id_parse(id, cset, &pos);
416 if (err < 0)
417 goto __fail;
418 while (*pos && isspace(*pos))
419 pos++;
420 if (type == SEQUENCE_ELEMENT_TYPE_CSET_NEW) {
421 snd_ctl_elem_info_malloc(&info2);
422 snd_ctl_elem_info_set_id(info2, id);
423 err = parse_cset_new_info(info2, pos, &pos);
424 if (err < 0 || !*pos) {
425 uc_error("undefined or wrong id config for cset-new", cset);
426 err = -EINVAL;
427 goto __fail;
428 }
429 while (*pos && isspace(*pos))
430 pos++;
431 }
432 if (!*pos) {
433 if (type != SEQUENCE_ELEMENT_TYPE_CTL_REMOVE) {
434 uc_error("undefined value for cset >%s<", cset);
435 err = -EINVAL;
436 goto __fail;
437 }
438 } else if (type == SEQUENCE_ELEMENT_TYPE_CTL_REMOVE) {
439 uc_error("extra value for ctl-remove >%s<", cset);
440 err = -EINVAL;
441 goto __fail;
442 }
443
444 snd_ctl_elem_info_set_id(info, id);
445 err = snd_ctl_elem_info(ctl, info);
446 if (type == SEQUENCE_ELEMENT_TYPE_CSET_NEW ||
447 type == SEQUENCE_ELEMENT_TYPE_CTL_REMOVE) {
448 if (err >= 0) {
449 err = snd_ctl_elem_remove(ctl, id);
450 if (err < 0) {
451 uc_error("unable to remove control");
452 err = -EINVAL;
453 goto __fail;
454 }
455 }
456 if (type == SEQUENCE_ELEMENT_TYPE_CTL_REMOVE)
457 goto __ok;
458 err = __snd_ctl_add_elem_set(ctl, info2, info2->owner, info2->count);
459 if (err < 0) {
460 uc_error("unable to create new control");
461 goto __fail;
462 }
463 /* new id copy */
464 snd_ctl_elem_info_get_id(info2, id);
465 snd_ctl_elem_info_set_id(info, id);
466 } else if (err < 0)
467 goto __fail;
468 if (type == SEQUENCE_ELEMENT_TYPE_CSET_TLV) {
469 if (!snd_ctl_elem_info_is_tlv_writable(info)) {
470 err = -EINVAL;
471 goto __fail;
472 }
473 err = read_tlv_file(&res, pos);
474 if (err < 0)
475 goto __fail;
476 err = snd_ctl_elem_tlv_write(ctl, id, res);
477 if (err < 0)
478 goto __fail;
479 } else {
480 snd_ctl_elem_value_set_id(value, id);
481 err = snd_ctl_elem_read(ctl, value);
482 if (err < 0)
483 goto __fail;
484 if (type == SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE)
485 err = binary_file_parse(value, info, pos);
486 else
487 err = snd_ctl_ascii_value_parse(ctl, value, info, pos);
488 if (err < 0)
489 goto __fail;
490 err = snd_ctl_elem_write(ctl, value);
491 if (err < 0)
492 goto __fail;
493 if (type == SEQUENCE_ELEMENT_TYPE_CSET_NEW) {
494 unsigned int idx;
495 for (idx = 1; idx < (unsigned int)info2->owner; idx++) {
496 value->id.numid += 1;
497 err = snd_ctl_elem_write(ctl, value);
498 if (err < 0)
499 goto __fail;
500 }
501 }
502 }
503 __ok:
504 err = 0;
505 __fail:
506 free(id);
507 free(value);
508 if (info2) {
509 if (info2->type == SND_CTL_ELEM_TYPE_ENUMERATED)
510 free((void *)(size_t)info2->value.enumerated.names_ptr);
511 free(info2);
512 }
513 free(info);
514 free(res);
515
516 return err;
517 }
518
execute_sysw(const char *sysw)519 static int execute_sysw(const char *sysw)
520 {
521 char path[PATH_MAX];
522 const char *e;
523 char *s, *value;
524 ssize_t wlen;
525 size_t len;
526 int fd, myerrno;
527 bool ignore_error = false;
528
529 if (sysw == NULL || *sysw == '\0')
530 return 0;
531
532 if (sysw[0] == '-') {
533 ignore_error = true;
534 sysw++;
535 }
536
537 if (sysw[0] == ':')
538 return -EINVAL;
539
540 s = strdup(sysw[0] != '/' ? sysw : sysw + 1);
541 if (s == NULL)
542 return -ENOMEM;
543
544 value = strchr(s, ':');
545 if (!value) {
546 free(s);
547 return -EINVAL;
548 }
549 *value = '\0';
550 value++;
551 len = strlen(value);
552 if (len < 1) {
553 free(s);
554 return -EINVAL;
555 }
556
557 e = uc_mgr_sysfs_root();
558 if (e == NULL) {
559 free(s);
560 return -EINVAL;
561 }
562 snprintf(path, sizeof(path), "%s/%s", e, s);
563
564 fd = open(path, O_WRONLY|O_CLOEXEC);
565 if (fd < 0) {
566 free(s);
567 if (ignore_error)
568 return 0;
569 uc_error("unable to open '%s' for write", path);
570 return -EINVAL;
571 }
572 wlen = write(fd, value, len);
573 myerrno = errno;
574 close(fd);
575
576 if (ignore_error)
577 goto __end;
578
579 if (wlen != (ssize_t)len) {
580 uc_error("unable to write '%s' to '%s': %s", value, path, strerror(myerrno));
581 free(s);
582 return -EINVAL;
583 }
584
585 __end:
586 free(s);
587 return 0;
588 }
589
590 int _snd_config_save_node_value(snd_config_t *n, snd_output_t *out, unsigned int level);
591
execute_cfgsave(snd_use_case_mgr_t *uc_mgr, const char *filename)592 static int execute_cfgsave(snd_use_case_mgr_t *uc_mgr, const char *filename)
593 {
594 snd_config_t *config = uc_mgr->local_config;
595 char *file, *root;
596 snd_output_t *out;
597 bool with_root = false;
598 int err = 0;
599
600 file = strdup(filename);
601 if (!file)
602 return -ENOMEM;
603 root = strchr(file, ':');
604 if (config && root) {
605 *root++ = '\0';
606 if (*root == '+') {
607 with_root = true;
608 root++;
609 }
610 err = snd_config_search(config, root, &config);
611 if (err < 0) {
612 uc_error("Unable to find subtree '%s'", root);
613 goto _err;
614 }
615 }
616
617 err = snd_output_stdio_open(&out, file, "w+");
618 if (err < 0) {
619 uc_error("unable to open file '%s': %s", file, snd_strerror(err));
620 goto _err;
621 }
622 if (!config || snd_config_is_empty(config)) {
623 snd_output_close(out);
624 goto _err;
625 }
626 if (with_root) {
627 snd_output_printf(out, "%s ", root);
628 err = _snd_config_save_node_value(config, out, 0);
629 } else {
630 err = snd_config_save(config, out);
631 }
632 snd_output_close(out);
633 if (err < 0) {
634 uc_error("unable to save configuration: %s", snd_strerror(err));
635 goto _err;
636 }
637 _err:
638 free(file);
639 return err;
640 }
641
rewrite_device_value(snd_use_case_mgr_t *uc_mgr, const char *name, char **value)642 static int rewrite_device_value(snd_use_case_mgr_t *uc_mgr, const char *name, char **value)
643 {
644 char *sval;
645 size_t l;
646 static const char **s, *_prefix[] = {
647 "PlaybackCTL",
648 "CaptureCTL",
649 "PlaybackMixer",
650 "CaptureMixer",
651 "PlaybackPCM",
652 "CapturePCM",
653 NULL
654 };
655
656 if (!uc_mgr_has_local_config(uc_mgr))
657 return 0;
658 for (s = _prefix; *s && *value; s++) {
659 if (strcmp(*s, name) != 0)
660 continue;
661 l = strlen(*value) + 9 + 1;
662 sval = malloc(l);
663 if (sval == NULL) {
664 free(*value);
665 *value = NULL;
666 return -ENOMEM;
667 }
668 snprintf(sval, l, "_ucm%04X.%s", uc_mgr->ucm_card_number, *value);
669 free(*value);
670 *value = sval;
671 break;
672 }
673 return 0;
674 }
675
run_device_sequence(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, const char *name, bool enable)676 static int run_device_sequence(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
677 const char *name, bool enable)
678 {
679 struct use_case_device *device;
680
681 if (verb == NULL) {
682 uc_error("error: enadev2 / disdev2 must be executed inside the verb context");
683 return -ENOENT;
684 }
685
686 device = find_device(uc_mgr, verb, name, 0);
687 if (device == NULL) {
688 uc_error("error: unable to find device '%s'\n", name);
689 return -ENOENT;
690 }
691
692 return execute_sequence(uc_mgr, verb,
693 enable ? &device->enable_list : &device->disable_list,
694 &device->value_list,
695 &verb->value_list,
696 &uc_mgr->value_list);
697 }
698
run_device_all_sequence(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)699 static int run_device_all_sequence(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
700 {
701 struct use_case_device *device;
702 struct list_head *pos;
703 int err;
704
705 if (verb == NULL) {
706 uc_error("error: disdevall must be executed inside the verb context");
707 return -ENOENT;
708 }
709
710 list_for_each(pos, &verb->device_list) {
711 device = list_entry(pos, struct use_case_device, list);
712
713 err = execute_sequence(uc_mgr, verb,
714 &device->disable_list,
715 &device->value_list,
716 &verb->value_list,
717 &uc_mgr->value_list);
718 if (err < 0)
719 return err;
720 }
721 return 0;
722 }
723
724 /**
725 * \brief Execute the sequence
726 * \param uc_mgr Use case manager
727 * \param seq Sequence
728 * \return zero on success, otherwise a negative error code
729 */
execute_sequence(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, struct list_head *seq, struct list_head *value_list1, struct list_head *value_list2, struct list_head *value_list3)730 static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
731 struct use_case_verb *verb,
732 struct list_head *seq,
733 struct list_head *value_list1,
734 struct list_head *value_list2,
735 struct list_head *value_list3)
736 {
737 struct list_head *pos;
738 struct sequence_element *s;
739 char *cdev = NULL;
740 snd_ctl_t *ctl = NULL;
741 struct ctl_list *ctl_list;
742 bool ignore_error;
743 int err = 0;
744
745 if (uc_mgr->sequence_hops > 100) {
746 uc_error("error: too many inner sequences!");
747 return -EINVAL;
748 }
749 uc_mgr->sequence_hops++;
750 list_for_each(pos, seq) {
751 s = list_entry(pos, struct sequence_element, list);
752 switch (s->type) {
753 case SEQUENCE_ELEMENT_TYPE_CDEV:
754 cdev = strdup(s->data.cdev);
755 if (cdev == NULL)
756 goto __fail_nomem;
757 if (rewrite_device_value(uc_mgr, "PlaybackCTL", &cdev))
758 goto __fail_nomem;
759 break;
760 case SEQUENCE_ELEMENT_TYPE_CSET:
761 case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
762 case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
763 case SEQUENCE_ELEMENT_TYPE_CSET_NEW:
764 case SEQUENCE_ELEMENT_TYPE_CTL_REMOVE:
765 if (cdev == NULL && uc_mgr->in_component_domain) {
766 /* For sequence of a component device, use
767 * its parent's cdev stored by ucm manager.
768 */
769 if (uc_mgr->cdev == NULL) {
770 uc_error("cdev is not defined!");
771 return err;
772 }
773
774 cdev = strndup(uc_mgr->cdev, PATH_MAX);
775 if (!cdev)
776 return -ENOMEM;
777 } else if (cdev == NULL) {
778 char *playback_ctl = NULL;
779 char *capture_ctl = NULL;
780
781 err = get_value3(uc_mgr, &playback_ctl, "PlaybackCTL",
782 value_list1,
783 value_list2,
784 value_list3);
785 if (err < 0 && err != -ENOENT) {
786 uc_error("cdev is not defined!");
787 return err;
788 }
789 err = get_value3(uc_mgr, &capture_ctl, "CaptureCTL",
790 value_list1,
791 value_list2,
792 value_list3);
793 if (err < 0 && err != -ENOENT) {
794 free(playback_ctl);
795 uc_error("cdev is not defined!");
796 return err;
797 }
798 if (playback_ctl == NULL &&
799 capture_ctl == NULL) {
800 uc_error("cdev is not defined!");
801 return -EINVAL;
802 }
803 if (playback_ctl != NULL &&
804 capture_ctl != NULL &&
805 strcmp(playback_ctl, capture_ctl) != 0) {
806 free(playback_ctl);
807 free(capture_ctl);
808 uc_error("cdev is not equal for playback and capture!");
809 return -EINVAL;
810 }
811 if (playback_ctl != NULL) {
812 cdev = playback_ctl;
813 free(capture_ctl);
814 } else {
815 cdev = capture_ctl;
816 }
817 }
818 if (ctl == NULL) {
819 err = uc_mgr_open_ctl(uc_mgr, &ctl_list, cdev, 1);
820 if (err < 0) {
821 uc_error("unable to open ctl device '%s'", cdev);
822 goto __fail;
823 }
824 ctl = ctl_list->ctl;
825 }
826 err = execute_cset(ctl, s->data.cset, s->type);
827 if (err < 0) {
828 uc_error("unable to execute cset '%s'", s->data.cset);
829 goto __fail;
830 }
831 break;
832 case SEQUENCE_ELEMENT_TYPE_SYSSET:
833 err = execute_sysw(s->data.sysw);
834 if (err < 0)
835 goto __fail;
836 break;
837 case SEQUENCE_ELEMENT_TYPE_SLEEP:
838 usleep(s->data.sleep);
839 break;
840 case SEQUENCE_ELEMENT_TYPE_EXEC:
841 if (s->data.exec == NULL)
842 break;
843 ignore_error = s->data.exec[0] == '-';
844 err = uc_mgr_exec(s->data.exec + (ignore_error ? 1 : 0));
845 if (ignore_error == false && err != 0) {
846 uc_error("exec '%s' failed (exit code %d)", s->data.exec, err);
847 goto __fail;
848 }
849 break;
850 case SEQUENCE_ELEMENT_TYPE_SHELL:
851 if (s->data.exec == NULL)
852 break;
853 ignore_error = s->data.exec[0] == '-';
854 shell_retry:
855 err = system(s->data.exec + (ignore_error ? 1 : 0));
856 if (WIFSIGNALED(err)) {
857 err = -EINTR;
858 } if (WIFEXITED(err)) {
859 if (ignore_error == false && WEXITSTATUS(err) != 0) {
860 uc_error("command '%s' failed (exit code %d)", s->data.exec, WEXITSTATUS(err));
861 err = -EINVAL;
862 goto __fail;
863 }
864 } else if (err < 0) {
865 if (errno == EAGAIN)
866 goto shell_retry;
867 err = -errno;
868 goto __fail;
869 }
870 break;
871 case SEQUENCE_ELEMENT_TYPE_CMPT_SEQ:
872 /* Execute enable or disable sequence of a component
873 * device. Pass the cdev defined by the machine device.
874 */
875 err = execute_component_seq(uc_mgr,
876 &s->data.cmpt_seq,
877 value_list1,
878 value_list2,
879 value_list3,
880 cdev);
881 if (err < 0)
882 goto __fail;
883 break;
884 case SEQUENCE_ELEMENT_TYPE_CFGSAVE:
885 err = execute_cfgsave(uc_mgr, s->data.cfgsave);
886 if (err < 0)
887 goto __fail;
888 break;
889 case SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ:
890 case SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ:
891 err = run_device_sequence(uc_mgr, verb, s->data.device,
892 s->type == SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ);
893 if (err < 0)
894 goto __fail;
895 break;
896 case SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_ALL:
897 err = run_device_all_sequence(uc_mgr, verb);
898 if (err < 0)
899 goto __fail;
900 break;
901 default:
902 uc_error("unknown sequence command %i", s->type);
903 break;
904 }
905 }
906 free(cdev);
907 uc_mgr->sequence_hops--;
908 return 0;
909 __fail_nomem:
910 err = -ENOMEM;
911 __fail:
912 free(cdev);
913 uc_mgr->sequence_hops--;
914 return err;
915
916 }
917
918 /* Execute enable or disable sequence of a component device.
919 *
920 * For a component device (a codec or embedded DSP), its sequence doesn't
921 * specify the sound card device 'cdev', because a component can be reused
922 * by different sound cards (machines). So when executing its sequence, a
923 * parameter 'cdev' is used to pass cdev defined by the sequence of its
924 * parent, the machine device. UCM manger will store the cdev when entering
925 * the component domain.
926 */
execute_component_seq(snd_use_case_mgr_t *uc_mgr, struct component_sequence *cmpt_seq, struct list_head *value_list1 ATTRIBUTE_UNUSED, struct list_head *value_list2 ATTRIBUTE_UNUSED, struct list_head *value_list3 ATTRIBUTE_UNUSED, char *cdev)927 static int execute_component_seq(snd_use_case_mgr_t *uc_mgr,
928 struct component_sequence *cmpt_seq,
929 struct list_head *value_list1 ATTRIBUTE_UNUSED,
930 struct list_head *value_list2 ATTRIBUTE_UNUSED,
931 struct list_head *value_list3 ATTRIBUTE_UNUSED,
932 char *cdev)
933 {
934 struct use_case_device *device = cmpt_seq->device;
935 struct list_head *seq;
936 int err;
937
938 /* enter component domain and store cdev for the component */
939 uc_mgr->in_component_domain = 1;
940 uc_mgr->cdev = cdev;
941
942 /* choose enable or disable sequence of the component device */
943 if (cmpt_seq->enable)
944 seq = &device->enable_list;
945 else
946 seq = &device->disable_list;
947
948 /* excecute the sequence of the component dev */
949 err = execute_sequence(uc_mgr, uc_mgr->active_verb, seq,
950 &device->value_list,
951 &uc_mgr->active_verb->value_list,
952 &uc_mgr->value_list);
953
954 /* exit component domain and clear cdev */
955 uc_mgr->in_component_domain = 0;
956 uc_mgr->cdev = NULL;
957
958 return err;
959 }
960
add_auto_value(snd_use_case_mgr_t *uc_mgr, const char *key, char *value)961 static int add_auto_value(snd_use_case_mgr_t *uc_mgr, const char *key, char *value)
962 {
963 char *s;
964 int err;
965
966 err = get_value1(uc_mgr, &value, &uc_mgr->value_list, key);
967 if (err == -ENOENT) {
968 s = strdup(value);
969 if (s == NULL)
970 return -ENOMEM;
971 return uc_mgr_add_value(&uc_mgr->value_list, key, s);
972 } else if (err < 0) {
973 return err;
974 }
975 free(value);
976 return 0;
977 }
978
add_auto_values(snd_use_case_mgr_t *uc_mgr)979 static int add_auto_values(snd_use_case_mgr_t *uc_mgr)
980 {
981 struct ctl_list *ctl_list;
982 const char *id;
983 char buf[40];
984 int err;
985
986 ctl_list = uc_mgr_get_master_ctl(uc_mgr);
987 if (ctl_list) {
988 id = snd_ctl_card_info_get_id(ctl_list->ctl_info);
989 snprintf(buf, sizeof(buf), "hw:%s", id);
990 err = add_auto_value(uc_mgr, "PlaybackCTL", buf);
991 if (err < 0)
992 return err;
993 err = add_auto_value(uc_mgr, "CaptureCTL", buf);
994 if (err < 0)
995 return err;
996 }
997 return 0;
998 }
999
1000 /**
1001 * \brief execute default commands
1002 * \param uc_mgr Use case manager
1003 * \param force Force run
1004 * \return zero on success, otherwise a negative error code
1005 */
set_defaults(snd_use_case_mgr_t *uc_mgr, bool force)1006 static int set_defaults(snd_use_case_mgr_t *uc_mgr, bool force)
1007 {
1008 int err;
1009
1010 if (!force && uc_mgr->default_list_executed)
1011 return 0;
1012 err = execute_sequence(uc_mgr, NULL, &uc_mgr->default_list,
1013 &uc_mgr->value_list, NULL, NULL);
1014 if (err < 0) {
1015 uc_error("Unable to execute default sequence");
1016 return err;
1017 }
1018 uc_mgr->default_list_executed = 1;
1019 return 0;
1020 }
1021
1022 /**
1023 * \brief Import master config and execute the default sequence
1024 * \param uc_mgr Use case manager
1025 * \return zero on success, otherwise a negative error code
1026 */
import_master_config(snd_use_case_mgr_t *uc_mgr)1027 static int import_master_config(snd_use_case_mgr_t *uc_mgr)
1028 {
1029 int err;
1030
1031 err = uc_mgr_import_master_config(uc_mgr);
1032 if (err < 0)
1033 return err;
1034 return add_auto_values(uc_mgr);
1035 }
1036
1037 /**
1038 * \brief Check, if the UCM configuration is empty
1039 * \param uc_mgr Use case Manager
1040 * \return zero on success, otherwise a negative error code
1041 */
check_empty_configuration(snd_use_case_mgr_t *uc_mgr)1042 static int check_empty_configuration(snd_use_case_mgr_t *uc_mgr)
1043 {
1044 int err;
1045 char *value;
1046
1047 err = get_value(uc_mgr, "Linked", &value, NULL, NULL, 1);
1048 if (err >= 0) {
1049 err = strcasecmp(value, "true") == 0 ||
1050 strcmp(value, "1") == 0;
1051 free(value);
1052 if (err)
1053 return 0;
1054 }
1055 if (!list_empty(&uc_mgr->verb_list))
1056 return 0;
1057 if (!list_empty(&uc_mgr->fixedboot_list))
1058 return 0;
1059 if (!list_empty(&uc_mgr->boot_list))
1060 return 0;
1061 return -ENXIO;
1062 }
1063
1064 /**
1065 * \brief Universal find - string in a list
1066 * \param list List of structures
1067 * \param offset Offset of list structure
1068 * \param soffset Offset of string structure
1069 * \param match String to match
1070 * \return structure on success, otherwise a NULL (not found)
1071 */
find0(struct list_head *list, unsigned long offset, unsigned long soffset, const char *match)1072 static void *find0(struct list_head *list,
1073 unsigned long offset,
1074 unsigned long soffset,
1075 const char *match)
1076 {
1077 struct list_head *pos;
1078 char *ptr, *str;
1079
1080 list_for_each(pos, list) {
1081 ptr = list_entry_offset(pos, char, offset);
1082 str = *((char **)(ptr + soffset));
1083 if (strcmp(str, match) == 0)
1084 return ptr;
1085 }
1086 return NULL;
1087 }
1088
1089 #define find(list, type, member, value, match) \
1090 find0(list, (unsigned long)(&((type *)0)->member), \
1091 (unsigned long)(&((type *)0)->value), match)
1092
1093 /**
1094 * \brief Universal string list
1095 * \param list List of structures
1096 * \param result Result list
1097 * \param offset Offset of list structure
1098 * \param s1offset Offset of string structure
1099 * \return count of items on success, otherwise a negative error code
1100 */
get_list0(struct list_head *list, const char **result[], unsigned long offset, unsigned long s1offset)1101 static int get_list0(struct list_head *list,
1102 const char **result[],
1103 unsigned long offset,
1104 unsigned long s1offset)
1105 {
1106 char **res;
1107 int cnt;
1108 struct list_head *pos;
1109 char *ptr, *str1;
1110
1111 cnt = alloc_str_list(list, 1, &res);
1112 if (cnt <= 0) {
1113 *result = NULL;
1114 return cnt;
1115 }
1116 *result = (const char **)res;
1117 list_for_each(pos, list) {
1118 ptr = list_entry_offset(pos, char, offset);
1119 str1 = *((char **)(ptr + s1offset));
1120 if (str1 != NULL) {
1121 *res = strdup(str1);
1122 if (*res == NULL)
1123 goto __fail;
1124 } else {
1125 *res = NULL;
1126 }
1127 res++;
1128 }
1129 return cnt;
1130 __fail:
1131 snd_use_case_free_list(*result, cnt);
1132 return -ENOMEM;
1133 }
1134
1135 #define get_list(list, result, type, member, s1) \
1136 get_list0(list, result, \
1137 (unsigned long)(&((type *)0)->member), \
1138 (unsigned long)(&((type *)0)->s1))
1139
1140 /**
1141 * \brief Universal string list - pair of strings
1142 * \param list List of structures
1143 * \param result Result list
1144 * \param offset Offset of list structure
1145 * \param s1offset Offset of string structure
1146 * \param s1offset Offset of string structure
1147 * \return count of items on success, otherwise a negative error code
1148 */
get_list20(struct list_head *list, const char **result[], unsigned long offset, unsigned long s1offset, unsigned long s2offset)1149 static int get_list20(struct list_head *list,
1150 const char **result[],
1151 unsigned long offset,
1152 unsigned long s1offset,
1153 unsigned long s2offset)
1154 {
1155 char **res;
1156 int cnt;
1157 struct list_head *pos;
1158 char *ptr, *str1, *str2;
1159
1160 cnt = alloc_str_list(list, 2, &res);
1161 if (cnt <= 0) {
1162 *result = NULL;
1163 return cnt;
1164 }
1165 *result = (const char **)res;
1166 list_for_each(pos, list) {
1167 ptr = list_entry_offset(pos, char, offset);
1168 str1 = *((char **)(ptr + s1offset));
1169 if (str1 != NULL) {
1170 *res = strdup(str1);
1171 if (*res == NULL)
1172 goto __fail;
1173 } else {
1174 *res = NULL;
1175 }
1176 res++;
1177 str2 = *((char **)(ptr + s2offset));
1178 if (str2 != NULL) {
1179 *res = strdup(str2);
1180 if (*res == NULL)
1181 goto __fail;
1182 } else {
1183 *res = NULL;
1184 }
1185 res++;
1186 }
1187 return cnt;
1188 __fail:
1189 snd_use_case_free_list(*result, cnt);
1190 return -ENOMEM;
1191 }
1192
1193 #define get_list2(list, result, type, member, s1, s2) \
1194 get_list20(list, result, \
1195 (unsigned long)(&((type *)0)->member), \
1196 (unsigned long)(&((type *)0)->s1), \
1197 (unsigned long)(&((type *)0)->s2))
1198
1199 /**
1200 * \brief Find verb
1201 * \param uc_mgr Use case manager
1202 * \param verb_name verb to find
1203 * \return structure on success, otherwise a NULL (not found)
1204 */
find_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name)1205 static inline struct use_case_verb *find_verb(snd_use_case_mgr_t *uc_mgr,
1206 const char *verb_name)
1207 {
1208 return find(&uc_mgr->verb_list,
1209 struct use_case_verb, list, name,
1210 verb_name);
1211 }
1212
is_devlist_supported(snd_use_case_mgr_t *uc_mgr, struct dev_list *dev_list)1213 static int is_devlist_supported(snd_use_case_mgr_t *uc_mgr,
1214 struct dev_list *dev_list)
1215 {
1216 struct dev_list_node *device;
1217 struct use_case_device *adev;
1218 struct list_head *pos, *pos1;
1219 int found_ret;
1220
1221 switch (dev_list->type) {
1222 case DEVLIST_NONE:
1223 default:
1224 return 1;
1225 case DEVLIST_SUPPORTED:
1226 found_ret = 1;
1227 break;
1228 case DEVLIST_CONFLICTING:
1229 found_ret = 0;
1230 break;
1231 }
1232
1233 list_for_each(pos, &dev_list->list) {
1234 device = list_entry(pos, struct dev_list_node, list);
1235
1236 list_for_each(pos1, &uc_mgr->active_devices) {
1237 adev = list_entry(pos1, struct use_case_device,
1238 active_list);
1239 if (!strcmp(device->name, adev->name))
1240 return found_ret;
1241 }
1242 }
1243 return 1 - found_ret;
1244 }
1245
is_modifier_supported(snd_use_case_mgr_t *uc_mgr, struct use_case_modifier *modifier)1246 static inline int is_modifier_supported(snd_use_case_mgr_t *uc_mgr,
1247 struct use_case_modifier *modifier)
1248 {
1249 return is_devlist_supported(uc_mgr, &modifier->dev_list);
1250 }
1251
is_device_supported(snd_use_case_mgr_t *uc_mgr, struct use_case_device *device)1252 static inline int is_device_supported(snd_use_case_mgr_t *uc_mgr,
1253 struct use_case_device *device)
1254 {
1255 return is_devlist_supported(uc_mgr, &device->dev_list);
1256 }
1257
1258 /**
1259 * \brief Find device
1260 * \param verb Use case verb
1261 * \param device_name device to find
1262 * \return structure on success, otherwise a NULL (not found)
1263 */
1264 static inline struct use_case_device *
find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, const char *device_name, int check_supported)1265 find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
1266 const char *device_name, int check_supported)
1267 {
1268 struct use_case_device *device;
1269 struct list_head *pos;
1270
1271 list_for_each(pos, &verb->device_list) {
1272 device = list_entry(pos, struct use_case_device, list);
1273
1274 if (strcmp(device_name, device->name))
1275 continue;
1276
1277 if (check_supported &&
1278 !is_device_supported(uc_mgr, device))
1279 continue;
1280
1281 return device;
1282 }
1283 return NULL;
1284 }
1285
1286 /**
1287 * \brief Find modifier
1288 * \param verb Use case verb
1289 * \param modifier_name modifier to find
1290 * \return structure on success, otherwise a NULL (not found)
1291 */
1292 static struct use_case_modifier *
find_modifier(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, const char *modifier_name, int check_supported)1293 find_modifier(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
1294 const char *modifier_name, int check_supported)
1295 {
1296 struct use_case_modifier *modifier;
1297 struct list_head *pos;
1298
1299 list_for_each(pos, &verb->modifier_list) {
1300 modifier = list_entry(pos, struct use_case_modifier, list);
1301
1302 if (strcmp(modifier->name, modifier_name))
1303 continue;
1304
1305 if (check_supported &&
1306 !is_modifier_supported(uc_mgr, modifier))
1307 continue;
1308
1309 return modifier;
1310 }
1311 return NULL;
1312 }
1313
device_status(snd_use_case_mgr_t *uc_mgr, const char *device_name)1314 long device_status(snd_use_case_mgr_t *uc_mgr,
1315 const char *device_name)
1316 {
1317 struct use_case_device *dev;
1318 struct list_head *pos;
1319
1320 list_for_each(pos, &uc_mgr->active_devices) {
1321 dev = list_entry(pos, struct use_case_device, active_list);
1322 if (strcmp(dev->name, device_name) == 0)
1323 return 1;
1324 }
1325 return 0;
1326 }
1327
modifier_status(snd_use_case_mgr_t *uc_mgr, const char *modifier_name)1328 long modifier_status(snd_use_case_mgr_t *uc_mgr,
1329 const char *modifier_name)
1330 {
1331 struct use_case_modifier *mod;
1332 struct list_head *pos;
1333
1334 list_for_each(pos, &uc_mgr->active_modifiers) {
1335 mod = list_entry(pos, struct use_case_modifier, active_list);
1336 if (strcmp(mod->name, modifier_name) == 0)
1337 return 1;
1338 }
1339 return 0;
1340 }
1341
1342 /**
1343 * \brief Set verb
1344 * \param uc_mgr Use case manager
1345 * \param verb verb to set
1346 * \param enable nonzero = enable, zero = disable
1347 * \return zero on success, otherwise a negative error code
1348 */
set_verb(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, int enable)1349 static int set_verb(snd_use_case_mgr_t *uc_mgr,
1350 struct use_case_verb *verb,
1351 int enable)
1352 {
1353 struct list_head *seq;
1354 int err;
1355
1356 if (enable) {
1357 err = set_defaults(uc_mgr, false);
1358 if (err < 0)
1359 return err;
1360 seq = &verb->enable_list;
1361 } else {
1362 seq = &verb->disable_list;
1363 }
1364 err = execute_sequence(uc_mgr, verb, seq,
1365 &verb->value_list,
1366 &uc_mgr->value_list,
1367 NULL);
1368 if (enable && err >= 0)
1369 uc_mgr->active_verb = verb;
1370 return err;
1371 }
1372
1373 /**
1374 * \brief Set modifier
1375 * \param uc_mgr Use case manager
1376 * \param modifier modifier to set
1377 * \param enable nonzero = enable, zero = disable
1378 * \return zero on success, otherwise a negative error code
1379 */
set_modifier(snd_use_case_mgr_t *uc_mgr, struct use_case_modifier *modifier, int enable)1380 static int set_modifier(snd_use_case_mgr_t *uc_mgr,
1381 struct use_case_modifier *modifier,
1382 int enable)
1383 {
1384 struct list_head *seq;
1385 int err;
1386
1387 if (modifier_status(uc_mgr, modifier->name) == enable)
1388 return 0;
1389
1390 if (enable) {
1391 seq = &modifier->enable_list;
1392 } else {
1393 seq = &modifier->disable_list;
1394 }
1395 err = execute_sequence(uc_mgr, uc_mgr->active_verb, seq,
1396 &modifier->value_list,
1397 &uc_mgr->active_verb->value_list,
1398 &uc_mgr->value_list);
1399 if (enable && err >= 0) {
1400 list_add_tail(&modifier->active_list, &uc_mgr->active_modifiers);
1401 } else if (!enable) {
1402 list_del(&modifier->active_list);
1403 }
1404 return err;
1405 }
1406
1407 /**
1408 * \brief Set device
1409 * \param uc_mgr Use case manager
1410 * \param device device to set
1411 * \param enable nonzero = enable, zero = disable
1412 * \return zero on success, otherwise a negative error code
1413 */
set_device(snd_use_case_mgr_t *uc_mgr, struct use_case_device *device, int enable)1414 static int set_device(snd_use_case_mgr_t *uc_mgr,
1415 struct use_case_device *device,
1416 int enable)
1417 {
1418 struct list_head *seq;
1419 int err;
1420
1421 if (device_status(uc_mgr, device->name) == enable)
1422 return 0;
1423
1424 if (enable) {
1425 seq = &device->enable_list;
1426 } else {
1427 seq = &device->disable_list;
1428 }
1429 err = execute_sequence(uc_mgr, uc_mgr->active_verb, seq,
1430 &device->value_list,
1431 &uc_mgr->active_verb->value_list,
1432 &uc_mgr->value_list);
1433 if (enable && err >= 0) {
1434 list_add_tail(&device->active_list, &uc_mgr->active_devices);
1435 } else if (!enable) {
1436 list_del(&device->active_list);
1437 }
1438 return err;
1439 }
1440
1441 /**
1442 * \brief Do the full reset
1443 * \param uc_mgr Use case manager
1444 * \return zero on success, otherwise a negative error code
1445 */
do_reset(snd_use_case_mgr_t *uc_mgr)1446 static int do_reset(snd_use_case_mgr_t *uc_mgr)
1447 {
1448 int err;
1449
1450 err = set_defaults(uc_mgr, true);
1451 INIT_LIST_HEAD(&uc_mgr->active_modifiers);
1452 INIT_LIST_HEAD(&uc_mgr->active_devices);
1453 uc_mgr->active_verb = NULL;
1454 return err;
1455 }
1456
1457 /**
1458 * \brief Parse open arguments
1459 * \param uc_mgr Use case manager
1460 * \param name name of card to open
1461 * \return the rest of the card name to open
1462 */
parse_open_variables(snd_use_case_mgr_t *uc_mgr, const char *name)1463 const char *parse_open_variables(snd_use_case_mgr_t *uc_mgr, const char *name)
1464 {
1465 const char *end, *id;
1466 char *args, *var;
1467 snd_config_t *cfg, *n;
1468 snd_config_iterator_t i, next;
1469 char vname[128];
1470 size_t l;
1471 int err;
1472
1473 end = strstr(name, ">>>");
1474 if (end == NULL)
1475 return name;
1476 l = end - name - 3;
1477 args = alloca(l + 1);
1478 strncpy(args, name + 3, l);
1479 args[l] = '\0';
1480
1481 err = snd_config_load_string(&cfg, args, 0);
1482 if (err < 0) {
1483 uc_error("error: open arguments are not valid (%s)", args);
1484 goto skip;
1485 }
1486
1487 /* set arguments */
1488 snd_config_for_each(i, next, cfg) {
1489 n = snd_config_iterator_entry(i);
1490 err = snd_config_get_id(n, &id);
1491 if (err < 0)
1492 goto skip;
1493 err = snd_config_get_ascii(n, &var);
1494 if (err < 0)
1495 goto skip;
1496 snprintf(vname, sizeof(vname), "@%s", id);
1497 err = uc_mgr_set_variable(uc_mgr, vname, var);
1498 free(var);
1499 if (err < 0)
1500 goto skip;
1501 }
1502
1503 skip:
1504 snd_config_delete(cfg);
1505 return end + 3;
1506 }
1507
snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr, const char *card_name)1508 int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr,
1509 const char *card_name)
1510 {
1511 snd_use_case_mgr_t *mgr;
1512 int err;
1513
1514 /* create a new UCM */
1515 mgr = calloc(1, sizeof(snd_use_case_mgr_t));
1516 if (mgr == NULL)
1517 return -ENOMEM;
1518 INIT_LIST_HEAD(&mgr->verb_list);
1519 INIT_LIST_HEAD(&mgr->fixedboot_list);
1520 INIT_LIST_HEAD(&mgr->boot_list);
1521 INIT_LIST_HEAD(&mgr->default_list);
1522 INIT_LIST_HEAD(&mgr->value_list);
1523 INIT_LIST_HEAD(&mgr->active_modifiers);
1524 INIT_LIST_HEAD(&mgr->active_devices);
1525 INIT_LIST_HEAD(&mgr->ctl_list);
1526 INIT_LIST_HEAD(&mgr->variable_list);
1527 pthread_mutex_init(&mgr->mutex, NULL);
1528
1529 if (card_name && *card_name == '-') {
1530 card_name++;
1531 mgr->suppress_nodev_errors = 1;
1532 }
1533
1534 if (card_name && card_name[0] == '<' && card_name[1] == '<' && card_name[2] == '<')
1535 card_name = parse_open_variables(mgr, card_name);
1536
1537 err = uc_mgr_card_open(mgr);
1538 if (err < 0) {
1539 uc_mgr_free(mgr);
1540 return err;
1541 }
1542
1543 mgr->card_name = strdup(card_name);
1544 if (mgr->card_name == NULL) {
1545 err = -ENOMEM;
1546 goto _err;
1547 }
1548
1549 /* get info on use_cases and verify against card */
1550 err = import_master_config(mgr);
1551 if (err < 0) {
1552 if (err == -ENXIO && mgr->suppress_nodev_errors)
1553 goto _err;
1554 uc_error("error: failed to import %s use case configuration %d",
1555 card_name, err);
1556 goto _err;
1557 }
1558
1559 err = check_empty_configuration(mgr);
1560 if (err < 0) {
1561 uc_error("error: failed to import %s (empty configuration)", card_name);
1562 goto _err;
1563 }
1564
1565 *uc_mgr = mgr;
1566 return 0;
1567
1568 _err:
1569 uc_mgr_card_close(mgr);
1570 uc_mgr_free(mgr);
1571 return err;
1572 }
1573
snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr)1574 int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr)
1575 {
1576 int err;
1577
1578 pthread_mutex_lock(&uc_mgr->mutex);
1579
1580 do_reset(uc_mgr);
1581
1582 uc_mgr_free_verb(uc_mgr);
1583
1584 uc_mgr->default_list_executed = 0;
1585
1586 /* reload all use cases */
1587 err = import_master_config(uc_mgr);
1588 if (err < 0) {
1589 uc_error("error: failed to reload use cases");
1590 pthread_mutex_unlock(&uc_mgr->mutex);
1591 return -EINVAL;
1592 }
1593
1594 pthread_mutex_unlock(&uc_mgr->mutex);
1595 return err;
1596 }
1597
snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr)1598 int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr)
1599 {
1600 uc_mgr_card_close(uc_mgr);
1601 uc_mgr_free(uc_mgr);
1602
1603 return 0;
1604 }
1605
1606 /*
1607 * Tear down current use case verb, device and modifier.
1608 */
dismantle_use_case(snd_use_case_mgr_t *uc_mgr)1609 static int dismantle_use_case(snd_use_case_mgr_t *uc_mgr)
1610 {
1611 struct list_head *pos, *npos;
1612 struct use_case_modifier *modifier;
1613 struct use_case_device *device;
1614 int err;
1615
1616 list_for_each_safe(pos, npos, &uc_mgr->active_modifiers) {
1617 modifier = list_entry(pos, struct use_case_modifier,
1618 active_list);
1619 err = set_modifier(uc_mgr, modifier, 0);
1620 if (err < 0)
1621 uc_error("Unable to disable modifier %s", modifier->name);
1622 }
1623 INIT_LIST_HEAD(&uc_mgr->active_modifiers);
1624
1625 list_for_each_safe(pos, npos, &uc_mgr->active_devices) {
1626 device = list_entry(pos, struct use_case_device,
1627 active_list);
1628 err = set_device(uc_mgr, device, 0);
1629 if (err < 0)
1630 uc_error("Unable to disable device %s", device->name);
1631 }
1632 INIT_LIST_HEAD(&uc_mgr->active_devices);
1633
1634 err = set_verb(uc_mgr, uc_mgr->active_verb, 0);
1635 if (err < 0) {
1636 uc_error("Unable to disable verb %s", uc_mgr->active_verb->name);
1637 return err;
1638 }
1639 uc_mgr->active_verb = NULL;
1640
1641 err = set_defaults(uc_mgr, true);
1642
1643 return err;
1644 }
1645
snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr)1646 int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr)
1647 {
1648 int err;
1649
1650 pthread_mutex_lock(&uc_mgr->mutex);
1651 err = do_reset(uc_mgr);
1652 pthread_mutex_unlock(&uc_mgr->mutex);
1653 return err;
1654 }
1655
1656 /**
1657 * \brief Get list of verbs in pair verbname+comment
1658 * \param list Returned list
1659 * \return Number of list entries if success, otherwise a negative error code
1660 */
get_verb_list(snd_use_case_mgr_t *uc_mgr, const char **list[])1661 static int get_verb_list(snd_use_case_mgr_t *uc_mgr, const char **list[])
1662 {
1663 return get_list2(&uc_mgr->verb_list, list,
1664 struct use_case_verb, list,
1665 name, comment);
1666 }
1667
1668 /**
1669 * \brief Get list of devices in pair devicename+comment
1670 * \param list Returned list
1671 * \param verbname For verb (NULL = current)
1672 * \return Number of list entries if success, otherwise a negative error code
1673 */
get_device_list(snd_use_case_mgr_t *uc_mgr, const char **list[], char *verbname)1674 static int get_device_list(snd_use_case_mgr_t *uc_mgr, const char **list[],
1675 char *verbname)
1676 {
1677 struct use_case_verb *verb;
1678
1679 if (verbname) {
1680 verb = find_verb(uc_mgr, verbname);
1681 } else {
1682 verb = uc_mgr->active_verb;
1683 }
1684 if (verb == NULL)
1685 return -ENOENT;
1686 return get_list2(&verb->device_list, list,
1687 struct use_case_device, list,
1688 name, comment);
1689 }
1690
1691 /**
1692 * \brief Get list of modifiers in pair devicename+comment
1693 * \param list Returned list
1694 * \param verbname For verb (NULL = current)
1695 * \return Number of list entries if success, otherwise a negative error code
1696 */
get_modifier_list(snd_use_case_mgr_t *uc_mgr, const char **list[], char *verbname)1697 static int get_modifier_list(snd_use_case_mgr_t *uc_mgr, const char **list[],
1698 char *verbname)
1699 {
1700 struct use_case_verb *verb;
1701 if (verbname) {
1702 verb = find_verb(uc_mgr, verbname);
1703 } else {
1704 verb = uc_mgr->active_verb;
1705 }
1706 if (verb == NULL)
1707 return -ENOENT;
1708 return get_list2(&verb->modifier_list, list,
1709 struct use_case_modifier, list,
1710 name, comment);
1711 }
1712
1713 /**
1714 * \brief Get list of supported/conflicting devices
1715 * \param list Returned list
1716 * \param name Name of modifier or verb to query
1717 * \param type Type of device list entries to return
1718 * \return Number of list entries if success, otherwise a negative error code
1719 */
get_supcon_device_list(snd_use_case_mgr_t *uc_mgr, const char **list[], char *name, enum dev_list_type type)1720 static int get_supcon_device_list(snd_use_case_mgr_t *uc_mgr,
1721 const char **list[], char *name,
1722 enum dev_list_type type)
1723 {
1724 char *str;
1725 struct use_case_verb *verb;
1726 struct use_case_modifier *modifier;
1727 struct use_case_device *device;
1728
1729 if (!name)
1730 return -ENOENT;
1731
1732 str = strchr(name, '/');
1733 if (str) {
1734 *str = '\0';
1735 verb = find_verb(uc_mgr, str + 1);
1736 }
1737 else {
1738 verb = uc_mgr->active_verb;
1739 }
1740 if (!verb)
1741 return -ENOENT;
1742
1743 modifier = find_modifier(uc_mgr, verb, name, 0);
1744 if (modifier) {
1745 if (modifier->dev_list.type != type) {
1746 *list = NULL;
1747 return 0;
1748 }
1749 return get_list(&modifier->dev_list.list, list,
1750 struct dev_list_node, list,
1751 name);
1752 }
1753
1754 device = find_device(uc_mgr, verb, name, 0);
1755 if (device) {
1756 if (device->dev_list.type != type) {
1757 *list = NULL;
1758 return 0;
1759 }
1760 return get_list(&device->dev_list.list, list,
1761 struct dev_list_node, list,
1762 name);
1763 }
1764
1765 return -ENOENT;
1766 }
1767
1768 /**
1769 * \brief Get list of supported devices
1770 * \param list Returned list
1771 * \param name Name of verb or modifier to query
1772 * \return Number of list entries if success, otherwise a negative error code
1773 */
get_supported_device_list(snd_use_case_mgr_t *uc_mgr, const char **list[], char *name)1774 static int get_supported_device_list(snd_use_case_mgr_t *uc_mgr,
1775 const char **list[], char *name)
1776 {
1777 return get_supcon_device_list(uc_mgr, list, name, DEVLIST_SUPPORTED);
1778 }
1779
1780 /**
1781 * \brief Get list of conflicting devices
1782 * \param list Returned list
1783 * \param name Name of verb or modifier to query
1784 * \return Number of list entries if success, otherwise a negative error code
1785 */
get_conflicting_device_list(snd_use_case_mgr_t *uc_mgr, const char **list[], char *name)1786 static int get_conflicting_device_list(snd_use_case_mgr_t *uc_mgr,
1787 const char **list[], char *name)
1788 {
1789 return get_supcon_device_list(uc_mgr, list, name, DEVLIST_CONFLICTING);
1790 }
1791
1792 #ifndef DOC_HIDDEN
1793 struct myvalue {
1794 struct list_head list;
1795 const char *text;
1796 };
1797 #endif
1798
1799 /**
1800 * \brief Convert myvalue list string list
1801 * \param list myvalue list
1802 * \param res string list
1803 * \retval Number of list entries if success, otherwise a negativer error code
1804 */
myvalue_to_str_list(struct list_head *list, char ***res)1805 static int myvalue_to_str_list(struct list_head *list, char ***res)
1806 {
1807 struct list_head *pos;
1808 struct myvalue *value;
1809 char **p;
1810 int cnt;
1811
1812 cnt = alloc_str_list(list, 1, res);
1813 if (cnt < 0)
1814 return cnt;
1815 p = *res;
1816 list_for_each(pos, list) {
1817 value = list_entry(pos, struct myvalue, list);
1818 *p = strdup(value->text);
1819 if (*p == NULL) {
1820 snd_use_case_free_list((const char **)p, cnt);
1821 return -ENOMEM;
1822 }
1823 p++;
1824 }
1825 return cnt;
1826 }
1827
1828 /**
1829 * \brief Free myvalue list
1830 * \param list myvalue list
1831 */
myvalue_list_free(struct list_head *list)1832 static void myvalue_list_free(struct list_head *list)
1833 {
1834 struct list_head *pos, *npos;
1835 struct myvalue *value;
1836
1837 list_for_each_safe(pos, npos, list) {
1838 value = list_entry(pos, struct myvalue, list);
1839 list_del(&value->list);
1840 free(value);
1841 }
1842 }
1843
1844 /**
1845 * \brief Merge one value to the myvalue list
1846 * \param list The list with values
1847 * \param value The value to be merged (without duplicates)
1848 * \return 1 if dup, 0 if success, otherwise a negative error code
1849 */
merge_value(struct list_head *list, const char *text)1850 static int merge_value(struct list_head *list, const char *text)
1851 {
1852 struct list_head *pos;
1853 struct myvalue *value;
1854
1855 list_for_each(pos, list) {
1856 value = list_entry(pos, struct myvalue, list);
1857 if (strcmp(value->text, text) == 0)
1858 return 1;
1859 }
1860 value = malloc(sizeof(*value));
1861 if (value == NULL)
1862 return -ENOMEM;
1863 value->text = text;
1864 list_add_tail(&value->list, list);
1865 return 0;
1866 }
1867
1868 /**
1869 * \brief Find all values for given identifier
1870 * \param list Returned list
1871 * \param source Source list with ucm_value structures
1872 * \return Zero if success, otherwise a negative error code
1873 */
add_identifiers(struct list_head *list, struct list_head *source)1874 static int add_identifiers(struct list_head *list,
1875 struct list_head *source)
1876 {
1877 struct ucm_value *v;
1878 struct list_head *pos;
1879 int err;
1880
1881 list_for_each(pos, source) {
1882 v = list_entry(pos, struct ucm_value, list);
1883 err = merge_value(list, v->name);
1884 if (err < 0)
1885 return err;
1886 }
1887 return 0;
1888 }
1889
1890 /**
1891 * \brief Find all values for given identifier
1892 * \param list Returned list
1893 * \param identifier Identifier
1894 * \param source Source list with ucm_value structures
1895 */
add_values(struct list_head *list, const char *identifier, struct list_head *source)1896 static int add_values(struct list_head *list,
1897 const char *identifier,
1898 struct list_head *source)
1899 {
1900 struct ucm_value *v;
1901 struct list_head *pos;
1902 int err;
1903
1904 list_for_each(pos, source) {
1905 v = list_entry(pos, struct ucm_value, list);
1906 if (check_identifier(identifier, v->name)) {
1907 err = merge_value(list, v->data);
1908 if (err < 0)
1909 return err;
1910 }
1911 }
1912 return 0;
1913 }
1914
1915 /**
1916 * \brief compare two identifiers
1917 */
identifier_cmp(const void *_a, const void *_b)1918 static int identifier_cmp(const void *_a, const void *_b)
1919 {
1920 const char * const *a = _a;
1921 const char * const *b = _b;
1922 return strcmp(*a, *b);
1923 }
1924
1925 /**
1926 * \brief Get list of available identifiers
1927 * \param list Returned list
1928 * \param name Name of verb or modifier to query
1929 * \return Number of list entries if success, otherwise a negative error code
1930 */
get_identifiers_list(snd_use_case_mgr_t *uc_mgr, const char **list[], char *name)1931 static int get_identifiers_list(snd_use_case_mgr_t *uc_mgr,
1932 const char **list[], char *name)
1933 {
1934 struct use_case_verb *verb;
1935 struct use_case_modifier *modifier;
1936 struct use_case_device *device;
1937 struct list_head mylist;
1938 struct list_head *value_list;
1939 char *str, **res;
1940 int err;
1941
1942 if (!name)
1943 return -ENOENT;
1944
1945 str = strchr(name, '/');
1946 if (str) {
1947 *str = '\0';
1948 verb = find_verb(uc_mgr, str + 1);
1949 }
1950 else {
1951 verb = uc_mgr->active_verb;
1952 }
1953 if (!verb)
1954 return -ENOENT;
1955
1956 value_list = NULL;
1957 modifier = find_modifier(uc_mgr, verb, name, 0);
1958 if (modifier) {
1959 value_list = &modifier->value_list;
1960 } else {
1961 device = find_device(uc_mgr, verb, name, 0);
1962 if (device)
1963 value_list = &device->value_list;
1964 }
1965 if (value_list == NULL)
1966 return -ENOENT;
1967
1968 INIT_LIST_HEAD(&mylist);
1969 err = add_identifiers(&mylist, &uc_mgr->value_list);
1970 if (err < 0)
1971 goto __fail;
1972 err = add_identifiers(&mylist, &verb->value_list);
1973 if (err < 0)
1974 goto __fail;
1975 err = add_identifiers(&mylist, value_list);
1976 if (err < 0)
1977 goto __fail;
1978 err = myvalue_to_str_list(&mylist, &res);
1979 if (err > 0)
1980 *list = (const char **)res;
1981 else if (err == 0)
1982 *list = NULL;
1983 __fail:
1984 myvalue_list_free(&mylist);
1985 if (err <= 0)
1986 return err;
1987 qsort(*list, err, sizeof(char *), identifier_cmp);
1988 return err;
1989 }
1990
1991 /**
1992 * \brief Get list of values
1993 * \param list Returned list
1994 * \param verbname For verb (NULL = current)
1995 * \return Number of list entries if success, otherwise a negative error code
1996 */
get_value_list(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char **list[], char *verbname)1997 static int get_value_list(snd_use_case_mgr_t *uc_mgr,
1998 const char *identifier,
1999 const char **list[],
2000 char *verbname)
2001 {
2002 struct list_head mylist, *pos;
2003 struct use_case_verb *verb;
2004 struct use_case_device *dev;
2005 struct use_case_modifier *mod;
2006 char **res;
2007 int err;
2008
2009 if (verbname) {
2010 verb = find_verb(uc_mgr, verbname);
2011 } else {
2012 verb = uc_mgr->active_verb;
2013 }
2014 if (verb == NULL)
2015 return -ENOENT;
2016 INIT_LIST_HEAD(&mylist);
2017 err = add_values(&mylist, identifier, &uc_mgr->value_list);
2018 if (err < 0)
2019 goto __fail;
2020 err = add_values(&mylist, identifier, &verb->value_list);
2021 if (err < 0)
2022 goto __fail;
2023 list_for_each(pos, &verb->device_list) {
2024 dev = list_entry(pos, struct use_case_device, list);
2025 err = add_values(&mylist, identifier, &dev->value_list);
2026 if (err < 0)
2027 goto __fail;
2028 }
2029 list_for_each(pos, &verb->modifier_list) {
2030 mod = list_entry(pos, struct use_case_modifier, list);
2031 err = add_values(&mylist, identifier, &mod->value_list);
2032 if (err < 0)
2033 goto __fail;
2034 }
2035 err = myvalue_to_str_list(&mylist, &res);
2036 if (err > 0)
2037 *list = (const char **)res;
2038 else if (err == 0)
2039 *list = NULL;
2040 __fail:
2041 myvalue_list_free(&mylist);
2042 return err;
2043 }
2044
2045 /**
2046 * \brief Get list of enabled devices
2047 * \param list Returned list
2048 * \param verbname For verb (NULL = current)
2049 * \return Number of list entries if success, otherwise a negative error code
2050 */
get_enabled_device_list(snd_use_case_mgr_t *uc_mgr, const char **list[])2051 static int get_enabled_device_list(snd_use_case_mgr_t *uc_mgr,
2052 const char **list[])
2053 {
2054 if (uc_mgr->active_verb == NULL)
2055 return -EINVAL;
2056 return get_list(&uc_mgr->active_devices, list,
2057 struct use_case_device, active_list,
2058 name);
2059 }
2060
2061 /**
2062 * \brief Get list of enabled modifiers
2063 * \param list Returned list
2064 * \param verbname For verb (NULL = current)
2065 * \return Number of list entries if success, otherwise a negative error code
2066 */
get_enabled_modifier_list(snd_use_case_mgr_t *uc_mgr, const char **list[])2067 static int get_enabled_modifier_list(snd_use_case_mgr_t *uc_mgr,
2068 const char **list[])
2069 {
2070 if (uc_mgr->active_verb == NULL)
2071 return -EINVAL;
2072 return get_list(&uc_mgr->active_modifiers, list,
2073 struct use_case_modifier, active_list,
2074 name);
2075 }
2076
snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char **list[])2077 int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr,
2078 const char *identifier,
2079 const char **list[])
2080 {
2081 char *str, *str1;
2082 int err;
2083
2084 if (uc_mgr == NULL || identifier == NULL)
2085 return uc_mgr_scan_master_configs(list);
2086 pthread_mutex_lock(&uc_mgr->mutex);
2087 if (strcmp(identifier, "_verbs") == 0)
2088 err = get_verb_list(uc_mgr, list);
2089 else if (strcmp(identifier, "_enadevs") == 0)
2090 err = get_enabled_device_list(uc_mgr, list);
2091 else if (strcmp(identifier, "_enamods") == 0)
2092 err = get_enabled_modifier_list(uc_mgr, list);
2093 else {
2094 str1 = strchr(identifier, '/');
2095 if (str1) {
2096 str = strdup(str1 + 1);
2097 if (str == NULL) {
2098 err = -ENOMEM;
2099 goto __end;
2100 }
2101 } else {
2102 str = NULL;
2103 }
2104 if (check_identifier(identifier, "_devices"))
2105 err = get_device_list(uc_mgr, list, str);
2106 else if (check_identifier(identifier, "_modifiers"))
2107 err = get_modifier_list(uc_mgr, list, str);
2108 else if (check_identifier(identifier, "_identifiers"))
2109 err = get_identifiers_list(uc_mgr, list, str);
2110 else if (check_identifier(identifier, "_supporteddevs"))
2111 err = get_supported_device_list(uc_mgr, list, str);
2112 else if (check_identifier(identifier, "_conflictingdevs"))
2113 err = get_conflicting_device_list(uc_mgr, list, str);
2114 else if (identifier[0] == '_')
2115 err = -ENOENT;
2116 else
2117 err = get_value_list(uc_mgr, identifier, list, str);
2118 if (str)
2119 free(str);
2120 }
2121 __end:
2122 pthread_mutex_unlock(&uc_mgr->mutex);
2123 return err;
2124 }
2125
get_value1(snd_use_case_mgr_t *uc_mgr, char **value, struct list_head *value_list, const char *identifier)2126 static int get_value1(snd_use_case_mgr_t *uc_mgr, char **value,
2127 struct list_head *value_list, const char *identifier)
2128 {
2129 struct ucm_value *val;
2130 struct list_head *pos;
2131 int err;
2132
2133 if (!value_list)
2134 return -ENOENT;
2135
2136 list_for_each(pos, value_list) {
2137 val = list_entry(pos, struct ucm_value, list);
2138 if (check_identifier(identifier, val->name)) {
2139 if (uc_mgr->conf_format < 2) {
2140 *value = strdup(val->data);
2141 if (*value == NULL)
2142 return -ENOMEM;
2143 return 0;
2144 }
2145 err = uc_mgr_get_substituted_value(uc_mgr, value, val->data);
2146 if (err < 0)
2147 return err;
2148 return rewrite_device_value(uc_mgr, val->name, value);
2149 }
2150 }
2151 return -ENOENT;
2152 }
2153
get_value3(snd_use_case_mgr_t *uc_mgr, char **value, const char *identifier, struct list_head *value_list1, struct list_head *value_list2, struct list_head *value_list3)2154 static int get_value3(snd_use_case_mgr_t *uc_mgr,
2155 char **value,
2156 const char *identifier,
2157 struct list_head *value_list1,
2158 struct list_head *value_list2,
2159 struct list_head *value_list3)
2160 {
2161 int err;
2162
2163 err = get_value1(uc_mgr, value, value_list1, identifier);
2164 if (err >= 0 || err != -ENOENT)
2165 return err;
2166 err = get_value1(uc_mgr, value, value_list2, identifier);
2167 if (err >= 0 || err != -ENOENT)
2168 return err;
2169 err = get_value1(uc_mgr, value, value_list3, identifier);
2170 if (err >= 0 || err != -ENOENT)
2171 return err;
2172 return -ENOENT;
2173 }
2174
2175 /**
2176 * \brief Get value
2177 * \param uc_mgr Use case manager
2178 * \param identifier Value identifier (string)
2179 * \param value Returned value string
2180 * \param item Modifier or Device name (string)
2181 * \return Zero on success (value is filled), otherwise a negative error code
2182 */
get_value(snd_use_case_mgr_t *uc_mgr, const char *identifier, char **value, const char *mod_dev_name, const char *verb_name, int exact)2183 static int get_value(snd_use_case_mgr_t *uc_mgr,
2184 const char *identifier,
2185 char **value,
2186 const char *mod_dev_name,
2187 const char *verb_name,
2188 int exact)
2189 {
2190 struct use_case_verb *verb;
2191 struct use_case_modifier *mod;
2192 struct use_case_device *dev;
2193 int err;
2194
2195 if (mod_dev_name || verb_name || !exact) {
2196 if (verb_name && strlen(verb_name)) {
2197 verb = find_verb(uc_mgr, verb_name);
2198 } else {
2199 verb = uc_mgr->active_verb;
2200 }
2201 if (verb) {
2202 if (mod_dev_name) {
2203 mod = find_modifier(uc_mgr, verb,
2204 mod_dev_name, 0);
2205 if (mod) {
2206 err = get_value1(uc_mgr, value,
2207 &mod->value_list,
2208 identifier);
2209 if (err >= 0 || err != -ENOENT)
2210 return err;
2211 }
2212
2213 dev = find_device(uc_mgr, verb,
2214 mod_dev_name, 0);
2215 if (dev) {
2216 err = get_value1(uc_mgr, value,
2217 &dev->value_list,
2218 identifier);
2219 if (err >= 0 || err != -ENOENT)
2220 return err;
2221 }
2222
2223 if (exact)
2224 return -ENOENT;
2225 }
2226
2227 err = get_value1(uc_mgr, value, &verb->value_list, identifier);
2228 if (err >= 0 || err != -ENOENT)
2229 return err;
2230 }
2231
2232 if (exact)
2233 return -ENOENT;
2234 }
2235
2236 err = get_value1(uc_mgr, value, &uc_mgr->value_list, identifier);
2237 if (err >= 0 || err != -ENOENT)
2238 return err;
2239
2240 return -ENOENT;
2241 }
2242
2243 /**
2244 * \brief Get private alsa-lib configuration (ASCII)
2245 * \param uc_mgr Use case manager
2246 * \param str Returned value string
2247 * \return Zero on success (value is filled), otherwise a negative error code
2248 */
get_alibcfg(snd_use_case_mgr_t *uc_mgr, char **str)2249 static int get_alibcfg(snd_use_case_mgr_t *uc_mgr, char **str)
2250 {
2251 snd_output_t *out;
2252 size_t size;
2253 int err;
2254
2255 err = snd_output_buffer_open(&out);
2256 if (err < 0)
2257 return err;
2258 err = snd_config_save(uc_mgr->local_config, out);
2259 if (err >= 0) {
2260 size = snd_output_buffer_steal(out, str);
2261 if (*str)
2262 (*str)[size] = '\0';
2263 }
2264 snd_output_close(out);
2265 return 0;
2266 }
2267
2268 /**
2269 * \brief Get device prefix for private alsa-lib configuration
2270 * \param uc_mgr Use case manager
2271 * \param str Returned value string
2272 * \return Zero on success (value is filled), otherwise a negative error code
2273 */
get_alibpref(snd_use_case_mgr_t *uc_mgr, char **str)2274 static int get_alibpref(snd_use_case_mgr_t *uc_mgr, char **str)
2275 {
2276 const size_t l = 10;
2277 char *s;
2278
2279 s = malloc(l);
2280 if (s == NULL)
2281 return -ENOMEM;
2282 snprintf(s, l, "_ucm%04X.", uc_mgr->ucm_card_number);
2283 *str = s;
2284 return 0;
2285 }
2286
snd_use_case_get(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char **value)2287 int snd_use_case_get(snd_use_case_mgr_t *uc_mgr,
2288 const char *identifier,
2289 const char **value)
2290 {
2291 const char *slash1, *slash2, *mod_dev_after;
2292 const char *ident, *mod_dev, *verb;
2293 int exact = 0;
2294 int err;
2295
2296 pthread_mutex_lock(&uc_mgr->mutex);
2297 if (identifier == NULL) {
2298 *value = strdup(uc_mgr->card_name);
2299 if (*value == NULL) {
2300 err = -ENOMEM;
2301 goto __end;
2302 }
2303 err = 0;
2304 } else if (strcmp(identifier, "_verb") == 0) {
2305 if (uc_mgr->active_verb == NULL) {
2306 err = -ENOENT;
2307 goto __end;
2308 }
2309 *value = strdup(uc_mgr->active_verb->name);
2310 if (*value == NULL) {
2311 err = -ENOMEM;
2312 goto __end;
2313 }
2314 err = 0;
2315 } else if (strcmp(identifier, "_file") == 0) {
2316 /* get the conf file name of the opened card */
2317 if ((uc_mgr->card_name == NULL) ||
2318 (uc_mgr->conf_file_name == NULL) ||
2319 (uc_mgr->conf_file_name[0] == '\0')) {
2320 err = -ENOENT;
2321 goto __end;
2322 }
2323 *value = strdup(uc_mgr->conf_file_name);
2324 if (*value == NULL) {
2325 err = -ENOMEM;
2326 goto __end;
2327 }
2328 err = 0;
2329
2330 } else if (strcmp(identifier, "_alibcfg") == 0) {
2331 err = get_alibcfg(uc_mgr, (char **)value);
2332 } else if (strcmp(identifier, "_alibpref") == 0) {
2333 err = get_alibpref(uc_mgr, (char **)value);
2334 } else if (identifier[0] == '_') {
2335 err = -ENOENT;
2336 } else {
2337 if (identifier[0] == '=') {
2338 exact = 1;
2339 identifier++;
2340 }
2341
2342 slash1 = strchr(identifier, '/');
2343 if (slash1) {
2344 ident = strndup(identifier, slash1 - identifier);
2345
2346 slash2 = strchr(slash1 + 1, '/');
2347 if (slash2) {
2348 mod_dev_after = slash2;
2349 verb = slash2 + 1;
2350 }
2351 else {
2352 mod_dev_after = slash1 + strlen(slash1);
2353 verb = NULL;
2354 }
2355
2356 if (mod_dev_after == slash1 + 1)
2357 mod_dev = NULL;
2358 else
2359 mod_dev = strndup(slash1 + 1,
2360 mod_dev_after - (slash1 + 1));
2361 }
2362 else {
2363 ident = identifier;
2364 mod_dev = NULL;
2365 verb = NULL;
2366 }
2367
2368 err = get_value(uc_mgr, ident, (char **)value, mod_dev, verb,
2369 exact);
2370 if (ident != identifier)
2371 free((void *)ident);
2372 if (mod_dev)
2373 free((void *)mod_dev);
2374 }
2375 __end:
2376 pthread_mutex_unlock(&uc_mgr->mutex);
2377 return err;
2378 }
2379
2380 /*
2381 * a helper macro to obtain status and existence
2382 */
2383 #define geti(uc_mgr, status, ifind, str, value) ({ \
2384 long val = -EINVAL; \
2385 if (str) { \
2386 val = (status)((uc_mgr), (str)); \
2387 if (val >= 0) { \
2388 if ((ifind)((uc_mgr), (uc_mgr)->active_verb, (str), 0)) { \
2389 *(value) = val; \
2390 val = 0; \
2391 } else { \
2392 val = -ENOENT; \
2393 } \
2394 } \
2395 } \
2396 ; val; /* return value */ \
2397 })
2398
snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, const char *identifier, long *value)2399 int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr,
2400 const char *identifier,
2401 long *value)
2402 {
2403 char *str, *str1;
2404 int err;
2405
2406 pthread_mutex_lock(&uc_mgr->mutex);
2407 if (0) {
2408 /* nothing here - prepared for fixed identifiers */
2409 } else {
2410 str1 = strchr(identifier, '/');
2411 if (str1) {
2412 str = strdup(str1 + 1);
2413 if (str == NULL) {
2414 err = -ENOMEM;
2415 goto __end;
2416 }
2417 } else {
2418 str = NULL;
2419 }
2420 if (check_identifier(identifier, "_devstatus")) {
2421 err = geti(uc_mgr, device_status, find_device, str, value);
2422 } else if (check_identifier(identifier, "_modstatus")) {
2423 err = geti(uc_mgr, modifier_status, find_modifier, str, value);
2424 #if 0
2425 /*
2426 * enable this block if the else clause below is expanded to query
2427 * user-supplied values
2428 */
2429 } else if (identifier[0] == '_') {
2430 err = -ENOENT;
2431 #endif
2432 } else
2433 err = -ENOENT;
2434 if (str)
2435 free(str);
2436 }
2437 __end:
2438 pthread_mutex_unlock(&uc_mgr->mutex);
2439 return err;
2440 }
2441
set_fixedboot_user(snd_use_case_mgr_t *uc_mgr, const char *value)2442 static int set_fixedboot_user(snd_use_case_mgr_t *uc_mgr,
2443 const char *value)
2444 {
2445 int err;
2446
2447 if (value != NULL && *value) {
2448 uc_error("error: wrong value for _fboot (%s)", value);
2449 return -EINVAL;
2450 }
2451 if (list_empty(&uc_mgr->fixedboot_list))
2452 return -ENOENT;
2453 err = execute_sequence(uc_mgr, NULL, &uc_mgr->fixedboot_list,
2454 &uc_mgr->value_list, NULL, NULL);
2455 if (err < 0) {
2456 uc_error("Unable to execute force boot sequence");
2457 return err;
2458 }
2459 return err;
2460 }
2461
set_boot_user(snd_use_case_mgr_t *uc_mgr, const char *value)2462 static int set_boot_user(snd_use_case_mgr_t *uc_mgr,
2463 const char *value)
2464 {
2465 int err;
2466
2467 if (value != NULL && *value) {
2468 uc_error("error: wrong value for _boot (%s)", value);
2469 return -EINVAL;
2470 }
2471 if (list_empty(&uc_mgr->boot_list))
2472 return -ENOENT;
2473 err = execute_sequence(uc_mgr, NULL, &uc_mgr->boot_list,
2474 &uc_mgr->value_list, NULL, NULL);
2475 if (err < 0) {
2476 uc_error("Unable to execute boot sequence");
2477 return err;
2478 }
2479 return err;
2480 }
2481
set_defaults_user(snd_use_case_mgr_t *uc_mgr, const char *value)2482 static int set_defaults_user(snd_use_case_mgr_t *uc_mgr,
2483 const char *value)
2484 {
2485 if (value != NULL && *value) {
2486 uc_error("error: wrong value for _defaults (%s)", value);
2487 return -EINVAL;
2488 }
2489 return set_defaults(uc_mgr, false);
2490 }
2491
handle_transition_verb(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *new_verb)2492 static int handle_transition_verb(snd_use_case_mgr_t *uc_mgr,
2493 struct use_case_verb *new_verb)
2494 {
2495 struct list_head *pos;
2496 struct transition_sequence *trans;
2497 int err;
2498
2499 list_for_each(pos, &uc_mgr->active_verb->transition_list) {
2500 trans = list_entry(pos, struct transition_sequence, list);
2501 if (strcmp(trans->name, new_verb->name) == 0) {
2502 err = execute_sequence(uc_mgr, uc_mgr->active_verb,
2503 &trans->transition_list,
2504 &uc_mgr->active_verb->value_list,
2505 &uc_mgr->value_list,
2506 NULL);
2507 if (err >= 0)
2508 return 1;
2509 return err;
2510 }
2511 }
2512 return 0;
2513 }
2514
set_verb_user(snd_use_case_mgr_t *uc_mgr, const char *verb_name)2515 static int set_verb_user(snd_use_case_mgr_t *uc_mgr,
2516 const char *verb_name)
2517 {
2518 struct use_case_verb *verb;
2519 int err = 0;
2520
2521 if (uc_mgr->active_verb &&
2522 strcmp(uc_mgr->active_verb->name, verb_name) == 0)
2523 return 0;
2524 if (strcmp(verb_name, SND_USE_CASE_VERB_INACTIVE) != 0) {
2525 verb = find_verb(uc_mgr, verb_name);
2526 if (verb == NULL)
2527 return -ENOENT;
2528 } else {
2529 verb = NULL;
2530 }
2531 if (uc_mgr->active_verb) {
2532 err = handle_transition_verb(uc_mgr, verb);
2533 if (err == 0) {
2534 err = dismantle_use_case(uc_mgr);
2535 if (err < 0)
2536 return err;
2537 } else if (err == 1) {
2538 uc_mgr->active_verb = verb;
2539 verb = NULL;
2540 } else {
2541 verb = NULL; /* show error */
2542 }
2543 }
2544 if (verb) {
2545 err = set_verb(uc_mgr, verb, 1);
2546 if (err < 0)
2547 uc_error("error: failed to initialize new use case: %s",
2548 verb_name);
2549 }
2550 return err;
2551 }
2552
2553
set_device_user(snd_use_case_mgr_t *uc_mgr, const char *device_name, int enable)2554 static int set_device_user(snd_use_case_mgr_t *uc_mgr,
2555 const char *device_name,
2556 int enable)
2557 {
2558 struct use_case_device *device;
2559
2560 if (uc_mgr->active_verb == NULL)
2561 return -ENOENT;
2562 device = find_device(uc_mgr, uc_mgr->active_verb, device_name, 1);
2563 if (device == NULL)
2564 return -ENOENT;
2565 return set_device(uc_mgr, device, enable);
2566 }
2567
set_modifier_user(snd_use_case_mgr_t *uc_mgr, const char *modifier_name, int enable)2568 static int set_modifier_user(snd_use_case_mgr_t *uc_mgr,
2569 const char *modifier_name,
2570 int enable)
2571 {
2572 struct use_case_modifier *modifier;
2573
2574 if (uc_mgr->active_verb == NULL)
2575 return -ENOENT;
2576
2577 modifier = find_modifier(uc_mgr, uc_mgr->active_verb, modifier_name, 1);
2578 if (modifier == NULL)
2579 return -ENOENT;
2580 return set_modifier(uc_mgr, modifier, enable);
2581 }
2582
switch_device(snd_use_case_mgr_t *uc_mgr, const char *old_device, const char *new_device)2583 static int switch_device(snd_use_case_mgr_t *uc_mgr,
2584 const char *old_device,
2585 const char *new_device)
2586 {
2587 struct use_case_device *xold, *xnew;
2588 struct transition_sequence *trans;
2589 struct list_head *pos;
2590 int err, seq_found = 0;
2591
2592 if (uc_mgr->active_verb == NULL)
2593 return -ENOENT;
2594 if (device_status(uc_mgr, old_device) == 0) {
2595 uc_error("error: device %s not enabled", old_device);
2596 return -EINVAL;
2597 }
2598 if (device_status(uc_mgr, new_device) != 0) {
2599 uc_error("error: device %s already enabled", new_device);
2600 return -EINVAL;
2601 }
2602 xold = find_device(uc_mgr, uc_mgr->active_verb, old_device, 1);
2603 if (xold == NULL)
2604 return -ENOENT;
2605 list_del(&xold->active_list);
2606 xnew = find_device(uc_mgr, uc_mgr->active_verb, new_device, 1);
2607 list_add_tail(&xold->active_list, &uc_mgr->active_devices);
2608 if (xnew == NULL)
2609 return -ENOENT;
2610 err = 0;
2611 list_for_each(pos, &xold->transition_list) {
2612 trans = list_entry(pos, struct transition_sequence, list);
2613 if (strcmp(trans->name, new_device) == 0) {
2614 err = execute_sequence(uc_mgr, uc_mgr->active_verb,
2615 &trans->transition_list,
2616 &xold->value_list,
2617 &uc_mgr->active_verb->value_list,
2618 &uc_mgr->value_list);
2619 if (err >= 0) {
2620 list_del(&xold->active_list);
2621 list_add_tail(&xnew->active_list, &uc_mgr->active_devices);
2622 }
2623 seq_found = 1;
2624 break;
2625 }
2626 }
2627 if (!seq_found) {
2628 err = set_device(uc_mgr, xold, 0);
2629 if (err < 0)
2630 return err;
2631 err = set_device(uc_mgr, xnew, 1);
2632 if (err < 0)
2633 return err;
2634 }
2635 return err;
2636 }
2637
switch_modifier(snd_use_case_mgr_t *uc_mgr, const char *old_modifier, const char *new_modifier)2638 static int switch_modifier(snd_use_case_mgr_t *uc_mgr,
2639 const char *old_modifier,
2640 const char *new_modifier)
2641 {
2642 struct use_case_modifier *xold, *xnew;
2643 struct transition_sequence *trans;
2644 struct list_head *pos;
2645 int err, seq_found = 0;
2646
2647 if (uc_mgr->active_verb == NULL)
2648 return -ENOENT;
2649 if (modifier_status(uc_mgr, old_modifier) == 0) {
2650 uc_error("error: modifier %s not enabled", old_modifier);
2651 return -EINVAL;
2652 }
2653 if (modifier_status(uc_mgr, new_modifier) != 0) {
2654 uc_error("error: modifier %s already enabled", new_modifier);
2655 return -EINVAL;
2656 }
2657 xold = find_modifier(uc_mgr, uc_mgr->active_verb, old_modifier, 1);
2658 if (xold == NULL)
2659 return -ENOENT;
2660 xnew = find_modifier(uc_mgr, uc_mgr->active_verb, new_modifier, 1);
2661 if (xnew == NULL)
2662 return -ENOENT;
2663 err = 0;
2664 list_for_each(pos, &xold->transition_list) {
2665 trans = list_entry(pos, struct transition_sequence, list);
2666 if (strcmp(trans->name, new_modifier) == 0) {
2667 err = execute_sequence(uc_mgr, uc_mgr->active_verb,
2668 &trans->transition_list,
2669 &xold->value_list,
2670 &uc_mgr->active_verb->value_list,
2671 &uc_mgr->value_list);
2672 if (err >= 0) {
2673 list_del(&xold->active_list);
2674 list_add_tail(&xnew->active_list, &uc_mgr->active_modifiers);
2675 }
2676 seq_found = 1;
2677 break;
2678 }
2679 }
2680 if (!seq_found) {
2681 err = set_modifier(uc_mgr, xold, 0);
2682 if (err < 0)
2683 return err;
2684 err = set_modifier(uc_mgr, xnew, 1);
2685 if (err < 0)
2686 return err;
2687 }
2688 return err;
2689 }
2690
snd_use_case_set(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char *value)2691 int snd_use_case_set(snd_use_case_mgr_t *uc_mgr,
2692 const char *identifier,
2693 const char *value)
2694 {
2695 char *str, *str1;
2696 int err = 0;
2697
2698 pthread_mutex_lock(&uc_mgr->mutex);
2699 if (strcmp(identifier, "_fboot") == 0)
2700 err = set_fixedboot_user(uc_mgr, value);
2701 else if (strcmp(identifier, "_boot") == 0)
2702 err = set_boot_user(uc_mgr, value);
2703 else if (strcmp(identifier, "_defaults") == 0)
2704 err = set_defaults_user(uc_mgr, value);
2705 else if (strcmp(identifier, "_verb") == 0)
2706 err = set_verb_user(uc_mgr, value);
2707 else if (strcmp(identifier, "_enadev") == 0)
2708 err = set_device_user(uc_mgr, value, 1);
2709 else if (strcmp(identifier, "_disdev") == 0)
2710 err = set_device_user(uc_mgr, value, 0);
2711 else if (strcmp(identifier, "_enamod") == 0)
2712 err = set_modifier_user(uc_mgr, value, 1);
2713 else if (strcmp(identifier, "_dismod") == 0)
2714 err = set_modifier_user(uc_mgr, value, 0);
2715 else {
2716 str1 = strchr(identifier, '/');
2717 if (str1) {
2718 str = strdup(str1 + 1);
2719 if (str == NULL) {
2720 err = -ENOMEM;
2721 goto __end;
2722 }
2723 } else {
2724 err = -EINVAL;
2725 goto __end;
2726 }
2727 if (check_identifier(identifier, "_swdev"))
2728 err = switch_device(uc_mgr, str, value);
2729 else if (check_identifier(identifier, "_swmod"))
2730 err = switch_modifier(uc_mgr, str, value);
2731 else
2732 err = -EINVAL;
2733 if (str)
2734 free(str);
2735 }
2736 __end:
2737 pthread_mutex_unlock(&uc_mgr->mutex);
2738 return err;
2739 }
2740
2741 /**
2742 * \brief Parse control element identifier
2743 * \param dst Element identifier
2744 * \param ucm_id Use case identifier
2745 * \param value String value to be parsed
2746 * \return Zero if success, otherwise a negative error code
2747 */
snd_use_case_parse_ctl_elem_id(snd_ctl_elem_id_t *dst, const char *ucm_id, const char *value)2748 int snd_use_case_parse_ctl_elem_id(snd_ctl_elem_id_t *dst,
2749 const char *ucm_id,
2750 const char *value)
2751 {
2752 snd_ctl_elem_iface_t iface;
2753 int jack_control;
2754
2755 jack_control = strcmp(ucm_id, "JackControl") == 0;
2756 if (!jack_control &&
2757 strcmp(ucm_id, "PlaybackVolume") &&
2758 strcmp(ucm_id, "PlaybackSwitch") &&
2759 strcmp(ucm_id, "CaptureVolume") &&
2760 strcmp(ucm_id, "CaptureSwitch"))
2761 return -EINVAL;
2762 snd_ctl_elem_id_clear(dst);
2763 if (strcasestr(value, "name="))
2764 return __snd_ctl_ascii_elem_id_parse(dst, value, NULL);
2765 iface = SND_CTL_ELEM_IFACE_MIXER;
2766 if (jack_control)
2767 iface = SND_CTL_ELEM_IFACE_CARD;
2768 snd_ctl_elem_id_set_interface(dst, iface);
2769 snd_ctl_elem_id_set_name(dst, value);
2770 return 0;
2771 }
2772
2773 /**
2774 * \brief Parse mixer element identifier
2775 * \param dst Simple mixer element identifier
2776 * \param ucm_id Use case identifier
2777 * \param value String value to be parsed
2778 * \return Zero if success, otherwise a negative error code
2779 */
snd_use_case_parse_selem_id(snd_mixer_selem_id_t *dst, const char *ucm_id, const char *value)2780 int snd_use_case_parse_selem_id(snd_mixer_selem_id_t *dst,
2781 const char *ucm_id,
2782 const char *value)
2783 {
2784 #ifdef BUILD_MIXER
2785 if (strcmp(ucm_id, "PlaybackMixerId") == 0 ||
2786 strcmp(ucm_id, "CaptureMixerId") == 0)
2787 return snd_mixer_selem_id_parse(dst, value);
2788 #endif
2789 return -EINVAL;
2790 }
2791