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