tarina

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

amixer.c (53111B)


      1 /*
      2  *   ALSA command line mixer utility
      3  *   Copyright (c) 1999-2000 by Jaroslav Kysela <perex@perex.cz>
      4  *
      5  *   This program is free software; you can redistribute it and/or modify
      6  *   it under the terms of the GNU General Public License as published by
      7  *   the Free Software Foundation; either version 2 of the License, or
      8  *   (at your option) any later version.
      9  *
     10  *   This program is distributed in the hope that it will be useful,
     11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *   GNU General Public License for more details.
     14  *
     15  *   You should have received a copy of the GNU General Public License
     16  *   along with this program; if not, write to the Free Software
     17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     18  *
     19  */
     20 
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <getopt.h>
     25 #include <stdarg.h>
     26 #include <ctype.h>
     27 #include <math.h>
     28 #include <errno.h>
     29 #include <assert.h>
     30 #include <alsa/asoundlib.h>
     31 #include <sys/poll.h>
     32 #include <stdint.h>
     33 #include "amixer.h"
     34 #include "../alsamixer/volume_mapping.h"
     35 
     36 #define LEVEL_BASIC		(1<<0)
     37 #define LEVEL_INACTIVE		(1<<1)
     38 #define LEVEL_ID		(1<<2)
     39 
     40 static int quiet = 0;
     41 static int debugflag = 0;
     42 static int no_check = 0;
     43 static int smixer_level = 0;
     44 static int ignore_error = 0;
     45 static struct snd_mixer_selem_regopt smixer_options;
     46 static char card[64] = "default";
     47 
     48 static void error(const char *fmt,...)
     49 {
     50 	va_list va;
     51 
     52 	va_start(va, fmt);
     53 	fprintf(stderr, "amixer: ");
     54 	vfprintf(stderr, fmt, va);
     55 	fprintf(stderr, "\n");
     56 	va_end(va);
     57 }
     58 
     59 static int help(void)
     60 {
     61 	printf("Usage: amixer <options> [command]\n");
     62 	printf("\nAvailable options:\n");
     63 	printf("  -h,--help       this help\n");
     64 	printf("  -c,--card N     select the card\n");
     65 	printf("  -D,--device N   select the device, default '%s'\n", card);
     66 	printf("  -d,--debug      debug mode\n");
     67 	printf("  -n,--nocheck    do not perform range checking\n");
     68 	printf("  -v,--version    print version of this program\n");
     69 	printf("  -q,--quiet      be quiet\n");
     70 	printf("  -i,--inactive   show also inactive controls\n");
     71 	printf("  -a,--abstract L select abstraction level (none or basic)\n");
     72 	printf("  -s,--stdin      Read and execute commands from stdin sequentially\n");
     73 	printf("  -R,--raw-volume Use the raw value (default)\n");
     74 	printf("  -M,--mapped-volume Use the mapped volume\n");
     75 	printf("\nAvailable commands:\n");
     76 	printf("  scontrols       show all mixer simple controls\n");
     77 	printf("  scontents	  show contents of all mixer simple controls (default command)\n");
     78 	printf("  sset sID P      set contents for one mixer simple control\n");
     79 	printf("  sget sID        get contents for one mixer simple control\n");
     80 	printf("  controls        show all controls for given card\n");
     81 	printf("  contents        show contents of all controls for given card\n");
     82 	printf("  cset cID P      set control contents for one control\n");
     83 	printf("  cget cID        get control contents for one control\n");
     84 	return 0;
     85 }
     86 
     87 static int info(void)
     88 {
     89 	int err;
     90 	snd_ctl_t *handle;
     91 	snd_mixer_t *mhandle;
     92 	snd_ctl_card_info_t *info;
     93 	snd_ctl_elem_list_t *clist;
     94 	snd_ctl_card_info_alloca(&info);
     95 	snd_ctl_elem_list_alloca(&clist);
     96 	
     97 	if ((err = snd_ctl_open(&handle, card, 0)) < 0) {
     98 		error("Control device %s open error: %s", card, snd_strerror(err));
     99 		return err;
    100 	}
    101 	
    102 	if ((err = snd_ctl_card_info(handle, info)) < 0) {
    103 		error("Control device %s hw info error: %s", card, snd_strerror(err));
    104 		return err;
    105 	}
    106 	printf("Card %s '%s'/'%s'\n", card, snd_ctl_card_info_get_id(info),
    107 	       snd_ctl_card_info_get_longname(info));
    108 	printf("  Mixer name	: '%s'\n", snd_ctl_card_info_get_mixername(info));
    109 	printf("  Components	: '%s'\n", snd_ctl_card_info_get_components(info));
    110 	if ((err = snd_ctl_elem_list(handle, clist)) < 0) {
    111 		error("snd_ctl_elem_list failure: %s", snd_strerror(err));
    112 	} else {
    113 		printf("  Controls      : %i\n", snd_ctl_elem_list_get_count(clist));
    114 	}
    115 	snd_ctl_close(handle);
    116 	if ((err = snd_mixer_open(&mhandle, 0)) < 0) {
    117 		error("Mixer open error: %s", snd_strerror(err));
    118 		return err;
    119 	}
    120 	if (smixer_level == 0 && (err = snd_mixer_attach(mhandle, card)) < 0) {
    121 		error("Mixer attach %s error: %s", card, snd_strerror(err));
    122 		snd_mixer_close(mhandle);
    123 		return err;
    124 	}
    125 	if ((err = snd_mixer_selem_register(mhandle, smixer_level > 0 ? &smixer_options : NULL, NULL)) < 0) {
    126 		error("Mixer register error: %s", snd_strerror(err));
    127 		snd_mixer_close(mhandle);
    128 		return err;
    129 	}
    130 	err = snd_mixer_load(mhandle);
    131 	if (err < 0) {
    132 		error("Mixer load %s error: %s", card, snd_strerror(err));
    133 		snd_mixer_close(mhandle);
    134 		return err;
    135 	}
    136 	printf("  Simple ctrls  : %i\n", snd_mixer_get_count(mhandle));
    137 	snd_mixer_close(mhandle);
    138 	return 0;
    139 }
    140 
    141 static const char *control_type(snd_ctl_elem_info_t *info)
    142 {
    143 	return snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(info));
    144 }
    145 
    146 static const char *control_access(snd_ctl_elem_info_t *info)
    147 {
    148 	static char result[10];
    149 	char *res = result;
    150 
    151 	*res++ = snd_ctl_elem_info_is_readable(info) ? 'r' : '-';
    152 	*res++ = snd_ctl_elem_info_is_writable(info) ? 'w' : '-';
    153 	*res++ = snd_ctl_elem_info_is_inactive(info) ? 'i' : '-';
    154 	*res++ = snd_ctl_elem_info_is_volatile(info) ? 'v' : '-';
    155 	*res++ = snd_ctl_elem_info_is_locked(info) ? 'l' : '-';
    156 	*res++ = snd_ctl_elem_info_is_tlv_readable(info) ? 'R' : '-';
    157 	*res++ = snd_ctl_elem_info_is_tlv_writable(info) ? 'W' : '-';
    158 	*res++ = snd_ctl_elem_info_is_tlv_commandable(info) ? 'C' : '-';
    159 	*res++ = '\0';
    160 	return result;
    161 }
    162 
    163 #define check_range(val, min, max) \
    164 	(no_check ? (val) : ((val < min) ? (min) : (val > max) ? (max) : (val))) 
    165 #if 0
    166 static int convert_range(int val, int omin, int omax, int nmin, int nmax)
    167 {
    168 	int orange = omax - omin, nrange = nmax - nmin;
    169 	
    170 	if (orange == 0)
    171 		return 0;
    172 	return rint((((double)nrange * ((double)val - (double)omin)) + ((double)orange / 2.0)) / ((double)orange + (double)nmin));
    173 }
    174 #endif
    175 
    176 #if 0
    177 static int convert_db_range(int val, int omin, int omax, int nmin, int nmax)
    178 {
    179 	int orange = omax - omin, nrange = nmax - nmin;
    180 	
    181 	if (orange == 0)
    182 		return 0;
    183 	return rint((((double)nrange * ((double)val - (double)omin)) + ((double)orange / 2.0)) / (double)orange + (double)nmin);
    184 }
    185 #endif
    186 
    187 /* Fuction to convert from volume to percentage. val = volume */
    188 
    189 static int convert_prange(long val, long min, long max)
    190 {
    191 	long range = max - min;
    192 	int tmp;
    193 
    194 	if (range == 0)
    195 		return 0;
    196 	val -= min;
    197 	tmp = rint((double)val/(double)range * 100);
    198 	return tmp;
    199 }
    200 
    201 /* Function to convert from percentage to volume. val = percentage */
    202 
    203 #define convert_prange1(val, min, max) \
    204 	ceil((val) * ((max) - (min)) * 0.01 + (min))
    205 
    206 struct volume_ops {
    207 	int (*get_range)(snd_mixer_elem_t *elem, long *min, long *max);
    208 	int (*get)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t c,
    209 		   long *value);
    210 	int (*set)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t c,
    211 		   long value, int dir);
    212 };
    213 	
    214 enum { VOL_RAW, VOL_DB, VOL_MAP };
    215 
    216 struct volume_ops_set {
    217 	int (*has_volume)(snd_mixer_elem_t *elem);
    218 	struct volume_ops v[3];
    219 };
    220 
    221 static int set_playback_dB(snd_mixer_elem_t *elem,
    222 			   snd_mixer_selem_channel_id_t c, long value, int dir)
    223 {
    224 	return snd_mixer_selem_set_playback_dB(elem, c, value, dir);
    225 }
    226 
    227 static int set_capture_dB(snd_mixer_elem_t *elem,
    228 			  snd_mixer_selem_channel_id_t c, long value, int dir)
    229 {
    230 	return snd_mixer_selem_set_capture_dB(elem, c, value, dir);
    231 }
    232 
    233 static int set_playback_raw_volume(snd_mixer_elem_t *elem,
    234 				   snd_mixer_selem_channel_id_t c,
    235 				   long value, int dir)
    236 {
    237 	return snd_mixer_selem_set_playback_volume(elem, c, value);
    238 }
    239 
    240 static int set_capture_raw_volume(snd_mixer_elem_t *elem,
    241 				  snd_mixer_selem_channel_id_t c,
    242 				  long value, int dir)
    243 {
    244 	return snd_mixer_selem_set_capture_volume(elem, c, value);
    245 }
    246 
    247 /* FIXME: normalize to int32 space to be compatible with other types */
    248 #define MAP_VOL_RES	(INT32_MAX / 100)
    249 
    250 static int get_mapped_volume_range(snd_mixer_elem_t *elem,
    251 				   long *pmin, long *pmax)
    252 {
    253 	*pmin = 0;
    254 	*pmax = MAP_VOL_RES;
    255 	return 0;
    256 }
    257 
    258 static int get_playback_mapped_volume(snd_mixer_elem_t *elem,
    259 				      snd_mixer_selem_channel_id_t c,
    260 				      long *value)
    261 {
    262 	*value = (rint)(get_normalized_playback_volume(elem, c) * MAP_VOL_RES);
    263 	return 0;
    264 }
    265 
    266 static int set_playback_mapped_volume(snd_mixer_elem_t *elem,
    267 				      snd_mixer_selem_channel_id_t c,
    268 				      long value, int dir)
    269 {
    270 	return set_normalized_playback_volume(elem, c,
    271 					      (double)value / MAP_VOL_RES, dir);
    272 }
    273 
    274 static int get_capture_mapped_volume(snd_mixer_elem_t *elem,
    275 				     snd_mixer_selem_channel_id_t c,
    276 				     long *value)
    277 {
    278 	*value = (rint)(get_normalized_capture_volume(elem, c) * MAP_VOL_RES);
    279 	return 0;
    280 }
    281 
    282 static int set_capture_mapped_volume(snd_mixer_elem_t *elem,
    283 				     snd_mixer_selem_channel_id_t c,
    284 				     long value, int dir)
    285 {
    286 	return set_normalized_capture_volume(elem, c,
    287 					     (double)value / MAP_VOL_RES, dir);
    288 }
    289 
    290 static const struct volume_ops_set vol_ops[2] = {
    291 	{
    292 		.has_volume = snd_mixer_selem_has_playback_volume,
    293 		.v = {{ snd_mixer_selem_get_playback_volume_range,
    294 			snd_mixer_selem_get_playback_volume,
    295 			set_playback_raw_volume },
    296 		      { snd_mixer_selem_get_playback_dB_range,
    297 			snd_mixer_selem_get_playback_dB,
    298 			set_playback_dB },
    299 		      { get_mapped_volume_range,
    300 			get_playback_mapped_volume,
    301 			set_playback_mapped_volume },
    302 		},
    303 	},
    304 	{
    305 		.has_volume = snd_mixer_selem_has_capture_volume,
    306 		.v = {{ snd_mixer_selem_get_capture_volume_range,
    307 			snd_mixer_selem_get_capture_volume,
    308 			set_capture_raw_volume },
    309 		      { snd_mixer_selem_get_capture_dB_range,
    310 			snd_mixer_selem_get_capture_dB,
    311 			set_capture_dB },
    312 		      { get_mapped_volume_range,
    313 			get_capture_mapped_volume,
    314 			set_capture_mapped_volume },
    315 		},
    316 	},
    317 };
    318 
    319 static int std_vol_type = VOL_RAW;
    320 
    321 static int set_volume_simple(snd_mixer_elem_t *elem,
    322 			     snd_mixer_selem_channel_id_t chn,
    323 			     char **ptr, int dir)
    324 {
    325 	long val, orig, pmin, pmax;
    326 	char *p = *ptr, *s;
    327 	int invalid = 0, percent = 0, err = 0;
    328 	int vol_type;
    329 	double scale = 1.0;
    330 	int correct = 0;
    331 
    332 	if (! vol_ops[dir].has_volume(elem))
    333 		invalid = 1;
    334 
    335 	if (*p == ':')
    336 		p++;
    337 	if (*p == '\0' || (!isdigit(*p) && *p != '-'))
    338 		goto skip;
    339 
    340 	s = p;
    341 	val = strtol(s, &p, 10);
    342 	if (*p == '.') {
    343 		p++;
    344 		strtol(p, &p, 10);
    345 	}
    346 	if (*p == '%') {
    347 		vol_type = std_vol_type;
    348 		percent = 1;
    349 		p++;
    350 	} else if (toupper(p[0]) == 'D' && toupper(p[1]) == 'B') {
    351 		vol_type = VOL_DB;
    352 		p += 2;
    353 		scale = 100;
    354 	} else {
    355 		vol_type = VOL_RAW;
    356 	}
    357 
    358 	if (*p && !strchr(",:+-", *p))
    359 		invalid = 1;
    360 
    361 	val = (long)(strtod(s, NULL) * scale);
    362 	if (vol_ops[dir].v[vol_type].get_range(elem, &pmin, &pmax) < 0)
    363 		invalid = 1;
    364 	if (percent)
    365 		val = (long)convert_prange1(val, pmin, pmax);
    366 	if (*p == '+' || *p == '-') {
    367 		if (! invalid) {
    368 			if (vol_ops[dir].v[vol_type].get(elem, chn, &orig) < 0)
    369 				invalid = 1;
    370 			if (*p == '+') {
    371 				val = orig + val;
    372 				correct = 1;
    373 			} else {
    374 				val = orig - val;
    375 				correct = -1;
    376 			}
    377 		}
    378 		p++;
    379 	}
    380 
    381 	if (*p && !strchr(",:", *p))
    382 		invalid = 1;
    383 
    384 	if (! invalid) {
    385 		val = check_range(val, pmin, pmax);
    386 		err = vol_ops[dir].v[vol_type].set(elem, chn, val, correct);
    387 	}
    388  skip:
    389 	if (*p == ',')
    390 		p++;
    391 	*ptr = p;
    392 	return err ? err : (invalid ? -ENOENT : 0);
    393 }
    394 
    395 static int get_bool_simple(char **ptr, char *str, int invert, int orig)
    396 {
    397 	if (**ptr == ':')
    398 		(*ptr)++;
    399 	if (!strncasecmp(*ptr, str, strlen(str))) {
    400 		orig = 1 ^ (invert ? 1 : 0);
    401 		while (**ptr != '\0' && **ptr != ',' && **ptr != ':')
    402 			(*ptr)++;
    403 	}
    404 	if (**ptr == ',' || **ptr == ':')
    405 		(*ptr)++;
    406 	return orig;
    407 }
    408 		
    409 static int simple_skip_word(char **ptr, char *str)
    410 {
    411 	char *xptr = *ptr;
    412 	if (*xptr == ':')
    413 		xptr++;
    414 	if (!strncasecmp(xptr, str, strlen(str))) {
    415 		while (*xptr != '\0' && *xptr != ',' && *xptr != ':')
    416 			xptr++;
    417 		if (*xptr == ',' || *xptr == ':')
    418 			xptr++;
    419 		*ptr = xptr;
    420 		return 1;
    421 	}
    422 	return 0;
    423 }
    424 		
    425 static void show_control_id(snd_ctl_elem_id_t *id)
    426 {
    427 	char *str;
    428 
    429 	str = snd_ctl_ascii_elem_id_get(id);
    430 	if (str)
    431 		printf("%s", str);
    432 	free(str);
    433 }
    434 
    435 static void print_spaces(unsigned int spaces)
    436 {
    437 	while (spaces-- > 0)
    438 		putc(' ', stdout);
    439 }
    440 
    441 static void print_dB(long dB)
    442 {
    443 	if (dB < 0) {
    444 		printf("-%li.%02lidB", -dB / 100, -dB % 100);
    445 	} else {
    446 		printf("%li.%02lidB", dB / 100, dB % 100);
    447 	}
    448 }
    449 
    450 static void decode_tlv(unsigned int spaces, unsigned int *tlv, unsigned int tlv_size)
    451 {
    452 	unsigned int type = tlv[0];
    453 	unsigned int size;
    454 	unsigned int idx = 0;
    455 	const char *chmap_type = NULL;
    456 
    457 	if (tlv_size < 2 * sizeof(unsigned int)) {
    458 		printf("TLV size error!\n");
    459 		return;
    460 	}
    461 	print_spaces(spaces);
    462 	printf("| ");
    463 	type = tlv[idx++];
    464 	size = tlv[idx++];
    465 	tlv_size -= 2 * sizeof(unsigned int);
    466 	if (size > tlv_size) {
    467 		printf("TLV size error (%i, %i, %i)!\n", type, size, tlv_size);
    468 		return;
    469 	}
    470 	switch (type) {
    471 	case SND_CTL_TLVT_CONTAINER:
    472 		printf("container\n");
    473 		size += sizeof(unsigned int) -1;
    474 		size /= sizeof(unsigned int);
    475 		while (idx < size) {
    476 			if (tlv[idx+1] > (size - idx) * sizeof(unsigned int)) {
    477 				printf("TLV size error in compound!\n");
    478 				return;
    479 			}
    480 			decode_tlv(spaces + 2, tlv + idx, tlv[idx+1] + 8);
    481 			idx += 2 + (tlv[idx+1] + sizeof(unsigned int) - 1) / sizeof(unsigned int);
    482 		}
    483 		break;
    484 	case SND_CTL_TLVT_DB_SCALE:
    485 		printf("dBscale-");
    486 		if (size != 2 * sizeof(unsigned int)) {
    487 			while (size > 0) {
    488 				printf("0x%08x,", tlv[idx++]);
    489 				size -= sizeof(unsigned int);
    490 			}
    491 		} else {
    492 			printf("min=");
    493 			print_dB((int)tlv[2]);
    494 			printf(",step=");
    495 			print_dB(tlv[3] & 0xffff);
    496 			printf(",mute=%i", (tlv[3] >> 16) & 1);
    497 		}
    498 		break;
    499 #ifdef SND_CTL_TLVT_DB_LINEAR
    500 	case SND_CTL_TLVT_DB_LINEAR:
    501 		printf("dBlinear-");
    502 		if (size != 2 * sizeof(unsigned int)) {
    503 			while (size > 0) {
    504 				printf("0x%08x,", tlv[idx++]);
    505 				size -= sizeof(unsigned int);
    506 			}
    507 		} else {
    508 			printf("min=");
    509 			print_dB((int)tlv[2]);
    510 			printf(",max=");
    511 			print_dB((int)tlv[3]);
    512 		}
    513 		break;
    514 #endif
    515 #ifdef SND_CTL_TLVT_DB_RANGE
    516 	case SND_CTL_TLVT_DB_RANGE:
    517 		printf("dBrange-\n");
    518 		if ((size % (6 * sizeof(unsigned int))) != 0) {
    519 			while (size > 0) {
    520 				printf("0x%08x,", tlv[idx++]);
    521 				size -= sizeof(unsigned int);
    522 			}
    523 			break;
    524 		}
    525 		while (size > 0) {
    526 			print_spaces(spaces + 2);
    527 			printf("rangemin=%i,", tlv[idx++]);
    528 			printf(",rangemax=%i\n", tlv[idx++]);
    529 			decode_tlv(spaces + 4, tlv + idx, 4 * sizeof(unsigned int));
    530 			idx += 4;
    531 			size -= 6 * sizeof(unsigned int);
    532 		}
    533 		break;
    534 #endif
    535 #ifdef SND_CTL_TLVT_DB_MINMAX
    536 	case SND_CTL_TLVT_DB_MINMAX:
    537 	case SND_CTL_TLVT_DB_MINMAX_MUTE:
    538 		if (type == SND_CTL_TLVT_DB_MINMAX_MUTE)
    539 			printf("dBminmaxmute-");
    540 		else
    541 			printf("dBminmax-");
    542 		if (size != 2 * sizeof(unsigned int)) {
    543 			while (size > 0) {
    544 				printf("0x%08x,", tlv[idx++]);
    545 				size -= sizeof(unsigned int);
    546 			}
    547 		} else {
    548 			printf("min=");
    549 			print_dB((int)tlv[2]);
    550 			printf(",max=");
    551 			print_dB((int)tlv[3]);
    552 		}
    553 		break;
    554 #endif
    555 #ifdef SND_CTL_TLVT_CHMAP_FIXED
    556 	case SND_CTL_TLVT_CHMAP_FIXED:
    557 		chmap_type = "fixed";
    558 		/* Fall through */
    559 	case SND_CTL_TLVT_CHMAP_VAR:
    560 		if (!chmap_type)
    561 			chmap_type = "variable";
    562 		/* Fall through */
    563 	case SND_CTL_TLVT_CHMAP_PAIRED:
    564 		if (!chmap_type)
    565 			chmap_type = "paired";
    566 		printf("chmap-%s=", chmap_type);
    567 
    568 		while (size > 0) {
    569 			printf("%s", snd_pcm_chmap_name(tlv[idx++]));
    570 			size -= sizeof(unsigned int);
    571 			if (size > 0)
    572 				printf(",");
    573 		}
    574 		break;
    575 #endif
    576 	default:
    577 		printf("unk-%i-", type);
    578 		while (size > 0) {
    579 			printf("0x%08x,", tlv[idx++]);
    580 			size -= sizeof(unsigned int);
    581 		}
    582 		break;
    583 	}
    584 	putc('\n', stdout);
    585 }
    586 
    587 static int show_control(const char *space, snd_hctl_elem_t *elem,
    588 			int level)
    589 {
    590 	int err;
    591 	unsigned int item, idx, count, *tlv;
    592 	snd_ctl_elem_type_t type;
    593 	snd_ctl_elem_id_t *id;
    594 	snd_ctl_elem_info_t *info;
    595 	snd_ctl_elem_value_t *control;
    596 	snd_aes_iec958_t iec958;
    597 	snd_ctl_elem_id_alloca(&id);
    598 	snd_ctl_elem_info_alloca(&info);
    599 	snd_ctl_elem_value_alloca(&control);
    600 	if ((err = snd_hctl_elem_info(elem, info)) < 0) {
    601 		error("Control %s snd_hctl_elem_info error: %s\n", card, snd_strerror(err));
    602 		return err;
    603 	}
    604 	if (level & LEVEL_ID) {
    605 		snd_hctl_elem_get_id(elem, id);
    606 		show_control_id(id);
    607 		printf("\n");
    608 	}
    609 	count = snd_ctl_elem_info_get_count(info);
    610 	type = snd_ctl_elem_info_get_type(info);
    611 	printf("%s; type=%s,access=%s,values=%i", space, control_type(info), control_access(info), count);
    612 	switch (type) {
    613 	case SND_CTL_ELEM_TYPE_INTEGER:
    614 		printf(",min=%li,max=%li,step=%li\n", 
    615 		       snd_ctl_elem_info_get_min(info),
    616 		       snd_ctl_elem_info_get_max(info),
    617 		       snd_ctl_elem_info_get_step(info));
    618 		break;
    619 	case SND_CTL_ELEM_TYPE_INTEGER64:
    620 		printf(",min=%Li,max=%Li,step=%Li\n", 
    621 		       snd_ctl_elem_info_get_min64(info),
    622 		       snd_ctl_elem_info_get_max64(info),
    623 		       snd_ctl_elem_info_get_step64(info));
    624 		break;
    625 	case SND_CTL_ELEM_TYPE_ENUMERATED:
    626 	{
    627 		unsigned int items = snd_ctl_elem_info_get_items(info);
    628 		printf(",items=%u\n", items);
    629 		for (item = 0; item < items; item++) {
    630 			snd_ctl_elem_info_set_item(info, item);
    631 			if ((err = snd_hctl_elem_info(elem, info)) < 0) {
    632 				error("Control %s element info error: %s\n", card, snd_strerror(err));
    633 				return err;
    634 			}
    635 			printf("%s; Item #%u '%s'\n", space, item, snd_ctl_elem_info_get_item_name(info));
    636 		}
    637 		break;
    638 	}
    639 	default:
    640 		printf("\n");
    641 		break;
    642 	}
    643 	if (level & LEVEL_BASIC) {
    644 		if (!snd_ctl_elem_info_is_readable(info))
    645 			goto __skip_read;
    646 		if ((err = snd_hctl_elem_read(elem, control)) < 0) {
    647 			error("Control %s element read error: %s\n", card, snd_strerror(err));
    648 			return err;
    649 		}
    650 		printf("%s: values=", space);
    651 		for (idx = 0; idx < count; idx++) {
    652 			if (idx > 0)
    653 				printf(",");
    654 			switch (type) {
    655 			case SND_CTL_ELEM_TYPE_BOOLEAN:
    656 				printf("%s", snd_ctl_elem_value_get_boolean(control, idx) ? "on" : "off");
    657 				break;
    658 			case SND_CTL_ELEM_TYPE_INTEGER:
    659 				printf("%li", snd_ctl_elem_value_get_integer(control, idx));
    660 				break;
    661 			case SND_CTL_ELEM_TYPE_INTEGER64:
    662 				printf("%Li", snd_ctl_elem_value_get_integer64(control, idx));
    663 				break;
    664 			case SND_CTL_ELEM_TYPE_ENUMERATED:
    665 				printf("%u", snd_ctl_elem_value_get_enumerated(control, idx));
    666 				break;
    667 			case SND_CTL_ELEM_TYPE_BYTES:
    668 				printf("0x%02x", snd_ctl_elem_value_get_byte(control, idx));
    669 				break;
    670 			case SND_CTL_ELEM_TYPE_IEC958:
    671 				snd_ctl_elem_value_get_iec958(control, &iec958);
    672 				printf("[AES0=0x%02x AES1=0x%02x AES2=0x%02x AES3=0x%02x]",
    673 				       iec958.status[0], iec958.status[1],
    674 				       iec958.status[2], iec958.status[3]);
    675 				break;
    676 			default:
    677 				printf("?");
    678 				break;
    679 			}
    680 		}
    681 		printf("\n");
    682 	      __skip_read:
    683 		if (!snd_ctl_elem_info_is_tlv_readable(info))
    684 			goto __skip_tlv;
    685 		/* skip ASoC ext bytes controls that may have huge binary TLV data */
    686 		if (type == SND_CTL_ELEM_TYPE_BYTES &&
    687 				!snd_ctl_elem_info_is_readable(info) &&
    688 				!snd_ctl_elem_info_is_writable(info)) {
    689 			printf("%s; ASoC TLV Byte control, skipping bytes dump\n", space);
    690 			goto __skip_tlv;
    691 		}
    692 
    693 		tlv = malloc(4096);
    694 		if ((err = snd_hctl_elem_tlv_read(elem, tlv, 4096)) < 0) {
    695 			error("Control %s element TLV read error: %s\n", card, snd_strerror(err));
    696 			free(tlv);
    697 			return err;
    698 		}
    699 		decode_tlv(strlen(space), tlv, 4096);
    700 		free(tlv);
    701 	}
    702       __skip_tlv:
    703 	return 0;
    704 }
    705 
    706 static int controls(int level)
    707 {
    708 	int err;
    709 	snd_hctl_t *handle;
    710 	snd_hctl_elem_t *elem;
    711 	snd_ctl_elem_id_t *id;
    712 	snd_ctl_elem_info_t *info;
    713 	snd_ctl_elem_id_alloca(&id);
    714 	snd_ctl_elem_info_alloca(&info);
    715 	
    716 	if ((err = snd_hctl_open(&handle, card, 0)) < 0) {
    717 		error("Control %s open error: %s", card, snd_strerror(err));
    718 		return err;
    719 	}
    720 	if ((err = snd_hctl_load(handle)) < 0) {
    721 		error("Control %s local error: %s\n", card, snd_strerror(err));
    722 		return err;
    723 	}
    724 	for (elem = snd_hctl_first_elem(handle); elem; elem = snd_hctl_elem_next(elem)) {
    725 		if ((err = snd_hctl_elem_info(elem, info)) < 0) {
    726 			error("Control %s snd_hctl_elem_info error: %s\n", card, snd_strerror(err));
    727 			return err;
    728 		}
    729 		if (!(level & LEVEL_INACTIVE) && snd_ctl_elem_info_is_inactive(info))
    730 			continue;
    731 		snd_hctl_elem_get_id(elem, id);
    732 		show_control_id(id);
    733 		printf("\n");
    734 		if (level & LEVEL_BASIC)
    735 			show_control("  ", elem, 1);
    736 	}
    737 	snd_hctl_close(handle);
    738 	return 0;
    739 }
    740 
    741 static void show_selem_volume(snd_mixer_elem_t *elem, 
    742 			      snd_mixer_selem_channel_id_t chn, int dir,
    743 			      long min, long max)
    744 {
    745 	long raw, val;
    746 	vol_ops[dir].v[VOL_RAW].get(elem, chn, &raw);
    747 	if (std_vol_type == VOL_RAW)
    748 		val = convert_prange(raw, min, max);
    749 	else {
    750 		vol_ops[dir].v[std_vol_type].get(elem, chn, &val);
    751 		val = convert_prange(val, 0, MAP_VOL_RES);
    752 	}
    753 	printf(" %li [%li%%]", raw, val);
    754 	if (!vol_ops[dir].v[VOL_DB].get(elem, chn, &val)) {
    755 		printf(" [");
    756 		print_dB(val);
    757 		printf("]");
    758 	}
    759 }
    760 
    761 static int show_selem(snd_mixer_t *handle, snd_mixer_selem_id_t *id, const char *space, int level)
    762 {
    763 	snd_mixer_selem_channel_id_t chn;
    764 	long pmin = 0, pmax = 0;
    765 	long cmin = 0, cmax = 0;
    766 	int psw, csw;
    767 	int pmono, cmono, mono_ok = 0;
    768 	snd_mixer_elem_t *elem;
    769 	
    770 	elem = snd_mixer_find_selem(handle, id);
    771 	if (!elem) {
    772 		error("Mixer %s simple element not found", card);
    773 		return -ENOENT;
    774 	}
    775 
    776 	if (level & LEVEL_BASIC) {
    777 		printf("%sCapabilities:", space);
    778 		if (snd_mixer_selem_has_common_volume(elem)) {
    779 			printf(" volume");
    780 			if (snd_mixer_selem_has_playback_volume_joined(elem))
    781 				printf(" volume-joined");
    782 		} else {
    783 			if (snd_mixer_selem_has_playback_volume(elem)) {
    784 				printf(" pvolume");
    785 				if (snd_mixer_selem_has_playback_volume_joined(elem))
    786 					printf(" pvolume-joined");
    787 			}
    788 			if (snd_mixer_selem_has_capture_volume(elem)) {
    789 				printf(" cvolume");
    790 				if (snd_mixer_selem_has_capture_volume_joined(elem))
    791 					printf(" cvolume-joined");
    792 			}
    793 		}
    794 		if (snd_mixer_selem_has_common_switch(elem)) {
    795 			printf(" switch");
    796 			if (snd_mixer_selem_has_playback_switch_joined(elem))
    797 				printf(" switch-joined");
    798 		} else {
    799 			if (snd_mixer_selem_has_playback_switch(elem)) {
    800 				printf(" pswitch");
    801 				if (snd_mixer_selem_has_playback_switch_joined(elem))
    802 					printf(" pswitch-joined");
    803 			}
    804 			if (snd_mixer_selem_has_capture_switch(elem)) {
    805 				printf(" cswitch");
    806 				if (snd_mixer_selem_has_capture_switch_joined(elem))
    807 					printf(" cswitch-joined");
    808 				if (snd_mixer_selem_has_capture_switch_exclusive(elem))
    809 					printf(" cswitch-exclusive");
    810 			}
    811 		}
    812 		if (snd_mixer_selem_is_enum_playback(elem)) {
    813 			printf(" penum");
    814 		} else if (snd_mixer_selem_is_enum_capture(elem)) {
    815 			printf(" cenum");
    816 		} else if (snd_mixer_selem_is_enumerated(elem)) {
    817 			printf(" enum");
    818 		}
    819 		printf("\n");
    820 		if (snd_mixer_selem_is_enumerated(elem)) {
    821 			int i, items;
    822 			unsigned int idx;
    823 			/*
    824 			 * See snd_ctl_elem_init_enum_names() in
    825 			 * sound/core/control.c.
    826 			 */
    827 			char itemname[64];
    828 			items = snd_mixer_selem_get_enum_items(elem);
    829 			printf("  Items:");
    830 			for (i = 0; i < items; i++) {
    831 				snd_mixer_selem_get_enum_item_name(elem, i, sizeof(itemname) - 1, itemname);
    832 				printf(" '%s'", itemname);
    833 			}
    834 			printf("\n");
    835 			for (i = 0; !snd_mixer_selem_get_enum_item(elem, i, &idx); i++) {
    836 				snd_mixer_selem_get_enum_item_name(elem, idx, sizeof(itemname) - 1, itemname);
    837 				printf("  Item%d: '%s'\n", i, itemname);
    838 			}
    839 			return 0; /* no more thing to do */
    840 		}
    841 		if (snd_mixer_selem_has_capture_switch_exclusive(elem))
    842 			printf("%sCapture exclusive group: %i\n", space,
    843 			       snd_mixer_selem_get_capture_group(elem));
    844 		if (snd_mixer_selem_has_playback_volume(elem) ||
    845 		    snd_mixer_selem_has_playback_switch(elem)) {
    846 			printf("%sPlayback channels:", space);
    847 			if (snd_mixer_selem_is_playback_mono(elem)) {
    848 				printf(" Mono");
    849 			} else {
    850 				int first = 1;
    851 				for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++){
    852 					if (!snd_mixer_selem_has_playback_channel(elem, chn))
    853 						continue;
    854 					if (!first)
    855 						printf(" -");
    856 					printf(" %s", snd_mixer_selem_channel_name(chn));
    857 					first = 0;
    858 				}
    859 			}
    860 			printf("\n");
    861 		}
    862 		if (snd_mixer_selem_has_capture_volume(elem) ||
    863 		    snd_mixer_selem_has_capture_switch(elem)) {
    864 			printf("%sCapture channels:", space);
    865 			if (snd_mixer_selem_is_capture_mono(elem)) {
    866 				printf(" Mono");
    867 			} else {
    868 				int first = 1;
    869 				for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++){
    870 					if (!snd_mixer_selem_has_capture_channel(elem, chn))
    871 						continue;
    872 					if (!first)
    873 						printf(" -");
    874 					printf(" %s", snd_mixer_selem_channel_name(chn));
    875 					first = 0;
    876 				}
    877 			}
    878 			printf("\n");
    879 		}
    880 		if (snd_mixer_selem_has_playback_volume(elem) ||
    881 		    snd_mixer_selem_has_capture_volume(elem)) {
    882 			printf("%sLimits:", space);
    883 			if (snd_mixer_selem_has_common_volume(elem)) {
    884 				snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax);
    885 				snd_mixer_selem_get_capture_volume_range(elem, &cmin, &cmax);
    886 				printf(" %li - %li", pmin, pmax);
    887 			} else {
    888 				if (snd_mixer_selem_has_playback_volume(elem)) {
    889 					snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax);
    890 					printf(" Playback %li - %li", pmin, pmax);
    891 				}
    892 				if (snd_mixer_selem_has_capture_volume(elem)) {
    893 					snd_mixer_selem_get_capture_volume_range(elem, &cmin, &cmax);
    894 					printf(" Capture %li - %li", cmin, cmax);
    895 				}
    896 			}
    897 			printf("\n");
    898 		}
    899 		pmono = snd_mixer_selem_has_playback_channel(elem, SND_MIXER_SCHN_MONO) &&
    900 		        (snd_mixer_selem_is_playback_mono(elem) || 
    901 			 (!snd_mixer_selem_has_playback_volume(elem) &&
    902 			  !snd_mixer_selem_has_playback_switch(elem)));
    903 		cmono = snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_MONO) &&
    904 		        (snd_mixer_selem_is_capture_mono(elem) || 
    905 			 (!snd_mixer_selem_has_capture_volume(elem) &&
    906 			  !snd_mixer_selem_has_capture_switch(elem)));
    907 #if 0
    908 		printf("pmono = %i, cmono = %i (%i, %i, %i, %i)\n", pmono, cmono,
    909 				snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_MONO),
    910 				snd_mixer_selem_is_capture_mono(elem),
    911 				snd_mixer_selem_has_capture_volume(elem),
    912 				snd_mixer_selem_has_capture_switch(elem));
    913 #endif
    914 		if (pmono || cmono) {
    915 			if (!mono_ok) {
    916 				printf("%s%s:", space, "Mono");
    917 				mono_ok = 1;
    918 			}
    919 			if (snd_mixer_selem_has_common_volume(elem)) {
    920 				show_selem_volume(elem, SND_MIXER_SCHN_MONO, 0, pmin, pmax);
    921 			}
    922 			if (snd_mixer_selem_has_common_switch(elem)) {
    923 				snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_MONO, &psw);
    924 				printf(" [%s]", psw ? "on" : "off");
    925 			}
    926 		}
    927 		if (pmono && snd_mixer_selem_has_playback_channel(elem, SND_MIXER_SCHN_MONO)) {
    928 			int title = 0;
    929 			if (!mono_ok) {
    930 				printf("%s%s:", space, "Mono");
    931 				mono_ok = 1;
    932 			}
    933 			if (!snd_mixer_selem_has_common_volume(elem)) {
    934 				if (snd_mixer_selem_has_playback_volume(elem)) {
    935 					printf(" Playback");
    936 					title = 1;
    937 					show_selem_volume(elem, SND_MIXER_SCHN_MONO, 0, pmin, pmax);
    938 				}
    939 			}
    940 			if (!snd_mixer_selem_has_common_switch(elem)) {
    941 				if (snd_mixer_selem_has_playback_switch(elem)) {
    942 					if (!title)
    943 						printf(" Playback");
    944 					snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_MONO, &psw);
    945 					printf(" [%s]", psw ? "on" : "off");
    946 				}
    947 			}
    948 		}
    949 		if (cmono && snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_MONO)) {
    950 			int title = 0;
    951 			if (!mono_ok) {
    952 				printf("%s%s:", space, "Mono");
    953 				mono_ok = 1;
    954 			}
    955 			if (!snd_mixer_selem_has_common_volume(elem)) {
    956 				if (snd_mixer_selem_has_capture_volume(elem)) {
    957 					printf(" Capture");
    958 					title = 1;
    959 					show_selem_volume(elem, SND_MIXER_SCHN_MONO, 1, cmin, cmax);
    960 				}
    961 			}
    962 			if (!snd_mixer_selem_has_common_switch(elem)) {
    963 				if (snd_mixer_selem_has_capture_switch(elem)) {
    964 					if (!title)
    965 						printf(" Capture");
    966 					snd_mixer_selem_get_capture_switch(elem, SND_MIXER_SCHN_MONO, &csw);
    967 					printf(" [%s]", csw ? "on" : "off");
    968 				}
    969 			}
    970 		}
    971 		if (pmono || cmono)
    972 			printf("\n");
    973 		if (!pmono || !cmono) {
    974 			for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
    975 				if ((pmono || !snd_mixer_selem_has_playback_channel(elem, chn)) &&
    976 				    (cmono || !snd_mixer_selem_has_capture_channel(elem, chn)))
    977 					continue;
    978 				printf("%s%s:", space, snd_mixer_selem_channel_name(chn));
    979 				if (!pmono && !cmono && snd_mixer_selem_has_common_volume(elem)) {
    980 					show_selem_volume(elem, chn, 0, pmin, pmax);
    981 				}
    982 				if (!pmono && !cmono && snd_mixer_selem_has_common_switch(elem)) {
    983 					snd_mixer_selem_get_playback_switch(elem, chn, &psw);
    984 					printf(" [%s]", psw ? "on" : "off");
    985 				}
    986 				if (!pmono && snd_mixer_selem_has_playback_channel(elem, chn)) {
    987 					int title = 0;
    988 					if (!snd_mixer_selem_has_common_volume(elem)) {
    989 						if (snd_mixer_selem_has_playback_volume(elem)) {
    990 							printf(" Playback");
    991 							title = 1;
    992 							show_selem_volume(elem, chn, 0, pmin, pmax);
    993 						}
    994 					}
    995 					if (!snd_mixer_selem_has_common_switch(elem)) {
    996 						if (snd_mixer_selem_has_playback_switch(elem)) {
    997 							if (!title)
    998 								printf(" Playback");
    999 							snd_mixer_selem_get_playback_switch(elem, chn, &psw);
   1000 							printf(" [%s]", psw ? "on" : "off");
   1001 						}
   1002 					}
   1003 				}
   1004 				if (!cmono && snd_mixer_selem_has_capture_channel(elem, chn)) {
   1005 					int title = 0;
   1006 					if (!snd_mixer_selem_has_common_volume(elem)) {
   1007 						if (snd_mixer_selem_has_capture_volume(elem)) {
   1008 							printf(" Capture");
   1009 							title = 1;
   1010 							show_selem_volume(elem, chn, 1, cmin, cmax);
   1011 						}
   1012 					}
   1013 					if (!snd_mixer_selem_has_common_switch(elem)) {
   1014 						if (snd_mixer_selem_has_capture_switch(elem)) {
   1015 							if (!title)
   1016 								printf(" Capture");
   1017 							snd_mixer_selem_get_capture_switch(elem, chn, &csw);
   1018 							printf(" [%s]", csw ? "on" : "off");
   1019 						}
   1020 					}
   1021 				}
   1022 				printf("\n");
   1023 			}
   1024 		}
   1025 	}
   1026 	return 0;
   1027 }
   1028 
   1029 static int selems(int level)
   1030 {
   1031 	int err;
   1032 	snd_mixer_t *handle;
   1033 	snd_mixer_selem_id_t *sid;
   1034 	snd_mixer_elem_t *elem;
   1035 	snd_mixer_selem_id_alloca(&sid);
   1036 	
   1037 	if ((err = snd_mixer_open(&handle, 0)) < 0) {
   1038 		error("Mixer %s open error: %s", card, snd_strerror(err));
   1039 		return err;
   1040 	}
   1041 	if (smixer_level == 0 && (err = snd_mixer_attach(handle, card)) < 0) {
   1042 		error("Mixer attach %s error: %s", card, snd_strerror(err));
   1043 		snd_mixer_close(handle);
   1044 		return err;
   1045 	}
   1046 	if ((err = snd_mixer_selem_register(handle, smixer_level > 0 ? &smixer_options : NULL, NULL)) < 0) {
   1047 		error("Mixer register error: %s", snd_strerror(err));
   1048 		snd_mixer_close(handle);
   1049 		return err;
   1050 	}
   1051 	err = snd_mixer_load(handle);
   1052 	if (err < 0) {
   1053 		error("Mixer %s load error: %s", card, snd_strerror(err));
   1054 		snd_mixer_close(handle);
   1055 		return err;
   1056 	}
   1057 	for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem)) {
   1058 		snd_mixer_selem_get_id(elem, sid);
   1059 		if (!(level & LEVEL_INACTIVE) && !snd_mixer_selem_is_active(elem))
   1060 			continue;
   1061 		printf("Simple mixer control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
   1062 		show_selem(handle, sid, "  ", level);
   1063 	}
   1064 	snd_mixer_close(handle);
   1065 	return 0;
   1066 }
   1067 
   1068 static int parse_simple_id(const char *str, snd_mixer_selem_id_t *sid)
   1069 {
   1070 	int c, size;
   1071 	char buf[128];
   1072 	char *ptr = buf;
   1073 
   1074 	while (*str == ' ' || *str == '\t')
   1075 		str++;
   1076 	if (!(*str))
   1077 		return -EINVAL;
   1078 	size = 1;	/* for '\0' */
   1079 	if (*str != '"' && *str != '\'') {
   1080 		while (*str && *str != ',') {
   1081 			if (size < (int)sizeof(buf)) {
   1082 				*ptr++ = *str;
   1083 				size++;
   1084 			}
   1085 			str++;
   1086 		}
   1087 	} else {
   1088 		c = *str++;
   1089 		while (*str && *str != c) {
   1090 			if (size < (int)sizeof(buf)) {
   1091 				*ptr++ = *str;
   1092 				size++;
   1093 			}
   1094 			str++;
   1095 		}
   1096 		if (*str == c)
   1097 			str++;
   1098 	}
   1099 	if (*str == '\0') {
   1100 		snd_mixer_selem_id_set_index(sid, 0);
   1101 		*ptr = 0;
   1102 		goto _set;
   1103 	}
   1104 	if (*str != ',')
   1105 		return -EINVAL;
   1106 	*ptr = 0;	/* terminate the string */
   1107 	str++;
   1108 	if (!isdigit(*str))
   1109 		return -EINVAL;
   1110 	snd_mixer_selem_id_set_index(sid, atoi(str));
   1111        _set:
   1112 	snd_mixer_selem_id_set_name(sid, buf);
   1113 	return 0;
   1114 }
   1115 
   1116 static int cset(int argc, char *argv[], int roflag, int keep_handle)
   1117 {
   1118 	int err;
   1119 	static snd_ctl_t *handle = NULL;
   1120 	snd_ctl_elem_info_t *info;
   1121 	snd_ctl_elem_id_t *id;
   1122 	snd_ctl_elem_value_t *control;
   1123 	snd_ctl_elem_info_alloca(&info);
   1124 	snd_ctl_elem_id_alloca(&id);
   1125 	snd_ctl_elem_value_alloca(&control);
   1126 
   1127 	if (argc < 1) {
   1128 		fprintf(stderr, "Specify a full control identifier: [[iface=<iface>,][name='name',][index=<index>,][device=<device>,][subdevice=<subdevice>]]|[numid=<numid>]\n");
   1129 		return -EINVAL;
   1130 	}
   1131 	if (snd_ctl_ascii_elem_id_parse(id, argv[0])) {
   1132 		fprintf(stderr, "Wrong control identifier: %s\n", argv[0]);
   1133 		return -EINVAL;
   1134 	}
   1135 	if (debugflag) {
   1136 		printf("VERIFY ID: ");
   1137 		show_control_id(id);
   1138 		printf("\n");
   1139 	}
   1140 	if (handle == NULL &&
   1141 	    (err = snd_ctl_open(&handle, card, 0)) < 0) {
   1142 		error("Control %s open error: %s\n", card, snd_strerror(err));
   1143 		return err;
   1144 	}
   1145 	snd_ctl_elem_info_set_id(info, id);
   1146 	if ((err = snd_ctl_elem_info(handle, info)) < 0) {
   1147 		if (ignore_error)
   1148 			return 0;
   1149 		error("Cannot find the given element from control %s\n", card);
   1150 		if (! keep_handle) {
   1151 			snd_ctl_close(handle);
   1152 			handle = NULL;
   1153 		}
   1154 		return err;
   1155 	}
   1156 	snd_ctl_elem_info_get_id(info, id);     /* FIXME: Remove it when hctl find works ok !!! */
   1157 	if (!roflag) {
   1158 		snd_ctl_elem_value_set_id(control, id);
   1159 		if ((err = snd_ctl_elem_read(handle, control)) < 0) {
   1160 			if (ignore_error)
   1161 				return 0;
   1162 			error("Cannot read the given element from control %s\n", card);
   1163 			if (! keep_handle) {
   1164 				snd_ctl_close(handle);
   1165 				handle = NULL;
   1166 			}
   1167 			return err;
   1168 		}
   1169 		err = snd_ctl_ascii_value_parse(handle, control, info, argv[1]);
   1170 		if (err < 0) {
   1171  			if (!ignore_error)
   1172 				error("Control %s parse error: %s\n", card, snd_strerror(err));
   1173 			if (!keep_handle) {
   1174 				snd_ctl_close(handle);
   1175 				handle = NULL;
   1176 			}
   1177 			return ignore_error ? 0 : err;
   1178 		}
   1179 		if ((err = snd_ctl_elem_write(handle, control)) < 0) {
   1180 			if (!ignore_error)
   1181 				error("Control %s element write error: %s\n", card, snd_strerror(err));
   1182 			if (!keep_handle) {
   1183 				snd_ctl_close(handle);
   1184 				handle = NULL;
   1185 			}
   1186 			return ignore_error ? 0 : err;
   1187 		}
   1188 	}
   1189 	if (! keep_handle) {
   1190 		snd_ctl_close(handle);
   1191 		handle = NULL;
   1192 	}
   1193 	if (!quiet) {
   1194 		snd_hctl_t *hctl;
   1195 		snd_hctl_elem_t *elem;
   1196 		if ((err = snd_hctl_open(&hctl, card, 0)) < 0) {
   1197 			error("Control %s open error: %s\n", card, snd_strerror(err));
   1198 			return err;
   1199 		}
   1200 		if ((err = snd_hctl_load(hctl)) < 0) {
   1201 			error("Control %s load error: %s\n", card, snd_strerror(err));
   1202 			return err;
   1203 		}
   1204 		elem = snd_hctl_find_elem(hctl, id);
   1205 		if (elem)
   1206 			show_control("  ", elem, LEVEL_BASIC | LEVEL_ID);
   1207 		else
   1208 			printf("Could not find the specified element\n");
   1209 		snd_hctl_close(hctl);
   1210 	}
   1211 	return 0;
   1212 }
   1213 
   1214 typedef struct channel_mask {
   1215 	char *name;
   1216 	unsigned int mask;
   1217 } channel_mask_t;
   1218 static const channel_mask_t chanmask[] = {
   1219 	{"frontleft", 1 << SND_MIXER_SCHN_FRONT_LEFT},
   1220 	{"frontright", 1 << SND_MIXER_SCHN_FRONT_RIGHT},
   1221 	{"frontcenter", 1 << SND_MIXER_SCHN_FRONT_CENTER},
   1222 	{"front", ((1 << SND_MIXER_SCHN_FRONT_LEFT) |
   1223 		   (1 << SND_MIXER_SCHN_FRONT_RIGHT))},
   1224 	{"center", 1 << SND_MIXER_SCHN_FRONT_CENTER},
   1225 	{"rearleft", 1 << SND_MIXER_SCHN_REAR_LEFT},
   1226 	{"rearright", 1 << SND_MIXER_SCHN_REAR_RIGHT},
   1227 	{"rear", ((1 << SND_MIXER_SCHN_REAR_LEFT) |
   1228 		  (1 << SND_MIXER_SCHN_REAR_RIGHT))},
   1229 	{"woofer", 1 << SND_MIXER_SCHN_WOOFER},
   1230 	{NULL, 0}
   1231 };
   1232 
   1233 static unsigned int channels_mask(char **arg, unsigned int def)
   1234 {
   1235 	const channel_mask_t *c;
   1236 
   1237 	for (c = chanmask; c->name; c++) {
   1238 		if (strncasecmp(*arg, c->name, strlen(c->name)) == 0) {
   1239 			while (**arg != '\0' && **arg != ',' && **arg != ' ' && **arg != '\t')
   1240 				(*arg)++;
   1241 			if (**arg == ',' || **arg == ' ' || **arg == '\t')
   1242 				(*arg)++;
   1243 			return c->mask;
   1244 		}
   1245 	}
   1246 	return def;
   1247 }
   1248 
   1249 static unsigned int dir_mask(char **arg, unsigned int def)
   1250 {
   1251 	int findend = 0;
   1252 
   1253 	if (strncasecmp(*arg, "playback", 8) == 0)
   1254 		def = findend = 1;
   1255 	else if (strncasecmp(*arg, "capture", 8) == 0)
   1256 		def = findend = 2;
   1257 	if (findend) {
   1258 		while (**arg != '\0' && **arg != ',' && **arg != ' ' && **arg != '\t')
   1259 			(*arg)++;
   1260 		if (**arg == ',' || **arg == ' ' || **arg == '\t')
   1261 			(*arg)++;
   1262 	}
   1263 	return def;
   1264 }
   1265 
   1266 static int get_enum_item_index(snd_mixer_elem_t *elem, char **ptrp)
   1267 {
   1268 	char *ptr = *ptrp;
   1269 	int items, i, len;
   1270 
   1271 	/* See snd_ctl_elem_init_enum_names() in sound/core/control.c. */
   1272 	char name[64];
   1273 	
   1274 	items = snd_mixer_selem_get_enum_items(elem);
   1275 	if (items <= 0)
   1276 		return -1;
   1277 
   1278 	for (i = 0; i < items; i++) {
   1279 		if (snd_mixer_selem_get_enum_item_name(elem, i, sizeof(name)-1, name) < 0)
   1280 			continue;
   1281 
   1282 		len = strlen(name);
   1283 		if (! strncmp(name, ptr, len)) {
   1284 			if (! ptr[len] || ptr[len] == ',' || ptr[len] == '\n') {
   1285 				ptr += len;
   1286 				*ptrp = ptr;
   1287 				return i;
   1288 			}
   1289 		}
   1290 	}
   1291 	return -1;
   1292 }
   1293 
   1294 static int sset_enum(snd_mixer_elem_t *elem, unsigned int argc, char **argv)
   1295 {
   1296 	unsigned int idx, item = 0;
   1297 	int check_flag = ignore_error ? 0 : -1;
   1298 
   1299 	for (idx = 1; idx < argc; idx++) {
   1300 		char *ptr = argv[idx];
   1301 		while (*ptr) {
   1302 			int ival = get_enum_item_index(elem, &ptr);
   1303 			if (ival < 0)
   1304 				return check_flag;
   1305 			if (snd_mixer_selem_set_enum_item(elem, item++, ival) >= 0)
   1306 				check_flag = 1;
   1307 			/* skip separators */
   1308 			while (*ptr == ',' || isspace(*ptr))
   1309 				ptr++;
   1310 		}
   1311 	}
   1312 	return check_flag;
   1313 }
   1314 
   1315 static int sset_channels(snd_mixer_elem_t *elem, unsigned int argc, char **argv)
   1316 {
   1317 	unsigned int channels = ~0U;
   1318 	unsigned int dir = 3, okflag = 3;
   1319 	unsigned int idx;
   1320 	snd_mixer_selem_channel_id_t chn;
   1321 	int check_flag = ignore_error ? 0 : -1;
   1322 
   1323 	for (idx = 1; idx < argc; idx++) {
   1324 		char *ptr = argv[idx], *optr;
   1325 		int multi, firstchn = 1;
   1326 		channels = channels_mask(&ptr, channels);
   1327 		if (*ptr == '\0')
   1328 			continue;
   1329 		dir = dir_mask(&ptr, dir);
   1330 		if (*ptr == '\0')
   1331 			continue;
   1332 		multi = (strchr(ptr, ',') != NULL);
   1333 		optr = ptr;
   1334 		for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
   1335 			char *sptr = NULL;
   1336 			int ival;
   1337 
   1338 			if (!(channels & (1 << chn)))
   1339 				continue;
   1340 
   1341 			if ((dir & 1) && snd_mixer_selem_has_playback_channel(elem, chn)) {
   1342 				sptr = ptr;
   1343 				if (!strncmp(ptr, "mute", 4) && snd_mixer_selem_has_playback_switch(elem)) {
   1344 					snd_mixer_selem_get_playback_switch(elem, chn, &ival);
   1345 					if (snd_mixer_selem_set_playback_switch(elem, chn, get_bool_simple(&ptr, "mute", 1, ival)) >= 0)
   1346 						check_flag = 1;
   1347 				} else if (!strncmp(ptr, "off", 3) && snd_mixer_selem_has_playback_switch(elem)) {
   1348 					snd_mixer_selem_get_playback_switch(elem, chn, &ival);
   1349 					if (snd_mixer_selem_set_playback_switch(elem, chn, get_bool_simple(&ptr, "off", 1, ival)) >= 0)
   1350 						check_flag = 1;
   1351 				} else if (!strncmp(ptr, "unmute", 6) && snd_mixer_selem_has_playback_switch(elem)) {
   1352 					snd_mixer_selem_get_playback_switch(elem, chn, &ival);
   1353 					if (snd_mixer_selem_set_playback_switch(elem, chn, get_bool_simple(&ptr, "unmute", 0, ival)) >= 0)
   1354 						check_flag = 1;
   1355 				} else if (!strncmp(ptr, "on", 2) && snd_mixer_selem_has_playback_switch(elem)) {
   1356 					snd_mixer_selem_get_playback_switch(elem, chn, &ival);
   1357 					if (snd_mixer_selem_set_playback_switch(elem, chn, get_bool_simple(&ptr, "on", 0, ival)) >= 0)
   1358 						check_flag = 1;
   1359 				} else if (!strncmp(ptr, "toggle", 6) && snd_mixer_selem_has_playback_switch(elem)) {
   1360 					if (firstchn || !snd_mixer_selem_has_playback_switch_joined(elem)) {
   1361 						snd_mixer_selem_get_playback_switch(elem, chn, &ival);
   1362 						if (snd_mixer_selem_set_playback_switch(elem, chn, (ival ? 1 : 0) ^ 1) >= 0)
   1363 							check_flag = 1;
   1364 					}
   1365 					simple_skip_word(&ptr, "toggle");
   1366 				} else if (isdigit(*ptr) || *ptr == '-' || *ptr == '+') {
   1367 					if (set_volume_simple(elem, chn, &ptr, 0) >= 0)
   1368 						check_flag = 1;
   1369 				} else if (simple_skip_word(&ptr, "cap") || simple_skip_word(&ptr, "rec") ||
   1370 					   simple_skip_word(&ptr, "nocap") || simple_skip_word(&ptr, "norec")) {
   1371 					/* nothing */
   1372 				} else {
   1373 					okflag &= ~1;
   1374 				}
   1375 			}
   1376 			if ((dir & 2) && snd_mixer_selem_has_capture_channel(elem, chn)) {
   1377 				if (sptr != NULL)
   1378 					ptr = sptr;
   1379 				sptr = ptr;
   1380 				if (!strncmp(ptr, "cap", 3) && snd_mixer_selem_has_capture_switch(elem)) {
   1381 					snd_mixer_selem_get_capture_switch(elem, chn, &ival);
   1382 					if (snd_mixer_selem_set_capture_switch(elem, chn, get_bool_simple(&ptr, "cap", 0, ival)) >= 0)
   1383 						check_flag = 1;
   1384 				} else if (!strncmp(ptr, "rec", 3) && snd_mixer_selem_has_capture_switch(elem)) {
   1385 					snd_mixer_selem_get_capture_switch(elem, chn, &ival);
   1386 					if (snd_mixer_selem_set_capture_switch(elem, chn, get_bool_simple(&ptr, "rec", 0, ival)) >= 0)
   1387 						check_flag = 1;
   1388 				} else if (!strncmp(ptr, "nocap", 5) && snd_mixer_selem_has_capture_switch(elem)) {
   1389 					snd_mixer_selem_get_capture_switch(elem, chn, &ival);
   1390 					if (snd_mixer_selem_set_capture_switch(elem, chn, get_bool_simple(&ptr, "nocap", 1, ival)) >= 0)
   1391 						check_flag = 1;
   1392 				} else if (!strncmp(ptr, "norec", 5) && snd_mixer_selem_has_capture_switch(elem)) {
   1393 					snd_mixer_selem_get_capture_switch(elem, chn, &ival);
   1394 					if (snd_mixer_selem_set_capture_switch(elem, chn, get_bool_simple(&ptr, "norec", 1, ival)) >= 0)
   1395 						check_flag = 1;
   1396 				} else if (!strncmp(ptr, "toggle", 6) && snd_mixer_selem_has_capture_switch(elem)) {
   1397 					if (firstchn || !snd_mixer_selem_has_capture_switch_joined(elem)) {
   1398 						snd_mixer_selem_get_capture_switch(elem, chn, &ival);
   1399 						if (snd_mixer_selem_set_capture_switch(elem, chn, (ival ? 1 : 0) ^ 1) >= 0)
   1400 							check_flag = 1;
   1401 					}
   1402 					simple_skip_word(&ptr, "toggle");
   1403 				} else if (isdigit(*ptr) || *ptr == '-' || *ptr == '+') {
   1404 					if (set_volume_simple(elem, chn, &ptr, 1) >= 0)
   1405 						check_flag = 1;
   1406 				} else if (simple_skip_word(&ptr, "mute") || simple_skip_word(&ptr, "off") ||
   1407 					   simple_skip_word(&ptr, "unmute") || simple_skip_word(&ptr, "on")) {
   1408 					/* nothing */
   1409 				} else {
   1410 					okflag &= ~2;
   1411 				}
   1412 			}
   1413 			if (okflag == 0) {
   1414 				if (debugflag) {
   1415 					if (dir & 1)
   1416 						error("Unknown playback setup '%s'..", ptr);
   1417 					if (dir & 2)
   1418 						error("Unknown capture setup '%s'..", ptr);
   1419 				}
   1420 				return 0; /* just skip it */
   1421 			}
   1422 			if (!multi)
   1423 				ptr = optr;
   1424 			firstchn = 0;
   1425 		}
   1426 	}
   1427 	return check_flag;
   1428 }
   1429 
   1430 static int sset(unsigned int argc, char *argv[], int roflag, int keep_handle)
   1431 {
   1432 	int err = 0;
   1433 	static snd_mixer_t *handle = NULL;
   1434 	snd_mixer_elem_t *elem;
   1435 	snd_mixer_selem_id_t *sid;
   1436 	snd_mixer_selem_id_alloca(&sid);
   1437 
   1438 	if (argc < 1) {
   1439 		fprintf(stderr, "Specify a scontrol identifier: 'name',index\n");
   1440 		return 1;
   1441 	}
   1442 	if (parse_simple_id(argv[0], sid)) {
   1443 		fprintf(stderr, "Wrong scontrol identifier: %s\n", argv[0]);
   1444 		return 1;
   1445 	}
   1446 	if (!roflag && argc < 2) {
   1447 		fprintf(stderr, "Specify what you want to set...\n");
   1448 		return 1;
   1449 	}
   1450 	if (handle == NULL) {
   1451 		if ((err = snd_mixer_open(&handle, 0)) < 0) {
   1452 			error("Mixer %s open error: %s\n", card, snd_strerror(err));
   1453 			return err;
   1454 		}
   1455 		if (smixer_level == 0 && (err = snd_mixer_attach(handle, card)) < 0) {
   1456 			error("Mixer attach %s error: %s", card, snd_strerror(err));
   1457 			snd_mixer_close(handle);
   1458 			handle = NULL;
   1459 			return err;
   1460 		}
   1461 		if ((err = snd_mixer_selem_register(handle, smixer_level > 0 ? &smixer_options : NULL, NULL)) < 0) {
   1462 			error("Mixer register error: %s", snd_strerror(err));
   1463 			snd_mixer_close(handle);
   1464 			handle = NULL;
   1465 			return err;
   1466 		}
   1467 		err = snd_mixer_load(handle);
   1468 		if (err < 0) {
   1469 			error("Mixer %s load error: %s", card, snd_strerror(err));
   1470 			snd_mixer_close(handle);
   1471 			handle = NULL;
   1472 			return err;
   1473 		}
   1474 	}
   1475 	elem = snd_mixer_find_selem(handle, sid);
   1476 	if (!elem) {
   1477 		if (ignore_error)
   1478 			return 0;
   1479 		error("Unable to find simple control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
   1480 		snd_mixer_close(handle);
   1481 		handle = NULL;
   1482 		return -ENOENT;
   1483 	}
   1484 	if (!roflag) {
   1485 		/* enum control */
   1486 		if (snd_mixer_selem_is_enumerated(elem))
   1487 			err = sset_enum(elem, argc, argv);
   1488 		else
   1489 			err = sset_channels(elem, argc, argv);
   1490 
   1491 		if (!err)
   1492 			goto done;
   1493 		if (err < 0) {
   1494 			error("Invalid command!");
   1495 			goto done;
   1496 		}
   1497 	}
   1498 	if (!quiet) {
   1499 		printf("Simple mixer control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
   1500 		show_selem(handle, sid, "  ", 1);
   1501 	}
   1502  done:
   1503 	if (! keep_handle) {
   1504 		snd_mixer_close(handle);
   1505 		handle = NULL;
   1506 	}
   1507 	return err < 0 ? 1 : 0;
   1508 }
   1509 
   1510 static void events_info(snd_hctl_elem_t *helem)
   1511 {
   1512 	snd_ctl_elem_id_t *id;
   1513 	snd_ctl_elem_id_alloca(&id);
   1514 	snd_hctl_elem_get_id(helem, id);
   1515 	printf("event info: ");
   1516 	show_control_id(id);
   1517 	printf("\n");
   1518 }
   1519 
   1520 static void events_value(snd_hctl_elem_t *helem)
   1521 {
   1522 	snd_ctl_elem_id_t *id;
   1523 	snd_ctl_elem_id_alloca(&id);
   1524 	snd_hctl_elem_get_id(helem, id);
   1525 	printf("event value: ");
   1526 	show_control_id(id);
   1527 	printf("\n");
   1528 }
   1529 
   1530 static void events_remove(snd_hctl_elem_t *helem)
   1531 {
   1532 	snd_ctl_elem_id_t *id;
   1533 	snd_ctl_elem_id_alloca(&id);
   1534 	snd_hctl_elem_get_id(helem, id);
   1535 	printf("event remove: ");
   1536 	show_control_id(id);
   1537 	printf("\n");
   1538 }
   1539 
   1540 static int element_callback(snd_hctl_elem_t *elem, unsigned int mask)
   1541 {
   1542 	if (mask == SND_CTL_EVENT_MASK_REMOVE) {
   1543 		events_remove(elem);
   1544 		return 0;
   1545 	}
   1546 	if (mask & SND_CTL_EVENT_MASK_INFO) 
   1547 		events_info(elem);
   1548 	if (mask & SND_CTL_EVENT_MASK_VALUE) 
   1549 		events_value(elem);
   1550 	return 0;
   1551 }
   1552 
   1553 static void events_add(snd_hctl_elem_t *helem)
   1554 {
   1555 	snd_ctl_elem_id_t *id;
   1556 	snd_ctl_elem_id_alloca(&id);
   1557 	snd_hctl_elem_get_id(helem, id);
   1558 	printf("event add: ");
   1559 	show_control_id(id);
   1560 	printf("\n");
   1561 	snd_hctl_elem_set_callback(helem, element_callback);
   1562 }
   1563 
   1564 static int ctl_callback(snd_hctl_t *ctl, unsigned int mask,
   1565 		 snd_hctl_elem_t *elem)
   1566 {
   1567 	if (mask & SND_CTL_EVENT_MASK_ADD)
   1568 		events_add(elem);
   1569 	return 0;
   1570 }
   1571 
   1572 static int events(int argc ATTRIBUTE_UNUSED, char *argv[] ATTRIBUTE_UNUSED)
   1573 {
   1574 	snd_hctl_t *handle;
   1575 	snd_hctl_elem_t *helem;
   1576 	int err;
   1577 
   1578 	if ((err = snd_hctl_open(&handle, card, 0)) < 0) {
   1579 		error("Control %s open error: %s\n", card, snd_strerror(err));
   1580 		return err;
   1581 	}
   1582 	snd_hctl_set_callback(handle, ctl_callback);
   1583 	if ((err = snd_hctl_load(handle)) < 0) {
   1584 		error("Control %s hbuild error: %s\n", card, snd_strerror(err));
   1585 		return err;
   1586 	}
   1587 	for (helem = snd_hctl_first_elem(handle); helem; helem = snd_hctl_elem_next(helem)) {
   1588 		snd_hctl_elem_set_callback(helem, element_callback);
   1589 	}
   1590 	printf("Ready to listen...\n");
   1591 	while (1) {
   1592 		int res = snd_hctl_wait(handle, -1);
   1593 		if (res >= 0) {
   1594 			printf("Poll ok: %i\n", res);
   1595 			res = snd_hctl_handle_events(handle);
   1596 			assert(res > 0);
   1597 		}
   1598 	}
   1599 	snd_hctl_close(handle);
   1600 	return 0;
   1601 }
   1602 
   1603 static void sevents_value(snd_mixer_selem_id_t *sid)
   1604 {
   1605 	printf("event value: '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
   1606 }
   1607 
   1608 static void sevents_info(snd_mixer_selem_id_t *sid)
   1609 {
   1610 	printf("event info: '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
   1611 }
   1612 
   1613 static void sevents_remove(snd_mixer_selem_id_t *sid)
   1614 {
   1615 	printf("event remove: '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
   1616 }
   1617 
   1618 static int melem_event(snd_mixer_elem_t *elem, unsigned int mask)
   1619 {
   1620 	snd_mixer_selem_id_t *sid;
   1621 	snd_mixer_selem_id_alloca(&sid);
   1622 	snd_mixer_selem_get_id(elem, sid);
   1623 	if (mask == SND_CTL_EVENT_MASK_REMOVE) {
   1624 		sevents_remove(sid);
   1625 		return 0;
   1626 	}
   1627 	if (mask & SND_CTL_EVENT_MASK_INFO) 
   1628 		sevents_info(sid);
   1629 	if (mask & SND_CTL_EVENT_MASK_VALUE) 
   1630 		sevents_value(sid);
   1631 	return 0;
   1632 }
   1633 
   1634 static void sevents_add(snd_mixer_elem_t *elem)
   1635 {
   1636 	snd_mixer_selem_id_t *sid;
   1637 	snd_mixer_selem_id_alloca(&sid);
   1638 	snd_mixer_selem_get_id(elem, sid);
   1639 	printf("event add: '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
   1640 	snd_mixer_elem_set_callback(elem, melem_event);
   1641 }
   1642 
   1643 static int mixer_event(snd_mixer_t *mixer, unsigned int mask,
   1644 		snd_mixer_elem_t *elem)
   1645 {
   1646 	if (mask & SND_CTL_EVENT_MASK_ADD)
   1647 		sevents_add(elem);
   1648 	return 0;
   1649 }
   1650 
   1651 static int sevents(int argc ATTRIBUTE_UNUSED, char *argv[] ATTRIBUTE_UNUSED)
   1652 {
   1653 	snd_mixer_t *handle;
   1654 	int err;
   1655 
   1656 	if ((err = snd_mixer_open(&handle, 0)) < 0) {
   1657 		error("Mixer %s open error: %s", card, snd_strerror(err));
   1658 		return err;
   1659 	}
   1660 	if (smixer_level == 0 && (err = snd_mixer_attach(handle, card)) < 0) {
   1661 		error("Mixer attach %s error: %s", card, snd_strerror(err));
   1662 		snd_mixer_close(handle);
   1663 		return err;
   1664 	}
   1665 	if ((err = snd_mixer_selem_register(handle, smixer_level > 0 ? &smixer_options : NULL, NULL)) < 0) {
   1666 		error("Mixer register error: %s", snd_strerror(err));
   1667 		snd_mixer_close(handle);
   1668 		return err;
   1669 	}
   1670 	snd_mixer_set_callback(handle, mixer_event);
   1671 	err = snd_mixer_load(handle);
   1672 	if (err < 0) {
   1673 		error("Mixer %s load error: %s", card, snd_strerror(err));
   1674 		snd_mixer_close(handle);
   1675 		return err;
   1676 	}
   1677 
   1678 	printf("Ready to listen...\n");
   1679 	while (1) {
   1680 		int res;
   1681 		res = snd_mixer_wait(handle, -1);
   1682 		if (res >= 0) {
   1683 			printf("Poll ok: %i\n", res);
   1684 			res = snd_mixer_handle_events(handle);
   1685 			assert(res >= 0);
   1686 		}
   1687 	}
   1688 	snd_mixer_close(handle);
   1689 	return 0;
   1690 }
   1691 
   1692 /*
   1693  * split a line into tokens
   1694  * the content in the line buffer is modified
   1695  */
   1696 static int split_line(char *buf, char **token, int max_token)
   1697 {
   1698 	char *dst;
   1699 	int n, esc, quote;
   1700 
   1701 	for (n = 0; n < max_token; n++) {
   1702 		while (isspace(*buf))
   1703 			buf++;
   1704 		if (! *buf || *buf == '\n')
   1705 			return n;
   1706 		/* skip comments */
   1707 		if (*buf == '#' || *buf == '!')
   1708 			return n;
   1709 		esc = 0;
   1710 		quote = 0;
   1711 		token[n] = buf;
   1712 		for (dst = buf; *buf && *buf != '\n'; buf++) {
   1713 			if (esc)
   1714 				esc = 0;
   1715 			else if (isspace(*buf) && !quote) {
   1716 				buf++;
   1717 				break;
   1718 			} else if (*buf == '\\') {
   1719 				esc = 1;
   1720 				continue;
   1721 			} else if (*buf == '\'' || *buf == '"') {
   1722 				if (! quote) {
   1723 					quote = *buf;
   1724 					continue;
   1725 				} else if (*buf == quote) {
   1726 					quote = 0;
   1727 					continue;
   1728 				}
   1729 			}
   1730 			*dst++ = *buf;
   1731 		}
   1732 		*dst = 0;
   1733 	}
   1734 	return n;
   1735 }
   1736 
   1737 #define MAX_ARGS	32
   1738 
   1739 static int exec_stdin(void)
   1740 {
   1741 	int narg;
   1742 	char buf[256], *args[MAX_ARGS];
   1743 	int err = 0;
   1744 
   1745 	/* quiet = 1; */
   1746 	ignore_error = 1;
   1747 
   1748 	while (fgets(buf, sizeof(buf), stdin)) {
   1749 		narg = split_line(buf, args, MAX_ARGS);
   1750 		if (narg > 0) {
   1751 			if (!strcmp(args[0], "sset") || !strcmp(args[0], "set"))
   1752 				err = sset(narg - 1, args + 1, 0, 1);
   1753 			else if (!strcmp(args[0], "cset"))
   1754 				err = cset(narg - 1, args + 1, 0, 1);
   1755 			if (err < 0)
   1756 				return 1;
   1757 		}
   1758 	}
   1759 	return 0;
   1760 }
   1761 
   1762 
   1763 int main(int argc, char *argv[])
   1764 {
   1765 	int morehelp, level = 0;
   1766 	int read_stdin = 0;
   1767 	static const struct option long_option[] =
   1768 	{
   1769 		{"help", 0, NULL, 'h'},
   1770 		{"card", 1, NULL, 'c'},
   1771 		{"device", 1, NULL, 'D'},
   1772 		{"quiet", 0, NULL, 'q'},
   1773 		{"inactive", 0, NULL, 'i'},
   1774 		{"debug", 0, NULL, 'd'},
   1775 		{"nocheck", 0, NULL, 'n'},
   1776 		{"version", 0, NULL, 'v'},
   1777 		{"abstract", 1, NULL, 'a'},
   1778 		{"stdin", 0, NULL, 's'},
   1779 		{"raw-volume", 0, NULL, 'R'},
   1780 		{"mapped-volume", 0, NULL, 'M'},
   1781 		{NULL, 0, NULL, 0},
   1782 	};
   1783 
   1784 	morehelp = 0;
   1785 	while (1) {
   1786 		int c;
   1787 
   1788 		if ((c = getopt_long(argc, argv, "hc:D:qidnva:sRM", long_option, NULL)) < 0)
   1789 			break;
   1790 		switch (c) {
   1791 		case 'h':
   1792 			help();
   1793 			return 0;
   1794 		case 'c':
   1795 			{
   1796 				int i;
   1797 				i = snd_card_get_index(optarg);
   1798 				if (i >= 0 && i < 32)
   1799 					sprintf(card, "hw:%i", i);
   1800 				else {
   1801 					fprintf(stderr, "Invalid card number.\n");
   1802 					morehelp++;
   1803 				}
   1804 			}
   1805 			break;
   1806 		case 'D':
   1807 			strncpy(card, optarg, sizeof(card)-1);
   1808 			card[sizeof(card)-1] = '\0';
   1809 			break;
   1810 		case 'q':
   1811 			quiet = 1;
   1812 			break;
   1813 		case 'i':
   1814 			level |= LEVEL_INACTIVE;
   1815 			break;
   1816 		case 'd':
   1817 			debugflag = 1;
   1818 			break;
   1819 		case 'n':
   1820 			no_check = 1;
   1821 			break;
   1822 		case 'v':
   1823 			printf("amixer version " SND_UTIL_VERSION_STR "\n");
   1824 			return 1;
   1825 		case 'a':
   1826 			smixer_level = 1;
   1827 			memset(&smixer_options, 0, sizeof(smixer_options));
   1828 			smixer_options.ver = 1;
   1829 			if (!strcmp(optarg, "none"))
   1830 				smixer_options.abstract = SND_MIXER_SABSTRACT_NONE;
   1831 			else if (!strcmp(optarg, "basic"))
   1832 				smixer_options.abstract = SND_MIXER_SABSTRACT_BASIC;
   1833 			else {
   1834 				fprintf(stderr, "Select correct abstraction level (none or basic)...\n");
   1835 				morehelp++;
   1836 			}
   1837 			break;
   1838 		case 's':
   1839 			read_stdin = 1;
   1840 			break;
   1841 		case 'R':
   1842 			std_vol_type = VOL_RAW;
   1843 			break;
   1844 		case 'M':
   1845 			std_vol_type = VOL_MAP;
   1846 			break;
   1847 		default:
   1848 			fprintf(stderr, "Invalid switch or option needs an argument.\n");
   1849 			morehelp++;
   1850 		}
   1851 	}
   1852 	if (morehelp) {
   1853 		help();
   1854 		return 1;
   1855 	}
   1856 	smixer_options.device = card;
   1857 
   1858 	if (read_stdin)
   1859 		return exec_stdin();
   1860 
   1861 	if (argc - optind <= 0) {
   1862 		return selems(LEVEL_BASIC | level) ? 1 : 0;
   1863 	}
   1864 	if (!strcmp(argv[optind], "help")) {
   1865 		return help() ? 1 : 0;
   1866 	} else if (!strcmp(argv[optind], "info")) {
   1867 		return info() ? 1 : 0;
   1868 	} else if (!strcmp(argv[optind], "controls")) {
   1869 		return controls(level) ? 1 : 0;
   1870 	} else if (!strcmp(argv[optind], "contents")) {
   1871 		return controls(LEVEL_BASIC | level) ? 1 : 0;
   1872 	} else if (!strcmp(argv[optind], "scontrols") || !strcmp(argv[optind], "simple")) {
   1873 		return selems(level) ? 1 : 0;
   1874 	} else if (!strcmp(argv[optind], "scontents")) {
   1875 		return selems(LEVEL_BASIC | level) ? 1 : 0;
   1876 	} else if (!strcmp(argv[optind], "sset") || !strcmp(argv[optind], "set")) {
   1877 		return sset(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL, 0, 0) ? 1 : 0;
   1878 	} else if (!strcmp(argv[optind], "sget") || !strcmp(argv[optind], "get")) {
   1879 		return sset(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL, 1, 0) ? 1 : 0;
   1880 	} else if (!strcmp(argv[optind], "cset")) {
   1881 		return cset(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL, 0, 0) ? 1 : 0;
   1882 	} else if (!strcmp(argv[optind], "cget")) {
   1883 		return cset(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL, 1, 0) ? 1 : 0;
   1884 	} else if (!strcmp(argv[optind], "events")) {
   1885 		return events(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL);
   1886 	} else if (!strcmp(argv[optind], "sevents")) {
   1887 		return sevents(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL);
   1888 	} else {
   1889 		fprintf(stderr, "amixer: Unknown command '%s'...\n", argv[optind]);
   1890 	}
   1891 
   1892 	return 0;
   1893 }