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