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