Annotation of src/usr.bin/sndioctl/sndioctl.c, Revision 1.14
1.14 ! ratchov 1: /* $OpenBSD: sndioctl.c,v 1.13 2020/06/18 05:33:16 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.1 ratchov 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);
1.8 ratchov 754: print_par(g, 0);
1.1 ratchov 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))
1.8 ratchov 821: print_par(p, 0);
1.1 ratchov 822: } else
1.8 ratchov 823: print_par(g, 1);
1.1 ratchov 824: } else {
825: if (v_flag || !ismono(g)) {
826: for (p = g; p != NULL; p = nextpar(p))
1.8 ratchov 827: print_par(p, 0);
1.1 ratchov 828: } else
1.8 ratchov 829: print_par(g, 1);
1.1 ratchov 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)
1.8 ratchov 854: print_ent(i, "deleted");
1.1 ratchov 855: *pi = i->next;
856: free(i);
857: break;
858: }
859: }
860:
1.13 ratchov 861: switch (d->type) {
862: case SIOCTL_NUM:
863: case SIOCTL_SW:
864: case SIOCTL_VEC:
865: case SIOCTL_LIST:
1.14 ! ratchov 866: case SIOCTL_SEL:
1.13 ratchov 867: break;
868: default:
1.1 ratchov 869: return;
1.13 ratchov 870: }
1.1 ratchov 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");
1.8 ratchov 879: print_ent(i, "duplicate");
1.1 ratchov 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)
1.8 ratchov 897: print_ent(i, "added");
1.1 ratchov 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: {
1.14 ! ratchov 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: }
1.1 ratchov 921:
1.14 ! ratchov 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);
1.9 ratchov 930: }
1.14 ! ratchov 931: } else
! 932: i->curval = val;
! 933:
! 934: if (m_flag)
! 935: print_ent(i, "changed");
1.1 ratchov 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:
1.4 ratchov 947: while ((c = getopt(argc, argv, "df:imnqv")) != -1) {
1.1 ratchov 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;
1.3 ratchov 961: case 'n':
962: n_flag = 1;
963: break;
1.4 ratchov 964: case 'q':
965: q_flag = 1;
966: break;
1.1 ratchov 967: case 'v':
968: v_flag++;
969: break;
970: default:
971: fprintf(stderr, "usage: sndioctl "
1.4 ratchov 972: "[-dimnqv] [-f device] [command ...]\n");
1.1 ratchov 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: }
1.11 mestre 984:
985: if (pledge("stdio audio", NULL) == -1) {
986: fprintf(stderr, "%s: pledge: %s\n", getprogname(),
987: strerror(errno));
988: exit(1);
989: }
990:
1.1 ratchov 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();
1.4 ratchov 1015: if (!q_flag)
1016: list();
1.1 ratchov 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: nfds = sioctl_pollfd(hdl, pfds, POLLIN);
1026: if (nfds == 0)
1027: break;
1028: while (poll(pfds, nfds, -1) < 0) {
1029: if (errno != EINTR) {
1030: perror("poll");
1031: exit(1);
1032: }
1033: }
1034: revents = sioctl_revents(hdl, pfds);
1035: if (revents & POLLHUP) {
1036: fprintf(stderr, "disconnected\n");
1037: break;
1038: }
1039: }
1040: free(pfds);
1041: }
1042: sioctl_close(hdl);
1043: return 0;
1044: }