OSDN Git Service

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