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