tizen 2.3.1 release
[external/alsa-lib.git] / src / ucm / parser.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 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 <dirent.h>
35
36 /** The name of the environment variable containing the UCM directory */
37 #define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM"
38
39 static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
40                           struct list_head *base,
41                           snd_config_t *cfg);
42
43 /*
44  * Parse string
45  */
46 int parse_string(snd_config_t *n, char **res)
47 {
48         int err;
49
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;
57 }
58
59 /*
60  * Parse safe ID
61  */
62 int parse_is_name_safe(const char *name)
63 {
64         if (strchr(name, '.')) {
65                 uc_error("char '.' not allowed in '%s'", name);
66                 return 0;
67         }
68         return 1;
69 }
70
71 int parse_get_safe_id(snd_config_t *n, const char **id)
72 {
73         int err;
74
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;
81 }
82
83 /*
84  * Parse transition
85  */
86 static int parse_transition(snd_use_case_mgr_t *uc_mgr,
87                             struct list_head *tlist,
88                             snd_config_t *cfg)
89 {
90         struct transition_sequence *tseq;
91         const char *id;
92         snd_config_iterator_t i, next;
93         snd_config_t *n;
94         int err;
95
96         if (snd_config_get_id(cfg, &id) < 0)
97                 return -EINVAL;
98
99         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
100                 uc_error("compound type expected for %s", id);
101                 return -EINVAL;
102         }
103
104         snd_config_for_each(i, next, cfg) {
105                 n = snd_config_iterator_entry(i);
106
107                 if (snd_config_get_id(n, &id) < 0)
108                         return -EINVAL;
109
110                 tseq = calloc(1, sizeof(*tseq));
111                 if (tseq == NULL)
112                         return -ENOMEM;
113                 INIT_LIST_HEAD(&tseq->transition_list);
114
115                 tseq->name = strdup(id);
116                 if (tseq->name == NULL) {
117                         free(tseq);
118                         return -ENOMEM;
119                 }
120         
121                 err = parse_sequence(uc_mgr, &tseq->transition_list, n);
122                 if (err < 0) {
123                         uc_mgr_free_transition_element(tseq);
124                         return err;
125                 }
126
127                 list_add(&tseq->list, tlist);
128         }
129         return 0;
130 }
131
132 /*
133  * Parse compound
134  */
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)
138 {
139         const char *id;
140         snd_config_iterator_t i, next;
141         snd_config_t *n;
142         int err;
143
144         if (snd_config_get_id(cfg, &id) < 0)
145                 return -EINVAL;
146         
147         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
148                 uc_error("compound type expected for %s", id);
149                 return -EINVAL;
150         }
151         /* parse compound */
152         snd_config_for_each(i, next, cfg) {
153                 n = snd_config_iterator_entry(i);
154
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;
158                 }
159                 
160                 err = fcn(uc_mgr, n, data1, data2);
161                 if (err < 0)
162                         return err;
163         }
164
165         return 0;
166 }
167
168 static int strip_legacy_dev_index(char *name)
169 {
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;
177         }
178         *dot = '\0';
179         return 0;
180 }
181
182 /*
183  * Parse device list
184  */
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)
189 {
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;
195
196         if (dev_list->type != DEVLIST_NONE) {
197                 uc_error("error: multiple supported or"
198                         " conflicting device lists");
199                 return -EEXIST;
200         }
201
202         if (snd_config_get_id(cfg, &id) < 0)
203                 return -EINVAL;
204
205         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
206                 uc_error("compound type expected for %s", id);
207                 return -EINVAL;
208         }
209
210         snd_config_for_each(i, next, cfg) {
211                 n = snd_config_iterator_entry(i);
212
213                 if (snd_config_get_id(n, &id) < 0)
214                         return -EINVAL;
215
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;
223                 }
224                 err = strip_legacy_dev_index(sdev->name);
225                 if (err < 0) {
226                         free(sdev->name);
227                         free(sdev);
228                         return err;
229                 }
230                 list_add(&sdev->list, &dev_list->list);
231         }
232
233         dev_list->type = type;
234
235         return 0;
236 }
237
238 /*
239  * Parse sequences.
240  *
241  * Sequence controls elements  are in the following form:-
242  *
243  * cdev "hw:0"
244  * cset "element_id_syntax value_syntax"
245  * usleep time
246  * exec "any unix command with arguments"
247  *
248  * e.g.
249  *      cset "name='Master Playback Switch' 0,0"
250  *      cset "iface=PCM,name='Disable HDMI',index=1 0"
251  */
252 static int parse_sequence(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
253                           struct list_head *base,
254                           snd_config_t *cfg)
255 {
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;
261
262         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
263                 uc_error("error: compound is expected for sequence definition");
264                 return -EINVAL;
265         }
266
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;
278                         }
279                         snd_config_get_string(n, &cmd);
280                         continue;
281                 }
282
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);
288
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;
295                         }
296                         continue;
297                 }
298
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;
305                         }
306                         continue;
307                 }
308
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;
315                         }
316                         continue;
317                 }
318
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;
325                         }
326                         curr->data.sleep *= 1000L;
327                         continue;
328                 }
329
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;
336                         }
337                         continue;
338                 }
339                 
340                 list_del(&curr->list);
341                 uc_mgr_free_sequence_element(curr);
342         }
343
344         return 0;
345 }
346
347 /*
348  * Parse values.
349  *
350  * Parse values describing PCM, control/mixer settings and stream parameters.
351  *
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"
357  * }
358  */
359 static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
360                           struct list_head *base,
361                           snd_config_t *cfg)
362 {
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;
371
372         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
373                 uc_error("error: compound is expected for value definition");
374                 return -EINVAL;
375         }
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;
382
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;
419                         }
420                         break;
421                 default:
422                         uc_error("error: invalid type %i in Value compound", type);
423                         return -EINVAL;
424                 }
425         }
426
427         return 0;
428 }
429
430 /*
431  * Parse Modifier Use cases
432  *
433  *      # Each modifier is described in new section. N modifiers are allowed
434  *      SectionModifier."Capture Voice" {
435  *
436  *              Comment "Record voice call"
437  *
438  *              SupportedDevice [
439  *                      "x"
440  *                      "y"
441  *              ]
442  *
443  *              ConflictingDevice [
444  *                      "x"
445  *                      "y"
446  *              ]
447  *
448  *              EnableSequence [
449  *                      ....
450  *              ]
451  *
452  *              DisableSequence [
453  *                      ...
454  *              ]
455  *
456  *              TransitionSequence."ToModifierName" [
457  *                      ...
458  *              ]
459  *
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"
466  *              }
467  *
468  *       }
469  *
470  * SupportedDevice and ConflictingDevice cannot be specified together.
471  * Both are optional.
472  */
473 static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
474                 snd_config_t *cfg,
475                 void *data1,
476                 void *data2)
477 {
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;
484
485         if (data2) {
486                 name = data2;
487                 if (!parse_is_name_safe(name))
488                         return -EINVAL;
489         }
490         else {
491                 if (parse_get_safe_id(cfg, &name) < 0)
492                         return -EINVAL;
493         }
494
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);
506
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;
512
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;
518                         }
519                         continue;
520                 }
521
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;
529                         }
530                 }
531
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;
539                         }
540                 }
541
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;
548                         }
549                         continue;
550                 }
551
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;
558                         }
559                         continue;
560                 }
561
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;
568                         }
569                         continue;
570                 }
571
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;
577                         }
578                         continue;
579                 }
580         }
581
582         return 0;
583 }
584
585 /*
586  * Parse Device Use Cases
587  *
588  *# Each device is described in new section. N devices are allowed
589  *SectionDevice."Headphones" {
590  *      Comment "Headphones connected to 3.5mm jack"
591  *
592  *      upportedDevice [
593  *              "x"
594  *              "y"
595  *      ]
596  *
597  *      ConflictingDevice [
598  *              "x"
599  *              "y"
600  *      ]
601  *
602  *      EnableSequence [
603  *              ....
604  *      ]
605  *
606  *      DisableSequence [
607  *              ...
608  *      ]
609  *
610  *      TransitionSequence."ToDevice" [
611  *              ...
612  *      ]
613  *
614  *      Value {
615  *              PlaybackVolume "name='Master Playback Volume',index=2"
616  *              PlaybackSwitch "name='Master Playback Switch',index=2"
617  *      }
618  * }
619  */
620 static int parse_device(snd_use_case_mgr_t *uc_mgr,
621                         snd_config_t *cfg,
622                         void *data1,
623                         void *data2)
624 {
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;
631
632         if (data2) {
633                 name = data2;
634                 if (!parse_is_name_safe(name))
635                         return -EINVAL;
636         }
637         else {
638                 if (parse_get_safe_id(cfg, &name) < 0)
639                         return -EINVAL;
640         }
641
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);
652
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;
658
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;
664                         }
665                         continue;
666                 }
667
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;
675                         }
676                 }
677
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;
685                         }
686                 }
687
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;
695                         }
696                         continue;
697                 }
698
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;
706                         }
707                         continue;
708                 }
709
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;
717                         }
718                         continue;
719                 }
720
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;
726                         }
727                         continue;
728                 }
729         }
730         return 0;
731 }
732
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)
737 {
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;
743
744         err = snd_config_get_id(cfg, &id);
745         if (err < 0)
746                 return err;
747
748         snd_config_for_each(i, next, cfg) {
749                 child_ctr++;
750                 if (child_ctr > 1) {
751                         break;
752                 }
753
754                 child = snd_config_iterator_entry(i);
755
756                 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
757                         legacy_format = 0;
758                         break;
759                 }
760
761                 if (snd_config_get_id(child, &idchild) < 0)
762                         return -EINVAL;
763
764                 if (strcmp(idchild, "0")) {
765                         legacy_format = 0;
766                         break;
767                 }
768         }
769         if (child_ctr != 1) {
770                 legacy_format = 0;
771         }
772
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);
777 }
778
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)
783 {
784         return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
785 }
786
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)
791 {
792         return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
793 }
794
795 /*
796  * Parse Verb Section
797  *
798  * # Example Use case verb section for Voice call blah
799  * # By Joe Blogs <joe@blogs.com>
800  *
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"
809  *      ]
810  *
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"
817  *      ]
818  *
819  *      # Optional transition verb
820  *      TransitionSequence."ToCaseName" [
821  *              msleep 1
822  *      ]
823  *
824  *      # Optional TQ and ALSA PCMs
825  *      Value {
826  *              TQ HiFi
827  *              CapturePCM "hw:0"
828  *              PlaybackPCM "hw:0"
829  *      }
830  * }
831  */
832 static int parse_verb(snd_use_case_mgr_t *uc_mgr,
833                       struct use_case_verb *verb,
834                       snd_config_t *cfg)
835 {
836         snd_config_iterator_t i, next;
837         snd_config_t *n;
838         int err;
839         
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;
846
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;
853                         }
854                         continue;
855                 }
856
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;
863                         }
864                         continue;
865                 }
866
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;
873                         }
874                         continue;
875                 }
876
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;
883                 }
884         }
885
886         return 0;
887 }
888
889 /*
890  * Parse a Use case verb file.
891  *
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.
899  */
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)
904 {
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;
912
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;
929
930         if (comment != NULL) {
931                 verb->comment = strdup(comment);
932                 if (verb->comment == NULL)
933                         return -ENOMEM;
934         }
935
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';
941         
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;
947         }
948
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;
955
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;
963                         }
964                         continue;
965                 }
966
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;
975                         }
976                         continue;
977                 }
978
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;
987                         }
988                         continue;
989                 }
990         }
991
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;
996         }
997         return 0;
998 }
999
1000 /*
1001  * Parse master section for "Use Case" and "File" tags.
1002  */
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)
1006 {
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;
1011
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;
1015         }
1016
1017         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1018                 uc_error("compound type expected for use case section");
1019                 return -EINVAL;
1020         }
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;
1027
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;
1034                         }
1035                         continue;
1036                 }
1037
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;
1044                         }
1045                         continue;
1046                 }
1047
1048                 uc_error("unknown field %s in master section");
1049         }
1050
1051         uc_dbg("use_case_name %s file '%s'", use_case_name, file);
1052
1053         /* do we have both use case name and file ? */
1054         if (!file) {
1055                 uc_error("error: use case missing file");
1056                 return -EINVAL;
1057         }
1058
1059         /* parse verb file */
1060         return parse_verb_file(uc_mgr, use_case_name, comment, file);
1061 }
1062
1063 /*
1064  * parse controls
1065  */
1066 static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
1067 {
1068         int err;
1069         
1070         if (!list_empty(&uc_mgr->default_list)) {
1071                 uc_error("Default list is not empty");
1072                 return -EINVAL;
1073         }
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;
1078         }
1079         
1080         return 0;
1081 }
1082
1083 /*
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.
1086  *
1087  * #Example master file for blah sound card
1088  * #By Joe Blogs <joe@bloggs.org>
1089  *
1090  * Comment "Nice Abstracted Soundcard"
1091  *
1092  * # The file is divided into Use case sections. One section per use case verb.
1093  *
1094  * SectionUseCase."Voice Call" {
1095  *      File "voice_call_blah"
1096  *      Comment "Make a voice phone call."
1097  * }
1098  *
1099  * SectionUseCase."HiFi" {
1100  *      File "hifi_blah"
1101  *      Comment "Play and record HiFi quality Music."
1102  * }
1103  *
1104  * # Define Value defaults
1105  *
1106  * ValueDefaults {
1107  *      PlaybackCTL "hw:CARD=0"
1108  *      CaptureCTL "hw:CARD=0"
1109  * }
1110  *
1111  * # This file also stores the default sound card state.
1112  *
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  *      ........
1122  * ]
1123  *
1124  * # End of example file.
1125  */
1126 static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
1127 {
1128         snd_config_iterator_t i, next;
1129         snd_config_t *n;
1130         const char *id;
1131         int err;
1132
1133         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
1134                 uc_error("compound type expected for master file");
1135                 return -EINVAL;
1136         }
1137
1138         /* parse master config sections */
1139         snd_config_for_each(i, next, cfg) {
1140
1141                 n = snd_config_iterator_entry(i);
1142                 if (snd_config_get_id(n, &id) < 0)
1143                         continue;
1144
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;
1150                         }
1151                         continue;
1152                 }
1153
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;
1162                 }
1163
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;
1170                 }
1171
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;
1178                         }
1179                         continue;
1180                 }
1181
1182                 uc_error("uknown master file field %s", id);
1183         }
1184         return 0;
1185 }
1186
1187 static int load_master_config(const char *card_name, snd_config_t **cfg)
1188 {
1189         char filename[MAX_FILE];
1190         char *env = getenv(ALSA_CONFIG_UCM_VAR);
1191         int err;
1192
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';
1197
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;
1203         }
1204
1205         return 0;
1206 }
1207
1208 /* load master use case file for sound card */
1209 int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
1210 {
1211         snd_config_t *cfg;
1212         int err;
1213
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);
1221
1222         return err;
1223 }
1224
1225 static int filename_filter(const struct dirent *dirent)
1226 {
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;
1236                 }
1237                 return 1;
1238         }
1239         return 0;
1240 }
1241
1242 /* scan all cards and comments */
1243 int uc_mgr_scan_master_configs(const char **_list[])
1244 {
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;
1252
1253         snprintf(filename, sizeof(filename)-1,
1254                 "%s", env ? env : ALSA_USE_CASE_DIR);
1255         filename[MAX_FILE-1] = '\0';
1256
1257         err = scandir(filename, &namelist, filename_filter, versionsort);
1258         if (err < 0) {
1259                 err = -errno;
1260                 uc_error("error: could not scan directory %s: %s",
1261                                 filename, strerror(-err));
1262                 return err;
1263         }
1264         cnt = err;
1265
1266         dfl[0] = '\0';
1267         if (strlen(filename) + 8 < sizeof(filename)) {
1268                 strcat(filename, "/default");
1269                 ss = readlink(filename, dfl, sizeof(dfl)-1);
1270                 if (ss >= 0) {
1271                         dfl[ss] = '\0';
1272                         dfl[sizeof(dfl)-1] = '\0';
1273                         if (dfl[0] && dfl[strlen(dfl)-1] == '/')
1274                                 dfl[strlen(dfl)-1] = '\0';
1275                 } else {
1276                         dfl[0] = '\0';
1277                 }
1278         }
1279
1280         list = calloc(1, cnt * 2 * sizeof(char *));
1281         if (list == NULL) {
1282                 err = -ENOMEM;
1283                 goto __err;
1284         }
1285
1286         for (i = 0; i < cnt; i++) {
1287                 err = load_master_config(namelist[i]->d_name, &cfg);
1288                 if (err < 0)
1289                         goto __err;
1290                 err = snd_config_search(cfg, "Comment", &c);
1291                 if (err >= 0) {
1292                         err = parse_string(c, (char **)&list[i*2+1]);
1293                         if (err < 0) {
1294                                 snd_config_delete(cfg);
1295                                 goto __err;
1296                         }
1297                 }
1298                 snd_config_delete(cfg);
1299                 list[i * 2] = strdup(namelist[i]->d_name);
1300                 if (list[i * 2] == NULL) {
1301                         err = -ENOMEM;
1302                         goto __err;
1303                 }
1304                 if (strcmp(dfl, list[i * 2]) == 0) {
1305                         /* default to top */
1306                         const char *save1 = list[i * 2];
1307                         const char *save2 = list[i * 2 + 1];
1308                         memmove(list + 2, list, i * 2 * sizeof(char *));
1309                         list[0] = save1;
1310                         list[1] = save2;
1311                 }
1312         }
1313         err = cnt * 2;
1314
1315       __err:
1316         for (i = 0; i < cnt; i++) {
1317                 free(namelist[i]);
1318                 if (err < 0) {
1319                         free((void *)list[i * 2]);
1320                         free((void *)list[i * 2 + 1]);
1321                 }
1322         }
1323         free(namelist);
1324
1325         if (err >= 0)
1326                 *_list = list;
1327
1328         return err;
1329 }