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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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 <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 
error(const char * fmt,...)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 
help(void)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 
info(void)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 
control_type(snd_ctl_elem_info_t * info)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 
control_access(snd_ctl_elem_info_t * info)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 
convert_prange(long val,long min,long max)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 
set_playback_dB(snd_mixer_elem_t * elem,snd_mixer_selem_channel_id_t c,long value,int dir)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 
set_capture_dB(snd_mixer_elem_t * elem,snd_mixer_selem_channel_id_t c,long value,int dir)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 
set_playback_raw_volume(snd_mixer_elem_t * elem,snd_mixer_selem_channel_id_t c,long value,int dir)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 
set_capture_raw_volume(snd_mixer_elem_t * elem,snd_mixer_selem_channel_id_t c,long value,int dir)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 
get_mapped_volume_range(snd_mixer_elem_t * elem,long * pmin,long * pmax)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 
get_playback_mapped_volume(snd_mixer_elem_t * elem,snd_mixer_selem_channel_id_t c,long * value)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 
set_playback_mapped_volume(snd_mixer_elem_t * elem,snd_mixer_selem_channel_id_t c,long value,int dir)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 
get_capture_mapped_volume(snd_mixer_elem_t * elem,snd_mixer_selem_channel_id_t c,long * value)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 
set_capture_mapped_volume(snd_mixer_elem_t * elem,snd_mixer_selem_channel_id_t c,long value,int dir)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 
set_volume_simple(snd_mixer_elem_t * elem,snd_mixer_selem_channel_id_t chn,char ** ptr,int dir)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 
get_bool_simple(char ** ptr,char * str,int invert,int orig)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 
simple_skip_word(char ** ptr,char * str)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 
show_control_id(snd_ctl_elem_id_t * id)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 
print_spaces(unsigned int spaces)435 static void print_spaces(unsigned int spaces)
436 {
437 	while (spaces-- > 0)
438 		putc(' ', stdout);
439 }
440 
print_dB(long dB)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 
decode_tlv(unsigned int spaces,unsigned int * tlv,unsigned int tlv_size)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 	int lf = 1;
457 
458 	if (tlv_size < 2 * sizeof(unsigned int)) {
459 		printf("TLV size error!\n");
460 		return;
461 	}
462 	print_spaces(spaces);
463 	printf("| ");
464 	type = tlv[idx++];
465 	size = tlv[idx++];
466 	tlv_size -= 2 * sizeof(unsigned int);
467 	if (size > tlv_size) {
468 		printf("TLV size error (%u, %u, %u)!\n", type, size, tlv_size);
469 		return;
470 	}
471 	switch (type) {
472 	case SND_CTL_TLVT_CONTAINER:
473 		printf("container\n");
474 		size += sizeof(unsigned int) -1;
475 		size /= sizeof(unsigned int);
476 		while (idx < size) {
477 			if (tlv[idx+1] > (size - idx) * sizeof(unsigned int)) {
478 				printf("TLV size error in compound!\n");
479 				return;
480 			}
481 			decode_tlv(spaces + 2, tlv + idx, tlv[idx+1] + 8);
482 			idx += 2 + (tlv[idx+1] + sizeof(unsigned int) - 1) / sizeof(unsigned int);
483 		}
484 		lf = 0;
485 		break;
486 	case SND_CTL_TLVT_DB_SCALE:
487 		printf("dBscale-");
488 		if (size != 2 * sizeof(unsigned int)) {
489 			while (size > 0) {
490 				printf("0x%08x,", tlv[idx++]);
491 				size -= sizeof(unsigned int);
492 			}
493 		} else {
494 			printf("min=");
495 			print_dB((int)tlv[2]);
496 			printf(",step=");
497 			print_dB(tlv[3] & 0xffff);
498 			printf(",mute=%i", (tlv[3] >> 16) & 1);
499 		}
500 		break;
501 #ifdef SND_CTL_TLVT_DB_LINEAR
502 	case SND_CTL_TLVT_DB_LINEAR:
503 		printf("dBlinear-");
504 		if (size != 2 * sizeof(unsigned int)) {
505 			while (size > 0) {
506 				printf("0x%08x,", tlv[idx++]);
507 				size -= sizeof(unsigned int);
508 			}
509 		} else {
510 			printf("min=");
511 			print_dB((int)tlv[2]);
512 			printf(",max=");
513 			print_dB((int)tlv[3]);
514 		}
515 		break;
516 #endif
517 #ifdef SND_CTL_TLVT_DB_RANGE
518 	case SND_CTL_TLVT_DB_RANGE:
519 		printf("dBrange-\n");
520 		if ((size % (6 * sizeof(unsigned int))) != 0) {
521 			while (size > 0) {
522 				printf("0x%08x,", tlv[idx++]);
523 				size -= sizeof(unsigned int);
524 			}
525 			break;
526 		}
527 		while (size > 0) {
528 			print_spaces(spaces + 2);
529 			printf("rangemin=%i,", tlv[idx++]);
530 			printf(",rangemax=%i\n", tlv[idx++]);
531 			decode_tlv(spaces + 4, tlv + idx, 4 * sizeof(unsigned int));
532 			idx += 4;
533 			size -= 6 * sizeof(unsigned int);
534 		}
535 		break;
536 #endif
537 #ifdef SND_CTL_TLVT_DB_MINMAX
538 	case SND_CTL_TLVT_DB_MINMAX:
539 	case SND_CTL_TLVT_DB_MINMAX_MUTE:
540 		if (type == SND_CTL_TLVT_DB_MINMAX_MUTE)
541 			printf("dBminmaxmute-");
542 		else
543 			printf("dBminmax-");
544 		if (size != 2 * sizeof(unsigned int)) {
545 			while (size > 0) {
546 				printf("0x%08x,", tlv[idx++]);
547 				size -= sizeof(unsigned int);
548 			}
549 		} else {
550 			printf("min=");
551 			print_dB((int)tlv[2]);
552 			printf(",max=");
553 			print_dB((int)tlv[3]);
554 		}
555 		break;
556 #endif
557 #ifdef SND_CTL_TLVT_CHMAP_FIXED
558 	case SND_CTL_TLVT_CHMAP_FIXED:
559 		chmap_type = "fixed";
560 		/* Fall through */
561 	case SND_CTL_TLVT_CHMAP_VAR:
562 		if (!chmap_type)
563 			chmap_type = "variable";
564 		/* Fall through */
565 	case SND_CTL_TLVT_CHMAP_PAIRED:
566 		if (!chmap_type)
567 			chmap_type = "paired";
568 		printf("chmap-%s=", chmap_type);
569 
570 		while (size > 0) {
571 			printf("%s", snd_pcm_chmap_name(tlv[idx++]));
572 			size -= sizeof(unsigned int);
573 			if (size > 0)
574 				printf(",");
575 		}
576 		break;
577 #endif
578 	default:
579 		printf("unk-%u-", type);
580 		while (size > 0) {
581 			printf("0x%08x,", tlv[idx++]);
582 			size -= sizeof(unsigned int);
583 		}
584 		break;
585 	}
586 	if (lf)
587 		putc('\n', stdout);
588 }
589 
show_control(const char * space,snd_hctl_elem_t * elem,int level)590 static int show_control(const char *space, snd_hctl_elem_t *elem,
591 			int level)
592 {
593 	int err;
594 	unsigned int item, idx, count, *tlv;
595 	snd_ctl_elem_type_t type;
596 	snd_ctl_elem_id_t *id;
597 	snd_ctl_elem_info_t *info;
598 	snd_ctl_elem_value_t *control;
599 	snd_aes_iec958_t iec958;
600 	snd_ctl_elem_id_alloca(&id);
601 	snd_ctl_elem_info_alloca(&info);
602 	snd_ctl_elem_value_alloca(&control);
603 	if ((err = snd_hctl_elem_info(elem, info)) < 0) {
604 		error("Control %s snd_hctl_elem_info error: %s\n", card, snd_strerror(err));
605 		return err;
606 	}
607 	if (level & LEVEL_ID) {
608 		snd_hctl_elem_get_id(elem, id);
609 		show_control_id(id);
610 		printf("\n");
611 	}
612 	count = snd_ctl_elem_info_get_count(info);
613 	type = snd_ctl_elem_info_get_type(info);
614 	printf("%s; type=%s,access=%s,values=%u", space, control_type(info), control_access(info), count);
615 	switch (type) {
616 	case SND_CTL_ELEM_TYPE_INTEGER:
617 		printf(",min=%li,max=%li,step=%li\n",
618 		       snd_ctl_elem_info_get_min(info),
619 		       snd_ctl_elem_info_get_max(info),
620 		       snd_ctl_elem_info_get_step(info));
621 		break;
622 	case SND_CTL_ELEM_TYPE_INTEGER64:
623 		printf(",min=%lli,max=%lli,step=%lli\n",
624 		       snd_ctl_elem_info_get_min64(info),
625 		       snd_ctl_elem_info_get_max64(info),
626 		       snd_ctl_elem_info_get_step64(info));
627 		break;
628 	case SND_CTL_ELEM_TYPE_ENUMERATED:
629 	{
630 		unsigned int items = snd_ctl_elem_info_get_items(info);
631 		printf(",items=%u\n", items);
632 		for (item = 0; item < items; item++) {
633 			snd_ctl_elem_info_set_item(info, item);
634 			if ((err = snd_hctl_elem_info(elem, info)) < 0) {
635 				error("Control %s element info error: %s\n", card, snd_strerror(err));
636 				return err;
637 			}
638 			printf("%s; Item #%u '%s'\n", space, item, snd_ctl_elem_info_get_item_name(info));
639 		}
640 		break;
641 	}
642 	default:
643 		printf("\n");
644 		break;
645 	}
646 	if (level & LEVEL_BASIC) {
647 		if (!snd_ctl_elem_info_is_readable(info))
648 			goto __skip_read;
649 		if ((err = snd_hctl_elem_read(elem, control)) < 0) {
650 			error("Control %s element read error: %s\n", card, snd_strerror(err));
651 			return err;
652 		}
653 		printf("%s: values=", space);
654 		for (idx = 0; idx < count; idx++) {
655 			if (idx > 0)
656 				printf(",");
657 			switch (type) {
658 			case SND_CTL_ELEM_TYPE_BOOLEAN:
659 				printf("%s", snd_ctl_elem_value_get_boolean(control, idx) ? "on" : "off");
660 				break;
661 			case SND_CTL_ELEM_TYPE_INTEGER:
662 				printf("%li", snd_ctl_elem_value_get_integer(control, idx));
663 				break;
664 			case SND_CTL_ELEM_TYPE_INTEGER64:
665 				printf("%lli", snd_ctl_elem_value_get_integer64(control, idx));
666 				break;
667 			case SND_CTL_ELEM_TYPE_ENUMERATED:
668 				printf("%u", snd_ctl_elem_value_get_enumerated(control, idx));
669 				break;
670 			case SND_CTL_ELEM_TYPE_BYTES:
671 				printf("0x%02x", snd_ctl_elem_value_get_byte(control, idx));
672 				break;
673 			case SND_CTL_ELEM_TYPE_IEC958:
674 				snd_ctl_elem_value_get_iec958(control, &iec958);
675 				printf("[AES0=0x%02x AES1=0x%02x AES2=0x%02x AES3=0x%02x]",
676 				       iec958.status[0], iec958.status[1],
677 				       iec958.status[2], iec958.status[3]);
678 				break;
679 			default:
680 				printf("?");
681 				break;
682 			}
683 		}
684 		printf("\n");
685 	      __skip_read:
686 		if (!snd_ctl_elem_info_is_tlv_readable(info))
687 			goto __skip_tlv;
688 		/* skip ASoC ext bytes controls that may have huge binary TLV data */
689 		if (type == SND_CTL_ELEM_TYPE_BYTES &&
690 				!snd_ctl_elem_info_is_readable(info) &&
691 				!snd_ctl_elem_info_is_writable(info)) {
692 			printf("%s; ASoC TLV Byte control, skipping bytes dump\n", space);
693 			goto __skip_tlv;
694 		}
695 
696 		tlv = malloc(4096);
697 		if ((err = snd_hctl_elem_tlv_read(elem, tlv, 4096)) < 0) {
698 			error("Control %s element TLV read error: %s\n", card, snd_strerror(err));
699 			free(tlv);
700 			return err;
701 		}
702 		decode_tlv(strlen(space), tlv, 4096);
703 		free(tlv);
704 	}
705       __skip_tlv:
706 	return 0;
707 }
708 
controls(int level)709 static int controls(int level)
710 {
711 	int err;
712 	snd_hctl_t *handle;
713 	snd_hctl_elem_t *elem;
714 	snd_ctl_elem_id_t *id;
715 	snd_ctl_elem_info_t *info;
716 	snd_ctl_elem_id_alloca(&id);
717 	snd_ctl_elem_info_alloca(&info);
718 
719 	if ((err = snd_hctl_open(&handle, card, 0)) < 0) {
720 		error("Control %s open error: %s", card, snd_strerror(err));
721 		return err;
722 	}
723 	if ((err = snd_hctl_load(handle)) < 0) {
724 		error("Control %s local error: %s\n", card, snd_strerror(err));
725 		return err;
726 	}
727 	for (elem = snd_hctl_first_elem(handle); elem; elem = snd_hctl_elem_next(elem)) {
728 		if ((err = snd_hctl_elem_info(elem, info)) < 0) {
729 			error("Control %s snd_hctl_elem_info error: %s\n", card, snd_strerror(err));
730 			return err;
731 		}
732 		if (!(level & LEVEL_INACTIVE) && snd_ctl_elem_info_is_inactive(info))
733 			continue;
734 		snd_hctl_elem_get_id(elem, id);
735 		show_control_id(id);
736 		printf("\n");
737 		if (level & LEVEL_BASIC)
738 			show_control("  ", elem, 1);
739 	}
740 	snd_hctl_close(handle);
741 	return 0;
742 }
743 
show_selem_volume(snd_mixer_elem_t * elem,snd_mixer_selem_channel_id_t chn,int dir,long min,long max)744 static void show_selem_volume(snd_mixer_elem_t *elem,
745 			      snd_mixer_selem_channel_id_t chn, int dir,
746 			      long min, long max)
747 {
748 	long raw, val;
749 	vol_ops[dir].v[VOL_RAW].get(elem, chn, &raw);
750 	if (std_vol_type == VOL_RAW)
751 		val = convert_prange(raw, min, max);
752 	else {
753 		vol_ops[dir].v[std_vol_type].get(elem, chn, &val);
754 		val = convert_prange(val, 0, MAP_VOL_RES);
755 	}
756 	printf(" %li [%li%%]", raw, val);
757 	if (!vol_ops[dir].v[VOL_DB].get(elem, chn, &val)) {
758 		printf(" [");
759 		print_dB(val);
760 		printf("]");
761 	}
762 }
763 
show_selem(snd_mixer_t * handle,snd_mixer_selem_id_t * id,const char * space,int level)764 static int show_selem(snd_mixer_t *handle, snd_mixer_selem_id_t *id, const char *space, int level)
765 {
766 	snd_mixer_selem_channel_id_t chn;
767 	long pmin = 0, pmax = 0;
768 	long cmin = 0, cmax = 0;
769 	int psw, csw;
770 	int pmono, cmono, mono_ok = 0;
771 	snd_mixer_elem_t *elem;
772 
773 	elem = snd_mixer_find_selem(handle, id);
774 	if (!elem) {
775 		error("Mixer %s simple element not found", card);
776 		return -ENOENT;
777 	}
778 
779 	if (level & LEVEL_BASIC) {
780 		printf("%sCapabilities:", space);
781 		if (snd_mixer_selem_has_common_volume(elem)) {
782 			printf(" volume");
783 			if (snd_mixer_selem_has_playback_volume_joined(elem))
784 				printf(" volume-joined");
785 		} else {
786 			if (snd_mixer_selem_has_playback_volume(elem)) {
787 				printf(" pvolume");
788 				if (snd_mixer_selem_has_playback_volume_joined(elem))
789 					printf(" pvolume-joined");
790 			}
791 			if (snd_mixer_selem_has_capture_volume(elem)) {
792 				printf(" cvolume");
793 				if (snd_mixer_selem_has_capture_volume_joined(elem))
794 					printf(" cvolume-joined");
795 			}
796 		}
797 		if (snd_mixer_selem_has_common_switch(elem)) {
798 			printf(" switch");
799 			if (snd_mixer_selem_has_playback_switch_joined(elem))
800 				printf(" switch-joined");
801 		} else {
802 			if (snd_mixer_selem_has_playback_switch(elem)) {
803 				printf(" pswitch");
804 				if (snd_mixer_selem_has_playback_switch_joined(elem))
805 					printf(" pswitch-joined");
806 			}
807 			if (snd_mixer_selem_has_capture_switch(elem)) {
808 				printf(" cswitch");
809 				if (snd_mixer_selem_has_capture_switch_joined(elem))
810 					printf(" cswitch-joined");
811 				if (snd_mixer_selem_has_capture_switch_exclusive(elem))
812 					printf(" cswitch-exclusive");
813 			}
814 		}
815 		if (snd_mixer_selem_is_enum_playback(elem)) {
816 			printf(" penum");
817 		} else if (snd_mixer_selem_is_enum_capture(elem)) {
818 			printf(" cenum");
819 		} else if (snd_mixer_selem_is_enumerated(elem)) {
820 			printf(" enum");
821 		}
822 		printf("\n");
823 		if (snd_mixer_selem_is_enumerated(elem)) {
824 			int i, items;
825 			unsigned int idx;
826 			/*
827 			 * See snd_ctl_elem_init_enum_names() in
828 			 * sound/core/control.c.
829 			 */
830 			char itemname[64];
831 			items = snd_mixer_selem_get_enum_items(elem);
832 			printf("  Items:");
833 			for (i = 0; i < items; i++) {
834 				snd_mixer_selem_get_enum_item_name(elem, i, sizeof(itemname) - 1, itemname);
835 				printf(" '%s'", itemname);
836 			}
837 			printf("\n");
838 			for (i = 0; !snd_mixer_selem_get_enum_item(elem, i, &idx); i++) {
839 				snd_mixer_selem_get_enum_item_name(elem, idx, sizeof(itemname) - 1, itemname);
840 				printf("  Item%d: '%s'\n", i, itemname);
841 			}
842 			return 0; /* no more thing to do */
843 		}
844 		if (snd_mixer_selem_has_capture_switch_exclusive(elem))
845 			printf("%sCapture exclusive group: %i\n", space,
846 			       snd_mixer_selem_get_capture_group(elem));
847 		if (snd_mixer_selem_has_playback_volume(elem) ||
848 		    snd_mixer_selem_has_playback_switch(elem)) {
849 			printf("%sPlayback channels:", space);
850 			if (snd_mixer_selem_is_playback_mono(elem)) {
851 				printf(" Mono");
852 			} else {
853 				int first = 1;
854 				for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++){
855 					if (!snd_mixer_selem_has_playback_channel(elem, chn))
856 						continue;
857 					if (!first)
858 						printf(" -");
859 					printf(" %s", snd_mixer_selem_channel_name(chn));
860 					first = 0;
861 				}
862 			}
863 			printf("\n");
864 		}
865 		if (snd_mixer_selem_has_capture_volume(elem) ||
866 		    snd_mixer_selem_has_capture_switch(elem)) {
867 			printf("%sCapture channels:", space);
868 			if (snd_mixer_selem_is_capture_mono(elem)) {
869 				printf(" Mono");
870 			} else {
871 				int first = 1;
872 				for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++){
873 					if (!snd_mixer_selem_has_capture_channel(elem, chn))
874 						continue;
875 					if (!first)
876 						printf(" -");
877 					printf(" %s", snd_mixer_selem_channel_name(chn));
878 					first = 0;
879 				}
880 			}
881 			printf("\n");
882 		}
883 		if (snd_mixer_selem_has_playback_volume(elem) ||
884 		    snd_mixer_selem_has_capture_volume(elem)) {
885 			printf("%sLimits:", space);
886 			if (snd_mixer_selem_has_common_volume(elem)) {
887 				snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax);
888 				snd_mixer_selem_get_capture_volume_range(elem, &cmin, &cmax);
889 				printf(" %li - %li", pmin, pmax);
890 			} else {
891 				if (snd_mixer_selem_has_playback_volume(elem)) {
892 					snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax);
893 					printf(" Playback %li - %li", pmin, pmax);
894 				}
895 				if (snd_mixer_selem_has_capture_volume(elem)) {
896 					snd_mixer_selem_get_capture_volume_range(elem, &cmin, &cmax);
897 					printf(" Capture %li - %li", cmin, cmax);
898 				}
899 			}
900 			printf("\n");
901 		}
902 		pmono = snd_mixer_selem_has_playback_channel(elem, SND_MIXER_SCHN_MONO) &&
903 		        (snd_mixer_selem_is_playback_mono(elem) ||
904 			 (!snd_mixer_selem_has_playback_volume(elem) &&
905 			  !snd_mixer_selem_has_playback_switch(elem)));
906 		cmono = snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_MONO) &&
907 		        (snd_mixer_selem_is_capture_mono(elem) ||
908 			 (!snd_mixer_selem_has_capture_volume(elem) &&
909 			  !snd_mixer_selem_has_capture_switch(elem)));
910 #if 0
911 		printf("pmono = %i, cmono = %i (%i, %i, %i, %i)\n", pmono, cmono,
912 				snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_MONO),
913 				snd_mixer_selem_is_capture_mono(elem),
914 				snd_mixer_selem_has_capture_volume(elem),
915 				snd_mixer_selem_has_capture_switch(elem));
916 #endif
917 		if (pmono || cmono) {
918 			if (!mono_ok) {
919 				printf("%s%s:", space, "Mono");
920 				mono_ok = 1;
921 			}
922 			if (snd_mixer_selem_has_common_volume(elem)) {
923 				show_selem_volume(elem, SND_MIXER_SCHN_MONO, 0, pmin, pmax);
924 			}
925 			if (snd_mixer_selem_has_common_switch(elem)) {
926 				snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_MONO, &psw);
927 				printf(" [%s]", psw ? "on" : "off");
928 			}
929 		}
930 		if (pmono && snd_mixer_selem_has_playback_channel(elem, SND_MIXER_SCHN_MONO)) {
931 			int title = 0;
932 			if (!mono_ok) {
933 				printf("%s%s:", space, "Mono");
934 				mono_ok = 1;
935 			}
936 			if (!snd_mixer_selem_has_common_volume(elem)) {
937 				if (snd_mixer_selem_has_playback_volume(elem)) {
938 					printf(" Playback");
939 					title = 1;
940 					show_selem_volume(elem, SND_MIXER_SCHN_MONO, 0, pmin, pmax);
941 				}
942 			}
943 			if (!snd_mixer_selem_has_common_switch(elem)) {
944 				if (snd_mixer_selem_has_playback_switch(elem)) {
945 					if (!title)
946 						printf(" Playback");
947 					snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_MONO, &psw);
948 					printf(" [%s]", psw ? "on" : "off");
949 				}
950 			}
951 		}
952 		if (cmono && snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_MONO)) {
953 			int title = 0;
954 			if (!mono_ok) {
955 				printf("%s%s:", space, "Mono");
956 				mono_ok = 1;
957 			}
958 			if (!snd_mixer_selem_has_common_volume(elem)) {
959 				if (snd_mixer_selem_has_capture_volume(elem)) {
960 					printf(" Capture");
961 					title = 1;
962 					show_selem_volume(elem, SND_MIXER_SCHN_MONO, 1, cmin, cmax);
963 				}
964 			}
965 			if (!snd_mixer_selem_has_common_switch(elem)) {
966 				if (snd_mixer_selem_has_capture_switch(elem)) {
967 					if (!title)
968 						printf(" Capture");
969 					snd_mixer_selem_get_capture_switch(elem, SND_MIXER_SCHN_MONO, &csw);
970 					printf(" [%s]", csw ? "on" : "off");
971 				}
972 			}
973 		}
974 		if (pmono || cmono)
975 			printf("\n");
976 		if (!pmono || !cmono) {
977 			for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
978 				if ((pmono || !snd_mixer_selem_has_playback_channel(elem, chn)) &&
979 				    (cmono || !snd_mixer_selem_has_capture_channel(elem, chn)))
980 					continue;
981 				printf("%s%s:", space, snd_mixer_selem_channel_name(chn));
982 				if (!pmono && !cmono && snd_mixer_selem_has_common_volume(elem)) {
983 					show_selem_volume(elem, chn, 0, pmin, pmax);
984 				}
985 				if (!pmono && !cmono && snd_mixer_selem_has_common_switch(elem)) {
986 					snd_mixer_selem_get_playback_switch(elem, chn, &psw);
987 					printf(" [%s]", psw ? "on" : "off");
988 				}
989 				if (!pmono && snd_mixer_selem_has_playback_channel(elem, chn)) {
990 					int title = 0;
991 					if (!snd_mixer_selem_has_common_volume(elem)) {
992 						if (snd_mixer_selem_has_playback_volume(elem)) {
993 							printf(" Playback");
994 							title = 1;
995 							show_selem_volume(elem, chn, 0, pmin, pmax);
996 						}
997 					}
998 					if (!snd_mixer_selem_has_common_switch(elem)) {
999 						if (snd_mixer_selem_has_playback_switch(elem)) {
1000 							if (!title)
1001 								printf(" Playback");
1002 							snd_mixer_selem_get_playback_switch(elem, chn, &psw);
1003 							printf(" [%s]", psw ? "on" : "off");
1004 						}
1005 					}
1006 				}
1007 				if (!cmono && snd_mixer_selem_has_capture_channel(elem, chn)) {
1008 					int title = 0;
1009 					if (!snd_mixer_selem_has_common_volume(elem)) {
1010 						if (snd_mixer_selem_has_capture_volume(elem)) {
1011 							printf(" Capture");
1012 							title = 1;
1013 							show_selem_volume(elem, chn, 1, cmin, cmax);
1014 						}
1015 					}
1016 					if (!snd_mixer_selem_has_common_switch(elem)) {
1017 						if (snd_mixer_selem_has_capture_switch(elem)) {
1018 							if (!title)
1019 								printf(" Capture");
1020 							snd_mixer_selem_get_capture_switch(elem, chn, &csw);
1021 							printf(" [%s]", csw ? "on" : "off");
1022 						}
1023 					}
1024 				}
1025 				printf("\n");
1026 			}
1027 		}
1028 	}
1029 	return 0;
1030 }
1031 
selems(int level)1032 static int selems(int level)
1033 {
1034 	int err;
1035 	snd_mixer_t *handle;
1036 	snd_mixer_selem_id_t *sid;
1037 	snd_mixer_elem_t *elem;
1038 	snd_mixer_selem_id_alloca(&sid);
1039 
1040 	if ((err = snd_mixer_open(&handle, 0)) < 0) {
1041 		error("Mixer %s open error: %s", card, snd_strerror(err));
1042 		return err;
1043 	}
1044 	if (smixer_level == 0 && (err = snd_mixer_attach(handle, card)) < 0) {
1045 		error("Mixer attach %s error: %s", card, snd_strerror(err));
1046 		snd_mixer_close(handle);
1047 		return err;
1048 	}
1049 	if ((err = snd_mixer_selem_register(handle, smixer_level > 0 ? &smixer_options : NULL, NULL)) < 0) {
1050 		error("Mixer register error: %s", snd_strerror(err));
1051 		snd_mixer_close(handle);
1052 		return err;
1053 	}
1054 	err = snd_mixer_load(handle);
1055 	if (err < 0) {
1056 		error("Mixer %s load error: %s", card, snd_strerror(err));
1057 		snd_mixer_close(handle);
1058 		return err;
1059 	}
1060 	for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem)) {
1061 		snd_mixer_selem_get_id(elem, sid);
1062 		if (!(level & LEVEL_INACTIVE) && !snd_mixer_selem_is_active(elem))
1063 			continue;
1064 		printf("Simple mixer control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
1065 		show_selem(handle, sid, "  ", level);
1066 	}
1067 	snd_mixer_close(handle);
1068 	return 0;
1069 }
1070 
parse_simple_id(const char * str,snd_mixer_selem_id_t * sid)1071 static int parse_simple_id(const char *str, snd_mixer_selem_id_t *sid)
1072 {
1073 	int c, size;
1074 	char buf[128];
1075 	char *ptr = buf;
1076 
1077 	while (*str == ' ' || *str == '\t')
1078 		str++;
1079 	if (!(*str))
1080 		return -EINVAL;
1081 	size = 1;	/* for '\0' */
1082 	if (*str != '"' && *str != '\'') {
1083 		while (*str && *str != ',') {
1084 			if (size < (int)sizeof(buf)) {
1085 				*ptr++ = *str;
1086 				size++;
1087 			}
1088 			str++;
1089 		}
1090 	} else {
1091 		c = *str++;
1092 		while (*str && *str != c) {
1093 			if (size < (int)sizeof(buf)) {
1094 				*ptr++ = *str;
1095 				size++;
1096 			}
1097 			str++;
1098 		}
1099 		if (*str == c)
1100 			str++;
1101 	}
1102 	if (*str == '\0') {
1103 		snd_mixer_selem_id_set_index(sid, 0);
1104 		*ptr = 0;
1105 		goto _set;
1106 	}
1107 	if (*str != ',')
1108 		return -EINVAL;
1109 	*ptr = 0;	/* terminate the string */
1110 	str++;
1111 	if (!isdigit(*str))
1112 		return -EINVAL;
1113 	snd_mixer_selem_id_set_index(sid, atoi(str));
1114        _set:
1115 	snd_mixer_selem_id_set_name(sid, buf);
1116 	return 0;
1117 }
1118 
cset(int argc,char * argv[],int roflag,int keep_handle)1119 static int cset(int argc, char *argv[], int roflag, int keep_handle)
1120 {
1121 	int err;
1122 	static snd_ctl_t *handle = NULL;
1123 	snd_ctl_elem_info_t *info;
1124 	snd_ctl_elem_id_t *id;
1125 	snd_ctl_elem_value_t *control;
1126 	snd_ctl_elem_info_alloca(&info);
1127 	snd_ctl_elem_id_alloca(&id);
1128 	snd_ctl_elem_value_alloca(&control);
1129 
1130 	if (argc < 1) {
1131 		fprintf(stderr, "Specify a full control identifier: [[iface=<iface>,][name='name',][index=<index>,][device=<device>,][subdevice=<subdevice>]]|[numid=<numid>]\n");
1132 		return -EINVAL;
1133 	}
1134 	if (snd_ctl_ascii_elem_id_parse(id, argv[0])) {
1135 		fprintf(stderr, "Wrong control identifier: %s\n", argv[0]);
1136 		return -EINVAL;
1137 	}
1138 	if (debugflag) {
1139 		printf("VERIFY ID: ");
1140 		show_control_id(id);
1141 		printf("\n");
1142 	}
1143 	if (handle == NULL &&
1144 	    (err = snd_ctl_open(&handle, card, 0)) < 0) {
1145 		error("Control %s open error: %s\n", card, snd_strerror(err));
1146 		return err;
1147 	}
1148 	snd_ctl_elem_info_set_id(info, id);
1149 	if ((err = snd_ctl_elem_info(handle, info)) < 0) {
1150 		if (ignore_error)
1151 			return 0;
1152 		error("Cannot find the given element from control %s\n", card);
1153 		if (! keep_handle) {
1154 			snd_ctl_close(handle);
1155 			handle = NULL;
1156 		}
1157 		return err;
1158 	}
1159 	snd_ctl_elem_info_get_id(info, id);     /* FIXME: Remove it when hctl find works ok !!! */
1160 	if (!roflag) {
1161 		snd_ctl_elem_value_set_id(control, id);
1162 		if ((err = snd_ctl_elem_read(handle, control)) < 0) {
1163 			if (ignore_error)
1164 				return 0;
1165 			error("Cannot read the given element from control %s\n", card);
1166 			if (! keep_handle) {
1167 				snd_ctl_close(handle);
1168 				handle = NULL;
1169 			}
1170 			return err;
1171 		}
1172 		err = snd_ctl_ascii_value_parse(handle, control, info, argv[1]);
1173 		if (err < 0) {
1174  			if (!ignore_error)
1175 				error("Control %s parse error: %s\n", card, snd_strerror(err));
1176 			if (!keep_handle) {
1177 				snd_ctl_close(handle);
1178 				handle = NULL;
1179 			}
1180 			return ignore_error ? 0 : err;
1181 		}
1182 		if ((err = snd_ctl_elem_write(handle, control)) < 0) {
1183 			if (!ignore_error)
1184 				error("Control %s element write error: %s\n", card, snd_strerror(err));
1185 			if (!keep_handle) {
1186 				snd_ctl_close(handle);
1187 				handle = NULL;
1188 			}
1189 			return ignore_error ? 0 : err;
1190 		}
1191 	}
1192 	if (! keep_handle) {
1193 		snd_ctl_close(handle);
1194 		handle = NULL;
1195 	}
1196 	if (!quiet) {
1197 		snd_hctl_t *hctl;
1198 		snd_hctl_elem_t *elem;
1199 		if ((err = snd_hctl_open(&hctl, card, 0)) < 0) {
1200 			error("Control %s open error: %s\n", card, snd_strerror(err));
1201 			return err;
1202 		}
1203 		if ((err = snd_hctl_load(hctl)) < 0) {
1204 			error("Control %s load error: %s\n", card, snd_strerror(err));
1205 			return err;
1206 		}
1207 		elem = snd_hctl_find_elem(hctl, id);
1208 		if (elem)
1209 			show_control("  ", elem, LEVEL_BASIC | LEVEL_ID);
1210 		else
1211 			printf("Could not find the specified element\n");
1212 		snd_hctl_close(hctl);
1213 	}
1214 	return 0;
1215 }
1216 
1217 typedef struct channel_mask {
1218 	char *name;
1219 	unsigned int mask;
1220 } channel_mask_t;
1221 static const channel_mask_t chanmask[] = {
1222 	{"frontleft", 1 << SND_MIXER_SCHN_FRONT_LEFT},
1223 	{"frontright", 1 << SND_MIXER_SCHN_FRONT_RIGHT},
1224 	{"frontcenter", 1 << SND_MIXER_SCHN_FRONT_CENTER},
1225 	{"front", ((1 << SND_MIXER_SCHN_FRONT_LEFT) |
1226 		   (1 << SND_MIXER_SCHN_FRONT_RIGHT))},
1227 	{"center", 1 << SND_MIXER_SCHN_FRONT_CENTER},
1228 	{"rearleft", 1 << SND_MIXER_SCHN_REAR_LEFT},
1229 	{"rearright", 1 << SND_MIXER_SCHN_REAR_RIGHT},
1230 	{"rear", ((1 << SND_MIXER_SCHN_REAR_LEFT) |
1231 		  (1 << SND_MIXER_SCHN_REAR_RIGHT))},
1232 	{"woofer", 1 << SND_MIXER_SCHN_WOOFER},
1233 	{NULL, 0}
1234 };
1235 
channels_mask(char ** arg,unsigned int def)1236 static unsigned int channels_mask(char **arg, unsigned int def)
1237 {
1238 	const channel_mask_t *c;
1239 
1240 	for (c = chanmask; c->name; c++) {
1241 		if (strncasecmp(*arg, c->name, strlen(c->name)) == 0) {
1242 			while (**arg != '\0' && **arg != ',' && **arg != ' ' && **arg != '\t')
1243 				(*arg)++;
1244 			if (**arg == ',' || **arg == ' ' || **arg == '\t')
1245 				(*arg)++;
1246 			return c->mask;
1247 		}
1248 	}
1249 	return def;
1250 }
1251 
dir_mask(char ** arg,unsigned int def)1252 static unsigned int dir_mask(char **arg, unsigned int def)
1253 {
1254 	int findend = 0;
1255 
1256 	if (strncasecmp(*arg, "playback", 8) == 0)
1257 		def = findend = 1;
1258 	else if (strncasecmp(*arg, "capture", 8) == 0)
1259 		def = findend = 2;
1260 	if (findend) {
1261 		while (**arg != '\0' && **arg != ',' && **arg != ' ' && **arg != '\t')
1262 			(*arg)++;
1263 		if (**arg == ',' || **arg == ' ' || **arg == '\t')
1264 			(*arg)++;
1265 	}
1266 	return def;
1267 }
1268 
get_enum_item_index(snd_mixer_elem_t * elem,char ** ptrp)1269 static int get_enum_item_index(snd_mixer_elem_t *elem, char **ptrp)
1270 {
1271 	char *ptr = *ptrp;
1272 	int items, i, len;
1273 
1274 	/* See snd_ctl_elem_init_enum_names() in sound/core/control.c. */
1275 	char name[64];
1276 
1277 	items = snd_mixer_selem_get_enum_items(elem);
1278 	if (items <= 0)
1279 		return -1;
1280 
1281 	for (i = 0; i < items; i++) {
1282 		if (snd_mixer_selem_get_enum_item_name(elem, i, sizeof(name)-1, name) < 0)
1283 			continue;
1284 
1285 		len = strlen(name);
1286 		if (! strncmp(name, ptr, len)) {
1287 			if (! ptr[len] || ptr[len] == ',' || ptr[len] == '\n') {
1288 				ptr += len;
1289 				*ptrp = ptr;
1290 				return i;
1291 			}
1292 		}
1293 	}
1294 	return -1;
1295 }
1296 
sset_enum(snd_mixer_elem_t * elem,unsigned int argc,char ** argv)1297 static int sset_enum(snd_mixer_elem_t *elem, unsigned int argc, char **argv)
1298 {
1299 	unsigned int idx, item = 0;
1300 	int check_flag = ignore_error ? 0 : -1;
1301 
1302 	for (idx = 1; idx < argc; idx++) {
1303 		char *ptr = argv[idx];
1304 		while (*ptr) {
1305 			int ival = get_enum_item_index(elem, &ptr);
1306 			if (ival < 0)
1307 				return check_flag;
1308 			if (snd_mixer_selem_set_enum_item(elem, item++, ival) >= 0)
1309 				check_flag = 1;
1310 			/* skip separators */
1311 			while (*ptr == ',' || isspace(*ptr))
1312 				ptr++;
1313 		}
1314 	}
1315 	return check_flag;
1316 }
1317 
sset_channels(snd_mixer_elem_t * elem,unsigned int argc,char ** argv)1318 static int sset_channels(snd_mixer_elem_t *elem, unsigned int argc, char **argv)
1319 {
1320 	unsigned int channels = ~0U;
1321 	unsigned int dir = 3, okflag = 3;
1322 	unsigned int idx;
1323 	snd_mixer_selem_channel_id_t chn;
1324 	int check_flag = ignore_error ? 0 : -1;
1325 
1326 	for (idx = 1; idx < argc; idx++) {
1327 		char *ptr = argv[idx], *optr;
1328 		int multi, firstchn = 1;
1329 		channels = channels_mask(&ptr, channels);
1330 		if (*ptr == '\0')
1331 			continue;
1332 		dir = dir_mask(&ptr, dir);
1333 		if (*ptr == '\0')
1334 			continue;
1335 		multi = (strchr(ptr, ',') != NULL);
1336 		optr = ptr;
1337 		for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
1338 			char *sptr = NULL;
1339 			int ival;
1340 
1341 			if (!(channels & (1 << chn)))
1342 				continue;
1343 
1344 			if ((dir & 1) && snd_mixer_selem_has_playback_channel(elem, chn)) {
1345 				sptr = ptr;
1346 				if (!strncmp(ptr, "mute", 4) && snd_mixer_selem_has_playback_switch(elem)) {
1347 					snd_mixer_selem_get_playback_switch(elem, chn, &ival);
1348 					if (snd_mixer_selem_set_playback_switch(elem, chn, get_bool_simple(&ptr, "mute", 1, ival)) >= 0)
1349 						check_flag = 1;
1350 				} else if (!strncmp(ptr, "off", 3) && snd_mixer_selem_has_playback_switch(elem)) {
1351 					snd_mixer_selem_get_playback_switch(elem, chn, &ival);
1352 					if (snd_mixer_selem_set_playback_switch(elem, chn, get_bool_simple(&ptr, "off", 1, ival)) >= 0)
1353 						check_flag = 1;
1354 				} else if (!strncmp(ptr, "unmute", 6) && snd_mixer_selem_has_playback_switch(elem)) {
1355 					snd_mixer_selem_get_playback_switch(elem, chn, &ival);
1356 					if (snd_mixer_selem_set_playback_switch(elem, chn, get_bool_simple(&ptr, "unmute", 0, ival)) >= 0)
1357 						check_flag = 1;
1358 				} else if (!strncmp(ptr, "on", 2) && snd_mixer_selem_has_playback_switch(elem)) {
1359 					snd_mixer_selem_get_playback_switch(elem, chn, &ival);
1360 					if (snd_mixer_selem_set_playback_switch(elem, chn, get_bool_simple(&ptr, "on", 0, ival)) >= 0)
1361 						check_flag = 1;
1362 				} else if (!strncmp(ptr, "toggle", 6) && snd_mixer_selem_has_playback_switch(elem)) {
1363 					if (firstchn || !snd_mixer_selem_has_playback_switch_joined(elem)) {
1364 						snd_mixer_selem_get_playback_switch(elem, chn, &ival);
1365 						if (snd_mixer_selem_set_playback_switch(elem, chn, (ival ? 1 : 0) ^ 1) >= 0)
1366 							check_flag = 1;
1367 					}
1368 					simple_skip_word(&ptr, "toggle");
1369 				} else if (isdigit(*ptr) || *ptr == '-' || *ptr == '+') {
1370 					if (set_volume_simple(elem, chn, &ptr, 0) >= 0)
1371 						check_flag = 1;
1372 				} else if (simple_skip_word(&ptr, "cap") || simple_skip_word(&ptr, "rec") ||
1373 					   simple_skip_word(&ptr, "nocap") || simple_skip_word(&ptr, "norec")) {
1374 					/* nothing */
1375 				} else {
1376 					okflag &= ~1;
1377 				}
1378 			}
1379 			if ((dir & 2) && snd_mixer_selem_has_capture_channel(elem, chn)) {
1380 				if (sptr != NULL)
1381 					ptr = sptr;
1382 				sptr = ptr;
1383 				if (!strncmp(ptr, "cap", 3) && snd_mixer_selem_has_capture_switch(elem)) {
1384 					snd_mixer_selem_get_capture_switch(elem, chn, &ival);
1385 					if (snd_mixer_selem_set_capture_switch(elem, chn, get_bool_simple(&ptr, "cap", 0, ival)) >= 0)
1386 						check_flag = 1;
1387 				} else if (!strncmp(ptr, "rec", 3) && snd_mixer_selem_has_capture_switch(elem)) {
1388 					snd_mixer_selem_get_capture_switch(elem, chn, &ival);
1389 					if (snd_mixer_selem_set_capture_switch(elem, chn, get_bool_simple(&ptr, "rec", 0, ival)) >= 0)
1390 						check_flag = 1;
1391 				} else if (!strncmp(ptr, "nocap", 5) && snd_mixer_selem_has_capture_switch(elem)) {
1392 					snd_mixer_selem_get_capture_switch(elem, chn, &ival);
1393 					if (snd_mixer_selem_set_capture_switch(elem, chn, get_bool_simple(&ptr, "nocap", 1, ival)) >= 0)
1394 						check_flag = 1;
1395 				} else if (!strncmp(ptr, "norec", 5) && snd_mixer_selem_has_capture_switch(elem)) {
1396 					snd_mixer_selem_get_capture_switch(elem, chn, &ival);
1397 					if (snd_mixer_selem_set_capture_switch(elem, chn, get_bool_simple(&ptr, "norec", 1, ival)) >= 0)
1398 						check_flag = 1;
1399 				} else if (!strncmp(ptr, "toggle", 6) && snd_mixer_selem_has_capture_switch(elem)) {
1400 					if (firstchn || !snd_mixer_selem_has_capture_switch_joined(elem)) {
1401 						snd_mixer_selem_get_capture_switch(elem, chn, &ival);
1402 						if (snd_mixer_selem_set_capture_switch(elem, chn, (ival ? 1 : 0) ^ 1) >= 0)
1403 							check_flag = 1;
1404 					}
1405 					simple_skip_word(&ptr, "toggle");
1406 				} else if (isdigit(*ptr) || *ptr == '-' || *ptr == '+') {
1407 					if (set_volume_simple(elem, chn, &ptr, 1) >= 0)
1408 						check_flag = 1;
1409 				} else if (simple_skip_word(&ptr, "mute") || simple_skip_word(&ptr, "off") ||
1410 					   simple_skip_word(&ptr, "unmute") || simple_skip_word(&ptr, "on")) {
1411 					/* nothing */
1412 				} else {
1413 					okflag &= ~2;
1414 				}
1415 			}
1416 			if (okflag == 0) {
1417 				if (debugflag) {
1418 					if (dir & 1)
1419 						error("Unknown playback setup '%s'..", ptr);
1420 					if (dir & 2)
1421 						error("Unknown capture setup '%s'..", ptr);
1422 				}
1423 				return 0; /* just skip it */
1424 			}
1425 			if (!multi)
1426 				ptr = optr;
1427 			firstchn = 0;
1428 		}
1429 	}
1430 	return check_flag;
1431 }
1432 
sset(unsigned int argc,char * argv[],int roflag,int keep_handle)1433 static int sset(unsigned int argc, char *argv[], int roflag, int keep_handle)
1434 {
1435 	int err = 0;
1436 	static snd_mixer_t *handle = NULL;
1437 	snd_mixer_elem_t *elem;
1438 	snd_mixer_selem_id_t *sid;
1439 	snd_mixer_selem_id_alloca(&sid);
1440 
1441 	if (argc < 1) {
1442 		fprintf(stderr, "Specify a scontrol identifier: 'name',index\n");
1443 		return 1;
1444 	}
1445 	if (parse_simple_id(argv[0], sid)) {
1446 		fprintf(stderr, "Wrong scontrol identifier: %s\n", argv[0]);
1447 		return 1;
1448 	}
1449 	if (!roflag && argc < 2) {
1450 		fprintf(stderr, "Specify what you want to set...\n");
1451 		return 1;
1452 	}
1453 	if (handle == NULL) {
1454 		if ((err = snd_mixer_open(&handle, 0)) < 0) {
1455 			error("Mixer %s open error: %s\n", card, snd_strerror(err));
1456 			return err;
1457 		}
1458 		if (smixer_level == 0 && (err = snd_mixer_attach(handle, card)) < 0) {
1459 			error("Mixer attach %s error: %s", card, snd_strerror(err));
1460 			snd_mixer_close(handle);
1461 			handle = NULL;
1462 			return err;
1463 		}
1464 		if ((err = snd_mixer_selem_register(handle, smixer_level > 0 ? &smixer_options : NULL, NULL)) < 0) {
1465 			error("Mixer register error: %s", snd_strerror(err));
1466 			snd_mixer_close(handle);
1467 			handle = NULL;
1468 			return err;
1469 		}
1470 		err = snd_mixer_load(handle);
1471 		if (err < 0) {
1472 			error("Mixer %s load error: %s", card, snd_strerror(err));
1473 			snd_mixer_close(handle);
1474 			handle = NULL;
1475 			return err;
1476 		}
1477 	}
1478 	elem = snd_mixer_find_selem(handle, sid);
1479 	if (!elem) {
1480 		if (ignore_error)
1481 			return 0;
1482 		error("Unable to find simple control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
1483 		snd_mixer_close(handle);
1484 		handle = NULL;
1485 		return -ENOENT;
1486 	}
1487 	if (!roflag) {
1488 		/* enum control */
1489 		if (snd_mixer_selem_is_enumerated(elem))
1490 			err = sset_enum(elem, argc, argv);
1491 		else
1492 			err = sset_channels(elem, argc, argv);
1493 
1494 		if (!err)
1495 			goto done;
1496 		if (err < 0) {
1497 			error("Invalid command!");
1498 			goto done;
1499 		}
1500 	}
1501 	if (!quiet) {
1502 		printf("Simple mixer control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
1503 		show_selem(handle, sid, "  ", 1);
1504 	}
1505  done:
1506 	if (! keep_handle) {
1507 		snd_mixer_close(handle);
1508 		handle = NULL;
1509 	}
1510 	return err < 0 ? 1 : 0;
1511 }
1512 
events_info(snd_hctl_elem_t * helem)1513 static void events_info(snd_hctl_elem_t *helem)
1514 {
1515 	snd_ctl_elem_id_t *id;
1516 	snd_ctl_elem_id_alloca(&id);
1517 	snd_hctl_elem_get_id(helem, id);
1518 	printf("event info: ");
1519 	show_control_id(id);
1520 	printf("\n");
1521 }
1522 
events_value(snd_hctl_elem_t * helem)1523 static void events_value(snd_hctl_elem_t *helem)
1524 {
1525 	snd_ctl_elem_id_t *id;
1526 	snd_ctl_elem_id_alloca(&id);
1527 	snd_hctl_elem_get_id(helem, id);
1528 	printf("event value: ");
1529 	show_control_id(id);
1530 	printf("\n");
1531 }
1532 
events_remove(snd_hctl_elem_t * helem)1533 static void events_remove(snd_hctl_elem_t *helem)
1534 {
1535 	snd_ctl_elem_id_t *id;
1536 	snd_ctl_elem_id_alloca(&id);
1537 	snd_hctl_elem_get_id(helem, id);
1538 	printf("event remove: ");
1539 	show_control_id(id);
1540 	printf("\n");
1541 }
1542 
element_callback(snd_hctl_elem_t * elem,unsigned int mask)1543 static int element_callback(snd_hctl_elem_t *elem, unsigned int mask)
1544 {
1545 	if (mask == SND_CTL_EVENT_MASK_REMOVE) {
1546 		events_remove(elem);
1547 		return 0;
1548 	}
1549 	if (mask & SND_CTL_EVENT_MASK_INFO)
1550 		events_info(elem);
1551 	if (mask & SND_CTL_EVENT_MASK_VALUE)
1552 		events_value(elem);
1553 	return 0;
1554 }
1555 
events_add(snd_hctl_elem_t * helem)1556 static void events_add(snd_hctl_elem_t *helem)
1557 {
1558 	snd_ctl_elem_id_t *id;
1559 	snd_ctl_elem_id_alloca(&id);
1560 	snd_hctl_elem_get_id(helem, id);
1561 	printf("event add: ");
1562 	show_control_id(id);
1563 	printf("\n");
1564 	snd_hctl_elem_set_callback(helem, element_callback);
1565 }
1566 
ctl_callback(snd_hctl_t * ctl,unsigned int mask,snd_hctl_elem_t * elem)1567 static int ctl_callback(snd_hctl_t *ctl, unsigned int mask,
1568 		 snd_hctl_elem_t *elem)
1569 {
1570 	if (mask & SND_CTL_EVENT_MASK_ADD)
1571 		events_add(elem);
1572 	return 0;
1573 }
1574 
events(int argc ATTRIBUTE_UNUSED,char * argv[]ATTRIBUTE_UNUSED)1575 static int events(int argc ATTRIBUTE_UNUSED, char *argv[] ATTRIBUTE_UNUSED)
1576 {
1577 	snd_hctl_t *handle;
1578 	snd_hctl_elem_t *helem;
1579 	int err;
1580 
1581 	if ((err = snd_hctl_open(&handle, card, 0)) < 0) {
1582 		error("Control %s open error: %s\n", card, snd_strerror(err));
1583 		return err;
1584 	}
1585 	snd_hctl_set_callback(handle, ctl_callback);
1586 	if ((err = snd_hctl_load(handle)) < 0) {
1587 		error("Control %s hbuild error: %s\n", card, snd_strerror(err));
1588 		return err;
1589 	}
1590 	for (helem = snd_hctl_first_elem(handle); helem; helem = snd_hctl_elem_next(helem)) {
1591 		snd_hctl_elem_set_callback(helem, element_callback);
1592 	}
1593 	printf("Ready to listen...\n");
1594 	while (1) {
1595 		int res = snd_hctl_wait(handle, -1);
1596 		if (res >= 0) {
1597 			printf("Poll ok: %i\n", res);
1598 			res = snd_hctl_handle_events(handle);
1599 			assert(res > 0);
1600 		}
1601 	}
1602 	snd_hctl_close(handle);
1603 	return 0;
1604 }
1605 
sevents_value(snd_mixer_selem_id_t * sid)1606 static void sevents_value(snd_mixer_selem_id_t *sid)
1607 {
1608 	printf("event value: '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
1609 }
1610 
sevents_info(snd_mixer_selem_id_t * sid)1611 static void sevents_info(snd_mixer_selem_id_t *sid)
1612 {
1613 	printf("event info: '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
1614 }
1615 
sevents_remove(snd_mixer_selem_id_t * sid)1616 static void sevents_remove(snd_mixer_selem_id_t *sid)
1617 {
1618 	printf("event remove: '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
1619 }
1620 
melem_event(snd_mixer_elem_t * elem,unsigned int mask)1621 static int melem_event(snd_mixer_elem_t *elem, unsigned int mask)
1622 {
1623 	snd_mixer_selem_id_t *sid;
1624 	snd_mixer_selem_id_alloca(&sid);
1625 	snd_mixer_selem_get_id(elem, sid);
1626 	if (mask == SND_CTL_EVENT_MASK_REMOVE) {
1627 		sevents_remove(sid);
1628 		return 0;
1629 	}
1630 	if (mask & SND_CTL_EVENT_MASK_INFO)
1631 		sevents_info(sid);
1632 	if (mask & SND_CTL_EVENT_MASK_VALUE)
1633 		sevents_value(sid);
1634 	return 0;
1635 }
1636 
sevents_add(snd_mixer_elem_t * elem)1637 static void sevents_add(snd_mixer_elem_t *elem)
1638 {
1639 	snd_mixer_selem_id_t *sid;
1640 	snd_mixer_selem_id_alloca(&sid);
1641 	snd_mixer_selem_get_id(elem, sid);
1642 	printf("event add: '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
1643 	snd_mixer_elem_set_callback(elem, melem_event);
1644 }
1645 
mixer_event(snd_mixer_t * mixer,unsigned int mask,snd_mixer_elem_t * elem)1646 static int mixer_event(snd_mixer_t *mixer, unsigned int mask,
1647 		snd_mixer_elem_t *elem)
1648 {
1649 	if (mask & SND_CTL_EVENT_MASK_ADD)
1650 		sevents_add(elem);
1651 	return 0;
1652 }
1653 
sevents(int argc ATTRIBUTE_UNUSED,char * argv[]ATTRIBUTE_UNUSED)1654 static int sevents(int argc ATTRIBUTE_UNUSED, char *argv[] ATTRIBUTE_UNUSED)
1655 {
1656 	snd_mixer_t *handle;
1657 	int err;
1658 
1659 	if ((err = snd_mixer_open(&handle, 0)) < 0) {
1660 		error("Mixer %s open error: %s", card, snd_strerror(err));
1661 		return err;
1662 	}
1663 	if (smixer_level == 0 && (err = snd_mixer_attach(handle, card)) < 0) {
1664 		error("Mixer attach %s error: %s", card, snd_strerror(err));
1665 		snd_mixer_close(handle);
1666 		return err;
1667 	}
1668 	if ((err = snd_mixer_selem_register(handle, smixer_level > 0 ? &smixer_options : NULL, NULL)) < 0) {
1669 		error("Mixer register error: %s", snd_strerror(err));
1670 		snd_mixer_close(handle);
1671 		return err;
1672 	}
1673 	snd_mixer_set_callback(handle, mixer_event);
1674 	err = snd_mixer_load(handle);
1675 	if (err < 0) {
1676 		error("Mixer %s load error: %s", card, snd_strerror(err));
1677 		snd_mixer_close(handle);
1678 		return err;
1679 	}
1680 
1681 	printf("Ready to listen...\n");
1682 	while (1) {
1683 		int res;
1684 		res = snd_mixer_wait(handle, -1);
1685 		if (res >= 0) {
1686 			printf("Poll ok: %i\n", res);
1687 			res = snd_mixer_handle_events(handle);
1688 			assert(res >= 0);
1689 		}
1690 	}
1691 	snd_mixer_close(handle);
1692 	return 0;
1693 }
1694 
1695 /*
1696  * split a line into tokens
1697  * the content in the line buffer is modified
1698  */
split_line(char * buf,char ** token,int max_token)1699 static int split_line(char *buf, char **token, int max_token)
1700 {
1701 	char *dst;
1702 	int n, esc, quote;
1703 
1704 	for (n = 0; n < max_token; n++) {
1705 		while (isspace(*buf))
1706 			buf++;
1707 		if (! *buf || *buf == '\n')
1708 			return n;
1709 		/* skip comments */
1710 		if (*buf == '#' || *buf == '!')
1711 			return n;
1712 		esc = 0;
1713 		quote = 0;
1714 		token[n] = buf;
1715 		for (dst = buf; *buf && *buf != '\n'; buf++) {
1716 			if (esc)
1717 				esc = 0;
1718 			else if (isspace(*buf) && !quote) {
1719 				buf++;
1720 				break;
1721 			} else if (*buf == '\\') {
1722 				esc = 1;
1723 				continue;
1724 			} else if (*buf == '\'' || *buf == '"') {
1725 				if (! quote) {
1726 					quote = *buf;
1727 					continue;
1728 				} else if (*buf == quote) {
1729 					quote = 0;
1730 					continue;
1731 				}
1732 			}
1733 			*dst++ = *buf;
1734 		}
1735 		*dst = 0;
1736 	}
1737 	return n;
1738 }
1739 
1740 #define MAX_ARGS	32
1741 
exec_stdin(void)1742 static int exec_stdin(void)
1743 {
1744 	int narg;
1745 	char buf[256], *args[MAX_ARGS];
1746 	int err = 0;
1747 
1748 	/* quiet = 1; */
1749 	ignore_error = 1;
1750 
1751 	while (fgets(buf, sizeof(buf), stdin)) {
1752 		narg = split_line(buf, args, MAX_ARGS);
1753 		if (narg > 0) {
1754 			if (!strcmp(args[0], "sset") || !strcmp(args[0], "set"))
1755 				err = sset(narg - 1, args + 1, 0, 1);
1756 			else if (!strcmp(args[0], "cset"))
1757 				err = cset(narg - 1, args + 1, 0, 1);
1758 			if (err < 0)
1759 				return 1;
1760 		}
1761 	}
1762 	return 0;
1763 }
1764 
1765 
main(int argc,char * argv[])1766 int main(int argc, char *argv[])
1767 {
1768 	int morehelp, level = 0;
1769 	int read_stdin = 0;
1770 	static const struct option long_option[] =
1771 	{
1772 		{"help", 0, NULL, 'h'},
1773 		{"card", 1, NULL, 'c'},
1774 		{"device", 1, NULL, 'D'},
1775 		{"quiet", 0, NULL, 'q'},
1776 		{"inactive", 0, NULL, 'i'},
1777 		{"debug", 0, NULL, 'd'},
1778 		{"nocheck", 0, NULL, 'n'},
1779 		{"version", 0, NULL, 'v'},
1780 		{"abstract", 1, NULL, 'a'},
1781 		{"stdin", 0, NULL, 's'},
1782 		{"raw-volume", 0, NULL, 'R'},
1783 		{"mapped-volume", 0, NULL, 'M'},
1784 		{NULL, 0, NULL, 0},
1785 	};
1786 
1787 	morehelp = 0;
1788 	while (1) {
1789 		int c;
1790 
1791 		if ((c = getopt_long(argc, argv, "hc:D:qidnva:sRM", long_option, NULL)) < 0)
1792 			break;
1793 		switch (c) {
1794 		case 'h':
1795 			help();
1796 			return 0;
1797 		case 'c':
1798 			{
1799 				int i;
1800 				i = snd_card_get_index(optarg);
1801 				if (i >= 0 && i < 32)
1802 					sprintf(card, "hw:%i", i);
1803 				else {
1804 					fprintf(stderr, "Invalid card number.\n");
1805 					morehelp++;
1806 				}
1807 			}
1808 			break;
1809 		case 'D':
1810 			strncpy(card, optarg, sizeof(card)-1);
1811 			card[sizeof(card)-1] = '\0';
1812 			break;
1813 		case 'q':
1814 			quiet = 1;
1815 			break;
1816 		case 'i':
1817 			level |= LEVEL_INACTIVE;
1818 			break;
1819 		case 'd':
1820 			debugflag = 1;
1821 			break;
1822 		case 'n':
1823 			no_check = 1;
1824 			break;
1825 		case 'v':
1826 			printf("amixer version " SND_UTIL_VERSION_STR "\n");
1827 			return 1;
1828 		case 'a':
1829 			smixer_level = 1;
1830 			memset(&smixer_options, 0, sizeof(smixer_options));
1831 			smixer_options.ver = 1;
1832 			if (!strcmp(optarg, "none"))
1833 				smixer_options.abstract = SND_MIXER_SABSTRACT_NONE;
1834 			else if (!strcmp(optarg, "basic"))
1835 				smixer_options.abstract = SND_MIXER_SABSTRACT_BASIC;
1836 			else {
1837 				fprintf(stderr, "Select correct abstraction level (none or basic)...\n");
1838 				morehelp++;
1839 			}
1840 			break;
1841 		case 's':
1842 			read_stdin = 1;
1843 			break;
1844 		case 'R':
1845 			std_vol_type = VOL_RAW;
1846 			break;
1847 		case 'M':
1848 			std_vol_type = VOL_MAP;
1849 			break;
1850 		default:
1851 			fprintf(stderr, "Invalid switch or option needs an argument.\n");
1852 			morehelp++;
1853 		}
1854 	}
1855 	if (morehelp) {
1856 		help();
1857 		return 1;
1858 	}
1859 	smixer_options.device = card;
1860 
1861 	if (read_stdin)
1862 		return exec_stdin();
1863 
1864 	if (argc - optind <= 0) {
1865 		return selems(LEVEL_BASIC | level) ? 1 : 0;
1866 	}
1867 	if (!strcmp(argv[optind], "help")) {
1868 		return help() ? 1 : 0;
1869 	} else if (!strcmp(argv[optind], "info")) {
1870 		return info() ? 1 : 0;
1871 	} else if (!strcmp(argv[optind], "controls")) {
1872 		return controls(level) ? 1 : 0;
1873 	} else if (!strcmp(argv[optind], "contents")) {
1874 		return controls(LEVEL_BASIC | level) ? 1 : 0;
1875 	} else if (!strcmp(argv[optind], "scontrols") || !strcmp(argv[optind], "simple")) {
1876 		return selems(level) ? 1 : 0;
1877 	} else if (!strcmp(argv[optind], "scontents")) {
1878 		return selems(LEVEL_BASIC | level) ? 1 : 0;
1879 	} else if (!strcmp(argv[optind], "sset") || !strcmp(argv[optind], "set")) {
1880 		return sset(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL, 0, 0) ? 1 : 0;
1881 	} else if (!strcmp(argv[optind], "sget") || !strcmp(argv[optind], "get")) {
1882 		return sset(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL, 1, 0) ? 1 : 0;
1883 	} else if (!strcmp(argv[optind], "cset")) {
1884 		return cset(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL, 0, 0) ? 1 : 0;
1885 	} else if (!strcmp(argv[optind], "cget")) {
1886 		return cset(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL, 1, 0) ? 1 : 0;
1887 	} else if (!strcmp(argv[optind], "events")) {
1888 		return events(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL);
1889 	} else if (!strcmp(argv[optind], "sevents")) {
1890 		return sevents(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL);
1891 	} else {
1892 		fprintf(stderr, "amixer: Unknown command '%s'...\n", argv[optind]);
1893 	}
1894 
1895 	return 0;
1896 }
1897