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