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