xref: /third_party/alsa-utils/alsactl/state.c (revision c72fcc34)
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
33static 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
46static char *num_str(long n)
47{
48	static char str[32];
49	sprintf(str, "%ld", n);
50	return str;
51}
52
53static 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
68static 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
83static 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
98static 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
117static 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
135static 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 */
165static 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
228static 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
538finish:
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
547static 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
650static 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
679static 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
717static 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
761static 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
773static 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
785static 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 */
807static 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 */
831static 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
855struct string_array {
856	unsigned int count;
857	const char **strings;
858};
859
860static 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
898static 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 */
1002static 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 */
1027static 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 */
1063static 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
1128static int restore_config_value(snd_ctl_t *handle, snd_ctl_elem_info_t *info,
1129				snd_ctl_elem_type_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
1177static int restore_config_value2(snd_ctl_t *handle, snd_ctl_elem_info_t *info,
1178				 snd_ctl_elem_type_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
1204static 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 |= (int)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
1481static 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
1591int 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, LOCK_TIMEOUT);
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	}
1665out:
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
1674int 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, lock_fd;
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			lock_fd = card_lock(iter.card, LOCK_TIMEOUT);
1699			if (lock_fd < 0) {
1700				finalerr = lock_fd;
1701				initfailed(iter.card, "lock", err);
1702				continue;
1703			}
1704			err = init(cfgdir, initfile, initflags | FLAG_UCM_FBOOT | FLAG_UCM_BOOT, cardname1);
1705			card_unlock(lock_fd, iter.card);
1706			if (err < 0) {
1707				finalerr = err;
1708				initfailed(iter.card, "init", err);
1709			}
1710			initfailed(iter.card, "restore", -ENOENT);
1711		}
1712		err = finalerr;
1713		if (iter.first)
1714			err = 0;	/* no cards, no error code */
1715		goto out;
1716	}
1717
1718	err = snd_card_iterator_sinit(&iter, cardname);
1719	if (err < 0)
1720		goto out;
1721	while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) {
1722		lock_fd = card_lock(iter.card, LOCK_TIMEOUT);
1723		if (lock_fd < 0) {
1724			initfailed(iter.card, "lock", lock_fd);
1725			finalerr = lock_fd;
1726			continue;
1727		}
1728		/* error is ignored */
1729		init_ucm(initflags | FLAG_UCM_FBOOT, iter.card);
1730		/* do a check if controls matches state file */
1731		if (do_init && set_controls(iter.card, config, 0)) {
1732			err = init(cfgdir, initfile, initflags | FLAG_UCM_BOOT, cardname1);
1733			if (err < 0) {
1734				initfailed(iter.card, "init", err);
1735				finalerr = err;
1736			}
1737		}
1738		if ((err = set_controls(iter.card, config, 1))) {
1739			if (!force_restore)
1740				finalerr = err;
1741			initfailed(iter.card, "restore", err);
1742		}
1743		card_unlock(lock_fd, iter.card);
1744	}
1745	err = finalerr ? finalerr : snd_card_iterator_error(&iter);
1746out:
1747	if (config)
1748		snd_config_delete(config);
1749	snd_config_update_free_global();
1750	return err;
1751}
1752