Annotation of src/usr.bin/sndioctl/sndioctl.c, Revision 1.8
1.8 ! ratchov 1: /* $OpenBSD: sndioctl.c,v 1.7 2020/05/17 05:32:01 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: }
435: if (more)
436: printf(",");
437: print_node(&e->desc.node1, mono);
1.7 ratchov 438: printf(":");
439: print_num(e);
1.1 ratchov 440: more = 1;
441: }
442: }
443: }
444:
445: /*
446: * print ``<parameter>=<value>'' string (including '\n')
447: */
448: void
1.8 ! ratchov 449: print_par(struct info *p, int mono)
1.1 ratchov 450: {
1.3 ratchov 451: if (!n_flag) {
452: if (p->desc.group[0] != 0) {
453: printf("%s", p->desc.group);
454: printf("/");
455: }
456: print_node(&p->desc.node0, mono);
457: printf(".%s=", p->desc.func);
1.1 ratchov 458: }
459: if (i_flag)
460: print_desc(p, mono);
461: else
462: print_val(p, mono);
463: printf("\n");
464: }
465:
466: /*
467: * parse a stream name or parameter name
468: */
469: int
470: parse_name(char **line, char *name)
471: {
472: char *p = *line;
473: unsigned len = 0;
474:
475: if (!isname_first(*p)) {
476: fprintf(stderr, "letter expected near '%s'\n", p);
477: return 0;
478: }
479: while (isname_next(*p)) {
480: if (len >= SIOCTL_NAMEMAX - 1) {
481: name[SIOCTL_NAMEMAX - 1] = '\0';
482: fprintf(stderr, "%s...: too long\n", name);
483: return 0;
484: }
485: name[len++] = *p;
486: p++;
487: }
488: name[len] = '\0';
489: *line = p;
490: return 1;
491: }
492:
493: /*
494: * parse a decimal integer
495: */
496: int
1.5 ratchov 497: parse_unit(char **line, int *num)
1.1 ratchov 498: {
499: char *p = *line;
500: unsigned int val;
501: int n;
502:
503: if (sscanf(p, "%u%n", &val, &n) != 1) {
504: fprintf(stderr, "number expected near '%s'\n", p);
505: return 0;
506: }
507: if (val >= 255) {
508: fprintf(stderr, "%d: too large\n", val);
509: return 0;
510: }
511: *num = val;
512: *line = p + n;
513: return 1;
514: }
515:
516: int
517: parse_val(char **line, float *num)
518: {
519: char *p = *line;
520: float val;
521: int n;
522:
523: if (sscanf(p, "%g%n", &val, &n) != 1) {
524: fprintf(stderr, "number expected near '%s'\n", p);
525: return 0;
526: }
527: if (val < 0 || val > 1) {
528: fprintf(stderr, "%g: expected number between 0 and 1\n", val);
529: return 0;
530: }
531: *num = val;
532: *line = p + n;
533: return 1;
534: }
535:
536: /*
537: * parse a sub-stream, eg. "spkr[7]"
538: */
539: int
540: parse_node(char **line, char *str, int *unit)
541: {
542: char *p = *line;
543:
544: if (!parse_name(&p, str))
545: return 0;
546: if (*p != '[') {
547: *unit = -1;
548: *line = p;
549: return 1;
550: }
551: p++;
552: if (!parse_unit(&p, unit))
553: return 0;
554: if (*p != ']') {
555: fprintf(stderr, "']' expected near '%s'\n", p);
556: return 0;
557: }
558: p++;
559: *line = p;
560: return 1;
561: }
562:
563: /*
564: * parse a decimal prefixed by the optional mode
565: */
566: int
567: parse_modeval(char **line, int *rmode, float *rval)
568: {
569: char *p = *line;
570: unsigned mode;
571:
572: switch (*p) {
573: case '+':
574: mode = MODE_ADD;
575: p++;
576: break;
577: case '-':
578: mode = MODE_SUB;
579: p++;
580: break;
581: case '!':
582: mode = MODE_TOGGLE;
583: p++;
584: break;
585: default:
586: mode = MODE_SET;
587: }
588: if (mode != MODE_TOGGLE) {
589: if (!parse_val(&p, rval))
590: return 0;
591: }
592: *line = p;
593: *rmode = mode;
594: return 1;
595: }
596:
597: /*
598: * dump the whole controls list, useful for debugging
599: */
600: void
601: dump(void)
602: {
603: struct info *i;
604:
605: for (i = infolist; i != NULL; i = i->next) {
606: printf("%03u:", i->ctladdr);
607: print_node(&i->desc.node0, 0);
608: printf(".%s", i->desc.func);
609: printf("=");
610: switch (i->desc.type) {
611: case SIOCTL_NUM:
612: case SIOCTL_SW:
613: printf("0..%d (%u)", i->desc.maxval, i->curval);
614: break;
615: case SIOCTL_VEC:
616: case SIOCTL_LIST:
617: print_node(&i->desc.node1, 0);
618: printf(":0..%d (%u)", i->desc.maxval, i->curval);
619: }
620: printf("\n");
621: }
622: }
623:
624: /*
625: * parse and execute a command ``<parameter>[=<value>]''
626: */
627: int
628: cmd(char *line)
629: {
630: char *pos, *group;
631: struct info *i, *e, *g;
632: char func[SIOCTL_NAMEMAX];
633: char astr[SIOCTL_NAMEMAX], vstr[SIOCTL_NAMEMAX];
634: int aunit, vunit;
635: unsigned npar = 0, nent = 0;
636: int comma, mode;
637: float val;
638:
639: pos = strrchr(line, '/');
640: if (pos != NULL) {
641: group = line;
642: pos[0] = 0;
643: pos++;
644: } else {
645: group = "";
646: pos = line;
647: }
648: if (!parse_node(&pos, astr, &aunit))
649: return 0;
650: if (*pos != '.') {
651: fprintf(stderr, "'.' expected near '%s'\n", pos);
652: return 0;
653: }
654: pos++;
655: if (!parse_name(&pos, func))
656: return 0;
657: for (g = infolist;; g = g->next) {
658: if (g == NULL) {
659: fprintf(stderr, "%s.%s: no such control\n", astr, func);
660: return 0;
661: }
662: if (strcmp(g->desc.group, group) == 0 &&
663: strcmp(g->desc.func, func) == 0 &&
664: strcmp(g->desc.node0.name, astr) == 0)
665: break;
666: }
667: g->mode = MODE_PRINT;
668: if (*pos != '=') {
669: if (*pos != '\0') {
670: fprintf(stderr, "junk at end of command\n");
671: return 0;
672: }
673: return 1;
674: }
675: pos++;
676: if (i_flag) {
677: printf("can't set values in info mode\n");
678: return 0;
679: }
680: npar = 0;
681: switch (g->desc.type) {
682: case SIOCTL_NUM:
683: case SIOCTL_SW:
684: if (!parse_modeval(&pos, &mode, &val))
685: return 0;
686: for (i = g; i != NULL; i = nextpar(i)) {
687: if (!matchpar(i, astr, aunit))
688: continue;
689: i->mode = mode;
690: i->newval = ftoi(val * i->desc.maxval);
691: npar++;
692: }
693: break;
694: case SIOCTL_VEC:
695: case SIOCTL_LIST:
696: for (i = g; i != NULL; i = nextpar(i)) {
697: if (!matchpar(i, astr, aunit))
698: continue;
699: for (e = i; e != NULL; e = nextent(e, 0)) {
700: e->newval = 0;
701: e->mode = MODE_SET;
702: }
703: npar++;
704: }
705: comma = 0;
706: for (;;) {
707: if (*pos == '\0')
708: break;
709: if (comma) {
710: if (*pos != ',')
711: break;
712: pos++;
713: }
714: if (!parse_node(&pos, vstr, &vunit))
715: return 0;
716: if (*pos == ':') {
717: pos++;
718: if (!parse_modeval(&pos, &mode, &val))
719: return 0;
720: } else {
721: val = 1.;
722: mode = MODE_SET;
723: }
724: nent = 0;
725: for (i = g; i != NULL; i = nextpar(i)) {
726: if (!matchpar(i, astr, aunit))
727: continue;
728: for (e = i; e != NULL; e = nextent(e, 0)) {
729: if (matchent(e, vstr, vunit)) {
730: e->newval = ftoi(val * e->desc.maxval);
731: e->mode = mode;
732: nent++;
733: }
734: }
735: }
736: if (nent == 0) {
737: /* XXX: use print_node()-like routine */
738: fprintf(stderr, "%s[%d]: invalid value\n", vstr, vunit);
1.8 ! ratchov 739: print_par(g, 0);
1.1 ratchov 740: exit(1);
741: }
742: comma = 1;
743: }
744: }
745: if (npar == 0) {
746: fprintf(stderr, "%s: invalid parameter\n", line);
747: exit(1);
748: }
749: if (*pos != '\0') {
750: printf("%s: junk at end of command\n", pos);
751: exit(1);
752: }
753: return 1;
754: }
755:
756: /*
757: * write the controls with the ``set'' flag on the device
758: */
759: void
760: commit(void)
761: {
762: struct info *i;
763: int val;
764:
765: for (i = infolist; i != NULL; i = i->next) {
766: val = 0xdeadbeef;
767: switch (i->mode) {
768: case MODE_IGNORE:
769: case MODE_PRINT:
770: continue;
771: case MODE_SET:
772: val = i->newval;
773: break;
774: case MODE_ADD:
775: val = i->curval + i->newval;
776: if (val > i->desc.maxval)
777: val = i->desc.maxval;
778: break;
779: case MODE_SUB:
780: val = i->curval - i->newval;
781: if (val < 0)
782: val = 0;
783: break;
784: case MODE_TOGGLE:
785: val = i->curval ? 0 : i->desc.maxval;
786: }
787: sioctl_setval(hdl, i->ctladdr, val);
788: i->curval = val;
789: }
790: }
791:
792: /*
793: * print all parameters
794: */
795: void
796: list(void)
797: {
798: struct info *p, *g;
799:
800: for (g = infolist; g != NULL; g = nextfunc(g)) {
801: if (g->mode == MODE_IGNORE)
802: continue;
803: if (i_flag) {
804: if (v_flag) {
805: for (p = g; p != NULL; p = nextpar(p))
1.8 ! ratchov 806: print_par(p, 0);
1.1 ratchov 807: } else
1.8 ! ratchov 808: print_par(g, 1);
1.1 ratchov 809: } else {
810: if (v_flag || !ismono(g)) {
811: for (p = g; p != NULL; p = nextpar(p))
1.8 ! ratchov 812: print_par(p, 0);
1.1 ratchov 813: } else
1.8 ! ratchov 814: print_par(g, 1);
1.1 ratchov 815: }
816: }
817: }
818:
819: /*
820: * register a new knob/button, called from the poll() loop. this may be
821: * called when label string changes, in which case we update the
822: * existing label widged rather than inserting a new one.
823: */
824: void
825: ondesc(void *arg, struct sioctl_desc *d, int curval)
826: {
827: struct info *i, **pi;
828: int cmp;
829:
830: if (d == NULL)
831: return;
832:
833: /*
834: * delete control
835: */
836: for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
837: if (d->addr == i->desc.addr) {
838: if (m_flag)
1.8 ! ratchov 839: print_ent(i, "deleted");
1.1 ratchov 840: *pi = i->next;
841: free(i);
842: break;
843: }
844: }
845:
846: if (d->type == SIOCTL_NONE)
847: return;
848:
849: /*
850: * find the right position to insert the new widget
851: */
852: for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
853: cmp = cmpdesc(d, &i->desc);
854: if (cmp == 0) {
855: fprintf(stderr, "fatal: duplicate control:\n");
1.8 ! ratchov 856: print_ent(i, "duplicate");
1.1 ratchov 857: exit(1);
858: }
859: if (cmp < 0)
860: break;
861: }
862: i = malloc(sizeof(struct info));
863: if (i == NULL) {
864: perror("malloc");
865: exit(1);
866: }
867: i->desc = *d;
868: i->ctladdr = d->addr;
869: i->curval = i->newval = curval;
870: i->mode = MODE_IGNORE;
871: i->next = *pi;
872: *pi = i;
873: if (m_flag)
1.8 ! ratchov 874: print_ent(i, "added");
1.1 ratchov 875: }
876:
877: /*
878: * update a knob/button state, called from the poll() loop
879: */
880: void
881: onctl(void *arg, unsigned addr, unsigned val)
882: {
883: struct info *i;
884:
885: for (i = infolist; i != NULL; i = i->next) {
886: if (i->ctladdr != addr)
887: continue;
888: i->curval = val;
889: if (m_flag)
1.8 ! ratchov 890: print_ent(i, "changed");
1.1 ratchov 891: }
892: }
893:
894: int
895: main(int argc, char **argv)
896: {
897: char *devname = SIO_DEVANY;
898: int i, c, d_flag = 0;
899: struct info *g;
900: struct pollfd *pfds;
901: int nfds, revents;
902:
1.4 ratchov 903: while ((c = getopt(argc, argv, "df:imnqv")) != -1) {
1.1 ratchov 904: switch (c) {
905: case 'd':
906: d_flag = 1;
907: break;
908: case 'f':
909: devname = optarg;
910: break;
911: case 'i':
912: i_flag = 1;
913: break;
914: case 'm':
915: m_flag = 1;
916: break;
1.3 ratchov 917: case 'n':
918: n_flag = 1;
919: break;
1.4 ratchov 920: case 'q':
921: q_flag = 1;
922: break;
1.1 ratchov 923: case 'v':
924: v_flag++;
925: break;
926: default:
927: fprintf(stderr, "usage: sndioctl "
1.4 ratchov 928: "[-dimnqv] [-f device] [command ...]\n");
1.1 ratchov 929: exit(1);
930: }
931: }
932: argc -= optind;
933: argv += optind;
934:
935: hdl = sioctl_open(devname, SIOCTL_READ | SIOCTL_WRITE, 0);
936: if (hdl == NULL) {
937: fprintf(stderr, "%s: can't open control device\n", devname);
938: exit(1);
939: }
940: if (!sioctl_ondesc(hdl, ondesc, NULL)) {
941: fprintf(stderr, "%s: can't get device description\n", devname);
942: exit(1);
943: }
944: sioctl_onval(hdl, onctl, NULL);
945:
946: if (d_flag) {
947: if (argc > 0) {
948: fprintf(stderr,
949: "commands are not allowed with -d option\n");
950: exit(1);
951: }
952: dump();
953: } else {
954: if (argc == 0) {
955: for (g = infolist; g != NULL; g = nextfunc(g))
956: g->mode = MODE_PRINT;
957: } else {
958: for (i = 0; i < argc; i++) {
959: if (!cmd(argv[i]))
960: return 1;
961: }
962: }
963: commit();
1.4 ratchov 964: if (!q_flag)
965: list();
1.1 ratchov 966: }
967: if (m_flag) {
968: pfds = malloc(sizeof(struct pollfd) * sioctl_nfds(hdl));
969: if (pfds == NULL) {
970: perror("malloc");
971: exit(1);
972: }
973: for (;;) {
974: nfds = sioctl_pollfd(hdl, pfds, POLLIN);
975: if (nfds == 0)
976: break;
977: while (poll(pfds, nfds, -1) < 0) {
978: if (errno != EINTR) {
979: perror("poll");
980: exit(1);
981: }
982: }
983: revents = sioctl_revents(hdl, pfds);
984: if (revents & POLLHUP) {
985: fprintf(stderr, "disconnected\n");
986: break;
987: }
988: }
989: free(pfds);
990: }
991: sioctl_close(hdl);
992: return 0;
993: }