• Home
  • History
  • Annotate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/iserver/alsa-lib-1.0.26/src/ucm/

Lines Matching defs:*

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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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.
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>
33 #include "ucm_local.h"
34 #include <dirent.h>
36 /** The name of the environment variable containing the UCM directory */
37 #define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM"
39 static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
40 struct list_head *base,
41 snd_config_t *cfg);
44 * Parse string
46 int parse_string(snd_config_t *n, char **res)
48 int err;
50 err = snd_config_get_string(n, (const char **)res);
51 if (err < 0)
52 return err;
53 *res = strdup(*res);
54 if (*res == NULL)
55 return -ENOMEM;
56 return 0;
60 * Parse safe ID
62 int parse_is_name_safe(const char *name)
64 if (strchr(name, '.')) {
65 uc_error("char '.' not allowed in '%s'", name);
66 return 0;
68 return 1;
71 int parse_get_safe_id(snd_config_t *n, const char **id)
73 int err;
75 err = snd_config_get_id(n, id);
76 if (err < 0)
77 return err;
78 if (!parse_is_name_safe((char *)(*id)))
79 return -EINVAL;
80 return 0;
84 * Parse transition
86 static int parse_transition(snd_use_case_mgr_t *uc_mgr,
87 struct list_head *tlist,
88 snd_config_t *cfg)
90 struct transition_sequence *tseq;
91 const char *id;
92 snd_config_iterator_t i, next;
93 snd_config_t *n;
94 int err;
96 if (snd_config_get_id(cfg, &id) < 0)
97 return -EINVAL;
99 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
100 uc_error("compound type expected for %s", id);
101 return -EINVAL;
104 snd_config_for_each(i, next, cfg) {
105 n = snd_config_iterator_entry(i);
107 if (snd_config_get_id(n, &id) < 0)
108 return -EINVAL;
110 tseq = calloc(1, sizeof(*tseq));
111 if (tseq == NULL)
112 return -ENOMEM;
113 INIT_LIST_HEAD(&tseq->transition_list);
115 tseq->name = strdup(id);
116 if (tseq->name == NULL) {
117 free(tseq);
118 return -ENOMEM;
121 err = parse_sequence(uc_mgr, &tseq->transition_list, n);
122 if (err < 0) {
123 uc_mgr_free_transition_element(tseq);
124 return err;
127 list_add(&tseq->list, tlist);
129 return 0;
133 * Parse compound
135 static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
136 int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
137 void *data1, void *data2)
139 const char *id;
140 snd_config_iterator_t i, next;
141 snd_config_t *n;
142 int err;
144 if (snd_config_get_id(cfg, &id) < 0)
145 return -EINVAL;
147 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
148 uc_error("compound type expected for %s", id);
149 return -EINVAL;
151 /* parse compound */
152 snd_config_for_each(i, next, cfg) {
153 n = snd_config_iterator_entry(i);
155 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
156 uc_error("compound type expected for %s, is %d", id, snd_config_get_type(cfg));
157 return -EINVAL;
160 err = fcn(uc_mgr, n, data1, data2);
161 if (err < 0)
162 return err;
165 return 0;
168 static int strip_legacy_dev_index(char *name)
170 char *dot = strchr(name, '.');
171 if (!dot)
172 return 0;
173 if (dot[1] != '0' || dot[2] != '\0') {
174 uc_error("device name %s contains a '.',"
175 " and is not legacy foo.0 format", name);
176 return -EINVAL;
178 *dot = '\0';
179 return 0;
183 * Parse device list
185 static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
186 struct dev_list *dev_list,
187 enum dev_list_type type,
188 snd_config_t *cfg)
190 struct dev_list_node *sdev;
191 const char *id;
192 snd_config_iterator_t i, next;
193 snd_config_t *n;
194 int err;
196 if (dev_list->type != DEVLIST_NONE) {
197 uc_error("error: multiple supported or"
198 " conflicting device lists");
199 return -EEXIST;
202 if (snd_config_get_id(cfg, &id) < 0)
203 return -EINVAL;
205 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
206 uc_error("compound type expected for %s", id);
207 return -EINVAL;
210 snd_config_for_each(i, next, cfg) {
211 n = snd_config_iterator_entry(i);
213 if (snd_config_get_id(n, &id) < 0)
214 return -EINVAL;
216 sdev = calloc(1, sizeof(struct dev_list_node));
217 if (sdev == NULL)
218 return -ENOMEM;
219 err = parse_string(n, &sdev->name);
220 if (err < 0) {
221 free(sdev);
222 return err;
224 err = strip_legacy_dev_index(sdev->name);
225 if (err < 0) {
226 free(sdev->name);
227 free(sdev);
228 return err;
230 list_add(&sdev->list, &dev_list->list);
233 dev_list->type = type;
235 return 0;
239 * Parse sequences.
241 * Sequence controls elements are in the following form:-
243 * cdev "hw:0"
244 * cset "element_id_syntax value_syntax"
245 * usleep time
246 * exec "any unix command with arguments"
248 * e.g.
249 * cset "name='Master Playback Switch' 0,0"
250 * cset "iface=PCM,name='Disable HDMI',index=1 0"
252 static int parse_sequence(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
253 struct list_head *base,
254 snd_config_t *cfg)
256 struct sequence_element *curr;
257 snd_config_iterator_t i, next;
258 snd_config_t *n;
259 int err, idx = 0;
260 const char *cmd = NULL;
262 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
263 uc_error("error: compound is expected for sequence definition");
264 return -EINVAL;
267 snd_config_for_each(i, next, cfg) {
268 const char *id;
269 idx ^= 1;
270 n = snd_config_iterator_entry(i);
271 err = snd_config_get_id(n, &id);
272 if (err < 0)
273 continue;
274 if (idx == 1) {
275 if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
276 uc_error("error: string type is expected for sequence command");
277 return -EINVAL;
279 snd_config_get_string(n, &cmd);
280 continue;
283 /* alloc new sequence element */
284 curr = calloc(1, sizeof(struct sequence_element));
285 if (curr == NULL)
286 return -ENOMEM;
287 list_add_tail(&curr->list, base);
289 if (strcmp(cmd, "cdev") == 0) {
290 curr->type = SEQUENCE_ELEMENT_TYPE_CDEV;
291 err = parse_string(n, &curr->data.cdev);
292 if (err < 0) {
293 uc_error("error: cdev requires a string!");
294 return err;
296 continue;
299 if (strcmp(cmd, "cset") == 0) {
300 curr->type = SEQUENCE_ELEMENT_TYPE_CSET;
301 err = parse_string(n, &curr->data.cset);
302 if (err < 0) {
303 uc_error("error: cset requires a string!");
304 return err;
306 continue;
309 if (strcmp(cmd, "usleep") == 0) {
310 curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
311 err = snd_config_get_integer(n, &curr->data.sleep);
312 if (err < 0) {
313 uc_error("error: usleep requires integer!");
314 return err;
316 continue;
319 if (strcmp(cmd, "msleep") == 0) {
320 curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
321 err = snd_config_get_integer(n, &curr->data.sleep);
322 if (err < 0) {
323 uc_error("error: msleep requires integer!");
324 return err;
326 curr->data.sleep *= 1000L;
327 continue;
330 if (strcmp(cmd, "exec") == 0) {
331 curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
332 err = parse_string(n, &curr->data.exec);
333 if (err < 0) {
334 uc_error("error: exec requires a string!");
335 return err;
337 continue;
340 list_del(&curr->list);
341 uc_mgr_free_sequence_element(curr);
344 return 0;
348 * Parse values.
350 * Parse values describing PCM, control/mixer settings and stream parameters.
352 * Value {
353 * TQ Voice
354 * CapturePCM "hw:1"
355 * PlaybackVolume "name='Master Playback Volume',index=2"
356 * PlaybackSwitch "name='Master Playback Switch',index=2"
359 static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
360 struct list_head *base,
361 snd_config_t *cfg)
363 struct ucm_value *curr;
364 snd_config_iterator_t i, next;
365 snd_config_t *n;
366 long l;
367 long long ll;
368 double d;
369 snd_config_type_t type;
370 int err;
372 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
373 uc_error("error: compound is expected for value definition");
374 return -EINVAL;
376 snd_config_for_each(i, next, cfg) {
377 const char *id;
378 n = snd_config_iterator_entry(i);
379 err = snd_config_get_id(n, &id);
380 if (err < 0)
381 continue;
383 /* alloc new value */
384 curr = calloc(1, sizeof(struct ucm_value));
385 if (curr == NULL)
386 return -ENOMEM;
387 list_add_tail(&curr->list, base);
388 curr->name = strdup(id);
389 if (curr->name == NULL)
390 return -ENOMEM;
391 type = snd_config_get_type(n);
392 switch (type) {
393 case SND_CONFIG_TYPE_INTEGER:
394 curr->data = malloc(16);
395 if (curr->data == NULL)
396 return -ENOMEM;
397 snd_config_get_integer(n, &l);
398 sprintf(curr->data, "%li", l);
399 break;
400 case SND_CONFIG_TYPE_INTEGER64:
401 curr->data = malloc(32);
402 if (curr->data == NULL)
403 return -ENOMEM;
404 snd_config_get_integer64(n, &ll);
405 sprintf(curr->data, "%lli", ll);
406 break;
407 case SND_CONFIG_TYPE_REAL:
408 curr->data = malloc(64);
409 if (curr->data == NULL)
410 return -ENOMEM;
411 snd_config_get_real(n, &d);
412 sprintf(curr->data, "%-16g", d);
413 break;
414 case SND_CONFIG_TYPE_STRING:
415 err = parse_string(n, &curr->data);
416 if (err < 0) {
417 uc_error("error: unable to parse a string for id '%s'!", id);
418 return err;
420 break;
421 default:
422 uc_error("error: invalid type %i in Value compound", type);
423 return -EINVAL;
427 return 0;
431 * Parse Modifier Use cases
433 * # Each modifier is described in new section. N modifiers are allowed
434 * SectionModifier."Capture Voice" {
436 * Comment "Record voice call"
438 * SupportedDevice [
439 * "x"
440 * "y"
443 * ConflictingDevice [
444 * "x"
445 * "y"
448 * EnableSequence [
449 * ....
452 * DisableSequence [
453 * ...
456 * TransitionSequence."ToModifierName" [
457 * ...
460 * # Optional TQ and ALSA PCMs
461 * Value {
462 * TQ Voice
463 * CapturePCM "hw:1"
464 * PlaybackVolume "name='Master Playback Volume',index=2"
465 * PlaybackSwitch "name='Master Playback Switch',index=2"
470 * SupportedDevice and ConflictingDevice cannot be specified together.
471 * Both are optional.
473 static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
474 snd_config_t *cfg,
475 void *data1,
476 void *data2)
478 struct use_case_verb *verb = data1;
479 struct use_case_modifier *modifier;
480 const char *name;
481 snd_config_iterator_t i, next;
482 snd_config_t *n;
483 int err;
485 if (data2) {
486 name = data2;
487 if (!parse_is_name_safe(name))
488 return -EINVAL;
490 else {
491 if (parse_get_safe_id(cfg, &name) < 0)
492 return -EINVAL;
495 /* allocate modifier */
496 modifier = calloc(1, sizeof(*modifier));
497 if (modifier == NULL)
498 return -ENOMEM;
499 INIT_LIST_HEAD(&modifier->enable_list);
500 INIT_LIST_HEAD(&modifier->disable_list);
501 INIT_LIST_HEAD(&modifier->transition_list);
502 INIT_LIST_HEAD(&modifier->dev_list.list);
503 INIT_LIST_HEAD(&modifier->value_list);
504 list_add_tail(&modifier->list, &verb->modifier_list);
505 modifier->name = strdup(name);
507 snd_config_for_each(i, next, cfg) {
508 const char *id;
509 n = snd_config_iterator_entry(i);
510 if (snd_config_get_id(n, &id) < 0)
511 continue;
513 if (strcmp(id, "Comment") == 0) {
514 err = parse_string(n, &modifier->comment);
515 if (err < 0) {
516 uc_error("error: failed to get modifier comment");
517 return err;
519 continue;
522 if (strcmp(id, "SupportedDevice") == 0) {
523 err = parse_device_list(uc_mgr, &modifier->dev_list,
524 DEVLIST_SUPPORTED, n);
525 if (err < 0) {
526 uc_error("error: failed to parse supported"
527 " device list");
528 return err;
532 if (strcmp(id, "ConflictingDevice") == 0) {
533 err = parse_device_list(uc_mgr, &modifier->dev_list,
534 DEVLIST_CONFLICTING, n);
535 if (err < 0) {
536 uc_error("error: failed to parse conflicting"
537 " device list");
538 return err;
542 if (strcmp(id, "EnableSequence") == 0) {
543 err = parse_sequence(uc_mgr, &modifier->enable_list, n);
544 if (err < 0) {
545 uc_error("error: failed to parse modifier"
546 " enable sequence");
547 return err;
549 continue;
552 if (strcmp(id, "DisableSequence") == 0) {
553 err = parse_sequence(uc_mgr, &modifier->disable_list, n);
554 if (err < 0) {
555 uc_error("error: failed to parse modifier"
556 " disable sequence");
557 return err;
559 continue;
562 if (strcmp(id, "TransitionSequence") == 0) {
563 err = parse_transition(uc_mgr, &modifier->transition_list, n);
564 if (err < 0) {
565 uc_error("error: failed to parse transition"
566 " modifier");
567 return err;
569 continue;
572 if (strcmp(id, "Value") == 0) {
573 err = parse_value(uc_mgr, &modifier->value_list, n);
574 if (err < 0) {
575 uc_error("error: failed to parse Value");
576 return err;
578 continue;
582 return 0;
586 * Parse Device Use Cases
588 *# Each device is described in new section. N devices are allowed
589 *SectionDevice."Headphones" {
590 * Comment "Headphones connected to 3.5mm jack"
592 * upportedDevice [
593 * "x"
594 * "y"
597 * ConflictingDevice [
598 * "x"
599 * "y"
602 * EnableSequence [
603 * ....
606 * DisableSequence [
607 * ...
610 * TransitionSequence."ToDevice" [
611 * ...
614 * Value {
615 * PlaybackVolume "name='Master Playback Volume',index=2"
616 * PlaybackSwitch "name='Master Playback Switch',index=2"
620 static int parse_device(snd_use_case_mgr_t *uc_mgr,
621 snd_config_t *cfg,
622 void *data1,
623 void *data2)
625 struct use_case_verb *verb = data1;
626 const char *name;
627 struct use_case_device *device;
628 snd_config_iterator_t i, next;
629 snd_config_t *n;
630 int err;
632 if (data2) {
633 name = data2;
634 if (!parse_is_name_safe(name))
635 return -EINVAL;
637 else {
638 if (parse_get_safe_id(cfg, &name) < 0)
639 return -EINVAL;
642 device = calloc(1, sizeof(*device));
643 if (device == NULL)
644 return -ENOMEM;
645 INIT_LIST_HEAD(&device->enable_list);
646 INIT_LIST_HEAD(&device->disable_list);
647 INIT_LIST_HEAD(&device->transition_list);
648 INIT_LIST_HEAD(&device->dev_list.list);
649 INIT_LIST_HEAD(&device->value_list);
650 list_add_tail(&device->list, &verb->device_list);
651 device->name = strdup(name);
653 snd_config_for_each(i, next, cfg) {
654 const char *id;
655 n = snd_config_iterator_entry(i);
656 if (snd_config_get_id(n, &id) < 0)
657 continue;
659 if (strcmp(id, "Comment") == 0) {
660 err = parse_string(n, &device->comment);
661 if (err < 0) {
662 uc_error("error: failed to get device comment");
663 return err;
665 continue;
668 if (strcmp(id, "SupportedDevice") == 0) {
669 err = parse_device_list(uc_mgr, &device->dev_list,
670 DEVLIST_SUPPORTED, n);
671 if (err < 0) {
672 uc_error("error: failed to parse supported"
673 " device list");
674 return err;
678 if (strcmp(id, "ConflictingDevice") == 0) {
679 err = parse_device_list(uc_mgr, &device->dev_list,
680 DEVLIST_CONFLICTING, n);
681 if (err < 0) {
682 uc_error("error: failed to parse conflicting"
683 " device list");
684 return err;
688 if (strcmp(id, "EnableSequence") == 0) {
689 uc_dbg("EnableSequence");
690 err = parse_sequence(uc_mgr, &device->enable_list, n);
691 if (err < 0) {
692 uc_error("error: failed to parse device enable"
693 " sequence");
694 return err;
696 continue;
699 if (strcmp(id, "DisableSequence") == 0) {
700 uc_dbg("DisableSequence");
701 err = parse_sequence(uc_mgr, &device->disable_list, n);
702 if (err < 0) {
703 uc_error("error: failed to parse device disable"
704 " sequence");
705 return err;
707 continue;
710 if (strcmp(id, "TransitionSequence") == 0) {
711 uc_dbg("TransitionSequence");
712 err = parse_transition(uc_mgr, &device->transition_list, n);
713 if (err < 0) {
714 uc_error("error: failed to parse transition"
715 " device");
716 return err;
718 continue;
721 if (strcmp(id, "Value") == 0) {
722 err = parse_value(uc_mgr, &device->value_list, n);
723 if (err < 0) {
724 uc_error("error: failed to parse Value");
725 return err;
727 continue;
730 return 0;
733 static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
734 snd_config_t *cfg,
735 int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
736 void *data1)
738 const char *id, *idchild;
739 int child_ctr = 0, legacy_format = 1;
740 snd_config_iterator_t i, next;
741 snd_config_t *child;
742 int err;
744 err = snd_config_get_id(cfg, &id);
745 if (err < 0)
746 return err;
748 snd_config_for_each(i, next, cfg) {
749 child_ctr++;
750 if (child_ctr > 1) {
751 break;
754 child = snd_config_iterator_entry(i);
756 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
757 legacy_format = 0;
758 break;
761 if (snd_config_get_id(child, &idchild) < 0)
762 return -EINVAL;
764 if (strcmp(idchild, "0")) {
765 legacy_format = 0;
766 break;
769 if (child_ctr != 1) {
770 legacy_format = 0;
773 if (legacy_format)
774 return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id);
775 else
776 return fcn(uc_mgr, cfg, data1, NULL);
779 static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
780 snd_config_t *cfg,
781 void *data1,
782 void *data2 ATTRIBUTE_UNUSED)
784 return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
787 static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
788 snd_config_t *cfg,
789 void *data1,
790 void *data2 ATTRIBUTE_UNUSED)
792 return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
796 * Parse Verb Section
798 * # Example Use case verb section for Voice call blah
799 * # By Joe Blogs <joe@blogs.com>
801 * SectionVerb {
802 * # enable and disable sequences are compulsory
803 * EnableSequence [
804 * cset "name='Master Playback Switch',index=2 0,0"
805 * cset "name='Master Playback Volume',index=2 25,25"
806 * msleep 50
807 * cset "name='Master Playback Switch',index=2 1,1"
808 * cset "name='Master Playback Volume',index=2 50,50"
811 * DisableSequence [
812 * cset "name='Master Playback Switch',index=2 0,0"
813 * cset "name='Master Playback Volume',index=2 25,25"
814 * msleep 50
815 * cset "name='Master Playback Switch',index=2 1,1"
816 * cset "name='Master Playback Volume',index=2 50,50"
819 * # Optional transition verb
820 * TransitionSequence."ToCaseName" [
821 * msleep 1
824 * # Optional TQ and ALSA PCMs
825 * Value {
826 * TQ HiFi
827 * CapturePCM "hw:0"
828 * PlaybackPCM "hw:0"
832 static int parse_verb(snd_use_case_mgr_t *uc_mgr,
833 struct use_case_verb *verb,
834 snd_config_t *cfg)
836 snd_config_iterator_t i, next;
837 snd_config_t *n;
838 int err;
840 /* parse verb section */
841 snd_config_for_each(i, next, cfg) {
842 const char *id;
843 n = snd_config_iterator_entry(i);
844 if (snd_config_get_id(n, &id) < 0)
845 continue;
847 if (strcmp(id, "EnableSequence") == 0) {
848 uc_dbg("Parse EnableSequence");
849 err = parse_sequence(uc_mgr, &verb->enable_list, n);
850 if (err < 0) {
851 uc_error("error: failed to parse verb enable sequence");
852 return err;
854 continue;
857 if (strcmp(id, "DisableSequence") == 0) {
858 uc_dbg("Parse DisableSequence");
859 err = parse_sequence(uc_mgr, &verb->disable_list, n);
860 if (err < 0) {
861 uc_error("error: failed to parse verb disable sequence");
862 return err;
864 continue;
867 if (strcmp(id, "TransitionSequence") == 0) {
868 uc_dbg("Parse TransitionSequence");
869 err = parse_transition(uc_mgr, &verb->transition_list, n);
870 if (err < 0) {
871 uc_error("error: failed to parse transition sequence");
872 return err;
874 continue;
877 if (strcmp(id, "Value") == 0) {
878 uc_dbg("Parse Value");
879 err = parse_value(uc_mgr, &verb->value_list, n);
880 if (err < 0)
881 return err;
882 continue;
886 return 0;
890 * Parse a Use case verb file.
892 * This file contains the following :-
893 * o Verb enable and disable sequences.
894 * o Supported Device enable and disable sequences for verb.
895 * o Supported Modifier enable and disable sequences for verb
896 * o Optional QoS for the verb and modifiers.
897 * o Optional PCM device ID for verb and modifiers
898 * o Alias kcontrols IDs for master and volumes and mutes.
900 static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
901 const char *use_case_name,
902 const char *comment,
903 const char *file)
905 snd_config_iterator_t i, next;
906 snd_config_t *n;
907 struct use_case_verb *verb;
908 snd_config_t *cfg;
909 char filename[MAX_FILE];
910 char *env = getenv(ALSA_CONFIG_UCM_VAR);
911 int err;
913 /* allocate verb */
914 verb = calloc(1, sizeof(struct use_case_verb));
915 if (verb == NULL)
916 return -ENOMEM;
917 INIT_LIST_HEAD(&verb->enable_list);
918 INIT_LIST_HEAD(&verb->disable_list);
919 INIT_LIST_HEAD(&verb->transition_list);
920 INIT_LIST_HEAD(&verb->device_list);
921 INIT_LIST_HEAD(&verb->modifier_list);
922 INIT_LIST_HEAD(&verb->value_list);
923 list_add_tail(&verb->list, &uc_mgr->verb_list);
924 if (use_case_name == NULL)
925 return -EINVAL;
926 verb->name = strdup(use_case_name);
927 if (verb->name == NULL)
928 return -ENOMEM;
930 if (comment != NULL) {
931 verb->comment = strdup(comment);
932 if (verb->comment == NULL)
933 return -ENOMEM;
936 /* open Verb file for reading */
937 snprintf(filename, sizeof(filename), "%s/%s/%s",
938 env ? env : ALSA_USE_CASE_DIR,
939 uc_mgr->card_name, file);
940 filename[sizeof(filename)-1] = '\0';
942 err = uc_mgr_config_load(filename, &cfg);
943 if (err < 0) {
944 uc_error("error: failed to open verb file %s : %d",
945 filename, -errno);
946 return err;
949 /* parse master config sections */
950 snd_config_for_each(i, next, cfg) {
951 const char *id;
952 n = snd_config_iterator_entry(i);
953 if (snd_config_get_id(n, &id) < 0)
954 continue;
956 /* find verb section and parse it */
957 if (strcmp(id, "SectionVerb") == 0) {
958 err = parse_verb(uc_mgr, verb, n);
959 if (err < 0) {
960 uc_error("error: %s failed to parse verb",
961 file);
962 return err;
964 continue;
967 /* find device sections and parse them */
968 if (strcmp(id, "SectionDevice") == 0) {
969 err = parse_compound(uc_mgr, n,
970 parse_device_name, verb, NULL);
971 if (err < 0) {
972 uc_error("error: %s failed to parse device",
973 file);
974 return err;
976 continue;
979 /* find modifier sections and parse them */
980 if (strcmp(id, "SectionModifier") == 0) {
981 err = parse_compound(uc_mgr, n,
982 parse_modifier_name, verb, NULL);
983 if (err < 0) {
984 uc_error("error: %s failed to parse modifier",
985 file);
986 return err;
988 continue;
992 /* use case verb must have at least 1 device */
993 if (list_empty(&verb->device_list)) {
994 uc_error("error: no use case device defined", file);
995 return -EINVAL;
997 return 0;
1001 * Parse master section for "Use Case" and "File" tags.
1003 static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
1004 void *data1 ATTRIBUTE_UNUSED,
1005 void *data2 ATTRIBUTE_UNUSED)
1007 snd_config_iterator_t i, next;
1008 snd_config_t *n;
1009 const char *use_case_name, *file = NULL, *comment = NULL;
1010 int err;
1012 if (snd_config_get_id(cfg, &use_case_name) < 0) {
1013 uc_error("unable to get name for use case section");
1014 return -EINVAL;
1017 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1018 uc_error("compound type expected for use case section");
1019 return -EINVAL;
1021 /* parse master config sections */
1022 snd_config_for_each(i, next, cfg) {
1023 const char *id;
1024 n = snd_config_iterator_entry(i);
1025 if (snd_config_get_id(n, &id) < 0)
1026 continue;
1028 /* get use case verb file name */
1029 if (strcmp(id, "File") == 0) {
1030 err = snd_config_get_string(n, &file);
1031 if (err < 0) {
1032 uc_error("failed to get File");
1033 return err;
1035 continue;
1038 /* get optional use case comment */
1039 if (strncmp(id, "Comment", 7) == 0) {
1040 err = snd_config_get_string(n, &comment);
1041 if (err < 0) {
1042 uc_error("error: failed to get Comment");
1043 return err;
1045 continue;
1048 uc_error("unknown field %s in master section");
1051 uc_dbg("use_case_name %s file '%s'", use_case_name, file);
1053 /* do we have both use case name and file ? */
1054 if (!file) {
1055 uc_error("error: use case missing file");
1056 return -EINVAL;
1059 /* parse verb file */
1060 return parse_verb_file(uc_mgr, use_case_name, comment, file);
1064 * parse controls
1066 static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
1068 int err;
1070 if (!list_empty(&uc_mgr->default_list)) {
1071 uc_error("Default list is not empty");
1072 return -EINVAL;
1074 err = parse_sequence(uc_mgr, &uc_mgr->default_list, cfg);
1075 if (err < 0) {
1076 uc_error("Unable to parse SectionDefaults");
1077 return err;
1080 return 0;
1084 * Each sound card has a master sound card file that lists all the supported
1085 * use case verbs for that sound card. i.e.
1087 * #Example master file for blah sound card
1088 * #By Joe Blogs <joe@bloggs.org>
1090 * Comment "Nice Abstracted Soundcard"
1092 * # The file is divided into Use case sections. One section per use case verb.
1094 * SectionUseCase."Voice Call" {
1095 * File "voice_call_blah"
1096 * Comment "Make a voice phone call."
1099 * SectionUseCase."HiFi" {
1100 * File "hifi_blah"
1101 * Comment "Play and record HiFi quality Music."
1104 * # Define Value defaults
1106 * ValueDefaults {
1107 * PlaybackCTL "hw:CARD=0"
1108 * CaptureCTL "hw:CARD=0"
1111 * # This file also stores the default sound card state.
1113 * SectionDefaults [
1114 * cset "name='Master Playback Switch',index=2 1,1"
1115 * cset "name='Master Playback Volume',index=2 25,25"
1116 * cset "name='Master Mono Playback',index=1 0"
1117 * cset "name='Master Mono Playback Volume',index=1 0"
1118 * cset "name='PCM Switch',index=2 1,1"
1119 * exec "some binary here"
1120 * msleep 50
1121 * ........
1124 * # End of example file.
1126 static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
1128 snd_config_iterator_t i, next;
1129 snd_config_t *n;
1130 const char *id;
1131 int err;
1133 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1134 uc_error("compound type expected for master file");
1135 return -EINVAL;
1138 /* parse master config sections */
1139 snd_config_for_each(i, next, cfg) {
1141 n = snd_config_iterator_entry(i);
1142 if (snd_config_get_id(n, &id) < 0)
1143 continue;
1145 if (strcmp(id, "Comment") == 0) {
1146 err = parse_string(n, &uc_mgr->comment);
1147 if (err < 0) {
1148 uc_error("error: failed to get master comment");
1149 return err;
1151 continue;
1154 /* find use case section and parse it */
1155 if (strcmp(id, "SectionUseCase") == 0) {
1156 err = parse_compound(uc_mgr, n,
1157 parse_master_section,
1158 NULL, NULL);
1159 if (err < 0)
1160 return err;
1161 continue;
1164 /* find default control values section and parse it */
1165 if (strcmp(id, "SectionDefaults") == 0) {
1166 err = parse_controls(uc_mgr, n);
1167 if (err < 0)
1168 return err;
1169 continue;
1172 /* get the default values */
1173 if (strcmp(id, "ValueDefaults") == 0) {
1174 err = parse_value(uc_mgr, &uc_mgr->value_list, n);
1175 if (err < 0) {
1176 uc_error("error: failed to parse ValueDefaults");
1177 return err;
1179 continue;
1182 uc_error("uknown master file field %s", id);
1184 return 0;
1187 static int load_master_config(const char *card_name, snd_config_t **cfg)
1189 char filename[MAX_FILE];
1190 char *env = getenv(ALSA_CONFIG_UCM_VAR);
1191 int err;
1193 snprintf(filename, sizeof(filename)-1,
1194 "%s/%s/%s.conf", env ? env : ALSA_USE_CASE_DIR,
1195 card_name, card_name);
1196 filename[MAX_FILE-1] = '\0';
1198 err = uc_mgr_config_load(filename, cfg);
1199 if (err < 0) {
1200 uc_error("error: could not parse configuration for card %s",
1201 card_name);
1202 return err;
1205 return 0;
1208 /* load master use case file for sound card */
1209 int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
1211 snd_config_t *cfg;
1212 int err;
1214 err = load_master_config(uc_mgr->card_name, &cfg);
1215 if (err < 0)
1216 return err;
1217 err = parse_master_file(uc_mgr, cfg);
1218 snd_config_delete(cfg);
1219 if (err < 0)
1220 uc_mgr_free_verb(uc_mgr);
1222 return err;
1225 static int filename_filter(const struct dirent *dirent)
1227 if (dirent == NULL)
1228 return 0;
1229 if (dirent->d_type == DT_DIR) {
1230 if (dirent->d_name[0] == '.') {
1231 if (dirent->d_name[1] == '\0')
1232 return 0;
1233 if (dirent->d_name[1] == '.' &&
1234 dirent->d_name[2] == '\0')
1235 return 0;
1237 return 1;
1239 return 0;
1242 /* scan all cards and comments */
1243 int uc_mgr_scan_master_configs(const char **_list[])
1245 char filename[MAX_FILE], dfl[MAX_FILE];
1246 char *env = getenv(ALSA_CONFIG_UCM_VAR);
1247 const char **list;
1248 snd_config_t *cfg, *c;
1249 int i, cnt, err;
1250 ssize_t ss;
1251 struct dirent **namelist;
1253 snprintf(filename, sizeof(filename)-1,
1254 "%s", env ? env : ALSA_USE_CASE_DIR);
1255 filename[MAX_FILE-1] = '\0';
1257 #ifdef _GNU_SOURCE
1258 #define SORTFUNC versionsort
1259 #else
1260 #define SORTFUNC alphasort
1261 #endif
1262 err = scandir(filename, &namelist, filename_filter, SORTFUNC);
1263 if (err < 0) {
1264 err = -errno;
1265 uc_error("error: could not scan directory %s: %s",
1266 filename, strerror(-err));
1267 return err;
1269 cnt = err;
1271 dfl[0] = '\0';
1272 if (strlen(filename) + 8 < sizeof(filename)) {
1273 strcat(filename, "/default");
1274 ss = readlink(filename, dfl, sizeof(dfl)-1);
1275 if (ss >= 0) {
1276 dfl[ss] = '\0';
1277 dfl[sizeof(dfl)-1] = '\0';
1278 if (dfl[0] && dfl[strlen(dfl)-1] == '/')
1279 dfl[strlen(dfl)-1] = '\0';
1280 } else {
1281 dfl[0] = '\0';
1285 list = calloc(1, cnt * 2 * sizeof(char *));
1286 if (list == NULL) {
1287 err = -ENOMEM;
1288 goto __err;
1291 for (i = 0; i < cnt; i++) {
1292 err = load_master_config(namelist[i]->d_name, &cfg);
1293 if (err < 0)
1294 goto __err;
1295 err = snd_config_search(cfg, "Comment", &c);
1296 if (err >= 0) {
1297 err = parse_string(c, (char **)&list[i*2+1]);
1298 if (err < 0) {
1299 snd_config_delete(cfg);
1300 goto __err;
1303 snd_config_delete(cfg);
1304 list[i * 2] = strdup(namelist[i]->d_name);
1305 if (list[i * 2] == NULL) {
1306 err = -ENOMEM;
1307 goto __err;
1309 if (strcmp(dfl, list[i * 2]) == 0) {
1310 /* default to top */
1311 const char *save1 = list[i * 2];
1312 const char *save2 = list[i * 2 + 1];
1313 memmove(list + 2, list, i * 2 * sizeof(char *));
1314 list[0] = save1;
1315 list[1] = save2;
1318 err = cnt * 2;
1320 __err:
1321 for (i = 0; i < cnt; i++) {
1322 free(namelist[i]);
1323 if (err < 0) {
1324 free((void *)list[i * 2]);
1325 free((void *)list[i * 2 + 1]);
1328 free(namelist);
1330 if (err >= 0)
1331 *_list = list;
1333 return err;