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