• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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