Annotation of src/usr.bin/sndioctl/sndioctl.c, Revision 1.17
1.17 ! ratchov 1: /* $OpenBSD: sndioctl.c,v 1.16 2021/03/03 09:40:43 ratchov Exp $ */
1.1 ratchov 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);
1.7 ratchov 51: void print_num(struct info *);
1.8 ratchov 52: void print_ent(struct info *, char *);
1.1 ratchov 53: void print_val(struct info *, int);
1.8 ratchov 54: void print_par(struct info *, int);
1.1 ratchov 55: int parse_name(char **, char *);
1.5 ratchov 56: int parse_unit(char **, int *);
1.1 ratchov 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;
1.4 ratchov 69: int i_flag = 0, v_flag = 0, m_flag = 0, n_flag = 0, q_flag = 0;
1.1 ratchov 70:
71: static inline int
1.12 ratchov 72: isname(int c)
1.1 ratchov 73: {
1.12 ratchov 74: return (c == '_') ||
75: (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
76: (c >= '0' && c <= '9');
1.1 ratchov 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;
1.14 ratchov 106: if (d1->type == SIOCTL_SEL ||
107: d1->type == SIOCTL_VEC ||
1.1 ratchov 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;
1.14 ratchov 297: case SIOCTL_SEL:
1.1 ratchov 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;
1.14 ratchov 346: case SIOCTL_SEL:
1.1 ratchov 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);
1.14 ratchov 360: if (p->desc.type != SIOCTL_SEL)
361: printf(":*");
1.1 ratchov 362: more = 1;
363: }
364: }
365: }
366:
1.7 ratchov 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:
1.1 ratchov 381: /*
1.8 ratchov 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;
1.14 ratchov 397: case SIOCTL_SEL:
1.8 ratchov 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: /*
1.1 ratchov 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:
1.7 ratchov 424: print_num(p);
1.1 ratchov 425: break;
1.14 ratchov 426: case SIOCTL_SEL:
1.1 ratchov 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: }
1.10 ratchov 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: }
1.1 ratchov 452: }
453: }
454: }
455:
456: /*
457: * print ``<parameter>=<value>'' string (including '\n')
458: */
459: void
1.8 ratchov 460: print_par(struct info *p, int mono)
1.1 ratchov 461: {
1.3 ratchov 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);
1.1 ratchov 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:
1.12 ratchov 486: if (!isname(*p)) {
487: fprintf(stderr, "letter or digit expected near '%s'\n", p);
1.1 ratchov 488: return 0;
489: }
1.12 ratchov 490: while (isname(*p)) {
1.1 ratchov 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
1.5 ratchov 508: parse_unit(char **line, int *num)
1.1 ratchov 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;
1.14 ratchov 626: case SIOCTL_SEL:
627: print_node(&i->desc.node1, 0);
628: break;
1.1 ratchov 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;
1.14 ratchov 708: case SIOCTL_SEL:
1.17 ! ratchov 709: if (*pos == '\0') {
! 710: fprintf(stderr, "%s.%s: expects value\n", astr, func);
! 711: exit(1);
! 712: }
! 713: /* FALLTROUGH */
1.1 ratchov 714: case SIOCTL_VEC:
715: case SIOCTL_LIST:
716: for (i = g; i != NULL; i = nextpar(i)) {
717: if (!matchpar(i, astr, aunit))
718: continue;
719: for (e = i; e != NULL; e = nextent(e, 0)) {
720: e->newval = 0;
721: e->mode = MODE_SET;
722: }
723: npar++;
724: }
725: comma = 0;
726: for (;;) {
727: if (*pos == '\0')
728: break;
729: if (comma) {
730: if (*pos != ',')
731: break;
732: pos++;
733: }
734: if (!parse_node(&pos, vstr, &vunit))
735: return 0;
736: if (*pos == ':') {
737: pos++;
738: if (!parse_modeval(&pos, &mode, &val))
739: return 0;
740: } else {
741: val = 1.;
742: mode = MODE_SET;
743: }
744: nent = 0;
745: for (i = g; i != NULL; i = nextpar(i)) {
746: if (!matchpar(i, astr, aunit))
747: continue;
748: for (e = i; e != NULL; e = nextent(e, 0)) {
749: if (matchent(e, vstr, vunit)) {
750: e->newval = ftoi(val * e->desc.maxval);
751: e->mode = mode;
752: nent++;
753: }
754: }
755: }
756: if (nent == 0) {
757: /* XXX: use print_node()-like routine */
758: fprintf(stderr, "%s[%d]: invalid value\n", vstr, vunit);
1.8 ratchov 759: print_par(g, 0);
1.1 ratchov 760: exit(1);
761: }
762: comma = 1;
763: }
764: }
765: if (npar == 0) {
766: fprintf(stderr, "%s: invalid parameter\n", line);
767: exit(1);
768: }
769: if (*pos != '\0') {
770: printf("%s: junk at end of command\n", pos);
771: exit(1);
772: }
773: return 1;
774: }
775:
776: /*
777: * write the controls with the ``set'' flag on the device
778: */
779: void
780: commit(void)
781: {
782: struct info *i;
783: int val;
784:
785: for (i = infolist; i != NULL; i = i->next) {
786: val = 0xdeadbeef;
787: switch (i->mode) {
788: case MODE_IGNORE:
789: case MODE_PRINT:
790: continue;
791: case MODE_SET:
792: val = i->newval;
793: break;
794: case MODE_ADD:
795: val = i->curval + i->newval;
796: if (val > i->desc.maxval)
797: val = i->desc.maxval;
798: break;
799: case MODE_SUB:
800: val = i->curval - i->newval;
801: if (val < 0)
802: val = 0;
803: break;
804: case MODE_TOGGLE:
805: val = i->curval ? 0 : i->desc.maxval;
806: }
807: sioctl_setval(hdl, i->ctladdr, val);
808: i->curval = val;
809: }
810: }
811:
812: /*
813: * print all parameters
814: */
815: void
816: list(void)
817: {
818: struct info *p, *g;
819:
820: for (g = infolist; g != NULL; g = nextfunc(g)) {
821: if (g->mode == MODE_IGNORE)
822: continue;
823: if (i_flag) {
824: if (v_flag) {
825: for (p = g; p != NULL; p = nextpar(p))
1.8 ratchov 826: print_par(p, 0);
1.1 ratchov 827: } else
1.8 ratchov 828: print_par(g, 1);
1.1 ratchov 829: } else {
830: if (v_flag || !ismono(g)) {
831: for (p = g; p != NULL; p = nextpar(p))
1.8 ratchov 832: print_par(p, 0);
1.1 ratchov 833: } else
1.8 ratchov 834: print_par(g, 1);
1.1 ratchov 835: }
836: }
837: }
838:
839: /*
840: * register a new knob/button, called from the poll() loop. this may be
841: * called when label string changes, in which case we update the
842: * existing label widged rather than inserting a new one.
843: */
844: void
845: ondesc(void *arg, struct sioctl_desc *d, int curval)
846: {
847: struct info *i, **pi;
848: int cmp;
849:
850: if (d == NULL)
851: return;
852:
853: /*
854: * delete control
855: */
856: for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
857: if (d->addr == i->desc.addr) {
858: if (m_flag)
1.8 ratchov 859: print_ent(i, "deleted");
1.1 ratchov 860: *pi = i->next;
861: free(i);
862: break;
863: }
864: }
865:
1.13 ratchov 866: switch (d->type) {
867: case SIOCTL_NUM:
868: case SIOCTL_SW:
869: case SIOCTL_VEC:
870: case SIOCTL_LIST:
1.14 ratchov 871: case SIOCTL_SEL:
1.13 ratchov 872: break;
873: default:
1.1 ratchov 874: return;
1.13 ratchov 875: }
1.1 ratchov 876:
877: /*
878: * find the right position to insert the new widget
879: */
880: for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
881: cmp = cmpdesc(d, &i->desc);
882: if (cmp == 0) {
883: fprintf(stderr, "fatal: duplicate control:\n");
1.8 ratchov 884: print_ent(i, "duplicate");
1.1 ratchov 885: exit(1);
886: }
887: if (cmp < 0)
888: break;
889: }
890: i = malloc(sizeof(struct info));
891: if (i == NULL) {
892: perror("malloc");
893: exit(1);
894: }
895: i->desc = *d;
896: i->ctladdr = d->addr;
897: i->curval = i->newval = curval;
898: i->mode = MODE_IGNORE;
899: i->next = *pi;
900: *pi = i;
901: if (m_flag)
1.8 ratchov 902: print_ent(i, "added");
1.1 ratchov 903: }
904:
905: /*
906: * update a knob/button state, called from the poll() loop
907: */
908: void
909: onctl(void *arg, unsigned addr, unsigned val)
910: {
1.14 ratchov 911: struct info *i, *j;
912:
913: i = infolist;
914: for (;;) {
915: if (i == NULL)
916: return;
917: if (i->ctladdr == addr)
918: break;
919: i = i->next;
920: }
921:
922: if (i->curval == val) {
923: print_ent(i, "eq");
924: return;
925: }
1.1 ratchov 926:
1.14 ratchov 927: if (i->desc.type == SIOCTL_SEL) {
928: for (j = infolist; j != NULL; j = j->next) {
929: if (strcmp(i->desc.group, j->desc.group) != 0 ||
930: strcmp(i->desc.node0.name, j->desc.node0.name) != 0 ||
931: strcmp(i->desc.func, j->desc.func) != 0 ||
932: i->desc.node0.unit != j->desc.node0.unit)
933: continue;
934: j->curval = (i->ctladdr == j->ctladdr);
1.9 ratchov 935: }
1.14 ratchov 936: } else
937: i->curval = val;
938:
939: if (m_flag)
940: print_ent(i, "changed");
1.1 ratchov 941: }
942:
943: int
944: main(int argc, char **argv)
945: {
946: char *devname = SIO_DEVANY;
947: int i, c, d_flag = 0;
948: struct info *g;
949: struct pollfd *pfds;
950: int nfds, revents;
951:
1.4 ratchov 952: while ((c = getopt(argc, argv, "df:imnqv")) != -1) {
1.1 ratchov 953: switch (c) {
954: case 'd':
955: d_flag = 1;
956: break;
957: case 'f':
958: devname = optarg;
959: break;
960: case 'i':
961: i_flag = 1;
962: break;
963: case 'm':
964: m_flag = 1;
965: break;
1.3 ratchov 966: case 'n':
967: n_flag = 1;
968: break;
1.4 ratchov 969: case 'q':
970: q_flag = 1;
971: break;
1.1 ratchov 972: case 'v':
973: v_flag++;
974: break;
975: default:
976: fprintf(stderr, "usage: sndioctl "
1.4 ratchov 977: "[-dimnqv] [-f device] [command ...]\n");
1.1 ratchov 978: exit(1);
979: }
980: }
981: argc -= optind;
982: argv += optind;
983:
984: hdl = sioctl_open(devname, SIOCTL_READ | SIOCTL_WRITE, 0);
985: if (hdl == NULL) {
986: fprintf(stderr, "%s: can't open control device\n", devname);
987: exit(1);
988: }
1.11 mestre 989:
990: if (pledge("stdio audio", NULL) == -1) {
991: fprintf(stderr, "%s: pledge: %s\n", getprogname(),
992: strerror(errno));
993: exit(1);
994: }
995:
1.1 ratchov 996: if (!sioctl_ondesc(hdl, ondesc, NULL)) {
997: fprintf(stderr, "%s: can't get device description\n", devname);
998: exit(1);
999: }
1000: sioctl_onval(hdl, onctl, NULL);
1001:
1002: if (d_flag) {
1003: if (argc > 0) {
1004: fprintf(stderr,
1005: "commands are not allowed with -d option\n");
1006: exit(1);
1007: }
1008: dump();
1009: } else {
1010: if (argc == 0) {
1011: for (g = infolist; g != NULL; g = nextfunc(g))
1012: g->mode = MODE_PRINT;
1013: } else {
1014: for (i = 0; i < argc; i++) {
1015: if (!cmd(argv[i]))
1016: return 1;
1017: }
1018: }
1019: commit();
1.4 ratchov 1020: if (!q_flag)
1021: list();
1.1 ratchov 1022: }
1023: if (m_flag) {
1024: pfds = malloc(sizeof(struct pollfd) * sioctl_nfds(hdl));
1025: if (pfds == NULL) {
1026: perror("malloc");
1027: exit(1);
1028: }
1029: for (;;) {
1.16 ratchov 1030: fflush(stdout);
1.1 ratchov 1031: nfds = sioctl_pollfd(hdl, pfds, POLLIN);
1032: if (nfds == 0)
1033: break;
1034: while (poll(pfds, nfds, -1) < 0) {
1035: if (errno != EINTR) {
1036: perror("poll");
1037: exit(1);
1038: }
1039: }
1040: revents = sioctl_revents(hdl, pfds);
1041: if (revents & POLLHUP) {
1042: fprintf(stderr, "disconnected\n");
1043: break;
1044: }
1045: }
1046: free(pfds);
1047: }
1048: sioctl_close(hdl);
1049: return 0;
1050: }