1 /* $OpenBSD: sndioctl.c,v 1.16 2021/03/03 09:40:43 ratchov Exp $ */ 2 /* 3 * Copyright (c) 2014-2020 Alexandre Ratchov <alex@caoua.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <errno.h> 18 #include <poll.h> 19 #include <sndio.h> 20 #include <stdlib.h> 21 #include <stdio.h> 22 #include <string.h> 23 #include <unistd.h> 24 25 struct info { 26 struct info *next; 27 struct sioctl_desc desc; 28 unsigned ctladdr; 29 #define MODE_IGNORE 0 /* ignore this value */ 30 #define MODE_PRINT 1 /* print-only, don't change value */ 31 #define MODE_SET 2 /* set to newval value */ 32 #define MODE_ADD 3 /* increase current value by newval */ 33 #define MODE_SUB 4 /* decrease current value by newval */ 34 #define MODE_TOGGLE 5 /* toggle current value */ 35 unsigned mode; 36 int curval, newval; 37 }; 38 39 int cmpdesc(struct sioctl_desc *, struct sioctl_desc *); 40 int isdiag(struct info *); 41 struct info *vecent(struct info *, char *, int); 42 struct info *nextfunc(struct info *); 43 struct info *nextpar(struct info *); 44 struct info *firstent(struct info *, char *); 45 struct info *nextent(struct info *, int); 46 int matchpar(struct info *, char *, int); 47 int matchent(struct info *, char *, int); 48 int ismono(struct info *); 49 void print_node(struct sioctl_node *, int); 50 void print_desc(struct info *, int); 51 void print_num(struct info *); 52 void print_ent(struct info *, char *); 53 void print_val(struct info *, int); 54 void print_par(struct info *, int); 55 int parse_name(char **, char *); 56 int parse_unit(char **, int *); 57 int parse_val(char **, float *); 58 int parse_node(char **, char *, int *); 59 int parse_modeval(char **, int *, float *); 60 void dump(void); 61 int cmd(char *); 62 void commit(void); 63 void list(void); 64 void ondesc(void *, struct sioctl_desc *, int); 65 void onctl(void *, unsigned, unsigned); 66 67 struct sioctl_hdl *hdl; 68 struct info *infolist; 69 int i_flag = 0, v_flag = 0, m_flag = 0, n_flag = 0, q_flag = 0; 70 71 static inline int 72 isname(int c) 73 { 74 return (c == '_') || 75 (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || 76 (c >= '0' && c <= '9'); 77 } 78 79 static int 80 ftoi(float f) 81 { 82 return f + 0.5; 83 } 84 85 /* 86 * compare two sioctl_desc structures, used to sort infolist 87 */ 88 int 89 cmpdesc(struct sioctl_desc *d1, struct sioctl_desc *d2) 90 { 91 int res; 92 93 res = strcmp(d1->group, d2->group); 94 if (res != 0) 95 return res; 96 res = strcmp(d1->node0.name, d2->node0.name); 97 if (res != 0) 98 return res; 99 res = d1->type - d2->type; 100 if (res != 0) 101 return res; 102 res = strcmp(d1->func, d2->func); 103 if (res != 0) 104 return res; 105 res = d1->node0.unit - d2->node0.unit; 106 if (d1->type == SIOCTL_SEL || 107 d1->type == SIOCTL_VEC || 108 d1->type == SIOCTL_LIST) { 109 if (res != 0) 110 return res; 111 res = strcmp(d1->node1.name, d2->node1.name); 112 if (res != 0) 113 return res; 114 res = d1->node1.unit - d2->node1.unit; 115 } 116 return res; 117 } 118 119 /* 120 * return true of the vector entry is diagonal 121 */ 122 int 123 isdiag(struct info *e) 124 { 125 if (e->desc.node0.unit < 0 || e->desc.node1.unit < 0) 126 return 1; 127 return e->desc.node1.unit == e->desc.node0.unit; 128 } 129 130 /* 131 * find the selector or vector entry with the given name and channels 132 */ 133 struct info * 134 vecent(struct info *i, char *vstr, int vunit) 135 { 136 while (i != NULL) { 137 if ((strcmp(i->desc.node1.name, vstr) == 0) && 138 (vunit < 0 || i->desc.node1.unit == vunit)) 139 break; 140 i = i->next; 141 } 142 return i; 143 } 144 145 /* 146 * skip all parameters with the same group, name, and func 147 */ 148 struct info * 149 nextfunc(struct info *i) 150 { 151 char *str, *group, *func; 152 153 group = i->desc.group; 154 func = i->desc.func; 155 str = i->desc.node0.name; 156 for (i = i->next; i != NULL; i = i->next) { 157 if (strcmp(i->desc.group, group) != 0 || 158 strcmp(i->desc.node0.name, str) != 0 || 159 strcmp(i->desc.func, func) != 0) 160 return i; 161 } 162 return NULL; 163 } 164 165 /* 166 * find the next parameter with the same group, name, func 167 */ 168 struct info * 169 nextpar(struct info *i) 170 { 171 char *str, *group, *func; 172 int unit; 173 174 group = i->desc.group; 175 func = i->desc.func; 176 str = i->desc.node0.name; 177 unit = i->desc.node0.unit; 178 for (i = i->next; i != NULL; i = i->next) { 179 if (strcmp(i->desc.group, group) != 0 || 180 strcmp(i->desc.node0.name, str) != 0 || 181 strcmp(i->desc.func, func) != 0) 182 break; 183 /* XXX: need to check for -1 ? */ 184 if (i->desc.node0.unit != unit) 185 return i; 186 } 187 return NULL; 188 } 189 190 /* 191 * return the first vector entry with the given name 192 */ 193 struct info * 194 firstent(struct info *g, char *vstr) 195 { 196 char *astr, *group, *func; 197 struct info *i; 198 199 group = g->desc.group; 200 astr = g->desc.node0.name; 201 func = g->desc.func; 202 for (i = g; i != NULL; i = i->next) { 203 if (strcmp(i->desc.group, group) != 0 || 204 strcmp(i->desc.node0.name, astr) != 0 || 205 strcmp(i->desc.func, func) != 0) 206 break; 207 if (!isdiag(i)) 208 continue; 209 if (strcmp(i->desc.node1.name, vstr) == 0) 210 return i; 211 } 212 return NULL; 213 } 214 215 /* 216 * find the next entry of the given vector, if the mono flag 217 * is set then the whole group is searched and off-diagonal entries are 218 * skipped 219 */ 220 struct info * 221 nextent(struct info *i, int mono) 222 { 223 char *str, *group, *func; 224 int unit; 225 226 group = i->desc.group; 227 func = i->desc.func; 228 str = i->desc.node0.name; 229 unit = i->desc.node0.unit; 230 for (i = i->next; i != NULL; i = i->next) { 231 if (strcmp(i->desc.group, group) != 0 || 232 strcmp(i->desc.node0.name, str) != 0 || 233 strcmp(i->desc.func, func) != 0) 234 return NULL; 235 if (mono) 236 return i; 237 if (i->desc.node0.unit == unit) 238 return i; 239 } 240 return NULL; 241 } 242 243 /* 244 * return true if parameter matches the given name and channel 245 */ 246 int 247 matchpar(struct info *i, char *astr, int aunit) 248 { 249 if (strcmp(i->desc.node0.name, astr) != 0) 250 return 0; 251 if (aunit < 0) 252 return 1; 253 else if (i->desc.node0.unit < 0) { 254 fprintf(stderr, "unit used for parameter with no unit\n"); 255 exit(1); 256 } 257 return i->desc.node0.unit == aunit; 258 } 259 260 /* 261 * return true if selector or vector entry matches the given name and 262 * channel range 263 */ 264 int 265 matchent(struct info *i, char *vstr, int vunit) 266 { 267 if (strcmp(i->desc.node1.name, vstr) != 0) 268 return 0; 269 if (vunit < 0) 270 return 1; 271 else if (i->desc.node1.unit < 0) { 272 fprintf(stderr, "unit used for parameter with no unit\n"); 273 exit(1); 274 } 275 return i->desc.node1.unit == vunit; 276 } 277 278 /* 279 * return true if the given group can be represented as a signle mono 280 * parameter 281 */ 282 int 283 ismono(struct info *g) 284 { 285 struct info *p1, *p2; 286 struct info *e1, *e2; 287 288 p1 = g; 289 switch (g->desc.type) { 290 case SIOCTL_NUM: 291 case SIOCTL_SW: 292 for (p2 = g; p2 != NULL; p2 = nextpar(p2)) { 293 if (p2->curval != p1->curval) 294 return 0; 295 } 296 break; 297 case SIOCTL_SEL: 298 case SIOCTL_VEC: 299 case SIOCTL_LIST: 300 for (p2 = g; p2 != NULL; p2 = nextpar(p2)) { 301 for (e2 = p2; e2 != NULL; e2 = nextent(e2, 0)) { 302 if (!isdiag(e2)) { 303 if (e2->curval != 0) 304 return 0; 305 } else { 306 e1 = vecent(p1, 307 e2->desc.node1.name, 308 p1->desc.node0.unit); 309 if (e1 == NULL) 310 continue; 311 if (e1->curval != e2->curval) 312 return 0; 313 } 314 } 315 } 316 break; 317 } 318 return 1; 319 } 320 321 /* 322 * print a sub-stream, eg. "spkr[4]" 323 */ 324 void 325 print_node(struct sioctl_node *c, int mono) 326 { 327 printf("%s", c->name); 328 if (!mono && c->unit >= 0) 329 printf("[%d]", c->unit); 330 } 331 332 /* 333 * print info about the parameter 334 */ 335 void 336 print_desc(struct info *p, int mono) 337 { 338 struct info *e; 339 int more; 340 341 switch (p->desc.type) { 342 case SIOCTL_NUM: 343 case SIOCTL_SW: 344 printf("*"); 345 break; 346 case SIOCTL_SEL: 347 case SIOCTL_VEC: 348 case SIOCTL_LIST: 349 more = 0; 350 for (e = p; e != NULL; e = nextent(e, mono)) { 351 if (mono) { 352 if (!isdiag(e)) 353 continue; 354 if (e != firstent(p, e->desc.node1.name)) 355 continue; 356 } 357 if (more) 358 printf(","); 359 print_node(&e->desc.node1, mono); 360 if (p->desc.type != SIOCTL_SEL) 361 printf(":*"); 362 more = 1; 363 } 364 } 365 } 366 367 void 368 print_num(struct info *p) 369 { 370 if (p->desc.maxval == 1) 371 printf("%d", p->curval); 372 else { 373 /* 374 * For now, maxval is always 127 or 255, 375 * so three decimals is always ideal. 376 */ 377 printf("%.3f", p->curval / (float)p->desc.maxval); 378 } 379 } 380 381 /* 382 * print a single control 383 */ 384 void 385 print_ent(struct info *e, char *comment) 386 { 387 if (e->desc.group[0] != 0) { 388 printf("%s", e->desc.group); 389 printf("/"); 390 } 391 print_node(&e->desc.node0, 0); 392 printf(".%s=", e->desc.func); 393 switch (e->desc.type) { 394 case SIOCTL_NONE: 395 printf("<removed>\n"); 396 break; 397 case SIOCTL_SEL: 398 case SIOCTL_VEC: 399 case SIOCTL_LIST: 400 print_node(&e->desc.node1, 0); 401 printf(":"); 402 /* FALLTHROUGH */ 403 case SIOCTL_SW: 404 case SIOCTL_NUM: 405 print_num(e); 406 } 407 if (comment) 408 printf("\t# %s", comment); 409 printf("\n"); 410 } 411 412 /* 413 * print parameter value 414 */ 415 void 416 print_val(struct info *p, int mono) 417 { 418 struct info *e; 419 int more; 420 421 switch (p->desc.type) { 422 case SIOCTL_NUM: 423 case SIOCTL_SW: 424 print_num(p); 425 break; 426 case SIOCTL_SEL: 427 case SIOCTL_VEC: 428 case SIOCTL_LIST: 429 more = 0; 430 for (e = p; e != NULL; e = nextent(e, mono)) { 431 if (mono) { 432 if (!isdiag(e)) 433 continue; 434 if (e != firstent(p, e->desc.node1.name)) 435 continue; 436 } 437 if (e->desc.maxval == 1) { 438 if (e->curval) { 439 if (more) 440 printf(","); 441 print_node(&e->desc.node1, mono); 442 more = 1; 443 } 444 } else { 445 if (more) 446 printf(","); 447 print_node(&e->desc.node1, mono); 448 printf(":"); 449 print_num(e); 450 more = 1; 451 } 452 } 453 } 454 } 455 456 /* 457 * print ``<parameter>=<value>'' string (including '\n') 458 */ 459 void 460 print_par(struct info *p, int mono) 461 { 462 if (!n_flag) { 463 if (p->desc.group[0] != 0) { 464 printf("%s", p->desc.group); 465 printf("/"); 466 } 467 print_node(&p->desc.node0, mono); 468 printf(".%s=", p->desc.func); 469 } 470 if (i_flag) 471 print_desc(p, mono); 472 else 473 print_val(p, mono); 474 printf("\n"); 475 } 476 477 /* 478 * parse a stream name or parameter name 479 */ 480 int 481 parse_name(char **line, char *name) 482 { 483 char *p = *line; 484 unsigned len = 0; 485 486 if (!isname(*p)) { 487 fprintf(stderr, "letter or digit expected near '%s'\n", p); 488 return 0; 489 } 490 while (isname(*p)) { 491 if (len >= SIOCTL_NAMEMAX - 1) { 492 name[SIOCTL_NAMEMAX - 1] = '\0'; 493 fprintf(stderr, "%s...: too long\n", name); 494 return 0; 495 } 496 name[len++] = *p; 497 p++; 498 } 499 name[len] = '\0'; 500 *line = p; 501 return 1; 502 } 503 504 /* 505 * parse a decimal integer 506 */ 507 int 508 parse_unit(char **line, int *num) 509 { 510 char *p = *line; 511 unsigned int val; 512 int n; 513 514 if (sscanf(p, "%u%n", &val, &n) != 1) { 515 fprintf(stderr, "number expected near '%s'\n", p); 516 return 0; 517 } 518 if (val >= 255) { 519 fprintf(stderr, "%d: too large\n", val); 520 return 0; 521 } 522 *num = val; 523 *line = p + n; 524 return 1; 525 } 526 527 int 528 parse_val(char **line, float *num) 529 { 530 char *p = *line; 531 float val; 532 int n; 533 534 if (sscanf(p, "%g%n", &val, &n) != 1) { 535 fprintf(stderr, "number expected near '%s'\n", p); 536 return 0; 537 } 538 if (val < 0 || val > 1) { 539 fprintf(stderr, "%g: expected number between 0 and 1\n", val); 540 return 0; 541 } 542 *num = val; 543 *line = p + n; 544 return 1; 545 } 546 547 /* 548 * parse a sub-stream, eg. "spkr[7]" 549 */ 550 int 551 parse_node(char **line, char *str, int *unit) 552 { 553 char *p = *line; 554 555 if (!parse_name(&p, str)) 556 return 0; 557 if (*p != '[') { 558 *unit = -1; 559 *line = p; 560 return 1; 561 } 562 p++; 563 if (!parse_unit(&p, unit)) 564 return 0; 565 if (*p != ']') { 566 fprintf(stderr, "']' expected near '%s'\n", p); 567 return 0; 568 } 569 p++; 570 *line = p; 571 return 1; 572 } 573 574 /* 575 * parse a decimal prefixed by the optional mode 576 */ 577 int 578 parse_modeval(char **line, int *rmode, float *rval) 579 { 580 char *p = *line; 581 unsigned mode; 582 583 switch (*p) { 584 case '+': 585 mode = MODE_ADD; 586 p++; 587 break; 588 case '-': 589 mode = MODE_SUB; 590 p++; 591 break; 592 case '!': 593 mode = MODE_TOGGLE; 594 p++; 595 break; 596 default: 597 mode = MODE_SET; 598 } 599 if (mode != MODE_TOGGLE) { 600 if (!parse_val(&p, rval)) 601 return 0; 602 } 603 *line = p; 604 *rmode = mode; 605 return 1; 606 } 607 608 /* 609 * dump the whole controls list, useful for debugging 610 */ 611 void 612 dump(void) 613 { 614 struct info *i; 615 616 for (i = infolist; i != NULL; i = i->next) { 617 printf("%03u:", i->ctladdr); 618 print_node(&i->desc.node0, 0); 619 printf(".%s", i->desc.func); 620 printf("="); 621 switch (i->desc.type) { 622 case SIOCTL_NUM: 623 case SIOCTL_SW: 624 printf("0..%d (%u)", i->desc.maxval, i->curval); 625 break; 626 case SIOCTL_SEL: 627 print_node(&i->desc.node1, 0); 628 break; 629 case SIOCTL_VEC: 630 case SIOCTL_LIST: 631 print_node(&i->desc.node1, 0); 632 printf(":0..%d (%u)", i->desc.maxval, i->curval); 633 } 634 printf("\n"); 635 } 636 } 637 638 /* 639 * parse and execute a command ``<parameter>[=<value>]'' 640 */ 641 int 642 cmd(char *line) 643 { 644 char *pos, *group; 645 struct info *i, *e, *g; 646 char func[SIOCTL_NAMEMAX]; 647 char astr[SIOCTL_NAMEMAX], vstr[SIOCTL_NAMEMAX]; 648 int aunit, vunit; 649 unsigned npar = 0, nent = 0; 650 int comma, mode; 651 float val; 652 653 pos = strrchr(line, '/'); 654 if (pos != NULL) { 655 group = line; 656 pos[0] = 0; 657 pos++; 658 } else { 659 group = ""; 660 pos = line; 661 } 662 if (!parse_node(&pos, astr, &aunit)) 663 return 0; 664 if (*pos != '.') { 665 fprintf(stderr, "'.' expected near '%s'\n", pos); 666 return 0; 667 } 668 pos++; 669 if (!parse_name(&pos, func)) 670 return 0; 671 for (g = infolist;; g = g->next) { 672 if (g == NULL) { 673 fprintf(stderr, "%s.%s: no such control\n", astr, func); 674 return 0; 675 } 676 if (strcmp(g->desc.group, group) == 0 && 677 strcmp(g->desc.func, func) == 0 && 678 strcmp(g->desc.node0.name, astr) == 0) 679 break; 680 } 681 g->mode = MODE_PRINT; 682 if (*pos != '=') { 683 if (*pos != '\0') { 684 fprintf(stderr, "junk at end of command\n"); 685 return 0; 686 } 687 return 1; 688 } 689 pos++; 690 if (i_flag) { 691 printf("can't set values in info mode\n"); 692 return 0; 693 } 694 npar = 0; 695 switch (g->desc.type) { 696 case SIOCTL_NUM: 697 case SIOCTL_SW: 698 if (!parse_modeval(&pos, &mode, &val)) 699 return 0; 700 for (i = g; i != NULL; i = nextpar(i)) { 701 if (!matchpar(i, astr, aunit)) 702 continue; 703 i->mode = mode; 704 i->newval = ftoi(val * i->desc.maxval); 705 npar++; 706 } 707 break; 708 case SIOCTL_SEL: 709 case SIOCTL_VEC: 710 case SIOCTL_LIST: 711 for (i = g; i != NULL; i = nextpar(i)) { 712 if (!matchpar(i, astr, aunit)) 713 continue; 714 for (e = i; e != NULL; e = nextent(e, 0)) { 715 e->newval = 0; 716 e->mode = MODE_SET; 717 } 718 npar++; 719 } 720 comma = 0; 721 for (;;) { 722 if (*pos == '\0') 723 break; 724 if (comma) { 725 if (*pos != ',') 726 break; 727 pos++; 728 } 729 if (!parse_node(&pos, vstr, &vunit)) 730 return 0; 731 if (*pos == ':') { 732 pos++; 733 if (!parse_modeval(&pos, &mode, &val)) 734 return 0; 735 } else { 736 val = 1.; 737 mode = MODE_SET; 738 } 739 nent = 0; 740 for (i = g; i != NULL; i = nextpar(i)) { 741 if (!matchpar(i, astr, aunit)) 742 continue; 743 for (e = i; e != NULL; e = nextent(e, 0)) { 744 if (matchent(e, vstr, vunit)) { 745 e->newval = ftoi(val * e->desc.maxval); 746 e->mode = mode; 747 nent++; 748 } 749 } 750 } 751 if (nent == 0) { 752 /* XXX: use print_node()-like routine */ 753 fprintf(stderr, "%s[%d]: invalid value\n", vstr, vunit); 754 print_par(g, 0); 755 exit(1); 756 } 757 comma = 1; 758 } 759 } 760 if (npar == 0) { 761 fprintf(stderr, "%s: invalid parameter\n", line); 762 exit(1); 763 } 764 if (*pos != '\0') { 765 printf("%s: junk at end of command\n", pos); 766 exit(1); 767 } 768 return 1; 769 } 770 771 /* 772 * write the controls with the ``set'' flag on the device 773 */ 774 void 775 commit(void) 776 { 777 struct info *i; 778 int val; 779 780 for (i = infolist; i != NULL; i = i->next) { 781 val = 0xdeadbeef; 782 switch (i->mode) { 783 case MODE_IGNORE: 784 case MODE_PRINT: 785 continue; 786 case MODE_SET: 787 val = i->newval; 788 break; 789 case MODE_ADD: 790 val = i->curval + i->newval; 791 if (val > i->desc.maxval) 792 val = i->desc.maxval; 793 break; 794 case MODE_SUB: 795 val = i->curval - i->newval; 796 if (val < 0) 797 val = 0; 798 break; 799 case MODE_TOGGLE: 800 val = i->curval ? 0 : i->desc.maxval; 801 } 802 sioctl_setval(hdl, i->ctladdr, val); 803 i->curval = val; 804 } 805 } 806 807 /* 808 * print all parameters 809 */ 810 void 811 list(void) 812 { 813 struct info *p, *g; 814 815 for (g = infolist; g != NULL; g = nextfunc(g)) { 816 if (g->mode == MODE_IGNORE) 817 continue; 818 if (i_flag) { 819 if (v_flag) { 820 for (p = g; p != NULL; p = nextpar(p)) 821 print_par(p, 0); 822 } else 823 print_par(g, 1); 824 } else { 825 if (v_flag || !ismono(g)) { 826 for (p = g; p != NULL; p = nextpar(p)) 827 print_par(p, 0); 828 } else 829 print_par(g, 1); 830 } 831 } 832 } 833 834 /* 835 * register a new knob/button, called from the poll() loop. this may be 836 * called when label string changes, in which case we update the 837 * existing label widged rather than inserting a new one. 838 */ 839 void 840 ondesc(void *arg, struct sioctl_desc *d, int curval) 841 { 842 struct info *i, **pi; 843 int cmp; 844 845 if (d == NULL) 846 return; 847 848 /* 849 * delete control 850 */ 851 for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) { 852 if (d->addr == i->desc.addr) { 853 if (m_flag) 854 print_ent(i, "deleted"); 855 *pi = i->next; 856 free(i); 857 break; 858 } 859 } 860 861 switch (d->type) { 862 case SIOCTL_NUM: 863 case SIOCTL_SW: 864 case SIOCTL_VEC: 865 case SIOCTL_LIST: 866 case SIOCTL_SEL: 867 break; 868 default: 869 return; 870 } 871 872 /* 873 * find the right position to insert the new widget 874 */ 875 for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) { 876 cmp = cmpdesc(d, &i->desc); 877 if (cmp == 0) { 878 fprintf(stderr, "fatal: duplicate control:\n"); 879 print_ent(i, "duplicate"); 880 exit(1); 881 } 882 if (cmp < 0) 883 break; 884 } 885 i = malloc(sizeof(struct info)); 886 if (i == NULL) { 887 perror("malloc"); 888 exit(1); 889 } 890 i->desc = *d; 891 i->ctladdr = d->addr; 892 i->curval = i->newval = curval; 893 i->mode = MODE_IGNORE; 894 i->next = *pi; 895 *pi = i; 896 if (m_flag) 897 print_ent(i, "added"); 898 } 899 900 /* 901 * update a knob/button state, called from the poll() loop 902 */ 903 void 904 onctl(void *arg, unsigned addr, unsigned val) 905 { 906 struct info *i, *j; 907 908 i = infolist; 909 for (;;) { 910 if (i == NULL) 911 return; 912 if (i->ctladdr == addr) 913 break; 914 i = i->next; 915 } 916 917 if (i->curval == val) { 918 print_ent(i, "eq"); 919 return; 920 } 921 922 if (i->desc.type == SIOCTL_SEL) { 923 for (j = infolist; j != NULL; j = j->next) { 924 if (strcmp(i->desc.group, j->desc.group) != 0 || 925 strcmp(i->desc.node0.name, j->desc.node0.name) != 0 || 926 strcmp(i->desc.func, j->desc.func) != 0 || 927 i->desc.node0.unit != j->desc.node0.unit) 928 continue; 929 j->curval = (i->ctladdr == j->ctladdr); 930 } 931 } else 932 i->curval = val; 933 934 if (m_flag) 935 print_ent(i, "changed"); 936 } 937 938 int 939 main(int argc, char **argv) 940 { 941 char *devname = SIO_DEVANY; 942 int i, c, d_flag = 0; 943 struct info *g; 944 struct pollfd *pfds; 945 int nfds, revents; 946 947 while ((c = getopt(argc, argv, "df:imnqv")) != -1) { 948 switch (c) { 949 case 'd': 950 d_flag = 1; 951 break; 952 case 'f': 953 devname = optarg; 954 break; 955 case 'i': 956 i_flag = 1; 957 break; 958 case 'm': 959 m_flag = 1; 960 break; 961 case 'n': 962 n_flag = 1; 963 break; 964 case 'q': 965 q_flag = 1; 966 break; 967 case 'v': 968 v_flag++; 969 break; 970 default: 971 fprintf(stderr, "usage: sndioctl " 972 "[-dimnqv] [-f device] [command ...]\n"); 973 exit(1); 974 } 975 } 976 argc -= optind; 977 argv += optind; 978 979 hdl = sioctl_open(devname, SIOCTL_READ | SIOCTL_WRITE, 0); 980 if (hdl == NULL) { 981 fprintf(stderr, "%s: can't open control device\n", devname); 982 exit(1); 983 } 984 985 if (pledge("stdio audio", NULL) == -1) { 986 fprintf(stderr, "%s: pledge: %s\n", getprogname(), 987 strerror(errno)); 988 exit(1); 989 } 990 991 if (!sioctl_ondesc(hdl, ondesc, NULL)) { 992 fprintf(stderr, "%s: can't get device description\n", devname); 993 exit(1); 994 } 995 sioctl_onval(hdl, onctl, NULL); 996 997 if (d_flag) { 998 if (argc > 0) { 999 fprintf(stderr, 1000 "commands are not allowed with -d option\n"); 1001 exit(1); 1002 } 1003 dump(); 1004 } else { 1005 if (argc == 0) { 1006 for (g = infolist; g != NULL; g = nextfunc(g)) 1007 g->mode = MODE_PRINT; 1008 } else { 1009 for (i = 0; i < argc; i++) { 1010 if (!cmd(argv[i])) 1011 return 1; 1012 } 1013 } 1014 commit(); 1015 if (!q_flag) 1016 list(); 1017 } 1018 if (m_flag) { 1019 pfds = malloc(sizeof(struct pollfd) * sioctl_nfds(hdl)); 1020 if (pfds == NULL) { 1021 perror("malloc"); 1022 exit(1); 1023 } 1024 for (;;) { 1025 fflush(stdout); 1026 nfds = sioctl_pollfd(hdl, pfds, POLLIN); 1027 if (nfds == 0) 1028 break; 1029 while (poll(pfds, nfds, -1) < 0) { 1030 if (errno != EINTR) { 1031 perror("poll"); 1032 exit(1); 1033 } 1034 } 1035 revents = sioctl_revents(hdl, pfds); 1036 if (revents & POLLHUP) { 1037 fprintf(stderr, "disconnected\n"); 1038 break; 1039 } 1040 } 1041 free(pfds); 1042 } 1043 sioctl_close(hdl); 1044 return 0; 1045 } 1046