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
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_display(struct info *);
51 void print_desc(struct info *, int);
52 void print_num(struct info *);
53 void print_ent(struct info *, char *);
54 void print_val(struct info *, int);
55 void print_par(struct info *, int);
56 int parse_name(char **, char *);
57 int parse_unit(char **, int *);
58 int parse_val(char **, float *);
59 int parse_node(char **, char *, int *);
60 int parse_modeval(char **, int *, float *);
61 void dump(void);
62 int cmd(char *);
63 void commit(void);
64 void list(void);
65 void ondesc(void *, struct sioctl_desc *, int);
66 void onctl(void *, unsigned, unsigned);
67
68 struct sioctl_hdl *hdl;
69 struct info *infolist;
70 int i_flag = 0, v_flag = 0, m_flag = 0, n_flag = 0, q_flag = 0;
71
72 static inline int
isname(int c)73 isname(int c)
74 {
75 return (c == '_') ||
76 (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
77 (c >= '0' && c <= '9');
78 }
79
80 static int
ftoi(float f)81 ftoi(float f)
82 {
83 return f + 0.5;
84 }
85
86 /*
87 * compare two sioctl_desc structures, used to sort infolist
88 */
89 int
cmpdesc(struct sioctl_desc * d1,struct sioctl_desc * d2)90 cmpdesc(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 */
123 int
isdiag(struct info * e)124 isdiag(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 */
134 struct info *
vecent(struct info * i,char * vstr,int vunit)135 vecent(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 */
149 struct info *
nextfunc(struct info * i)150 nextfunc(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 */
169 struct info *
nextpar(struct info * i)170 nextpar(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 */
194 struct info *
firstent(struct info * g,char * vstr)195 firstent(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 */
221 struct info *
nextent(struct info * i,int mono)222 nextent(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 */
247 int
matchpar(struct info * i,char * astr,int aunit)248 matchpar(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 */
265 int
matchent(struct info * i,char * vstr,int vunit)266 matchent(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 */
283 int
ismono(struct info * g)284 ismono(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 */
328 void
print_node(struct sioctl_node * c,int mono)329 print_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 */
340 void
print_display(struct info * p)341 print_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 */
361 void
print_desc(struct info * p,int mono)362 print_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
396 void
print_num(struct info * p)397 print_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 */
413 void
print_ent(struct info * e,char * comment)414 print_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 */
445 void
print_val(struct info * p,int mono)446 print_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 */
492 void
print_par(struct info * p,int mono)493 print_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 */
513 int
parse_name(char ** line,char * name)514 parse_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 */
540 int
parse_unit(char ** line,int * num)541 parse_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
560 int
parse_val(char ** line,float * num)561 parse_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 */
583 int
parse_node(char ** line,char * str,int * unit)584 parse_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 */
610 int
parse_modeval(char ** line,int * rmode,float * rval)611 parse_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 */
644 void
dump(void)645 dump(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 */
675 int
cmd(char * line)676 cmd(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 */
819 void
commit(void)820 commit(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 */
855 void
list(void)856 list(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 */
884 void
ondesc(void * arg,struct sioctl_desc * d,int curval)885 ondesc(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 */
943 void
onctl(void * arg,unsigned addr,unsigned val)944 onctl(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
978 int
main(int argc,char ** argv)979 main(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