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