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