1 /*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15 *
16 * Support for the verb/device/modifier core logic and API,
17 * command line tool and file parser was kindly sponsored by
18 * Texas Instruments Inc.
19 * Support for multiple active modifiers and devices,
20 * transition sequences, multiple client access and user defined use
21 * cases was kindly sponsored by Wolfson Microelectronics PLC.
22 *
23 * Copyright (C) 2008-2010 SlimLogic Ltd
24 * Copyright (C) 2010 Wolfson Microelectronics PLC
25 * Copyright (C) 2010 Texas Instruments Inc.
26 * Copyright (C) 2010 Red Hat Inc.
27 * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
28 * Stefan Schmidt <stefan@slimlogic.co.uk>
29 * Justin Xu <justinx@slimlogic.co.uk>
30 * Jaroslav Kysela <perex@perex.cz>
31 */
32
33 #include "ucm_local.h"
34
uc_mgr_error(const char * fmt,...)35 void uc_mgr_error(const char *fmt,...)
36 {
37 va_list va;
38 va_start(va, fmt);
39 fprintf(stderr, "ucm: ");
40 vfprintf(stderr, fmt, va);
41 va_end(va);
42 }
43
uc_mgr_stdout(const char * fmt,...)44 void uc_mgr_stdout(const char *fmt,...)
45 {
46 va_list va;
47 va_start(va, fmt);
48 vfprintf(stdout, fmt, va);
49 va_end(va);
50 }
51
uc_mgr_get_one_ctl(snd_use_case_mgr_t * uc_mgr)52 struct ctl_list *uc_mgr_get_one_ctl(snd_use_case_mgr_t *uc_mgr)
53 {
54 struct list_head *pos;
55 struct ctl_list *ctl_list = NULL;
56
57 list_for_each(pos, &uc_mgr->ctl_list) {
58 if (ctl_list) {
59 uc_error("multiple control device names were found!");
60 return NULL;
61 }
62 ctl_list = list_entry(pos, struct ctl_list, list);
63 }
64 return ctl_list;
65 }
66
uc_mgr_get_ctl(snd_use_case_mgr_t * uc_mgr)67 snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr)
68 {
69 struct ctl_list *ctl_list;
70
71 ctl_list = uc_mgr_get_one_ctl(uc_mgr);
72 if (ctl_list)
73 return ctl_list->ctl;
74 return NULL;
75 }
76
uc_mgr_free_ctl(struct ctl_list * ctl_list)77 static void uc_mgr_free_ctl(struct ctl_list *ctl_list)
78 {
79 struct list_head *pos, *npos;
80 struct ctl_dev *ctl_dev;
81
82 list_for_each_safe(pos, npos, &ctl_list->dev_list) {
83 ctl_dev = list_entry(pos, struct ctl_dev, list);
84 free(ctl_dev->device);
85 free(ctl_dev);
86 }
87 snd_ctl_card_info_free(ctl_list->ctl_info);
88 free(ctl_list);
89 }
90
uc_mgr_free_ctl_list(snd_use_case_mgr_t * uc_mgr)91 void uc_mgr_free_ctl_list(snd_use_case_mgr_t *uc_mgr)
92 {
93 struct list_head *pos, *npos;
94 struct ctl_list *ctl_list;
95
96 list_for_each_safe(pos, npos, &uc_mgr->ctl_list) {
97 ctl_list = list_entry(pos, struct ctl_list, list);
98 snd_ctl_close(ctl_list->ctl);
99 list_del(&ctl_list->list);
100 uc_mgr_free_ctl(ctl_list);
101 }
102 }
103
uc_mgr_ctl_add_dev(struct ctl_list * ctl_list,const char * device)104 static int uc_mgr_ctl_add_dev(struct ctl_list *ctl_list, const char *device)
105 {
106 struct list_head *pos;
107 struct ctl_dev *ctl_dev;
108
109 /* skip duplicates */
110 list_for_each(pos, &ctl_list->dev_list) {
111 ctl_dev = list_entry(pos, struct ctl_dev, list);
112 if (strcmp(ctl_dev->device, device) == 0)
113 return 0;
114 }
115
116 /* allocate new device name */
117 ctl_dev = malloc(sizeof(*ctl_dev));
118 if (ctl_dev == NULL)
119 return -ENOMEM;
120 ctl_dev->device = strdup(device);
121 if (ctl_dev->device == NULL) {
122 free(ctl_dev);
123 return -ENOMEM;
124 }
125 list_add_tail(&ctl_dev->list, &ctl_list->dev_list);
126 return 0;
127 }
128
uc_mgr_ctl_add(snd_use_case_mgr_t * uc_mgr,struct ctl_list * ctl_list,snd_ctl_t * ctl,int card,snd_ctl_card_info_t * info,const char * device)129 static int uc_mgr_ctl_add(snd_use_case_mgr_t *uc_mgr,
130 struct ctl_list *ctl_list,
131 snd_ctl_t *ctl, int card,
132 snd_ctl_card_info_t *info,
133 const char *device)
134 {
135 struct ctl_list *cl = NULL;
136 const char *id = snd_ctl_card_info_get_id(info);
137 char dev[MAX_CARD_LONG_NAME];
138 int err, hit = 0;
139
140 if (id == NULL || id[0] == '\0')
141 return -ENOENT;
142 if (!ctl_list) {
143 cl = malloc(sizeof(*cl));
144 if (cl == NULL)
145 return -ENOMEM;
146 INIT_LIST_HEAD(&cl->dev_list);
147 cl->ctl = ctl;
148 if (snd_ctl_card_info_malloc(&cl->ctl_info) < 0) {
149 free(cl);
150 return -ENOMEM;
151 }
152 snd_ctl_card_info_copy(cl->ctl_info, info);
153 ctl_list = cl;
154 }
155 if (card >= 0) {
156 snprintf(dev, sizeof(dev), "hw:%d", card);
157 hit |= !!(device && (strcmp(dev, device) == 0));
158 err = uc_mgr_ctl_add_dev(ctl_list, dev);
159 if (err < 0)
160 goto __nomem;
161 }
162 snprintf(dev, sizeof(dev), "hw:%s", id);
163 hit |= !!(device && (strcmp(dev, device) == 0));
164 err = uc_mgr_ctl_add_dev(ctl_list, dev);
165 if (err < 0)
166 goto __nomem;
167 /* the UCM name not based on the card name / id */
168 if (!hit && device) {
169 err = uc_mgr_ctl_add_dev(ctl_list, device);
170 if (err < 0)
171 goto __nomem;
172 }
173
174 list_add_tail(&ctl_list->list, &uc_mgr->ctl_list);
175 return 0;
176
177 __nomem:
178 if (ctl_list == cl)
179 uc_mgr_free_ctl(cl);
180 return -ENOMEM;
181 }
182
uc_mgr_open_ctl(snd_use_case_mgr_t * uc_mgr,snd_ctl_t ** ctl,const char * device)183 int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr,
184 snd_ctl_t **ctl,
185 const char *device)
186 {
187 struct list_head *pos1, *pos2;
188 struct ctl_list *ctl_list;
189 struct ctl_dev *ctl_dev;
190 snd_ctl_card_info_t *info;
191 const char *id;
192 int err, card;
193
194 snd_ctl_card_info_alloca(&info);
195
196 /* cache lookup */
197 list_for_each(pos1, &uc_mgr->ctl_list) {
198 ctl_list = list_entry(pos1, struct ctl_list, list);
199 list_for_each(pos2, &ctl_list->dev_list) {
200 ctl_dev = list_entry(pos2, struct ctl_dev, list);
201 if (strcmp(ctl_dev->device, device) == 0) {
202 *ctl = ctl_list->ctl;
203 return 0;
204 }
205 }
206 }
207
208 err = snd_ctl_open(ctl, device, 0);
209 if (err < 0)
210 return err;
211
212 id = NULL;
213 err = snd_ctl_card_info(*ctl, info);
214 if (err == 0)
215 id = snd_ctl_card_info_get_id(info);
216 if (err < 0 || id == NULL || id[0] == '\0') {
217 uc_error("control hardware info (%s): %s", device, snd_strerror(err));
218 snd_ctl_close(*ctl);
219 *ctl = NULL;
220 return err;
221 }
222
223 /* insert to cache, if just name differs */
224 list_for_each(pos1, &uc_mgr->ctl_list) {
225 ctl_list = list_entry(pos1, struct ctl_list, list);
226 if (strcmp(id, snd_ctl_card_info_get_id(ctl_list->ctl_info)) == 0) {
227 card = snd_card_get_index(id);
228 err = uc_mgr_ctl_add(uc_mgr, ctl_list, *ctl, card, info, device);
229 if (err < 0)
230 goto __nomem;
231 snd_ctl_close(*ctl);
232 *ctl = ctl_list->ctl;
233 return 0;
234 }
235 }
236
237 err = uc_mgr_ctl_add(uc_mgr, NULL, *ctl, -1, info, device);
238 if (err < 0)
239 goto __nomem;
240
241 return 0;
242
243 __nomem:
244 snd_ctl_close(*ctl);
245 *ctl = NULL;
246 return -ENOMEM;
247 }
248
uc_mgr_config_load(int format,const char * file,snd_config_t ** cfg)249 int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
250 {
251 FILE *fp;
252 snd_input_t *in;
253 snd_config_t *top;
254 const char *path, *default_paths[2];
255 int err;
256
257 fp = fopen(file, "r");
258 if (!fp) {
259 err = -errno;
260 __err0:
261 uc_error("could not open configuration file %s", file);
262 return err;
263 }
264 err = snd_input_stdio_attach(&in, fp, 1);
265 if (err < 0)
266 goto __err0;
267 err = snd_config_top(&top);
268 if (err < 0)
269 goto __err1;
270
271 if (format >= 2) {
272 path = getenv(ALSA_CONFIG_UCM2_VAR);
273 if (!path || path[0] == '\0')
274 path = ALSA_CONFIG_DIR "/ucm2";
275 } else {
276 path = getenv(ALSA_CONFIG_UCM_VAR);
277 if (!path || path[0] == '\0')
278 path = ALSA_CONFIG_DIR "/ucm";
279 }
280
281 default_paths[0] = path;
282 default_paths[1] = NULL;
283 err = _snd_config_load_with_include(top, in, 0, default_paths);
284 if (err < 0) {
285 uc_error("could not load configuration file %s", file);
286 goto __err2;
287 }
288 err = snd_input_close(in);
289 if (err < 0) {
290 in = NULL;
291 goto __err2;
292 }
293 *cfg = top;
294 return 0;
295
296 __err2:
297 snd_config_delete(top);
298 __err1:
299 if (in)
300 snd_input_close(in);
301 return err;
302 }
303
uc_mgr_free_value(struct list_head * base)304 void uc_mgr_free_value(struct list_head *base)
305 {
306 struct list_head *pos, *npos;
307 struct ucm_value *val;
308
309 list_for_each_safe(pos, npos, base) {
310 val = list_entry(pos, struct ucm_value, list);
311 free(val->name);
312 free(val->data);
313 list_del(&val->list);
314 free(val);
315 }
316 }
317
uc_mgr_free_dev_list(struct dev_list * dev_list)318 void uc_mgr_free_dev_list(struct dev_list *dev_list)
319 {
320 struct list_head *pos, *npos;
321 struct dev_list_node *dlist;
322
323 list_for_each_safe(pos, npos, &dev_list->list) {
324 dlist = list_entry(pos, struct dev_list_node, list);
325 free(dlist->name);
326 list_del(&dlist->list);
327 free(dlist);
328 }
329 }
330
uc_mgr_put_to_dev_list(struct dev_list * dev_list,const char * name)331 int uc_mgr_put_to_dev_list(struct dev_list *dev_list, const char *name)
332 {
333 struct list_head *pos;
334 struct dev_list_node *dlist;
335 char *n;
336
337 list_for_each(pos, &dev_list->list) {
338 dlist = list_entry(pos, struct dev_list_node, list);
339 if (strcmp(dlist->name, name) == 0)
340 return 0;
341 }
342
343 dlist = calloc(1, sizeof(*dlist));
344 if (dlist == NULL)
345 return -ENOMEM;
346 n = strdup(name);
347 if (n == NULL) {
348 free(dlist);
349 return -ENOMEM;
350 }
351 dlist->name = n;
352 list_add(&dlist->list, &dev_list->list);
353 return 0;
354 }
355
uc_mgr_rename_in_dev_list(struct dev_list * dev_list,const char * src,const char * dst)356 int uc_mgr_rename_in_dev_list(struct dev_list *dev_list, const char *src,
357 const char *dst)
358 {
359 struct list_head *pos;
360 struct dev_list_node *dlist;
361 char *dst1;
362
363 list_for_each(pos, &dev_list->list) {
364 dlist = list_entry(pos, struct dev_list_node, list);
365 if (strcmp(dlist->name, src) == 0) {
366 dst1 = strdup(dst);
367 if (dst1 == NULL)
368 return -ENOMEM;
369 free(dlist->name);
370 dlist->name = dst1;
371 return 0;
372 }
373 }
374 return -ENOENT;
375 }
376
uc_mgr_remove_from_dev_list(struct dev_list * dev_list,const char * name)377 int uc_mgr_remove_from_dev_list(struct dev_list *dev_list, const char *name)
378 {
379 struct list_head *pos;
380 struct dev_list_node *dlist;
381
382 list_for_each(pos, &dev_list->list) {
383 dlist = list_entry(pos, struct dev_list_node, list);
384 if (strcmp(dlist->name, name) == 0) {
385 free(dlist->name);
386 list_del(&dlist->list);
387 free(dlist);
388 return 0;
389 }
390 }
391 return -ENODEV;
392 }
393
uc_mgr_free_sequence_element(struct sequence_element * seq)394 void uc_mgr_free_sequence_element(struct sequence_element *seq)
395 {
396 if (seq == NULL)
397 return;
398 switch (seq->type) {
399 case SEQUENCE_ELEMENT_TYPE_CDEV:
400 free(seq->data.cdev);
401 break;
402 case SEQUENCE_ELEMENT_TYPE_CSET:
403 case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
404 case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
405 free(seq->data.cset);
406 break;
407 case SEQUENCE_ELEMENT_TYPE_EXEC:
408 free(seq->data.exec);
409 break;
410 default:
411 break;
412 }
413 free(seq);
414 }
415
uc_mgr_free_sequence(struct list_head * base)416 void uc_mgr_free_sequence(struct list_head *base)
417 {
418 struct list_head *pos, *npos;
419 struct sequence_element *seq;
420
421 list_for_each_safe(pos, npos, base) {
422 seq = list_entry(pos, struct sequence_element, list);
423 list_del(&seq->list);
424 uc_mgr_free_sequence_element(seq);
425 }
426 }
427
uc_mgr_free_transition_element(struct transition_sequence * tseq)428 void uc_mgr_free_transition_element(struct transition_sequence *tseq)
429 {
430 free(tseq->name);
431 uc_mgr_free_sequence(&tseq->transition_list);
432 free(tseq);
433 }
434
uc_mgr_free_transition(struct list_head * base)435 void uc_mgr_free_transition(struct list_head *base)
436 {
437 struct list_head *pos, *npos;
438 struct transition_sequence *tseq;
439
440 list_for_each_safe(pos, npos, base) {
441 tseq = list_entry(pos, struct transition_sequence, list);
442 list_del(&tseq->list);
443 uc_mgr_free_transition_element(tseq);
444 }
445 }
446
uc_mgr_free_dev_name_list(struct list_head * base)447 void uc_mgr_free_dev_name_list(struct list_head *base)
448 {
449 struct list_head *pos, *npos;
450 struct ucm_dev_name *dev;
451
452 list_for_each_safe(pos, npos, base) {
453 dev = list_entry(pos, struct ucm_dev_name, list);
454 list_del(&dev->list);
455 free(dev->name1);
456 free(dev->name2);
457 free(dev);
458 }
459 }
460
uc_mgr_free_modifier(struct list_head * base)461 void uc_mgr_free_modifier(struct list_head *base)
462 {
463 struct list_head *pos, *npos;
464 struct use_case_modifier *mod;
465
466 list_for_each_safe(pos, npos, base) {
467 mod = list_entry(pos, struct use_case_modifier, list);
468 free(mod->name);
469 free(mod->comment);
470 uc_mgr_free_sequence(&mod->enable_list);
471 uc_mgr_free_sequence(&mod->disable_list);
472 uc_mgr_free_transition(&mod->transition_list);
473 uc_mgr_free_dev_list(&mod->dev_list);
474 uc_mgr_free_value(&mod->value_list);
475 list_del(&mod->list);
476 free(mod);
477 }
478 }
479
uc_mgr_free_device(struct use_case_device * dev)480 void uc_mgr_free_device(struct use_case_device *dev)
481 {
482 free(dev->name);
483 free(dev->comment);
484 uc_mgr_free_sequence(&dev->enable_list);
485 uc_mgr_free_sequence(&dev->disable_list);
486 uc_mgr_free_transition(&dev->transition_list);
487 uc_mgr_free_dev_list(&dev->dev_list);
488 uc_mgr_free_value(&dev->value_list);
489 list_del(&dev->list);
490 free(dev);
491 }
492
uc_mgr_free_device_list(struct list_head * base)493 void uc_mgr_free_device_list(struct list_head *base)
494 {
495 struct list_head *pos, *npos;
496 struct use_case_device *dev;
497
498 list_for_each_safe(pos, npos, base) {
499 dev = list_entry(pos, struct use_case_device, list);
500 uc_mgr_free_device(dev);
501 }
502 }
503
uc_mgr_rename_device(struct use_case_verb * verb,const char * src,const char * dst)504 int uc_mgr_rename_device(struct use_case_verb *verb, const char *src,
505 const char *dst)
506 {
507 struct use_case_device *device;
508 struct list_head *pos, *npos;
509 char *dst1;
510
511 /* no errors when device is not found */
512 list_for_each_safe(pos, npos, &verb->device_list) {
513 device = list_entry(pos, struct use_case_device, list);
514 if (strcmp(device->name, src) == 0) {
515 dst1 = strdup(dst);
516 if (dst1 == NULL)
517 return -ENOMEM;
518 free(device->name);
519 device->name = dst1;
520 continue;
521 }
522 uc_mgr_rename_in_dev_list(&device->dev_list, src, dst);
523 }
524 return 0;
525 }
526
uc_mgr_remove_device(struct use_case_verb * verb,const char * name)527 int uc_mgr_remove_device(struct use_case_verb *verb, const char *name)
528 {
529 struct use_case_device *device;
530 struct list_head *pos, *npos;
531
532 list_for_each_safe(pos, npos, &verb->device_list) {
533 device = list_entry(pos, struct use_case_device, list);
534 if (strcmp(device->name, name) == 0) {
535 uc_mgr_free_device(device);
536 continue;
537 }
538 uc_mgr_remove_from_dev_list(&device->dev_list, name);
539 return 0;
540 }
541 return -ENOENT;
542 }
543
uc_mgr_free_verb(snd_use_case_mgr_t * uc_mgr)544 void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
545 {
546 struct list_head *pos, *npos;
547 struct use_case_verb *verb;
548
549 list_for_each_safe(pos, npos, &uc_mgr->verb_list) {
550 verb = list_entry(pos, struct use_case_verb, list);
551 free(verb->name);
552 free(verb->comment);
553 uc_mgr_free_sequence(&verb->enable_list);
554 uc_mgr_free_sequence(&verb->disable_list);
555 uc_mgr_free_transition(&verb->transition_list);
556 uc_mgr_free_value(&verb->value_list);
557 uc_mgr_free_device_list(&verb->device_list);
558 uc_mgr_free_device_list(&verb->cmpt_device_list);
559 uc_mgr_free_modifier(&verb->modifier_list);
560 uc_mgr_free_dev_name_list(&verb->rename_list);
561 uc_mgr_free_dev_name_list(&verb->remove_list);
562 list_del(&verb->list);
563 free(verb);
564 }
565 uc_mgr_free_sequence(&uc_mgr->default_list);
566 uc_mgr_free_value(&uc_mgr->value_list);
567 free(uc_mgr->comment);
568 free(uc_mgr->conf_dir_name);
569 free(uc_mgr->conf_file_name);
570 uc_mgr->comment = NULL;
571 uc_mgr->conf_dir_name = NULL;
572 uc_mgr->conf_file_name = NULL;
573 uc_mgr->active_verb = NULL;
574 INIT_LIST_HEAD(&uc_mgr->active_devices);
575 INIT_LIST_HEAD(&uc_mgr->active_modifiers);
576 }
577
uc_mgr_free(snd_use_case_mgr_t * uc_mgr)578 void uc_mgr_free(snd_use_case_mgr_t *uc_mgr)
579 {
580 uc_mgr_free_verb(uc_mgr);
581 uc_mgr_free_ctl_list(uc_mgr);
582 free(uc_mgr->card_name);
583 free(uc_mgr);
584 }
585