1 /*
2 * Advanced Linux Sound Architecture Control Program
3 * Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
4 * Jaroslav Kysela <perex@perex.cz>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 */
22
23 #include "aconfig.h"
24 #include "version.h"
25 #include <getopt.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include "alsactl.h"
31
32
id_str(snd_ctl_elem_id_t * id)33 static char *id_str(snd_ctl_elem_id_t *id)
34 {
35 static char str[128];
36 assert(id);
37 sprintf(str, "%i,%i,%i,%s,%i",
38 snd_ctl_elem_id_get_interface(id),
39 snd_ctl_elem_id_get_device(id),
40 snd_ctl_elem_id_get_subdevice(id),
41 snd_ctl_elem_id_get_name(id),
42 snd_ctl_elem_id_get_index(id));
43 return str;
44 }
45
num_str(long n)46 static char *num_str(long n)
47 {
48 static char str[32];
49 sprintf(str, "%ld", n);
50 return str;
51 }
52
snd_config_integer_add(snd_config_t * father,char * id,long integer)53 static int snd_config_integer_add(snd_config_t *father, char *id, long integer)
54 {
55 int err;
56 snd_config_t *leaf;
57 err = snd_config_imake_integer(&leaf, id, integer);
58 if (err < 0)
59 return err;
60 err = snd_config_add(father, leaf);
61 if (err < 0) {
62 snd_config_delete(leaf);
63 return err;
64 }
65 return 0;
66 }
67
snd_config_integer64_add(snd_config_t * father,char * id,long long integer)68 static int snd_config_integer64_add(snd_config_t *father, char *id, long long integer)
69 {
70 int err;
71 snd_config_t *leaf;
72 err = snd_config_imake_integer64(&leaf, id, integer);
73 if (err < 0)
74 return err;
75 err = snd_config_add(father, leaf);
76 if (err < 0) {
77 snd_config_delete(leaf);
78 return err;
79 }
80 return 0;
81 }
82
snd_config_string_add(snd_config_t * father,const char * id,const char * string)83 static int snd_config_string_add(snd_config_t *father, const char *id, const char *string)
84 {
85 int err;
86 snd_config_t *leaf;
87 err = snd_config_imake_string(&leaf, id, string);
88 if (err < 0)
89 return err;
90 err = snd_config_add(father, leaf);
91 if (err < 0) {
92 snd_config_delete(leaf);
93 return err;
94 }
95 return 0;
96 }
97
snd_config_compound_add(snd_config_t * father,const char * id,int join,snd_config_t ** node)98 static int snd_config_compound_add(snd_config_t *father, const char *id, int join,
99 snd_config_t **node)
100 {
101 int err;
102 snd_config_t *leaf;
103 err = snd_config_make_compound(&leaf, id, join);
104 if (err < 0)
105 return err;
106 err = snd_config_add(father, leaf);
107 if (err < 0) {
108 snd_config_delete(leaf);
109 return err;
110 }
111 *node = leaf;
112 return 0;
113 }
114
115 #define MAX_USER_TLV_SIZE 64
116
tlv_to_str(unsigned int * tlv)117 static char *tlv_to_str(unsigned int *tlv)
118 {
119 int i, len = tlv[1] / 4 + 2;
120 char *s, *p;
121
122 if (len >= MAX_USER_TLV_SIZE)
123 return NULL;
124 s = malloc(len * 8 + 1);
125 if (! s)
126 return NULL;
127 p = s;
128 for (i = 0; i < len; i++) {
129 sprintf(p, "%08x", tlv[i]);
130 p += 8;
131 }
132 return s;
133 }
134
str_to_tlv(const char * s)135 static unsigned int *str_to_tlv(const char *s)
136 {
137 int i, j, c, len;
138 unsigned int *tlv;
139
140 len = strlen(s);
141 if (len % 8) /* aligned to 4 bytes (= 8 letters) */
142 return NULL;
143 len /= 8;
144 if (len > MAX_USER_TLV_SIZE)
145 return NULL;
146 tlv = malloc(sizeof(int) * len);
147 if (! tlv)
148 return NULL;
149 for (i = 0; i < len; i++) {
150 tlv[i] = 0;
151 for (j = 0; j < 8; j++) {
152 if ((c = hextodigit(*s++)) < 0) {
153 free(tlv);
154 return NULL;
155 }
156 tlv[i] = (tlv[i] << 4) | c;
157 }
158 }
159 return tlv;
160 }
161
162 /*
163 * add the TLV string, dB ranges, and dB values to comment fields
164 */
add_tlv_comments(snd_ctl_t * handle,snd_ctl_elem_id_t * id,snd_ctl_elem_info_t * info,snd_ctl_elem_value_t * ctl,snd_config_t * comment)165 static int add_tlv_comments(snd_ctl_t *handle, snd_ctl_elem_id_t *id,
166 snd_ctl_elem_info_t *info, snd_ctl_elem_value_t *ctl,
167 snd_config_t *comment)
168 {
169 unsigned int tlv[MAX_USER_TLV_SIZE];
170 unsigned int *db;
171 long rangemin, rangemax;
172 long dbmin, dbmax, dbgain;
173 snd_config_t *value;
174 unsigned int i, count;
175 int err;
176
177 if (snd_ctl_elem_tlv_read(handle, id, tlv, sizeof(tlv)) < 0)
178 return 0; /* ignore error */
179
180 if (snd_ctl_elem_info_is_tlv_writable(info)) {
181 char *s = tlv_to_str(tlv);
182 if (s) {
183 err = snd_config_string_add(comment, "tlv", s);
184 free(s);
185 if (err < 0) {
186 error("snd_config_string_add: %s", snd_strerror(err));
187 return err;
188 }
189 }
190 }
191
192 err = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &db);
193 if (err <= 0)
194 return 0;
195
196 rangemin = snd_ctl_elem_info_get_min(info);
197 rangemax = snd_ctl_elem_info_get_max(info);
198 snd_tlv_get_dB_range(db, rangemin, rangemax, &dbmin, &dbmax);
199 if (err < 0)
200 return err;
201 snd_config_integer_add(comment, "dbmin", dbmin);
202 snd_config_integer_add(comment, "dbmax", dbmax);
203
204 if (snd_ctl_elem_info_get_type(info) == SND_CTL_ELEM_TYPE_INTEGER) {
205 err = snd_config_compound_add(comment, "dbvalue", 1, &value);
206 if (err < 0) {
207 error("snd_config_compound_add: %s", snd_strerror(err));
208 return err;
209 }
210 count = snd_ctl_elem_info_get_count(info);
211 for (i = 0; i < count; i++) {
212 err = snd_tlv_convert_to_dB(db, rangemin, rangemax,
213 snd_ctl_elem_value_get_integer(ctl, i), &dbgain);
214 if (err < 0) {
215 error("snd_tlv_convert_to_dB: %s", snd_strerror(err));
216 return err;
217 }
218 err = snd_config_integer_add(value, num_str(i), dbgain);
219 if (err < 0) {
220 error("snd_config_integer_add: %s", snd_strerror(err));
221 return err;
222 }
223 }
224 }
225 return 0;
226 }
227
get_control(snd_ctl_t * handle,snd_ctl_elem_id_t * id,snd_config_t * top)228 static int get_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, snd_config_t *top)
229 {
230 snd_ctl_elem_value_t *ctl;
231 snd_ctl_elem_info_t *info;
232 snd_config_t *control, *comment, *item = NULL, *value;
233 const char *s;
234 char buf[256];
235 unsigned int idx;
236 int err;
237 unsigned int device, subdevice, index;
238 const char *name;
239 snd_ctl_elem_type_t type;
240 unsigned int count;
241 snd_ctl_elem_value_alloca(&ctl);
242 snd_ctl_elem_info_alloca(&info);
243 snd_ctl_elem_info_set_id(info, id);
244 err = snd_ctl_elem_info(handle, info);
245 if (err < 0) {
246 error("Cannot read control info '%s': %s", id_str(id), snd_strerror(err));
247 return err;
248 }
249
250 if (!snd_ctl_elem_info_is_readable(info))
251 return 0;
252 snd_ctl_elem_value_set_id(ctl, id);
253 err = snd_ctl_elem_read(handle, ctl);
254 if (err < 0) {
255 error("Cannot read control '%s': %s", id_str(id), snd_strerror(err));
256 return err;
257 }
258
259 err = snd_config_compound_add(top, num_str(snd_ctl_elem_info_get_numid(info)), 0, &control);
260 if (err < 0) {
261 error("snd_config_compound_add: %s", snd_strerror(err));
262 return err;
263 }
264 err = snd_config_make_compound(&comment, "comment", 0);
265 if (err < 0) {
266 error("snd_config_make_compound: %s", snd_strerror(err));
267 return err;
268 }
269
270 buf[0] = '\0';
271 buf[1] = '\0';
272 if (snd_ctl_elem_info_is_readable(info))
273 strcat(buf, " read");
274 if (snd_ctl_elem_info_is_writable(info))
275 strcat(buf, " write");
276 if (snd_ctl_elem_info_is_inactive(info))
277 strcat(buf, " inactive");
278 if (snd_ctl_elem_info_is_volatile(info))
279 strcat(buf, " volatile");
280 if (snd_ctl_elem_info_is_locked(info))
281 strcat(buf, " locked");
282 if (snd_ctl_elem_info_is_user(info))
283 strcat(buf, " user");
284 err = snd_config_string_add(comment, "access", buf + 1);
285 if (err < 0) {
286 error("snd_config_string_add: %s", snd_strerror(err));
287 return err;
288 }
289
290 type = snd_ctl_elem_info_get_type(info);
291 device = snd_ctl_elem_info_get_device(info);
292 subdevice = snd_ctl_elem_info_get_subdevice(info);
293 index = snd_ctl_elem_info_get_index(info);
294 name = snd_ctl_elem_info_get_name(info);
295 count = snd_ctl_elem_info_get_count(info);
296 s = snd_ctl_elem_type_name(type);
297 err = snd_config_string_add(comment, "type", s);
298 if (err < 0) {
299 error("snd_config_string_add: %s", snd_strerror(err));
300 return err;
301 }
302 err = snd_config_integer_add(comment, "count", count);
303 if (err < 0) {
304 error("snd_config_integer_add: %s", snd_strerror(err));
305 return err;
306 }
307
308 switch (type) {
309 case SND_CTL_ELEM_TYPE_BOOLEAN:
310 break;
311 case SND_CTL_ELEM_TYPE_INTEGER:
312 {
313 long min = snd_ctl_elem_info_get_min(info);
314 long max = snd_ctl_elem_info_get_max(info);
315 long step = snd_ctl_elem_info_get_step(info);
316 if (step)
317 sprintf(buf, "%li - %li (step %li)", min, max, step);
318 else
319 sprintf(buf, "%li - %li", min, max);
320 err = snd_config_string_add(comment, "range", buf);
321 if (err < 0) {
322 error("snd_config_string_add: %s", snd_strerror(err));
323 return err;
324 }
325 if (snd_ctl_elem_info_is_tlv_readable(info)) {
326 err = add_tlv_comments(handle, id, info, ctl, comment);
327 if (err < 0)
328 return err;
329 }
330 break;
331 }
332 case SND_CTL_ELEM_TYPE_INTEGER64:
333 {
334 long long min = snd_ctl_elem_info_get_min64(info);
335 long long max = snd_ctl_elem_info_get_max64(info);
336 long long step = snd_ctl_elem_info_get_step64(info);
337 if (step)
338 sprintf(buf, "%lli - %lli (step %lli)", min, max, step);
339 else
340 sprintf(buf, "%lli - %lli", min, max);
341 err = snd_config_string_add(comment, "range", buf);
342 if (err < 0) {
343 error("snd_config_string_add: %s", snd_strerror(err));
344 return err;
345 }
346 break;
347 }
348 case SND_CTL_ELEM_TYPE_ENUMERATED:
349 {
350 unsigned int items;
351 err = snd_config_compound_add(comment, "item", 1, &item);
352 if (err < 0) {
353 error("snd_config_compound_add: %s", snd_strerror(err));
354 return err;
355 }
356 items = snd_ctl_elem_info_get_items(info);
357 for (idx = 0; idx < items; idx++) {
358 snd_ctl_elem_info_set_item(info, idx);
359 err = snd_ctl_elem_info(handle, info);
360 if (err < 0) {
361 error("snd_ctl_card_info: %s", snd_strerror(err));
362 return err;
363 }
364 err = snd_config_string_add(item, num_str(idx), snd_ctl_elem_info_get_item_name(info));
365 if (err < 0) {
366 error("snd_config_string_add: %s", snd_strerror(err));
367 return err;
368 }
369 }
370 break;
371 }
372 default:
373 break;
374 }
375 s = snd_ctl_elem_iface_name(snd_ctl_elem_info_get_interface(info));
376 err = snd_config_string_add(control, "iface", s);
377 if (err < 0) {
378 error("snd_config_string_add: %s", snd_strerror(err));
379 return err;
380 }
381 if (device != 0) {
382 err = snd_config_integer_add(control, "device", device);
383 if (err < 0) {
384 error("snd_config_integer_add: %s", snd_strerror(err));
385 return err;
386 }
387 }
388 if (subdevice != 0) {
389 err = snd_config_integer_add(control, "subdevice", subdevice);
390 if (err < 0) {
391 error("snd_config_integer_add: %s", snd_strerror(err));
392 return err;
393 }
394 }
395 err = snd_config_string_add(control, "name", name);
396 if (err < 0) {
397 error("snd_config_string_add: %s", snd_strerror(err));
398 return err;
399 }
400 if (index != 0) {
401 err = snd_config_integer_add(control, "index", index);
402 if (err < 0) {
403 error("snd_config_integer_add: %s", snd_strerror(err));
404 return err;
405 }
406 }
407
408 switch (type) {
409 case SND_CTL_ELEM_TYPE_BYTES:
410 case SND_CTL_ELEM_TYPE_IEC958:
411 {
412 size_t size = type == SND_CTL_ELEM_TYPE_BYTES ?
413 count : sizeof(snd_aes_iec958_t);
414 char buf[size * 2 + 1];
415 char *p = buf;
416 char *hex = "0123456789abcdef";
417 const unsigned char *bytes =
418 (const unsigned char *)snd_ctl_elem_value_get_bytes(ctl);
419 for (idx = 0; idx < size; idx++) {
420 int v = bytes[idx];
421 *p++ = hex[v >> 4];
422 *p++ = hex[v & 0x0f];
423 }
424 *p = '\0';
425 err = snd_config_string_add(control, "value", buf);
426 if (err < 0) {
427 error("snd_config_string_add: %s", snd_strerror(err));
428 return err;
429 }
430 goto finish;
431 }
432 default:
433 break;
434 }
435
436 if (count == 1) {
437 switch (type) {
438 case SND_CTL_ELEM_TYPE_BOOLEAN:
439 err = snd_config_string_add(control, "value", snd_ctl_elem_value_get_boolean(ctl, 0) ? "true" : "false");
440 if (err < 0) {
441 error("snd_config_string_add: %s", snd_strerror(err));
442 return err;
443 }
444 goto finish;
445 case SND_CTL_ELEM_TYPE_INTEGER:
446 err = snd_config_integer_add(control, "value", snd_ctl_elem_value_get_integer(ctl, 0));
447 if (err < 0) {
448 error("snd_config_integer_add: %s", snd_strerror(err));
449 return err;
450 }
451 goto finish;
452 case SND_CTL_ELEM_TYPE_INTEGER64:
453 err = snd_config_integer64_add(control, "value", snd_ctl_elem_value_get_integer64(ctl, 0));
454 if (err < 0) {
455 error("snd_config_integer64_add: %s", snd_strerror(err));
456 return err;
457 }
458 goto finish;
459 case SND_CTL_ELEM_TYPE_ENUMERATED:
460 {
461 unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, 0);
462 snd_config_t *c;
463 err = snd_config_search(item, num_str(v), &c);
464 if (err == 0) {
465 err = snd_config_get_string(c, &s);
466 assert(err == 0);
467 err = snd_config_string_add(control, "value", s);
468 } else {
469 err = snd_config_integer_add(control, "value", v);
470 }
471 if (err < 0)
472 error("snd_config add: %s", snd_strerror(err));
473 goto finish;
474 }
475 default:
476 error("Unknown control type: %d\n", type);
477 return -EINVAL;
478 }
479 }
480
481 err = snd_config_compound_add(control, "value", 1, &value);
482 if (err < 0) {
483 error("snd_config_compound_add: %s", snd_strerror(err));
484 return err;
485 }
486
487 switch (type) {
488 case SND_CTL_ELEM_TYPE_BOOLEAN:
489 for (idx = 0; idx < count; idx++) {
490 err = snd_config_string_add(value, num_str(idx), snd_ctl_elem_value_get_boolean(ctl, idx) ? "true" : "false");
491 if (err < 0) {
492 error("snd_config_string_add: %s", snd_strerror(err));
493 return err;
494 }
495 }
496 break;
497 case SND_CTL_ELEM_TYPE_INTEGER:
498 for (idx = 0; idx < count; idx++) {
499 err = snd_config_integer_add(value, num_str(idx), snd_ctl_elem_value_get_integer(ctl, idx));
500 if (err < 0) {
501 error("snd_config_integer_add: %s", snd_strerror(err));
502 return err;
503 }
504 }
505 break;
506 case SND_CTL_ELEM_TYPE_INTEGER64:
507 for (idx = 0; idx < count; idx++) {
508 err = snd_config_integer64_add(value, num_str(idx), snd_ctl_elem_value_get_integer64(ctl, idx));
509 if (err < 0) {
510 error("snd_config_integer64_add: %s", snd_strerror(err));
511 return err;
512 }
513 }
514 break;
515 case SND_CTL_ELEM_TYPE_ENUMERATED:
516 for (idx = 0; idx < count; idx++) {
517 unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, idx);
518 snd_config_t *c;
519 err = snd_config_search(item, num_str(v), &c);
520 if (err == 0) {
521 err = snd_config_get_string(c, &s);
522 assert(err == 0);
523 err = snd_config_string_add(value, num_str(idx), s);
524 } else {
525 err = snd_config_integer_add(value, num_str(idx), v);
526 }
527 if (err < 0) {
528 error("snd_config add: %s", snd_strerror(err));
529 return err;
530 }
531 }
532 break;
533 default:
534 error("Unknown control type: %d\n", type);
535 return -EINVAL;
536 }
537
538 finish:
539 err = snd_config_add(control, comment);
540 if (err < 0) {
541 error("snd_config_add: %s", snd_strerror(err));
542 return err;
543 }
544 return 0;
545 }
546
get_controls(int cardno,snd_config_t * top)547 static int get_controls(int cardno, snd_config_t *top)
548 {
549 snd_ctl_t *handle;
550 snd_ctl_card_info_t *info;
551 snd_config_t *state, *card, *control;
552 snd_ctl_elem_list_t *list;
553 snd_ctl_elem_id_t *elem_id;
554 unsigned int idx;
555 int err;
556 char name[32];
557 unsigned int count;
558 const char *id;
559 snd_ctl_card_info_alloca(&info);
560 snd_ctl_elem_list_alloca(&list);
561 snd_ctl_elem_id_alloca(&elem_id);
562
563 sprintf(name, "hw:%d", cardno);
564 err = snd_ctl_open(&handle, name, SND_CTL_READONLY);
565 if (err < 0) {
566 error("snd_ctl_open error: %s", snd_strerror(err));
567 return err;
568 }
569 err = snd_ctl_card_info(handle, info);
570 if (err < 0) {
571 error("snd_ctl_card_info error: %s", snd_strerror(err));
572 goto _close;
573 }
574 id = snd_ctl_card_info_get_id(info);
575 err = snd_config_search(top, "state", &state);
576 if (err == 0 &&
577 snd_config_get_type(state) != SND_CONFIG_TYPE_COMPOUND) {
578 error("config state node is not a compound");
579 err = -EINVAL;
580 goto _close;
581 }
582 if (err < 0) {
583 err = snd_config_compound_add(top, "state", 1, &state);
584 if (err < 0) {
585 error("snd_config_compound_add: %s", snd_strerror(err));
586 goto _close;
587 }
588 }
589 err = snd_config_search(state, id, &card);
590 if (err == 0 &&
591 snd_config_get_type(card) != SND_CONFIG_TYPE_COMPOUND) {
592 error("config state.%s node is not a compound", id);
593 err = -EINVAL;
594 goto _close;
595 }
596 if (err < 0) {
597 err = snd_config_compound_add(state, id, 0, &card);
598 if (err < 0) {
599 error("snd_config_compound_add: %s", snd_strerror(err));
600 goto _close;
601 }
602 }
603 err = snd_config_search(card, "control", &control);
604 if (err == 0) {
605 err = snd_config_delete(control);
606 if (err < 0) {
607 error("snd_config_delete: %s", snd_strerror(err));
608 goto _close;
609 }
610 }
611 err = snd_ctl_elem_list(handle, list);
612 if (err < 0) {
613 error("Cannot determine controls: %s", snd_strerror(err));
614 goto _close;
615 }
616 count = snd_ctl_elem_list_get_count(list);
617 err = snd_config_compound_add(card, "control", count > 0, &control);
618 if (err < 0) {
619 error("snd_config_compound_add: %s", snd_strerror(err));
620 goto _close;
621 }
622 if (count == 0) {
623 err = 0;
624 goto _close;
625 }
626 snd_ctl_elem_list_set_offset(list, 0);
627 if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
628 error("No enough memory...");
629 goto _close;
630 }
631 if ((err = snd_ctl_elem_list(handle, list)) < 0) {
632 error("Cannot determine controls (2): %s", snd_strerror(err));
633 goto _free;
634 }
635 for (idx = 0; idx < count; ++idx) {
636 snd_ctl_elem_list_get_id(list, idx, elem_id);
637 err = get_control(handle, elem_id, control);
638 if (err < 0)
639 goto _free;
640 }
641
642 err = 0;
643 _free:
644 snd_ctl_elem_list_free_space(list);
645 _close:
646 snd_ctl_close(handle);
647 return err;
648 }
649
config_iface(snd_config_t * n)650 static long config_iface(snd_config_t *n)
651 {
652 long i;
653 long long li;
654 snd_ctl_elem_iface_t idx;
655 const char *str;
656 switch (snd_config_get_type(n)) {
657 case SND_CONFIG_TYPE_INTEGER:
658 if (snd_config_get_integer(n, &i) < 0)
659 return -1;
660 return i;
661 case SND_CONFIG_TYPE_INTEGER64:
662 if (snd_config_get_integer64(n, &li) < 0)
663 return -1;
664 return li;
665 case SND_CONFIG_TYPE_STRING:
666 if (snd_config_get_string(n, &str) < 0)
667 return -1;
668 break;
669 default:
670 return -1;
671 }
672 for (idx = 0; idx <= SND_CTL_ELEM_IFACE_LAST; idx++) {
673 if (strcasecmp(snd_ctl_elem_iface_name(idx), str) == 0)
674 return idx;
675 }
676 return -1;
677 }
678
config_bool(snd_config_t * n,int doit)679 static int config_bool(snd_config_t *n, int doit)
680 {
681 const char *str;
682 long val;
683 long long lval;
684
685 switch (snd_config_get_type(n)) {
686 case SND_CONFIG_TYPE_INTEGER:
687 if (snd_config_get_integer(n, &val) < 0)
688 return -1;
689 if (val < 0 || val > 1)
690 return -1;
691 return val;
692 case SND_CONFIG_TYPE_INTEGER64:
693 if (snd_config_get_integer64(n, &lval) < 0)
694 return -1;
695 if (lval < 0 || lval > 1)
696 return -1;
697 return (int) lval;
698 case SND_CONFIG_TYPE_STRING:
699 if (snd_config_get_string(n, &str) < 0)
700 return -1;
701 break;
702 case SND_CONFIG_TYPE_COMPOUND:
703 if (!force_restore || !doit)
704 return -1;
705 n = snd_config_iterator_entry(snd_config_iterator_first(n));
706 return config_bool(n, doit);
707 default:
708 return -1;
709 }
710 if (strcmp(str, "on") == 0 || strcmp(str, "true") == 0)
711 return 1;
712 if (strcmp(str, "off") == 0 || strcmp(str, "false") == 0)
713 return 0;
714 return -1;
715 }
716
config_enumerated(snd_config_t * n,snd_ctl_t * handle,snd_ctl_elem_info_t * info,int doit)717 static int config_enumerated(snd_config_t *n, snd_ctl_t *handle,
718 snd_ctl_elem_info_t *info, int doit)
719 {
720 const char *str;
721 long val;
722 long long lval;
723 unsigned int idx, items;
724
725 switch (snd_config_get_type(n)) {
726 case SND_CONFIG_TYPE_INTEGER:
727 if (snd_config_get_integer(n, &val) < 0)
728 return -1;
729 return val;
730 case SND_CONFIG_TYPE_INTEGER64:
731 if (snd_config_get_integer64(n, &lval) < 0)
732 return -1;
733 return (int) lval;
734 case SND_CONFIG_TYPE_STRING:
735 if (snd_config_get_string(n, &str) < 0)
736 return -1;
737 break;
738 case SND_CONFIG_TYPE_COMPOUND:
739 if (!force_restore || !doit)
740 return -1;
741 n = snd_config_iterator_entry(snd_config_iterator_first(n));
742 return config_enumerated(n, handle, info, doit);
743 default:
744 return -1;
745 }
746 items = snd_ctl_elem_info_get_items(info);
747 for (idx = 0; idx < items; idx++) {
748 int err;
749 snd_ctl_elem_info_set_item(info, idx);
750 err = snd_ctl_elem_info(handle, info);
751 if (err < 0) {
752 error("snd_ctl_elem_info: %s", snd_strerror(err));
753 return err;
754 }
755 if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
756 return idx;
757 }
758 return -1;
759 }
760
config_integer(snd_config_t * n,long * val,int doit)761 static int config_integer(snd_config_t *n, long *val, int doit)
762 {
763 int err = snd_config_get_integer(n, val);
764 if (err < 0 && force_restore && doit) {
765 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND)
766 return err;
767 n = snd_config_iterator_entry(snd_config_iterator_first(n));
768 return config_integer(n, val, doit);
769 }
770 return err;
771 }
772
config_integer64(snd_config_t * n,long long * val,int doit)773 static int config_integer64(snd_config_t *n, long long *val, int doit)
774 {
775 int err = snd_config_get_integer64(n, val);
776 if (err < 0 && force_restore && doit) {
777 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND)
778 return err;
779 n = snd_config_iterator_entry(snd_config_iterator_first(n));
780 return config_integer64(n, val, doit);
781 }
782 return err;
783 }
784
check_comment_access(snd_config_t * conf,const char * str)785 static int check_comment_access(snd_config_t *conf, const char *str)
786 {
787 snd_config_iterator_t i, next;
788
789 snd_config_for_each(i, next, conf) {
790 snd_config_t *n = snd_config_iterator_entry(i);
791 const char *id, *s;
792 if (snd_config_get_id(n, &id) < 0)
793 continue;
794 if (strcmp(id, "access") == 0) {
795 if (snd_config_get_string(n, &s) < 0)
796 return 0;
797 if (strstr(s, str))
798 return 1;
799 }
800 }
801 return 0;
802 }
803
804 /*
805 * get the item type from the given comment config
806 */
get_comment_type(snd_config_t * n)807 static int get_comment_type(snd_config_t *n)
808 {
809 static const snd_ctl_elem_type_t types[] = {
810 SND_CTL_ELEM_TYPE_BOOLEAN,
811 SND_CTL_ELEM_TYPE_INTEGER,
812 SND_CTL_ELEM_TYPE_ENUMERATED,
813 SND_CTL_ELEM_TYPE_BYTES,
814 SND_CTL_ELEM_TYPE_IEC958,
815 SND_CTL_ELEM_TYPE_INTEGER64,
816 };
817 const char *type;
818 unsigned int i;
819
820 if (snd_config_get_string(n, &type) < 0)
821 return -EINVAL;
822 for (i = 0; i < ARRAY_SIZE(types); ++i)
823 if (strcmp(type, snd_ctl_elem_type_name(types[i])) == 0)
824 return types[i];
825 return -EINVAL;
826 }
827
828 /*
829 * get the value range from the given comment config
830 */
get_comment_range(snd_config_t * n,int ctype,long * imin,long * imax,long * istep)831 static int get_comment_range(snd_config_t *n, int ctype,
832 long *imin, long *imax, long *istep)
833 {
834 const char *s;
835 int err;
836
837 if (snd_config_get_string(n, &s) < 0)
838 return -EINVAL;
839 switch (ctype) {
840 case SND_CTL_ELEM_TYPE_INTEGER:
841 err = sscanf(s, "%li - %li (step %li)", imin, imax, istep);
842 if (err != 3) {
843 istep = 0;
844 err = sscanf(s, "%li - %li", imin, imax);
845 if (err != 2)
846 return -EINVAL;
847 }
848 break;
849 default:
850 return -EINVAL;
851 }
852 return 0;
853 }
854
855 struct string_array {
856 unsigned int count;
857 const char **strings;
858 };
859
get_comment_items(snd_config_t * n,struct string_array * items)860 static int get_comment_items(snd_config_t *n, struct string_array *items)
861 {
862 snd_config_iterator_t it, next;
863 unsigned int i;
864 int err;
865
866 snd_config_for_each(it, next, n) {
867 snd_config_t *item = snd_config_iterator_entry(it);
868 const char *id;
869 unsigned int numid;
870
871 if (snd_config_get_id(item, &id) < 0)
872 return -EINVAL;
873 numid = atoi(id);
874 if (numid > 999999)
875 return -EINVAL;
876
877 if (numid >= items->count) {
878 const char **strings = realloc(items->strings, (numid + 1) * sizeof(char *));
879 if (!strings)
880 return -ENOMEM;
881 for (i = items->count; i < numid + 1; ++i)
882 strings[i] = NULL;
883 items->count = numid + 1;
884 items->strings = strings;
885 }
886 err = snd_config_get_string(item, &items->strings[numid]);
887 if (err < 0)
888 return err;
889 }
890
891 for (i = 0; i < items->count; ++i)
892 if (!items->strings[i])
893 return -EINVAL;
894
895 return 0;
896 }
897
add_user_control(snd_ctl_t * handle,snd_ctl_elem_info_t * info,snd_config_t * conf)898 static int add_user_control(snd_ctl_t *handle, snd_ctl_elem_info_t *info, snd_config_t *conf)
899 {
900 snd_ctl_elem_id_t *id;
901 snd_config_iterator_t i, next;
902 long imin, imax, istep;
903 snd_ctl_elem_type_t ctype;
904 unsigned int count;
905 struct string_array enum_items;
906 int err;
907 unsigned int *tlv;
908
909 imin = imax = istep = 0;
910 count = 0;
911 ctype = SND_CTL_ELEM_TYPE_NONE;
912 enum_items.count = 0;
913 enum_items.strings = NULL;
914 tlv = NULL;
915 snd_config_for_each(i, next, conf) {
916 snd_config_t *n = snd_config_iterator_entry(i);
917 const char *id;
918 if (snd_config_get_id(n, &id) < 0)
919 continue;
920 if (strcmp(id, "type") == 0) {
921 err = get_comment_type(n);
922 if (err < 0)
923 goto error;
924 ctype = err;
925 continue;
926 }
927 if (strcmp(id, "range") == 0) {
928 err = get_comment_range(n, ctype, &imin, &imax, &istep);
929 if (err < 0)
930 goto error;
931 continue;
932 }
933 if (strcmp(id, "count") == 0) {
934 long v;
935 if ((err = snd_config_get_integer(n, &v)) < 0)
936 goto error;
937 count = v;
938 continue;
939 }
940 if (strcmp(id, "item") == 0) {
941 err = get_comment_items(n, &enum_items);
942 if (err < 0)
943 goto error;
944 continue;
945 }
946 if (strcmp(id, "tlv") == 0) {
947 const char *s;
948 if ((err = snd_config_get_string(n, &s)) < 0)
949 goto error;
950 if (tlv)
951 free(tlv);
952 if ((tlv = str_to_tlv(s)) == NULL) {
953 err = -EINVAL;
954 goto error;
955 }
956 continue;
957 }
958 }
959
960 snd_ctl_elem_id_alloca(&id);
961 snd_ctl_elem_info_get_id(info, id);
962 if (count <= 0)
963 count = 1;
964 switch (ctype) {
965 case SND_CTL_ELEM_TYPE_INTEGER:
966 if (imin > imax || istep > imax - imin) {
967 err = -EINVAL;
968 goto error;
969 }
970 err = snd_ctl_elem_add_integer(handle, id, count, imin, imax, istep);
971 if (err < 0)
972 goto error;
973 if (tlv)
974 snd_ctl_elem_tlv_write(handle, id, tlv);
975 break;
976 case SND_CTL_ELEM_TYPE_BOOLEAN:
977 err = snd_ctl_elem_add_boolean(handle, id, count);
978 break;
979 case SND_CTL_ELEM_TYPE_ENUMERATED:
980 err = snd_ctl_elem_add_enumerated(handle, id, count,
981 enum_items.count, enum_items.strings);
982 break;
983 case SND_CTL_ELEM_TYPE_IEC958:
984 err = snd_ctl_elem_add_iec958(handle, id);
985 break;
986 default:
987 err = -EINVAL;
988 break;
989 }
990
991 error:
992 free(tlv);
993 free(enum_items.strings);
994 if (err < 0)
995 return err;
996 return snd_ctl_elem_info(handle, info);
997 }
998
999 /*
1000 * check whether the config item has the same of compatible type
1001 */
check_comment_type(snd_config_t * conf,int type)1002 static int check_comment_type(snd_config_t *conf, int type)
1003 {
1004 snd_config_t *n;
1005 int ctype;
1006
1007 if (snd_config_search(conf, "type", &n) < 0)
1008 return 0; /* not defined */
1009 ctype = get_comment_type(n);
1010 if (ctype == type)
1011 return 0;
1012 if ((ctype == SND_CTL_ELEM_TYPE_BOOLEAN ||
1013 ctype == SND_CTL_ELEM_TYPE_INTEGER ||
1014 ctype == SND_CTL_ELEM_TYPE_INTEGER64 ||
1015 ctype == SND_CTL_ELEM_TYPE_ENUMERATED) &&
1016 (type == SND_CTL_ELEM_TYPE_BOOLEAN ||
1017 type == SND_CTL_ELEM_TYPE_INTEGER ||
1018 type == SND_CTL_ELEM_TYPE_INTEGER64 ||
1019 type == SND_CTL_ELEM_TYPE_ENUMERATED))
1020 return 0; /* OK, compatible */
1021 return -EINVAL;
1022 }
1023
1024 /*
1025 * convert from an old value to a new value with the same dB level
1026 */
convert_to_new_db(snd_config_t * value,long omin,long omax,long nmin,long nmax,long odbmin,long odbmax,snd_config_t * comment,const char * index,snd_ctl_t * device,snd_ctl_elem_id_t * id,int doit)1027 static int convert_to_new_db(snd_config_t *value, long omin, long omax,
1028 long nmin, long nmax,
1029 long odbmin, long odbmax,
1030 snd_config_t *comment, const char *index,
1031 snd_ctl_t *device, snd_ctl_elem_id_t *id,
1032 int doit)
1033 {
1034 snd_config_t *db_node;
1035 long db, val;
1036 int err;
1037
1038 if (snd_config_searchv(comment, &db_node, "dbvalue", index, NULL) < 0 ||
1039 snd_config_get_integer(db_node, &db) < 0) {
1040 err = config_integer(value, &val, doit);
1041 if (err < 0)
1042 return err;
1043 if (val < omin || val > omax)
1044 return -EINVAL;
1045 db = ((val - omin) * (odbmax - odbmin)) / (omax - omin) + odbmin;
1046 }
1047
1048 err = snd_ctl_convert_from_dB(device, id, db, &val, db > 0);
1049 if (err < 0)
1050 return err;
1051 if (val < nmin)
1052 val = nmin;
1053 else if (val > nmax)
1054 val = nmax;
1055 return snd_config_set_integer(value, val);
1056 }
1057
1058 /*
1059 * compare the current value range with the old range in comments.
1060 * also, if dB information is available, try to compare them.
1061 * if any change occurs, try to keep the same dB level.
1062 */
check_comment_range(snd_ctl_t * handle,snd_config_t * conf,snd_ctl_elem_info_t * info,snd_config_t * value,int doit)1063 static int check_comment_range(snd_ctl_t *handle, snd_config_t *conf,
1064 snd_ctl_elem_info_t *info, snd_config_t *value,
1065 int doit)
1066 {
1067 snd_config_t *n;
1068 long omin, omax, ostep;
1069 long nmin, nmax;
1070 long odbmin, odbmax;
1071 long ndbmin, ndbmax;
1072 snd_ctl_elem_id_t *id;
1073
1074 if (snd_config_search(conf, "range", &n) < 0)
1075 return 0;
1076 if (get_comment_range(n, SND_CTL_ELEM_TYPE_INTEGER,
1077 &omin, &omax, &ostep) < 0)
1078 return 0;
1079 nmin = snd_ctl_elem_info_get_min(info);
1080 nmax = snd_ctl_elem_info_get_max(info);
1081 if (omin != nmin && omax != nmax) {
1082 /* Hey, the range mismatches */
1083 if (!force_restore || !doit)
1084 return -EINVAL;
1085 }
1086 if (omin >= omax || nmin >= nmax)
1087 return 0; /* invalid values */
1088
1089 if (snd_config_search(conf, "dbmin", &n) < 0)
1090 return 0;
1091 if (config_integer(n, &odbmin, doit) < 0)
1092 return 0;
1093 if (snd_config_search(conf, "dbmax", &n) < 0)
1094 return 0;
1095 if (config_integer(n, &odbmax, doit) < 0)
1096 return 0;
1097 if (odbmin >= odbmax)
1098 return 0; /* invalid values */
1099 snd_ctl_elem_id_alloca(&id);
1100 snd_ctl_elem_info_get_id(info, id);
1101 if (snd_ctl_get_dB_range(handle, id, &ndbmin, &ndbmax) < 0)
1102 return 0;
1103 if (ndbmin >= ndbmax)
1104 return 0; /* invalid values */
1105 if (omin == nmin && omax == nmax &&
1106 odbmin == ndbmin && odbmax == ndbmax)
1107 return 0; /* OK, identical one */
1108
1109 /* Let's guess the current value from dB range */
1110 if (snd_config_get_type(value) == SND_CONFIG_TYPE_COMPOUND) {
1111 snd_config_iterator_t i, next;
1112 snd_config_for_each(i, next, value) {
1113 snd_config_t *n = snd_config_iterator_entry(i);
1114 const char *idxstr;
1115 if (snd_config_get_id(n, &idxstr) < 0)
1116 continue;
1117 convert_to_new_db(n, omin, omax, nmin, nmax,
1118 odbmin, odbmax, conf, idxstr,
1119 handle, id, doit);
1120 }
1121 } else
1122 convert_to_new_db(value, omin, omax, nmin, nmax,
1123 odbmin, odbmax, conf, "0",
1124 handle, id, doit);
1125 return 0;
1126 }
1127
restore_config_value(snd_ctl_t * handle,snd_ctl_elem_info_t * info,snd_ctl_elem_iface_t type,snd_config_t * value,snd_ctl_elem_value_t * ctl,int idx,int doit)1128 static int restore_config_value(snd_ctl_t *handle, snd_ctl_elem_info_t *info,
1129 snd_ctl_elem_iface_t type,
1130 snd_config_t *value,
1131 snd_ctl_elem_value_t *ctl, int idx,
1132 int doit)
1133 {
1134 long val;
1135 long long lval;
1136 int err;
1137
1138 switch (type) {
1139 case SND_CTL_ELEM_TYPE_BOOLEAN:
1140 val = config_bool(value, doit);
1141 if (val >= 0) {
1142 snd_ctl_elem_value_set_boolean(ctl, idx, val);
1143 return 1;
1144 }
1145 break;
1146 case SND_CTL_ELEM_TYPE_INTEGER:
1147 err = config_integer(value, &val, doit);
1148 if (err == 0) {
1149 snd_ctl_elem_value_set_integer(ctl, idx, val);
1150 return 1;
1151 }
1152 break;
1153 case SND_CTL_ELEM_TYPE_INTEGER64:
1154 err = config_integer64(value, &lval, doit);
1155 if (err == 0) {
1156 snd_ctl_elem_value_set_integer64(ctl, idx, lval);
1157 return 1;
1158 }
1159 break;
1160 case SND_CTL_ELEM_TYPE_ENUMERATED:
1161 val = config_enumerated(value, handle, info, doit);
1162 if (val >= 0) {
1163 snd_ctl_elem_value_set_enumerated(ctl, idx, val);
1164 return 1;
1165 }
1166 break;
1167 case SND_CTL_ELEM_TYPE_BYTES:
1168 case SND_CTL_ELEM_TYPE_IEC958:
1169 break;
1170 default:
1171 cerror(doit, "Unknow control type: %d", type);
1172 return -EINVAL;
1173 }
1174 return 0;
1175 }
1176
restore_config_value2(snd_ctl_t * handle,snd_ctl_elem_info_t * info,snd_ctl_elem_iface_t type,snd_config_t * value,snd_ctl_elem_value_t * ctl,int idx,unsigned int numid,int doit)1177 static int restore_config_value2(snd_ctl_t *handle, snd_ctl_elem_info_t *info,
1178 snd_ctl_elem_iface_t type,
1179 snd_config_t *value,
1180 snd_ctl_elem_value_t *ctl, int idx,
1181 unsigned int numid, int doit)
1182 {
1183 int err = restore_config_value(handle, info, type, value, ctl, idx, doit);
1184 long val;
1185
1186 if (err != 0)
1187 return err;
1188 switch (type) {
1189 case SND_CTL_ELEM_TYPE_BYTES:
1190 case SND_CTL_ELEM_TYPE_IEC958:
1191 err = snd_config_get_integer(value, &val);
1192 if (err < 0 || val < 0 || val > 255) {
1193 cerror(doit, "bad control.%d.value.%d content", numid, idx);
1194 return force_restore && doit ? 0 : -EINVAL;
1195 }
1196 snd_ctl_elem_value_set_byte(ctl, idx, val);
1197 return 1;
1198 default:
1199 break;
1200 }
1201 return 0;
1202 }
1203
set_control(snd_ctl_t * handle,snd_config_t * control,int * maxnumid,int doit)1204 static int set_control(snd_ctl_t *handle, snd_config_t *control,
1205 int *maxnumid, int doit)
1206 {
1207 snd_ctl_elem_value_t *ctl;
1208 snd_ctl_elem_info_t *info;
1209 snd_config_iterator_t i, next;
1210 unsigned int numid1;
1211 snd_ctl_elem_iface_t iface = -1;
1212 int iface1;
1213 const char *name1;
1214 unsigned int numid;
1215 snd_ctl_elem_type_t type;
1216 unsigned int count;
1217 long device = -1;
1218 long device1;
1219 long subdevice = -1;
1220 long subdevice1;
1221 const char *name = NULL;
1222 long index1;
1223 long index = -1;
1224 snd_config_t *value = NULL;
1225 snd_config_t *comment = NULL;
1226 unsigned int idx;
1227 int err;
1228 char *set;
1229 const char *id;
1230 snd_ctl_elem_value_alloca(&ctl);
1231 snd_ctl_elem_info_alloca(&info);
1232 if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
1233 cerror(doit, "control is not a compound");
1234 return -EINVAL;
1235 }
1236 err = snd_config_get_id(control, &id);
1237 if (err < 0) {
1238 cerror(doit, "unable to get id");
1239 return -EINVAL;
1240 }
1241 numid = atoi(id);
1242 if ((int)numid > *maxnumid)
1243 *maxnumid = numid;
1244 snd_config_for_each(i, next, control) {
1245 snd_config_t *n = snd_config_iterator_entry(i);
1246 const char *fld;
1247 if (snd_config_get_id(n, &fld) < 0)
1248 continue;
1249 if (strcmp(fld, "comment") == 0) {
1250 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1251 cerror(doit, "control.%d.%s is invalid", numid, fld);
1252 return -EINVAL;
1253 }
1254 comment = n;
1255 continue;
1256 }
1257 if (strcmp(fld, "iface") == 0) {
1258 iface = (snd_ctl_elem_iface_t)config_iface(n);
1259 if (iface < 0)
1260 return -EINVAL;
1261 continue;
1262 }
1263 if (strcmp(fld, "device") == 0) {
1264 if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
1265 cerror(doit, "control.%d.%s is invalid", numid, fld);
1266 return -EINVAL;
1267 }
1268 if (snd_config_get_integer(n, &device) < 0)
1269 return -EINVAL;
1270 continue;
1271 }
1272 if (strcmp(fld, "subdevice") == 0) {
1273 if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
1274 cerror(doit, "control.%d.%s is invalid", numid, fld);
1275 return -EINVAL;
1276 }
1277 if (snd_config_get_integer(n, &subdevice) < 0)
1278 return -EINVAL;
1279 continue;
1280 }
1281 if (strcmp(fld, "name") == 0) {
1282 if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
1283 cerror(doit, "control.%d.%s is invalid", numid, fld);
1284 return -EINVAL;
1285 }
1286 if (snd_config_get_string(n, &name) < 0)
1287 return -EINVAL;
1288 continue;
1289 }
1290 if (strcmp(fld, "index") == 0) {
1291 if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
1292 cerror(doit, "control.%d.%s is invalid", numid, fld);
1293 return -EINVAL;
1294 }
1295 if (snd_config_get_integer(n, &index) < 0)
1296 return -EINVAL;
1297 continue;
1298 }
1299 if (strcmp(fld, "value") == 0) {
1300 value = n;
1301 continue;
1302 }
1303 cerror(doit, "unknown control.%d.%s field", numid, fld);
1304 }
1305 if (!value) {
1306 cerror(doit, "missing control.%d.value", numid);
1307 return -EINVAL;
1308 }
1309 if (device < 0)
1310 device = 0;
1311 if (subdevice < 0)
1312 subdevice = 0;
1313 if (index < 0)
1314 index = 0;
1315
1316 err = -EINVAL;
1317 if (!force_restore) {
1318 snd_ctl_elem_info_set_numid(info, numid);
1319 err = snd_ctl_elem_info(handle, info);
1320 }
1321 if (err < 0 && name) {
1322 snd_ctl_elem_info_set_numid(info, 0);
1323 snd_ctl_elem_info_set_interface(info, iface);
1324 snd_ctl_elem_info_set_device(info, device);
1325 snd_ctl_elem_info_set_subdevice(info, subdevice);
1326 snd_ctl_elem_info_set_name(info, name);
1327 snd_ctl_elem_info_set_index(info, index);
1328 err = snd_ctl_elem_info(handle, info);
1329 if (err < 0 && comment && check_comment_access(comment, "user")) {
1330 err = add_user_control(handle, info, comment);
1331 if (err < 0) {
1332 cerror(doit, "failed to add user control #%d (%s)",
1333 numid, snd_strerror(err));
1334 return err;
1335 }
1336 }
1337 }
1338 if (err < 0) {
1339 cerror(doit, "failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
1340 return -ENOENT;
1341 }
1342 numid1 = snd_ctl_elem_info_get_numid(info);
1343 iface1 = snd_ctl_elem_info_get_interface(info);
1344 device1 = snd_ctl_elem_info_get_device(info);
1345 subdevice1 = snd_ctl_elem_info_get_subdevice(info);
1346 name1 = snd_ctl_elem_info_get_name(info);
1347 index1 = snd_ctl_elem_info_get_index(info);
1348 count = snd_ctl_elem_info_get_count(info);
1349 type = snd_ctl_elem_info_get_type(info);
1350 if (err |= numid != numid1 && !force_restore)
1351 cerror(doit, "warning: numid mismatch (%d/%d) for control #%d",
1352 numid, numid1, numid);
1353 if (err |= iface != iface1)
1354 cerror(doit, "warning: iface mismatch (%d/%d) for control #%d", iface, iface1, numid);
1355 if (err |= device != device1)
1356 cerror(doit, "warning: device mismatch (%ld/%ld) for control #%d", device, device1, numid);
1357 if (err |= subdevice != subdevice1)
1358 cerror(doit, "warning: subdevice mismatch (%ld/%ld) for control #%d", subdevice, subdevice1, numid);
1359 if (err |= strcmp(name, name1))
1360 cerror(doit, "warning: name mismatch (%s/%s) for control #%d", name, name1, numid);
1361 if (err |= index != index1)
1362 cerror(doit, "warning: index mismatch (%ld/%ld) for control #%d", index, index1, numid);
1363 if (err < 0) {
1364 cerror(doit, "failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
1365 return -ENOENT;
1366 }
1367
1368 if (comment) {
1369 if (check_comment_type(comment, type) < 0)
1370 cerror(doit, "incompatible field type for control #%d", numid);
1371 if (type == SND_CTL_ELEM_TYPE_INTEGER) {
1372 if (check_comment_range(handle, comment, info, value, doit) < 0) {
1373 cerror(doit, "value range mismatch for control #%d",
1374 numid);
1375 return -EINVAL;
1376 }
1377 }
1378 /* inactive controls are not restored */
1379 if (comment && check_comment_access(comment, "inactive"))
1380 return 0;
1381 }
1382
1383 if (snd_ctl_elem_info_is_inactive(info) ||
1384 !snd_ctl_elem_info_is_writable(info))
1385 return 0;
1386 snd_ctl_elem_value_set_numid(ctl, numid1);
1387
1388 if (count == 1) {
1389 err = restore_config_value(handle, info, type, value, ctl, 0, doit);
1390 if (err < 0)
1391 return err;
1392 if (err > 0)
1393 goto _ok;
1394 }
1395 switch (type) {
1396 case SND_CTL_ELEM_TYPE_BYTES:
1397 case SND_CTL_ELEM_TYPE_IEC958:
1398 {
1399 const char *buf;
1400 err = snd_config_get_string(value, &buf);
1401 if (err >= 0) {
1402 int c1 = 0;
1403 int len = strlen(buf);
1404 unsigned int idx = 0;
1405 int size = type == SND_CTL_ELEM_TYPE_BYTES ?
1406 count : sizeof(snd_aes_iec958_t);
1407 if (size * 2 != len) {
1408 cerror(doit, "bad control.%d.value contents\n", numid);
1409 return -EINVAL;
1410 }
1411 while (*buf) {
1412 int c = *buf++;
1413 if ((c = hextodigit(c)) < 0) {
1414 cerror(doit, "bad control.%d.value contents\n", numid);
1415 return -EINVAL;
1416 }
1417 if (idx % 2 == 1)
1418 snd_ctl_elem_value_set_byte(ctl, idx / 2, c1 << 4 | c);
1419 else
1420 c1 = c;
1421 idx++;
1422 }
1423 goto _ok;
1424 }
1425 }
1426 default:
1427 break;
1428 }
1429 if (snd_config_get_type(value) != SND_CONFIG_TYPE_COMPOUND) {
1430 if (!force_restore || !doit) {
1431 cerror(doit, "bad control.%d.value type", numid);
1432 return -EINVAL;
1433 }
1434 for (idx = 0; idx < count; ++idx) {
1435 err = restore_config_value2(handle, info, type, value,
1436 ctl, idx, numid, doit);
1437 if (err < 0)
1438 return err;
1439 }
1440 goto _ok;
1441 }
1442
1443 set = (char*) alloca(count);
1444 memset(set, 0, count);
1445 snd_config_for_each(i, next, value) {
1446 snd_config_t *n = snd_config_iterator_entry(i);
1447 const char *id;
1448 if (snd_config_get_id(n, &id) < 0)
1449 continue;
1450 idx = atoi(id);
1451 if (idx >= count || set[idx]) {
1452 cerror(doit, "bad control.%d.value index", numid);
1453 if (!force_restore || !doit)
1454 return -EINVAL;
1455 continue;
1456 }
1457 err = restore_config_value2(handle, info, type, n,
1458 ctl, idx, numid, doit);
1459 if (err < 0)
1460 return err;
1461 if (err > 0)
1462 set[idx] = 1;
1463 }
1464 for (idx = 0; idx < count; ++idx) {
1465 if (!set[idx]) {
1466 cerror(doit, "control.%d.value.%d is not specified", numid, idx);
1467 if (!force_restore || !doit)
1468 return -EINVAL;
1469 }
1470 }
1471
1472 _ok:
1473 err = doit ? snd_ctl_elem_write(handle, ctl) : 0;
1474 if (err < 0) {
1475 error("Cannot write control '%d:%ld:%ld:%s:%ld' : %s", (int)iface, device, subdevice, name, index, snd_strerror(err));
1476 return err;
1477 }
1478 return 0;
1479 }
1480
set_controls(int card,snd_config_t * top,int doit)1481 static int set_controls(int card, snd_config_t *top, int doit)
1482 {
1483 snd_ctl_t *handle;
1484 snd_ctl_card_info_t *info;
1485 snd_ctl_elem_list_t *list;
1486 snd_ctl_elem_info_t *elem_info;
1487 snd_ctl_elem_id_t *elem_id;
1488 snd_config_t *control;
1489 snd_config_iterator_t i, next;
1490 int err, maxnumid = -1, maxnumid2 = -1;
1491 unsigned int idx, count = 0;
1492 char name[32], tmpid[16];
1493 const char *id;
1494 snd_ctl_card_info_alloca(&info);
1495 snd_ctl_elem_list_alloca(&list);
1496 snd_ctl_elem_info_alloca(&elem_info);
1497 snd_ctl_elem_id_alloca(&elem_id);
1498 sprintf(name, "hw:%d", card);
1499 dbg("device='%s', doit=%i", name, doit);
1500 err = snd_ctl_open(&handle, name, 0);
1501 if (err < 0) {
1502 error("snd_ctl_open error: %s", snd_strerror(err));
1503 return err;
1504 }
1505 err = snd_ctl_card_info(handle, info);
1506 if (err < 0) {
1507 error("snd_ctl_card_info error: %s", snd_strerror(err));
1508 goto _close;
1509 }
1510 id = snd_ctl_card_info_get_id(info);
1511 dbg("card-info-id: '%s'", id);
1512 err = snd_config_searchv(top, &control, "state", id, "control", 0);
1513 if (err < 0) {
1514 if (force_restore) {
1515 sprintf(tmpid, "card%d", card);
1516 err = snd_config_searchv(top, &control, "state", tmpid, "control", 0);
1517 if (! err)
1518 id = tmpid;
1519 }
1520 if (err < 0) {
1521 fprintf(stderr, "No state is present for card %s\n", id);
1522 goto _close;
1523 }
1524 id = tmpid;
1525 }
1526 if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
1527 cerror(doit, "state.%s.control is not a compound\n", id);
1528 return -EINVAL;
1529 }
1530 snd_config_for_each(i, next, control) {
1531 snd_config_t *n = snd_config_iterator_entry(i);
1532 err = set_control(handle, n, &maxnumid, doit);
1533 if (err < 0 && (!force_restore || !doit))
1534 goto _close;
1535 }
1536
1537 if (doit)
1538 goto _close;
1539
1540 err = snd_ctl_elem_list(handle, list);
1541 if (err < 0) {
1542 error("Cannot determine controls: %s", snd_strerror(err));
1543 goto _close;
1544 }
1545 count = snd_ctl_elem_list_get_count(list);
1546 dbg("list count: %u", count);
1547 if (count == 0)
1548 goto _check;
1549 snd_ctl_elem_list_set_offset(list, 0);
1550 if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
1551 error("No enough memory...");
1552 goto _close;
1553 }
1554 if ((err = snd_ctl_elem_list(handle, list)) < 0) {
1555 error("Cannot determine controls (2): %s", snd_strerror(err));
1556 goto _free;
1557 }
1558 maxnumid2 = 0;
1559 /* skip non-readable elements */
1560 for (idx = 0; idx < count; ++idx) {
1561 snd_ctl_elem_info_clear(elem_info);
1562 snd_ctl_elem_list_get_id(list, idx, elem_id);
1563 snd_ctl_elem_info_set_id(elem_info, elem_id);
1564 if (snd_ctl_elem_info(handle, elem_info) == 0) {
1565 if (!snd_ctl_elem_info_is_readable(elem_info))
1566 continue;
1567 maxnumid2++;
1568 }
1569 }
1570
1571 /* check if we have additional controls in driver */
1572 /* in this case we should go through init procedure */
1573 _check:
1574 dbg("maxnumid=%i maxnumid2=%i", maxnumid, maxnumid2);
1575 if (maxnumid >= 0 && maxnumid != maxnumid2) {
1576 /* not very informative */
1577 /* but value is used for check only */
1578 err = -EAGAIN;
1579 dbg("more controls than maxnumid?");
1580 }
1581
1582 _free:
1583 if (count >= 0)
1584 snd_ctl_elem_list_free_space(list);
1585 _close:
1586 snd_ctl_close(handle);
1587 dbg("result code: %i", err);
1588 return err;
1589 }
1590
save_state(const char * file,const char * cardname)1591 int save_state(const char *file, const char *cardname)
1592 {
1593 int err;
1594 snd_config_t *config;
1595 snd_input_t *in;
1596 snd_output_t *out;
1597 int stdio;
1598 char *nfile = NULL;
1599 int lock_fd = -EINVAL;
1600 struct snd_card_iterator iter;
1601
1602 err = snd_config_top(&config);
1603 if (err < 0) {
1604 error("snd_config_top error: %s", snd_strerror(err));
1605 return err;
1606 }
1607 stdio = !strcmp(file, "-");
1608 if (!stdio) {
1609 nfile = malloc(strlen(file) + 5);
1610 if (nfile == NULL) {
1611 error("No enough memory...");
1612 err = -ENOMEM;
1613 goto out;
1614 }
1615 strcpy(nfile, file);
1616 strcat(nfile, ".new");
1617 lock_fd = state_lock(file, 10);
1618 if (lock_fd < 0) {
1619 err = lock_fd;
1620 goto out;
1621 }
1622 }
1623 if (!stdio && (err = snd_input_stdio_open(&in, file, "r")) >= 0) {
1624 err = snd_config_load(config, in);
1625 snd_input_close(in);
1626 #if 0
1627 if (err < 0) {
1628 error("snd_config_load error: %s", snd_strerror(err));
1629 goto out;
1630 }
1631 #endif
1632 }
1633
1634 err = snd_card_iterator_sinit(&iter, cardname);
1635 if (err < 0)
1636 goto out;
1637 while (snd_card_iterator_next(&iter)) {
1638 if ((err = get_controls(iter.card, config)))
1639 goto out;
1640 }
1641 if (iter.first) {
1642 err = snd_card_iterator_error(&iter);
1643 goto out;
1644 }
1645
1646 if (stdio) {
1647 err = snd_output_stdio_attach(&out, stdout, 0);
1648 } else {
1649 err = snd_output_stdio_open(&out, nfile, "w");
1650 }
1651 if (err < 0) {
1652 error("Cannot open %s for writing: %s", file, snd_strerror(err));
1653 err = -errno;
1654 goto out;
1655 }
1656 err = snd_config_save(config, out);
1657 snd_output_close(out);
1658 if (err < 0) {
1659 error("snd_config_save: %s", snd_strerror(err));
1660 } else if (nfile) {
1661 err = rename(nfile, file);
1662 if (err < 0)
1663 error("rename failed: %s (%s)", strerror(-err), file);
1664 }
1665 out:
1666 if (!stdio && lock_fd >= 0)
1667 state_unlock(lock_fd, file);
1668 free(nfile);
1669 snd_config_delete(config);
1670 snd_config_update_free_global();
1671 return err;
1672 }
1673
load_state(const char * cfgdir,const char * file,const char * initfile,int initflags,const char * cardname,int do_init)1674 int load_state(const char *cfgdir, const char *file,
1675 const char *initfile, int initflags,
1676 const char *cardname, int do_init)
1677 {
1678 int err, finalerr = 0, open_failed;
1679 struct snd_card_iterator iter;
1680 snd_config_t *config;
1681 const char *cardname1;
1682
1683 config = NULL;
1684 err = load_configuration(file, &config, &open_failed);
1685 if (err < 0 && !open_failed)
1686 return err;
1687
1688 if (open_failed) {
1689 error("Cannot open %s for reading: %s", file, snd_strerror(err));
1690 finalerr = err;
1691
1692 err = snd_card_iterator_sinit(&iter, cardname);
1693 if (err < 0)
1694 return err;
1695 while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) {
1696 if (!do_init)
1697 break;
1698 err = init(cfgdir, initfile, initflags | FLAG_UCM_FBOOT | FLAG_UCM_BOOT, cardname1);
1699 if (err < 0) {
1700 finalerr = err;
1701 initfailed(iter.card, "init", err);
1702 }
1703 initfailed(iter.card, "restore", -ENOENT);
1704 }
1705 err = finalerr;
1706 if (iter.first)
1707 err = 0; /* no cards, no error code */
1708 goto out;
1709 }
1710
1711 err = snd_card_iterator_sinit(&iter, cardname);
1712 if (err < 0)
1713 goto out;
1714 while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) {
1715 /* error is ignored */
1716 init_ucm(initflags | FLAG_UCM_FBOOT, iter.card);
1717 /* do a check if controls matches state file */
1718 if (do_init && set_controls(iter.card, config, 0)) {
1719 err = init(cfgdir, initfile, initflags | FLAG_UCM_BOOT, cardname1);
1720 if (err < 0) {
1721 initfailed(iter.card, "init", err);
1722 finalerr = err;
1723 }
1724 }
1725 if ((err = set_controls(iter.card, config, 1))) {
1726 if (!force_restore)
1727 finalerr = err;
1728 initfailed(iter.card, "restore", err);
1729 }
1730 }
1731 err = finalerr ? finalerr : snd_card_iterator_error(&iter);
1732 out:
1733 if (config)
1734 snd_config_delete(config);
1735 snd_config_update_free_global();
1736 return err;
1737 }
1738