1 /**
2 * \file control/setup.c
3 * \brief Routines to setup control primitives from configuration
4 * \author Abramo Bagnara <abramo@alsa-project.org>
5 * \author Jaroslav Kysela <perex@perex.cz>
6 * \date 2001
7 *
8 * Routines to setup control primitives from configuration
9 */
10 /*
11 * Control Interface - routines for setup from configuration
12 * Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
13 * Jaroslav Kysela <perex@perex.cz>
14 *
15 *
16 * This library is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU Lesser General Public License as
18 * published by the Free Software Foundation; either version 2.1 of
19 * the License, or (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU Lesser General Public License for more details.
25 *
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 *
30 */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include "local.h"
39
40 #ifndef DOC_HIDDEN
41 typedef struct {
42 unsigned int lock: 1;
43 unsigned int preserve: 1;
44 snd_ctl_elem_id_t *id;
45 snd_ctl_elem_info_t *info;
46 snd_ctl_elem_value_t *val;
47 snd_ctl_elem_value_t *mask;
48 snd_ctl_elem_value_t *old;
49 struct list_head list;
50 } snd_sctl_elem_t;
51
52 struct _snd_sctl {
53 int mode;
54 snd_ctl_t *ctl;
55 struct list_head elems;
56 };
57 #endif /* DOC_HIDDEN */
58
free_elems(snd_sctl_t * h)59 static int free_elems(snd_sctl_t *h)
60 {
61 int err = 0;
62 while (!list_empty(&h->elems)) {
63 snd_sctl_elem_t *elem = list_entry(h->elems.next, snd_sctl_elem_t, list);
64 snd_ctl_elem_id_free(elem->id);
65 snd_ctl_elem_info_free(elem->info);
66 snd_ctl_elem_value_free(elem->val);
67 snd_ctl_elem_value_free(elem->mask);
68 snd_ctl_elem_value_free(elem->old);
69 list_del(&elem->list);
70 free(elem);
71 }
72 if ((h->mode & SND_SCTL_NOFREE) == 0)
73 err = snd_ctl_close(h->ctl);
74 free(h);
75 return err;
76 }
77
78 /**
79 * \brief Install given values to control elements
80 * \param h Setup control handle
81 * \result zero if success, otherwise a negative error code
82 */
snd_sctl_install(snd_sctl_t * h)83 int snd_sctl_install(snd_sctl_t *h)
84 {
85 struct list_head *pos;
86 int err;
87 unsigned int k;
88 assert(h);
89 list_for_each(pos, &h->elems) {
90 snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list);
91 unsigned int count;
92 snd_ctl_elem_type_t type;
93 if (elem->lock) {
94 err = snd_ctl_elem_lock(h->ctl, elem->id);
95 if (err < 0) {
96 SNDERR("Cannot lock ctl elem");
97 return err;
98 }
99 }
100 err = snd_ctl_elem_read(h->ctl, elem->old);
101 if (err < 0) {
102 SNDERR("Cannot read ctl elem");
103 return err;
104 }
105 count = snd_ctl_elem_info_get_count(elem->info);
106 type = snd_ctl_elem_info_get_type(elem->info);
107 switch (type) {
108 case SND_CTL_ELEM_TYPE_BOOLEAN:
109 for (k = 0; k < count; ++k) {
110 int old, val, mask;
111 old = snd_ctl_elem_value_get_boolean(elem->old, k);
112 mask = snd_ctl_elem_value_get_boolean(elem->mask, k);
113 old &= ~mask;
114 if (old) {
115 val = snd_ctl_elem_value_get_boolean(elem->val, k);
116 val |= old;
117 snd_ctl_elem_value_set_boolean(elem->val, k, val);
118 }
119 }
120 break;
121 case SND_CTL_ELEM_TYPE_INTEGER:
122 for (k = 0; k < count; ++k) {
123 long old, val, mask;
124 old = snd_ctl_elem_value_get_integer(elem->old, k);
125 mask = snd_ctl_elem_value_get_integer(elem->mask, k);
126 old &= ~mask;
127 if (old) {
128 val = snd_ctl_elem_value_get_integer(elem->val, k);
129 val |= old;
130 snd_ctl_elem_value_set_integer(elem->val, k, val);
131 }
132 }
133 break;
134 case SND_CTL_ELEM_TYPE_ENUMERATED:
135 for (k = 0; k < count; ++k) {
136 unsigned int old, val, mask;
137 old = snd_ctl_elem_value_get_enumerated(elem->old, k);
138 mask = snd_ctl_elem_value_get_enumerated(elem->mask, k);
139 old &= ~mask;
140 if (old) {
141 val = snd_ctl_elem_value_get_enumerated(elem->val, k);
142 val |= old;
143 snd_ctl_elem_value_set_enumerated(elem->val, k, val);
144 }
145 }
146 break;
147 case SND_CTL_ELEM_TYPE_IEC958:
148 count = sizeof(snd_aes_iec958_t);
149 /* Fall through */
150 case SND_CTL_ELEM_TYPE_BYTES:
151 for (k = 0; k < count; ++k) {
152 unsigned char old, val, mask;
153 old = snd_ctl_elem_value_get_byte(elem->old, k);
154 mask = snd_ctl_elem_value_get_byte(elem->mask, k);
155 old &= ~mask;
156 if (old) {
157 val = snd_ctl_elem_value_get_byte(elem->val, k);
158 val |= old;
159 snd_ctl_elem_value_set_byte(elem->val, k, val);
160 }
161 }
162 break;
163 default:
164 assert(0);
165 break;
166 }
167 err = snd_ctl_elem_write(h->ctl, elem->val);
168 if (err < 0) {
169 SNDERR("Cannot write ctl elem");
170 return err;
171 }
172 }
173 return 0;
174 }
175
176 /**
177 * \brief Remove (restore) previous values from control elements
178 * \param h Setup control handle
179 * \result zero if success, otherwise a negative error code
180 */
snd_sctl_remove(snd_sctl_t * h)181 int snd_sctl_remove(snd_sctl_t *h)
182 {
183 struct list_head *pos;
184 int err;
185 assert(h);
186 list_for_each(pos, &h->elems) {
187 snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list);
188 if (elem->lock) {
189 err = snd_ctl_elem_unlock(h->ctl, elem->id);
190 if (err < 0) {
191 SNDERR("Cannot unlock ctl elem");
192 return err;
193 }
194 }
195 /* Only restore the old value if it differs from the requested
196 * value, because if it has changed restoring the old value
197 * overrides the change. Take for example, a voice modem with
198 * a .conf that sets preserve off-hook. Start playback (on-hook
199 * to off-hook), start record (off-hook to off-hook), stop
200 * playback (off-hook to restore on-hook), stop record (on-hook
201 * to restore off-hook), Clearly you don't want to leave the
202 * modem "on the phone" now that there isn't any playback or
203 * recording active.
204 */
205 if (elem->preserve && snd_ctl_elem_value_compare(elem->val, elem->old)) {
206 err = snd_ctl_elem_write(h->ctl, elem->old);
207 if (err < 0) {
208 SNDERR("Cannot restore ctl elem");
209 return err;
210 }
211 }
212 }
213 return 0;
214 }
215
snd_config_get_ctl_elem_enumerated(snd_config_t * n,snd_ctl_t * ctl,snd_ctl_elem_info_t * info)216 static int snd_config_get_ctl_elem_enumerated(snd_config_t *n, snd_ctl_t *ctl,
217 snd_ctl_elem_info_t *info)
218 {
219 const char *str;
220 long val;
221 unsigned int idx, items;
222 switch (snd_config_get_type(n)) {
223 case SND_CONFIG_TYPE_INTEGER:
224 snd_config_get_integer(n, &val);
225 return val;
226 case SND_CONFIG_TYPE_STRING:
227 snd_config_get_string(n, &str);
228 break;
229 default:
230 return -1;
231 }
232 items = snd_ctl_elem_info_get_items(info);
233 for (idx = 0; idx < items; idx++) {
234 int err;
235 snd_ctl_elem_info_set_item(info, idx);
236 err = snd_ctl_elem_info(ctl, info);
237 if (err < 0) {
238 SNDERR("Cannot obtain info for CTL elem");
239 return err;
240 }
241 if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
242 return idx;
243 }
244 return -1;
245 }
246
snd_config_get_ctl_elem_value(snd_config_t * conf,snd_ctl_t * ctl,snd_ctl_elem_value_t * val,snd_ctl_elem_value_t * mask,snd_ctl_elem_info_t * info)247 static int snd_config_get_ctl_elem_value(snd_config_t *conf,
248 snd_ctl_t *ctl,
249 snd_ctl_elem_value_t *val,
250 snd_ctl_elem_value_t *mask,
251 snd_ctl_elem_info_t *info)
252 {
253 int err;
254 snd_config_iterator_t i, next;
255 snd_ctl_elem_id_t id = {0};
256 snd_ctl_elem_type_t type;
257 unsigned int count;
258 long v;
259 long idx;
260 snd_ctl_elem_value_get_id(val, &id);
261 count = snd_ctl_elem_info_get_count(info);
262 type = snd_ctl_elem_info_get_type(info);
263 if (count == 1) {
264 switch (type) {
265 case SND_CTL_ELEM_TYPE_BOOLEAN:
266 v = snd_config_get_bool(conf);
267 if (v >= 0) {
268 snd_ctl_elem_value_set_boolean(val, 0, v);
269 if (mask)
270 snd_ctl_elem_value_set_boolean(mask, 0, 1);
271 return 0;
272 }
273 break;
274 case SND_CTL_ELEM_TYPE_INTEGER:
275 err = snd_config_get_integer(conf, &v);
276 if (err == 0) {
277 snd_ctl_elem_value_set_integer(val, 0, v);
278 if (mask)
279 snd_ctl_elem_value_set_integer(mask, 0, ~0L);
280 return 0;
281 }
282 break;
283 case SND_CTL_ELEM_TYPE_ENUMERATED:
284 v = snd_config_get_ctl_elem_enumerated(conf, ctl, info);
285 if (v >= 0) {
286 snd_ctl_elem_value_set_enumerated(val, 0, v);
287 if (mask)
288 snd_ctl_elem_value_set_enumerated(mask, 0, ~0);
289 return 0;
290 }
291 break;
292 case SND_CTL_ELEM_TYPE_BYTES:
293 case SND_CTL_ELEM_TYPE_IEC958:
294 break;
295 default:
296 SNDERR("Unknown control type: %d", type);
297 return -EINVAL;
298 }
299 }
300 switch (type) {
301 case SND_CTL_ELEM_TYPE_IEC958:
302 count = sizeof(snd_aes_iec958_t);
303 /* Fall through */
304 case SND_CTL_ELEM_TYPE_BYTES:
305 {
306 const char *buf;
307 err = snd_config_get_string(conf, &buf);
308 if (err >= 0) {
309 int c1 = 0;
310 unsigned int len = strlen(buf);
311 unsigned int idx = 0;
312 if (len % 2 != 0 || len > count * 2) {
313 _bad_content:
314 SNDERR("bad value content\n");
315 return -EINVAL;
316 }
317 while (*buf) {
318 int c = *buf++;
319 if (c >= '0' && c <= '9')
320 c -= '0';
321 else if (c >= 'a' && c <= 'f')
322 c = c - 'a' + 10;
323 else if (c >= 'A' && c <= 'F')
324 c = c - 'A' + 10;
325 else {
326 goto _bad_content;
327 }
328 if (idx % 2 == 1) {
329 snd_ctl_elem_value_set_byte(val, idx / 2, c1 << 4 | c);
330 if (mask)
331 snd_ctl_elem_value_set_byte(mask, idx / 2, 0xff);
332 } else
333 c1 = c;
334 idx++;
335 }
336 return 0;
337 }
338 }
339 default:
340 break;
341 }
342 if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
343 SNDERR("bad value type");
344 return -EINVAL;
345 }
346
347 snd_config_for_each(i, next, conf) {
348 snd_config_t *n = snd_config_iterator_entry(i);
349 const char *id;
350 if (snd_config_get_id(n, &id) < 0)
351 continue;
352 err = safe_strtol(id, &idx);
353 if (err < 0 || idx < 0 || (unsigned int) idx >= count) {
354 SNDERR("bad value index");
355 return -EINVAL;
356 }
357 switch (type) {
358 case SND_CTL_ELEM_TYPE_BOOLEAN:
359 v = snd_config_get_bool(n);
360 if (v < 0)
361 goto _bad_content;
362 snd_ctl_elem_value_set_boolean(val, idx, v);
363 if (mask)
364 snd_ctl_elem_value_set_boolean(mask, idx, 1);
365 break;
366 case SND_CTL_ELEM_TYPE_INTEGER:
367 err = snd_config_get_integer(n, &v);
368 if (err < 0)
369 goto _bad_content;
370 snd_ctl_elem_value_set_integer(val, idx, v);
371 if (mask)
372 snd_ctl_elem_value_set_integer(mask, idx, ~0L);
373 break;
374 case SND_CTL_ELEM_TYPE_ENUMERATED:
375 v = snd_config_get_ctl_elem_enumerated(n, ctl, info);
376 if (v < 0)
377 goto _bad_content;
378 snd_ctl_elem_value_set_enumerated(val, idx, v);
379 if (mask)
380 snd_ctl_elem_value_set_enumerated(mask, idx, ~0);
381 break;
382 case SND_CTL_ELEM_TYPE_BYTES:
383 case SND_CTL_ELEM_TYPE_IEC958:
384 err = snd_config_get_integer(n, &v);
385 if (err < 0 || v < 0 || v > 255)
386 goto _bad_content;
387 snd_ctl_elem_value_set_byte(val, idx, v);
388 if (mask)
389 snd_ctl_elem_value_set_byte(mask, idx, 0xff);
390 break;
391 default:
392 break;
393 }
394 }
395 return 0;
396 }
397
add_elem(snd_sctl_t * h,snd_config_t * _conf,snd_config_t * private_data,int * quit)398 static int add_elem(snd_sctl_t *h, snd_config_t *_conf, snd_config_t *private_data, int *quit)
399 {
400 snd_config_t *conf;
401 snd_config_iterator_t i, next;
402 int iface = SND_CTL_ELEM_IFACE_MIXER;
403 const char *name = NULL;
404 long index = 0;
405 long device = -1;
406 long subdevice = -1;
407 int lock = 0;
408 int preserve = 0;
409 int optional = 0;
410 int skip_rest = 0;
411 snd_config_t *value = NULL, *mask = NULL;
412 snd_sctl_elem_t *elem = NULL;
413 int err;
414 err = snd_config_expand(_conf, _conf, NULL, private_data, &conf);
415 if (err < 0)
416 return err;
417 snd_config_for_each(i, next, conf) {
418 snd_config_t *n = snd_config_iterator_entry(i);
419 const char *id;
420 if (snd_config_get_id(n, &id) < 0)
421 continue;
422 if (strcmp(id, "comment") == 0)
423 continue;
424 if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
425 const char *ptr;
426 if ((err = snd_config_get_string(n, &ptr)) < 0) {
427 SNDERR("field %s is not a string", id);
428 goto _err;
429 }
430 if ((err = snd_config_get_ctl_iface_ascii(ptr)) < 0) {
431 SNDERR("Invalid value for '%s'", id);
432 goto _err;
433 }
434 iface = err;
435 continue;
436 }
437 if (strcmp(id, "name") == 0) {
438 if ((err = snd_config_get_string(n, &name)) < 0) {
439 SNDERR("field %s is not a string", id);
440 goto _err;
441 }
442 continue;
443 }
444 if (strcmp(id, "index") == 0) {
445 if ((err = snd_config_get_integer(n, &index)) < 0) {
446 SNDERR("field %s is not an integer", id);
447 goto _err;
448 }
449 continue;
450 }
451 if (strcmp(id, "device") == 0) {
452 if ((err = snd_config_get_integer(n, &device)) < 0) {
453 SNDERR("field %s is not an integer", id);
454 goto _err;
455 }
456 continue;
457 }
458 if (strcmp(id, "subdevice") == 0) {
459 if ((err = snd_config_get_integer(n, &subdevice)) < 0) {
460 SNDERR("field %s is not an integer", id);
461 goto _err;
462 }
463 continue;
464 }
465 if (strcmp(id, "lock") == 0) {
466 err = snd_config_get_bool(n);
467 if (err < 0)
468 goto _err;
469 lock = err;
470 continue;
471 }
472 if (strcmp(id, "preserve") == 0) {
473 err = snd_config_get_bool(n);
474 if (err < 0)
475 goto _err;
476 preserve = err;
477 continue;
478 }
479 if (strcmp(id, "value") == 0) {
480 value = n;
481 continue;
482 }
483 if (strcmp(id, "mask") == 0) {
484 mask = n;
485 continue;
486 }
487 if (strcmp(id, "optional") == 0) {
488 err = snd_config_get_bool(n);
489 if (err < 0)
490 goto _err;
491 optional = err;
492 continue;
493 }
494 if (strcmp(id, "skip_rest") == 0) {
495 err = snd_config_get_bool(n);
496 if (err < 0)
497 goto _err;
498 skip_rest = err;
499 continue;
500 }
501 SNDERR("Unknown field %s", id);
502 return -EINVAL;
503 }
504 if (name == NULL) {
505 SNDERR("Missing control name");
506 err = -EINVAL;
507 goto _err;
508 }
509 if (value == NULL) {
510 SNDERR("Missing control value");
511 err = -EINVAL;
512 goto _err;
513 }
514 if (device < 0)
515 device = 0;
516 if (subdevice < 0)
517 subdevice = 0;
518 elem = calloc(1, sizeof(*elem));
519 if (!elem)
520 return -ENOMEM;
521 err = snd_ctl_elem_id_malloc(&elem->id);
522 if (err < 0)
523 goto _err;
524 err = snd_ctl_elem_info_malloc(&elem->info);
525 if (err < 0)
526 goto _err;
527 err = snd_ctl_elem_value_malloc(&elem->val);
528 if (err < 0)
529 goto _err;
530 err = snd_ctl_elem_value_malloc(&elem->mask);
531 if (err < 0)
532 goto _err;
533 err = snd_ctl_elem_value_malloc(&elem->old);
534 if (err < 0)
535 goto _err;
536 elem->lock = lock;
537 elem->preserve = preserve;
538 snd_ctl_elem_id_set_interface(elem->id, iface);
539 snd_ctl_elem_id_set_name(elem->id, name);
540 snd_ctl_elem_id_set_index(elem->id, index);
541 snd_ctl_elem_id_set_device(elem->id, device);
542 snd_ctl_elem_id_set_subdevice(elem->id, subdevice);
543 snd_ctl_elem_info_set_id(elem->info, elem->id);
544 err = snd_ctl_elem_info(h->ctl, elem->info);
545 if (err < 0) {
546 if (! optional)
547 SNDERR("Cannot obtain info for CTL elem (%s,'%s',%li,%li,%li): %s", snd_ctl_elem_iface_name(iface), name, index, device, subdevice, snd_strerror(err));
548 goto _err;
549 } else {
550 if (skip_rest)
551 *quit = 1;
552 }
553 snd_ctl_elem_value_set_id(elem->val, elem->id);
554 snd_ctl_elem_value_set_id(elem->old, elem->id);
555 if (mask) {
556 err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, NULL, elem->info);
557 if (err < 0)
558 goto _err;
559 err = snd_config_get_ctl_elem_value(mask, h->ctl, elem->mask, NULL, elem->info);
560 if (err < 0)
561 goto _err;
562 } else {
563 err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
564 if (err < 0)
565 goto _err;
566 }
567
568 err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
569 if (err < 0)
570 goto _err;
571 list_add_tail(&elem->list, &h->elems);
572
573 _err:
574 if (err < 0 && elem) {
575 if (elem->id)
576 snd_ctl_elem_id_free(elem->id);
577 if (elem->info)
578 snd_ctl_elem_info_free(elem->info);
579 if (elem->val)
580 snd_ctl_elem_value_free(elem->val);
581 if (elem->mask)
582 snd_ctl_elem_value_free(elem->mask);
583 if (elem->old)
584 snd_ctl_elem_value_free(elem->old);
585 free(elem);
586 if (err != -ENOMEM && optional)
587 err = 0; /* ignore the error */
588 }
589 if (conf)
590 snd_config_delete(conf);
591 return err;
592 }
593
594 /**
595 * \brief Build setup control handle
596 * \param sctl Result - setup control handle
597 * \param handle Master control handle
598 * \param conf Setup configuration
599 * \param private_data Private data for runtime evaluation
600 * \param mode Build mode - SND_SCTL_xxxx
601 * \result zero if success, otherwise a negative error code
602 */
snd_sctl_build(snd_sctl_t ** sctl,snd_ctl_t * handle,snd_config_t * conf,snd_config_t * private_data,int mode)603 int snd_sctl_build(snd_sctl_t **sctl, snd_ctl_t *handle, snd_config_t *conf, snd_config_t *private_data, int mode)
604 {
605 snd_sctl_t *h;
606 snd_config_iterator_t i, next;
607 int err, quit = 0;
608
609 assert(sctl);
610 assert(handle);
611 assert(conf);
612 *sctl = NULL;
613 if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
614 return -EINVAL;
615 h = calloc(1, sizeof(*h));
616 if (!h) {
617 if (mode & SND_SCTL_NOFREE)
618 return -ENOMEM;
619 snd_ctl_close(handle);
620 return -ENOMEM;
621 }
622 h->mode = mode;
623 h->ctl = handle;
624 INIT_LIST_HEAD(&h->elems);
625 snd_config_for_each(i, next, conf) {
626 snd_config_t *n = snd_config_iterator_entry(i);
627 err = add_elem(h, n, private_data, &quit);
628 if (err < 0) {
629 free_elems(h);
630 return err;
631 }
632 if (quit)
633 break;
634 }
635 *sctl = h;
636 return 0;
637 }
638
639 /**
640 * \brief Free setup control handle
641 * \param sctl Setup control handle
642 * \result zero if success, otherwise a negative error code
643 */
snd_sctl_free(snd_sctl_t * sctl)644 int snd_sctl_free(snd_sctl_t *sctl)
645 {
646 assert(sctl);
647 return free_elems(sctl);
648 }
649