tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

state.c (45045B)


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