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