xref: /openbsd-src/usr.bin/sndioctl/sndioctl.c (revision 4aaef610e0a6825076a40a5cd314cfc98aa2ffb1)
1*4aaef610Sratchov /*	$OpenBSD: sndioctl.c,v 1.21 2024/05/24 15:10:27 ratchov Exp $	*/
282fd4e08Sratchov /*
382fd4e08Sratchov  * Copyright (c) 2014-2020 Alexandre Ratchov <alex@caoua.org>
482fd4e08Sratchov  *
582fd4e08Sratchov  * Permission to use, copy, modify, and distribute this software for any
682fd4e08Sratchov  * purpose with or without fee is hereby granted, provided that the above
782fd4e08Sratchov  * copyright notice and this permission notice appear in all copies.
882fd4e08Sratchov  *
982fd4e08Sratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1082fd4e08Sratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1182fd4e08Sratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1282fd4e08Sratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1382fd4e08Sratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1482fd4e08Sratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1582fd4e08Sratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1682fd4e08Sratchov  */
1782fd4e08Sratchov #include <errno.h>
1882fd4e08Sratchov #include <poll.h>
1982fd4e08Sratchov #include <sndio.h>
2082fd4e08Sratchov #include <stdlib.h>
2182fd4e08Sratchov #include <stdio.h>
2282fd4e08Sratchov #include <string.h>
2382fd4e08Sratchov #include <unistd.h>
2482fd4e08Sratchov 
2582fd4e08Sratchov struct info {
2682fd4e08Sratchov 	struct info *next;
2782fd4e08Sratchov 	struct sioctl_desc desc;
2882fd4e08Sratchov 	unsigned ctladdr;
2982fd4e08Sratchov #define MODE_IGNORE	0	/* ignore this value */
3082fd4e08Sratchov #define MODE_PRINT	1	/* print-only, don't change value */
3182fd4e08Sratchov #define MODE_SET	2	/* set to newval value */
3282fd4e08Sratchov #define MODE_ADD	3	/* increase current value by newval */
3382fd4e08Sratchov #define MODE_SUB	4	/* decrease current value by newval */
3482fd4e08Sratchov #define MODE_TOGGLE	5	/* toggle current value */
3582fd4e08Sratchov 	unsigned mode;
3682fd4e08Sratchov 	int curval, newval;
3782fd4e08Sratchov };
3882fd4e08Sratchov 
3982fd4e08Sratchov int cmpdesc(struct sioctl_desc *, struct sioctl_desc *);
4082fd4e08Sratchov int isdiag(struct info *);
4182fd4e08Sratchov struct info *vecent(struct info *, char *, int);
4282fd4e08Sratchov struct info *nextfunc(struct info *);
4382fd4e08Sratchov struct info *nextpar(struct info *);
4482fd4e08Sratchov struct info *firstent(struct info *, char *);
4582fd4e08Sratchov struct info *nextent(struct info *, int);
4682fd4e08Sratchov int matchpar(struct info *, char *, int);
4782fd4e08Sratchov int matchent(struct info *, char *, int);
4882fd4e08Sratchov int ismono(struct info *);
4982fd4e08Sratchov void print_node(struct sioctl_node *, int);
50*4aaef610Sratchov void print_display(struct info *);
5182fd4e08Sratchov void print_desc(struct info *, int);
52070d2896Sratchov void print_num(struct info *);
53f707c874Sratchov void print_ent(struct info *, char *);
5482fd4e08Sratchov void print_val(struct info *, int);
55f707c874Sratchov void print_par(struct info *, int);
5682fd4e08Sratchov int parse_name(char **, char *);
576c473e8bSratchov int parse_unit(char **, int *);
5882fd4e08Sratchov int parse_val(char **, float *);
5982fd4e08Sratchov int parse_node(char **, char *, int *);
6082fd4e08Sratchov int parse_modeval(char **, int *, float *);
6182fd4e08Sratchov void dump(void);
6282fd4e08Sratchov int cmd(char *);
6382fd4e08Sratchov void commit(void);
6482fd4e08Sratchov void list(void);
6582fd4e08Sratchov void ondesc(void *, struct sioctl_desc *, int);
6682fd4e08Sratchov void onctl(void *, unsigned, unsigned);
6782fd4e08Sratchov 
6882fd4e08Sratchov struct sioctl_hdl *hdl;
6982fd4e08Sratchov struct info *infolist;
703b691801Sratchov int i_flag = 0, v_flag = 0, m_flag = 0, n_flag = 0, q_flag = 0;
7182fd4e08Sratchov 
7282fd4e08Sratchov static inline int
isname(int c)732d7b56acSratchov isname(int c)
7482fd4e08Sratchov {
752d7b56acSratchov 	return (c == '_') ||
762d7b56acSratchov 	    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
772d7b56acSratchov 	    (c >= '0' && c <= '9');
7882fd4e08Sratchov }
7982fd4e08Sratchov 
8082fd4e08Sratchov static int
ftoi(float f)8182fd4e08Sratchov ftoi(float f)
8282fd4e08Sratchov {
8382fd4e08Sratchov 	return f + 0.5;
8482fd4e08Sratchov }
8582fd4e08Sratchov 
8682fd4e08Sratchov /*
8782fd4e08Sratchov  * compare two sioctl_desc structures, used to sort infolist
8882fd4e08Sratchov  */
8982fd4e08Sratchov int
cmpdesc(struct sioctl_desc * d1,struct sioctl_desc * d2)9082fd4e08Sratchov cmpdesc(struct sioctl_desc *d1, struct sioctl_desc *d2)
9182fd4e08Sratchov {
9282fd4e08Sratchov 	int res;
9382fd4e08Sratchov 
9482fd4e08Sratchov 	res = strcmp(d1->group, d2->group);
9582fd4e08Sratchov 	if (res != 0)
9682fd4e08Sratchov 		return res;
9782fd4e08Sratchov 	res = strcmp(d1->node0.name, d2->node0.name);
9882fd4e08Sratchov 	if (res != 0)
9982fd4e08Sratchov 		return res;
10082fd4e08Sratchov 	res = d1->type - d2->type;
10182fd4e08Sratchov 	if (res != 0)
10282fd4e08Sratchov 		return res;
10382fd4e08Sratchov 	res = strcmp(d1->func, d2->func);
10482fd4e08Sratchov 	if (res != 0)
10582fd4e08Sratchov 		return res;
10682fd4e08Sratchov 	res = d1->node0.unit - d2->node0.unit;
10749f67e12Sratchov 	if (d1->type == SIOCTL_SEL ||
10849f67e12Sratchov 	    d1->type == SIOCTL_VEC ||
10982fd4e08Sratchov 	    d1->type == SIOCTL_LIST) {
11082fd4e08Sratchov 		if (res != 0)
11182fd4e08Sratchov 			return res;
11282fd4e08Sratchov 		res = strcmp(d1->node1.name, d2->node1.name);
11382fd4e08Sratchov 		if (res != 0)
11482fd4e08Sratchov 			return res;
11582fd4e08Sratchov 		res = d1->node1.unit - d2->node1.unit;
11682fd4e08Sratchov 	}
11782fd4e08Sratchov 	return res;
11882fd4e08Sratchov }
11982fd4e08Sratchov 
12082fd4e08Sratchov /*
12182fd4e08Sratchov  * return true of the vector entry is diagonal
12282fd4e08Sratchov  */
12382fd4e08Sratchov int
isdiag(struct info * e)12482fd4e08Sratchov isdiag(struct info *e)
12582fd4e08Sratchov {
12682fd4e08Sratchov 	if (e->desc.node0.unit < 0 || e->desc.node1.unit < 0)
12782fd4e08Sratchov 		return 1;
12882fd4e08Sratchov 	return e->desc.node1.unit == e->desc.node0.unit;
12982fd4e08Sratchov }
13082fd4e08Sratchov 
13182fd4e08Sratchov /*
13282fd4e08Sratchov  * find the selector or vector entry with the given name and channels
13382fd4e08Sratchov  */
13482fd4e08Sratchov struct info *
vecent(struct info * i,char * vstr,int vunit)13582fd4e08Sratchov vecent(struct info *i, char *vstr, int vunit)
13682fd4e08Sratchov {
13782fd4e08Sratchov 	while (i != NULL) {
13882fd4e08Sratchov 		if ((strcmp(i->desc.node1.name, vstr) == 0) &&
13982fd4e08Sratchov 		    (vunit < 0 || i->desc.node1.unit == vunit))
14082fd4e08Sratchov 			break;
14182fd4e08Sratchov 		i = i->next;
14282fd4e08Sratchov 	}
14382fd4e08Sratchov 	return i;
14482fd4e08Sratchov }
14582fd4e08Sratchov 
14682fd4e08Sratchov /*
14782fd4e08Sratchov  * skip all parameters with the same group, name, and func
14882fd4e08Sratchov  */
14982fd4e08Sratchov struct info *
nextfunc(struct info * i)15082fd4e08Sratchov nextfunc(struct info *i)
15182fd4e08Sratchov {
15282fd4e08Sratchov 	char *str, *group, *func;
15382fd4e08Sratchov 
15482fd4e08Sratchov 	group = i->desc.group;
15582fd4e08Sratchov 	func = i->desc.func;
15682fd4e08Sratchov 	str = i->desc.node0.name;
15782fd4e08Sratchov 	for (i = i->next; i != NULL; i = i->next) {
15882fd4e08Sratchov 		if (strcmp(i->desc.group, group) != 0 ||
15982fd4e08Sratchov 		    strcmp(i->desc.node0.name, str) != 0 ||
16082fd4e08Sratchov 		    strcmp(i->desc.func, func) != 0)
16182fd4e08Sratchov 			return i;
16282fd4e08Sratchov 	}
16382fd4e08Sratchov 	return NULL;
16482fd4e08Sratchov }
16582fd4e08Sratchov 
16682fd4e08Sratchov /*
16782fd4e08Sratchov  * find the next parameter with the same group, name, func
16882fd4e08Sratchov  */
16982fd4e08Sratchov struct info *
nextpar(struct info * i)17082fd4e08Sratchov nextpar(struct info *i)
17182fd4e08Sratchov {
17282fd4e08Sratchov 	char *str, *group, *func;
17382fd4e08Sratchov 	int unit;
17482fd4e08Sratchov 
17582fd4e08Sratchov 	group = i->desc.group;
17682fd4e08Sratchov 	func = i->desc.func;
17782fd4e08Sratchov 	str = i->desc.node0.name;
17882fd4e08Sratchov 	unit = i->desc.node0.unit;
17982fd4e08Sratchov 	for (i = i->next; i != NULL; i = i->next) {
18082fd4e08Sratchov 		if (strcmp(i->desc.group, group) != 0 ||
18182fd4e08Sratchov 		    strcmp(i->desc.node0.name, str) != 0 ||
18282fd4e08Sratchov 		    strcmp(i->desc.func, func) != 0)
18382fd4e08Sratchov 			break;
18482fd4e08Sratchov 		/* XXX: need to check for -1 ? */
18582fd4e08Sratchov 		if (i->desc.node0.unit != unit)
18682fd4e08Sratchov 			return i;
18782fd4e08Sratchov 	}
18882fd4e08Sratchov 	return NULL;
18982fd4e08Sratchov }
19082fd4e08Sratchov 
19182fd4e08Sratchov /*
19282fd4e08Sratchov  * return the first vector entry with the given name
19382fd4e08Sratchov  */
19482fd4e08Sratchov struct info *
firstent(struct info * g,char * vstr)19582fd4e08Sratchov firstent(struct info *g, char *vstr)
19682fd4e08Sratchov {
19782fd4e08Sratchov 	char *astr, *group, *func;
19882fd4e08Sratchov 	struct info *i;
19982fd4e08Sratchov 
20082fd4e08Sratchov 	group = g->desc.group;
20182fd4e08Sratchov 	astr = g->desc.node0.name;
20282fd4e08Sratchov 	func = g->desc.func;
20382fd4e08Sratchov 	for (i = g; i != NULL; i = i->next) {
20482fd4e08Sratchov 		if (strcmp(i->desc.group, group) != 0 ||
20582fd4e08Sratchov 		    strcmp(i->desc.node0.name, astr) != 0 ||
20682fd4e08Sratchov 		    strcmp(i->desc.func, func) != 0)
20782fd4e08Sratchov 			break;
20882fd4e08Sratchov 		if (!isdiag(i))
20982fd4e08Sratchov 			continue;
21082fd4e08Sratchov 		if (strcmp(i->desc.node1.name, vstr) == 0)
21182fd4e08Sratchov 			return i;
21282fd4e08Sratchov 	}
21382fd4e08Sratchov 	return NULL;
21482fd4e08Sratchov }
21582fd4e08Sratchov 
21682fd4e08Sratchov /*
21782fd4e08Sratchov  * find the next entry of the given vector, if the mono flag
21882fd4e08Sratchov  * is set then the whole group is searched and off-diagonal entries are
21982fd4e08Sratchov  * skipped
22082fd4e08Sratchov  */
22182fd4e08Sratchov struct info *
nextent(struct info * i,int mono)22282fd4e08Sratchov nextent(struct info *i, int mono)
22382fd4e08Sratchov {
22482fd4e08Sratchov 	char *str, *group, *func;
22582fd4e08Sratchov 	int unit;
22682fd4e08Sratchov 
22782fd4e08Sratchov 	group = i->desc.group;
22882fd4e08Sratchov 	func = i->desc.func;
22982fd4e08Sratchov 	str = i->desc.node0.name;
23082fd4e08Sratchov 	unit = i->desc.node0.unit;
23182fd4e08Sratchov 	for (i = i->next; i != NULL; i = i->next) {
23282fd4e08Sratchov 		if (strcmp(i->desc.group, group) != 0 ||
23382fd4e08Sratchov 		    strcmp(i->desc.node0.name, str) != 0 ||
23482fd4e08Sratchov 		    strcmp(i->desc.func, func) != 0)
23582fd4e08Sratchov 			return NULL;
23682fd4e08Sratchov 		if (mono)
23782fd4e08Sratchov 			return i;
23882fd4e08Sratchov 		if (i->desc.node0.unit == unit)
23982fd4e08Sratchov 			return i;
24082fd4e08Sratchov 	}
24182fd4e08Sratchov 	return NULL;
24282fd4e08Sratchov }
24382fd4e08Sratchov 
24482fd4e08Sratchov /*
24582fd4e08Sratchov  * return true if parameter matches the given name and channel
24682fd4e08Sratchov  */
24782fd4e08Sratchov int
matchpar(struct info * i,char * astr,int aunit)24882fd4e08Sratchov matchpar(struct info *i, char *astr, int aunit)
24982fd4e08Sratchov {
25082fd4e08Sratchov 	if (strcmp(i->desc.node0.name, astr) != 0)
25182fd4e08Sratchov 		return 0;
25282fd4e08Sratchov 	if (aunit < 0)
25382fd4e08Sratchov 		return 1;
25482fd4e08Sratchov 	else if (i->desc.node0.unit < 0) {
25582fd4e08Sratchov 		fprintf(stderr, "unit used for parameter with no unit\n");
25682fd4e08Sratchov 		exit(1);
25782fd4e08Sratchov 	}
25882fd4e08Sratchov 	return i->desc.node0.unit == aunit;
25982fd4e08Sratchov }
26082fd4e08Sratchov 
26182fd4e08Sratchov /*
26282fd4e08Sratchov  * return true if selector or vector entry matches the given name and
26382fd4e08Sratchov  * channel range
26482fd4e08Sratchov  */
26582fd4e08Sratchov int
matchent(struct info * i,char * vstr,int vunit)26682fd4e08Sratchov matchent(struct info *i, char *vstr, int vunit)
26782fd4e08Sratchov {
26882fd4e08Sratchov 	if (strcmp(i->desc.node1.name, vstr) != 0)
26982fd4e08Sratchov 		return 0;
27082fd4e08Sratchov 	if (vunit < 0)
27182fd4e08Sratchov 		return 1;
27282fd4e08Sratchov 	else if (i->desc.node1.unit < 0) {
27382fd4e08Sratchov 		fprintf(stderr, "unit used for parameter with no unit\n");
27482fd4e08Sratchov 		exit(1);
27582fd4e08Sratchov 	}
27682fd4e08Sratchov 	return i->desc.node1.unit == vunit;
27782fd4e08Sratchov }
27882fd4e08Sratchov 
27982fd4e08Sratchov /*
28082fd4e08Sratchov  * return true if the given group can be represented as a signle mono
28182fd4e08Sratchov  * parameter
28282fd4e08Sratchov  */
28382fd4e08Sratchov int
ismono(struct info * g)28482fd4e08Sratchov ismono(struct info *g)
28582fd4e08Sratchov {
28682fd4e08Sratchov 	struct info *p1, *p2;
28782fd4e08Sratchov 	struct info *e1, *e2;
28882fd4e08Sratchov 
28982fd4e08Sratchov 	p1 = g;
29082fd4e08Sratchov 	switch (g->desc.type) {
29182fd4e08Sratchov 	case SIOCTL_NUM:
29282fd4e08Sratchov 	case SIOCTL_SW:
29382fd4e08Sratchov 		for (p2 = g; p2 != NULL; p2 = nextpar(p2)) {
29482fd4e08Sratchov 			if (p2->curval != p1->curval)
29582fd4e08Sratchov 				return 0;
29682fd4e08Sratchov 		}
29782fd4e08Sratchov 		break;
29849f67e12Sratchov 	case SIOCTL_SEL:
29982fd4e08Sratchov 	case SIOCTL_VEC:
30082fd4e08Sratchov 	case SIOCTL_LIST:
30182fd4e08Sratchov 		for (p2 = g; p2 != NULL; p2 = nextpar(p2)) {
30282fd4e08Sratchov 			for (e2 = p2; e2 != NULL; e2 = nextent(e2, 0)) {
30382fd4e08Sratchov 				if (!isdiag(e2)) {
30482fd4e08Sratchov 					if (e2->curval != 0)
30582fd4e08Sratchov 						return 0;
30682fd4e08Sratchov 				} else {
30782fd4e08Sratchov 					e1 = vecent(p1,
30882fd4e08Sratchov 					    e2->desc.node1.name,
30982fd4e08Sratchov 					    p1->desc.node0.unit);
31082fd4e08Sratchov 					if (e1 == NULL)
31182fd4e08Sratchov 						continue;
31282fd4e08Sratchov 					if (e1->curval != e2->curval)
31382fd4e08Sratchov 						return 0;
314*4aaef610Sratchov 					if (strcmp(e1->desc.display,
315*4aaef610Sratchov 						e2->desc.display) != 0)
316*4aaef610Sratchov 						return 0;
31782fd4e08Sratchov 				}
31882fd4e08Sratchov 			}
31982fd4e08Sratchov 		}
32082fd4e08Sratchov 		break;
32182fd4e08Sratchov 	}
32282fd4e08Sratchov 	return 1;
32382fd4e08Sratchov }
32482fd4e08Sratchov 
32582fd4e08Sratchov /*
32682fd4e08Sratchov  * print a sub-stream, eg. "spkr[4]"
32782fd4e08Sratchov  */
32882fd4e08Sratchov void
print_node(struct sioctl_node * c,int mono)32982fd4e08Sratchov print_node(struct sioctl_node *c, int mono)
33082fd4e08Sratchov {
33182fd4e08Sratchov 	printf("%s", c->name);
33282fd4e08Sratchov 	if (!mono && c->unit >= 0)
33382fd4e08Sratchov 		printf("[%d]", c->unit);
33482fd4e08Sratchov }
33582fd4e08Sratchov 
33682fd4e08Sratchov /*
337*4aaef610Sratchov  * print display string, with '(' and ')' and non-printable chars removed
338*4aaef610Sratchov  * in order to match command syntax
339*4aaef610Sratchov  */
340*4aaef610Sratchov void
print_display(struct info * p)341*4aaef610Sratchov print_display(struct info *p)
342*4aaef610Sratchov {
343*4aaef610Sratchov 	char buf[SIOCTL_NAMEMAX], *s, *d;
344*4aaef610Sratchov 	unsigned int c;
345*4aaef610Sratchov 
346*4aaef610Sratchov 	s = p->desc.display;
347*4aaef610Sratchov 	d = buf;
348*4aaef610Sratchov 	while ((c = *s++) != 0) {
349*4aaef610Sratchov 		if (c == '(' || c == ')' || c < ' ')
350*4aaef610Sratchov 			continue;
351*4aaef610Sratchov 		*d++ = c;
352*4aaef610Sratchov 	}
353*4aaef610Sratchov 	*d = 0;
354*4aaef610Sratchov 	if (buf[0] != 0)
355*4aaef610Sratchov 		printf("(%s)", buf);
356*4aaef610Sratchov }
357*4aaef610Sratchov 
358*4aaef610Sratchov /*
35982fd4e08Sratchov  * print info about the parameter
36082fd4e08Sratchov  */
36182fd4e08Sratchov void
print_desc(struct info * p,int mono)36282fd4e08Sratchov print_desc(struct info *p, int mono)
36382fd4e08Sratchov {
36482fd4e08Sratchov 	struct info *e;
36582fd4e08Sratchov 	int more;
36682fd4e08Sratchov 
36782fd4e08Sratchov 	switch (p->desc.type) {
36882fd4e08Sratchov 	case SIOCTL_NUM:
36982fd4e08Sratchov 	case SIOCTL_SW:
37082fd4e08Sratchov 		printf("*");
371*4aaef610Sratchov 		print_display(p);
37282fd4e08Sratchov 		break;
37349f67e12Sratchov 	case SIOCTL_SEL:
37482fd4e08Sratchov 	case SIOCTL_VEC:
37582fd4e08Sratchov 	case SIOCTL_LIST:
37682fd4e08Sratchov 		more = 0;
37782fd4e08Sratchov 		for (e = p; e != NULL; e = nextent(e, mono)) {
37882fd4e08Sratchov 			if (mono) {
37982fd4e08Sratchov 				if (!isdiag(e))
38082fd4e08Sratchov 					continue;
38182fd4e08Sratchov 				if (e != firstent(p, e->desc.node1.name))
38282fd4e08Sratchov 					continue;
38382fd4e08Sratchov 			}
38482fd4e08Sratchov 			if (more)
38582fd4e08Sratchov 				printf(",");
38682fd4e08Sratchov 			print_node(&e->desc.node1, mono);
38749f67e12Sratchov 			if (p->desc.type != SIOCTL_SEL)
38882fd4e08Sratchov 				printf(":*");
389*4aaef610Sratchov 			if (e->desc.display[0] != 0)
390*4aaef610Sratchov 				print_display(e);
39182fd4e08Sratchov 			more = 1;
39282fd4e08Sratchov 		}
39382fd4e08Sratchov 	}
39482fd4e08Sratchov }
39582fd4e08Sratchov 
396070d2896Sratchov void
print_num(struct info * p)397070d2896Sratchov print_num(struct info *p)
398070d2896Sratchov {
399070d2896Sratchov 	if (p->desc.maxval == 1)
400070d2896Sratchov 		printf("%d", p->curval);
401070d2896Sratchov 	else {
402070d2896Sratchov 		/*
403070d2896Sratchov 		 * For now, maxval is always 127 or 255,
404070d2896Sratchov 		 * so three decimals is always ideal.
405070d2896Sratchov 		 */
406070d2896Sratchov 		printf("%.3f", p->curval / (float)p->desc.maxval);
407070d2896Sratchov 	}
408070d2896Sratchov }
409070d2896Sratchov 
41082fd4e08Sratchov /*
411f707c874Sratchov  * print a single control
412f707c874Sratchov  */
413f707c874Sratchov void
print_ent(struct info * e,char * comment)414f707c874Sratchov print_ent(struct info *e, char *comment)
415f707c874Sratchov {
416f707c874Sratchov 	if (e->desc.group[0] != 0) {
417f707c874Sratchov 		printf("%s", e->desc.group);
418f707c874Sratchov 		printf("/");
419f707c874Sratchov 	}
420f707c874Sratchov 	print_node(&e->desc.node0, 0);
421f707c874Sratchov 	printf(".%s=", e->desc.func);
422f707c874Sratchov 	switch (e->desc.type) {
423f707c874Sratchov 	case SIOCTL_NONE:
424f707c874Sratchov 		printf("<removed>\n");
425f707c874Sratchov 		break;
42649f67e12Sratchov 	case SIOCTL_SEL:
427f707c874Sratchov 	case SIOCTL_VEC:
428f707c874Sratchov 	case SIOCTL_LIST:
429f707c874Sratchov 		print_node(&e->desc.node1, 0);
430f707c874Sratchov 		printf(":");
431f707c874Sratchov 		/* FALLTHROUGH */
432f707c874Sratchov 	case SIOCTL_SW:
433f707c874Sratchov 	case SIOCTL_NUM:
434f707c874Sratchov 		print_num(e);
435f707c874Sratchov 	}
436*4aaef610Sratchov 	print_display(e);
437f707c874Sratchov 	if (comment)
438f707c874Sratchov 		printf("\t# %s", comment);
439f707c874Sratchov 	printf("\n");
440f707c874Sratchov }
441f707c874Sratchov 
442f707c874Sratchov /*
44382fd4e08Sratchov  * print parameter value
44482fd4e08Sratchov  */
44582fd4e08Sratchov void
print_val(struct info * p,int mono)44682fd4e08Sratchov print_val(struct info *p, int mono)
44782fd4e08Sratchov {
44882fd4e08Sratchov 	struct info *e;
44982fd4e08Sratchov 	int more;
45082fd4e08Sratchov 
45182fd4e08Sratchov 	switch (p->desc.type) {
45282fd4e08Sratchov 	case SIOCTL_NUM:
45382fd4e08Sratchov 	case SIOCTL_SW:
454070d2896Sratchov 		print_num(p);
455*4aaef610Sratchov 		print_display(p);
45682fd4e08Sratchov 		break;
45749f67e12Sratchov 	case SIOCTL_SEL:
45882fd4e08Sratchov 	case SIOCTL_VEC:
45982fd4e08Sratchov 	case SIOCTL_LIST:
46082fd4e08Sratchov 		more = 0;
46182fd4e08Sratchov 		for (e = p; e != NULL; e = nextent(e, mono)) {
46282fd4e08Sratchov 			if (mono) {
46382fd4e08Sratchov 				if (!isdiag(e))
46482fd4e08Sratchov 					continue;
46582fd4e08Sratchov 				if (e != firstent(p, e->desc.node1.name))
46682fd4e08Sratchov 					continue;
46782fd4e08Sratchov 			}
4686ce0bcf9Sratchov 			if (e->desc.maxval == 1) {
4696ce0bcf9Sratchov 				if (e->curval) {
4706ce0bcf9Sratchov 					if (more)
4716ce0bcf9Sratchov 						printf(",");
4726ce0bcf9Sratchov 					print_node(&e->desc.node1, mono);
473*4aaef610Sratchov 					print_display(e);
4746ce0bcf9Sratchov 					more = 1;
4756ce0bcf9Sratchov 				}
4766ce0bcf9Sratchov 			} else {
47782fd4e08Sratchov 				if (more)
47882fd4e08Sratchov 					printf(",");
47982fd4e08Sratchov 				print_node(&e->desc.node1, mono);
480070d2896Sratchov 				printf(":");
481070d2896Sratchov 				print_num(e);
482*4aaef610Sratchov 				print_display(e);
48382fd4e08Sratchov 				more = 1;
48482fd4e08Sratchov 			}
48582fd4e08Sratchov 		}
48682fd4e08Sratchov 	}
4876ce0bcf9Sratchov }
48882fd4e08Sratchov 
48982fd4e08Sratchov /*
49082fd4e08Sratchov  * print ``<parameter>=<value>'' string (including '\n')
49182fd4e08Sratchov  */
49282fd4e08Sratchov void
print_par(struct info * p,int mono)493f707c874Sratchov print_par(struct info *p, int mono)
49482fd4e08Sratchov {
49526791b1eSratchov 	if (!n_flag) {
49682fd4e08Sratchov 		if (p->desc.group[0] != 0) {
49782fd4e08Sratchov 			printf("%s", p->desc.group);
49882fd4e08Sratchov 			printf("/");
49982fd4e08Sratchov 		}
50082fd4e08Sratchov 		print_node(&p->desc.node0, mono);
50182fd4e08Sratchov 		printf(".%s=", p->desc.func);
50226791b1eSratchov 	}
50382fd4e08Sratchov 	if (i_flag)
50482fd4e08Sratchov 		print_desc(p, mono);
50582fd4e08Sratchov 	else
50682fd4e08Sratchov 		print_val(p, mono);
50782fd4e08Sratchov 	printf("\n");
50882fd4e08Sratchov }
50982fd4e08Sratchov 
51082fd4e08Sratchov /*
51182fd4e08Sratchov  * parse a stream name or parameter name
51282fd4e08Sratchov  */
51382fd4e08Sratchov int
parse_name(char ** line,char * name)51482fd4e08Sratchov parse_name(char **line, char *name)
51582fd4e08Sratchov {
51682fd4e08Sratchov 	char *p = *line;
51782fd4e08Sratchov 	unsigned len = 0;
51882fd4e08Sratchov 
5192d7b56acSratchov 	if (!isname(*p)) {
5202d7b56acSratchov 		fprintf(stderr, "letter or digit expected near '%s'\n", p);
52182fd4e08Sratchov 		return 0;
52282fd4e08Sratchov 	}
5232d7b56acSratchov 	while (isname(*p)) {
52482fd4e08Sratchov 		if (len >= SIOCTL_NAMEMAX - 1) {
52582fd4e08Sratchov 			name[SIOCTL_NAMEMAX - 1] = '\0';
52682fd4e08Sratchov 			fprintf(stderr, "%s...: too long\n", name);
52782fd4e08Sratchov 			return 0;
52882fd4e08Sratchov 		}
52982fd4e08Sratchov 		name[len++] = *p;
53082fd4e08Sratchov 		p++;
53182fd4e08Sratchov 	}
53282fd4e08Sratchov 	name[len] = '\0';
53382fd4e08Sratchov 	*line = p;
53482fd4e08Sratchov 	return 1;
53582fd4e08Sratchov }
53682fd4e08Sratchov 
53782fd4e08Sratchov /*
53882fd4e08Sratchov  * parse a decimal integer
53982fd4e08Sratchov  */
54082fd4e08Sratchov int
parse_unit(char ** line,int * num)5416c473e8bSratchov parse_unit(char **line, int *num)
54282fd4e08Sratchov {
54382fd4e08Sratchov 	char *p = *line;
54482fd4e08Sratchov 	unsigned int val;
54582fd4e08Sratchov 	int n;
54682fd4e08Sratchov 
54782fd4e08Sratchov 	if (sscanf(p, "%u%n", &val, &n) != 1) {
54882fd4e08Sratchov 		fprintf(stderr, "number expected near '%s'\n", p);
54982fd4e08Sratchov 		return 0;
55082fd4e08Sratchov 	}
55182fd4e08Sratchov 	if (val >= 255) {
55282fd4e08Sratchov 		fprintf(stderr, "%d: too large\n", val);
55382fd4e08Sratchov 		return 0;
55482fd4e08Sratchov 	}
55582fd4e08Sratchov 	*num = val;
55682fd4e08Sratchov 	*line = p + n;
55782fd4e08Sratchov 	return 1;
55882fd4e08Sratchov }
55982fd4e08Sratchov 
56082fd4e08Sratchov int
parse_val(char ** line,float * num)56182fd4e08Sratchov parse_val(char **line, float *num)
56282fd4e08Sratchov {
56382fd4e08Sratchov 	char *p = *line;
56482fd4e08Sratchov 	float val;
56582fd4e08Sratchov 	int n;
56682fd4e08Sratchov 
56782fd4e08Sratchov 	if (sscanf(p, "%g%n", &val, &n) != 1) {
56882fd4e08Sratchov 		fprintf(stderr, "number expected near '%s'\n", p);
56982fd4e08Sratchov 		return 0;
57082fd4e08Sratchov 	}
57182fd4e08Sratchov 	if (val < 0 || val > 1) {
57282fd4e08Sratchov 		fprintf(stderr, "%g: expected number between 0 and 1\n", val);
57382fd4e08Sratchov 		return 0;
57482fd4e08Sratchov 	}
57582fd4e08Sratchov 	*num = val;
57682fd4e08Sratchov 	*line = p + n;
57782fd4e08Sratchov 	return 1;
57882fd4e08Sratchov }
57982fd4e08Sratchov 
58082fd4e08Sratchov /*
58182fd4e08Sratchov  * parse a sub-stream, eg. "spkr[7]"
58282fd4e08Sratchov  */
58382fd4e08Sratchov int
parse_node(char ** line,char * str,int * unit)58482fd4e08Sratchov parse_node(char **line, char *str, int *unit)
58582fd4e08Sratchov {
58682fd4e08Sratchov 	char *p = *line;
58782fd4e08Sratchov 
58882fd4e08Sratchov 	if (!parse_name(&p, str))
58982fd4e08Sratchov 		return 0;
59082fd4e08Sratchov 	if (*p != '[') {
59182fd4e08Sratchov 		*unit = -1;
59282fd4e08Sratchov 		*line = p;
59382fd4e08Sratchov 		return 1;
59482fd4e08Sratchov 	}
59582fd4e08Sratchov 	p++;
59682fd4e08Sratchov 	if (!parse_unit(&p, unit))
59782fd4e08Sratchov 		return 0;
59882fd4e08Sratchov 	if (*p != ']') {
59982fd4e08Sratchov 		fprintf(stderr, "']' expected near '%s'\n", p);
60082fd4e08Sratchov 		return 0;
60182fd4e08Sratchov 	}
60282fd4e08Sratchov 	p++;
60382fd4e08Sratchov 	*line = p;
60482fd4e08Sratchov 	return 1;
60582fd4e08Sratchov }
60682fd4e08Sratchov 
60782fd4e08Sratchov /*
60882fd4e08Sratchov  * parse a decimal prefixed by the optional mode
60982fd4e08Sratchov  */
61082fd4e08Sratchov int
parse_modeval(char ** line,int * rmode,float * rval)61182fd4e08Sratchov parse_modeval(char **line, int *rmode, float *rval)
61282fd4e08Sratchov {
61382fd4e08Sratchov 	char *p = *line;
61482fd4e08Sratchov 	unsigned mode;
61582fd4e08Sratchov 
61682fd4e08Sratchov 	switch (*p) {
61782fd4e08Sratchov 	case '+':
61882fd4e08Sratchov 		mode = MODE_ADD;
61982fd4e08Sratchov 		p++;
62082fd4e08Sratchov 		break;
62182fd4e08Sratchov 	case '-':
62282fd4e08Sratchov 		mode = MODE_SUB;
62382fd4e08Sratchov 		p++;
62482fd4e08Sratchov 		break;
62582fd4e08Sratchov 	case '!':
62682fd4e08Sratchov 		mode = MODE_TOGGLE;
62782fd4e08Sratchov 		p++;
62882fd4e08Sratchov 		break;
62982fd4e08Sratchov 	default:
63082fd4e08Sratchov 		mode = MODE_SET;
63182fd4e08Sratchov 	}
63282fd4e08Sratchov 	if (mode != MODE_TOGGLE) {
63382fd4e08Sratchov 		if (!parse_val(&p, rval))
63482fd4e08Sratchov 			return 0;
63582fd4e08Sratchov 	}
63682fd4e08Sratchov 	*line = p;
63782fd4e08Sratchov 	*rmode = mode;
63882fd4e08Sratchov 	return 1;
63982fd4e08Sratchov }
64082fd4e08Sratchov 
64182fd4e08Sratchov /*
64282fd4e08Sratchov  * dump the whole controls list, useful for debugging
64382fd4e08Sratchov  */
64482fd4e08Sratchov void
dump(void)64582fd4e08Sratchov dump(void)
64682fd4e08Sratchov {
64782fd4e08Sratchov 	struct info *i;
64882fd4e08Sratchov 
64982fd4e08Sratchov 	for (i = infolist; i != NULL; i = i->next) {
65082fd4e08Sratchov 		printf("%03u:", i->ctladdr);
65182fd4e08Sratchov 		print_node(&i->desc.node0, 0);
65282fd4e08Sratchov 		printf(".%s", i->desc.func);
65382fd4e08Sratchov 		printf("=");
65482fd4e08Sratchov 		switch (i->desc.type) {
65582fd4e08Sratchov 		case SIOCTL_NUM:
65682fd4e08Sratchov 		case SIOCTL_SW:
65782fd4e08Sratchov 			printf("0..%d (%u)", i->desc.maxval, i->curval);
65882fd4e08Sratchov 			break;
65949f67e12Sratchov 		case SIOCTL_SEL:
66049f67e12Sratchov 			print_node(&i->desc.node1, 0);
66149f67e12Sratchov 			break;
66282fd4e08Sratchov 		case SIOCTL_VEC:
66382fd4e08Sratchov 		case SIOCTL_LIST:
66482fd4e08Sratchov 			print_node(&i->desc.node1, 0);
66582fd4e08Sratchov 			printf(":0..%d (%u)", i->desc.maxval, i->curval);
66682fd4e08Sratchov 		}
667*4aaef610Sratchov 		print_display(i);
66882fd4e08Sratchov 		printf("\n");
66982fd4e08Sratchov 	}
67082fd4e08Sratchov }
67182fd4e08Sratchov 
67282fd4e08Sratchov /*
67382fd4e08Sratchov  * parse and execute a command ``<parameter>[=<value>]''
67482fd4e08Sratchov  */
67582fd4e08Sratchov int
cmd(char * line)67682fd4e08Sratchov cmd(char *line)
67782fd4e08Sratchov {
67882fd4e08Sratchov 	char *pos, *group;
67982fd4e08Sratchov 	struct info *i, *e, *g;
68082fd4e08Sratchov 	char func[SIOCTL_NAMEMAX];
68182fd4e08Sratchov 	char astr[SIOCTL_NAMEMAX], vstr[SIOCTL_NAMEMAX];
68282fd4e08Sratchov 	int aunit, vunit;
68382fd4e08Sratchov 	unsigned npar = 0, nent = 0;
68482fd4e08Sratchov 	int comma, mode;
68582fd4e08Sratchov 	float val;
68682fd4e08Sratchov 
68782fd4e08Sratchov 	pos = strrchr(line, '/');
68882fd4e08Sratchov 	if (pos != NULL) {
68982fd4e08Sratchov 		group = line;
69082fd4e08Sratchov 		pos[0] = 0;
69182fd4e08Sratchov 		pos++;
69282fd4e08Sratchov 	} else {
69382fd4e08Sratchov 		group = "";
69482fd4e08Sratchov 		pos = line;
69582fd4e08Sratchov 	}
69682fd4e08Sratchov 	if (!parse_node(&pos, astr, &aunit))
69782fd4e08Sratchov 		return 0;
69882fd4e08Sratchov 	if (*pos != '.') {
69982fd4e08Sratchov 		fprintf(stderr, "'.' expected near '%s'\n", pos);
70082fd4e08Sratchov 		return 0;
70182fd4e08Sratchov 	}
70282fd4e08Sratchov 	pos++;
70382fd4e08Sratchov 	if (!parse_name(&pos, func))
70482fd4e08Sratchov 		return 0;
70582fd4e08Sratchov 	for (g = infolist;; g = g->next) {
70682fd4e08Sratchov 		if (g == NULL) {
70782fd4e08Sratchov 			fprintf(stderr, "%s.%s: no such control\n", astr, func);
70882fd4e08Sratchov 			return 0;
70982fd4e08Sratchov 		}
71082fd4e08Sratchov 		if (strcmp(g->desc.group, group) == 0 &&
71182fd4e08Sratchov 		    strcmp(g->desc.func, func) == 0 &&
71282fd4e08Sratchov 		    strcmp(g->desc.node0.name, astr) == 0)
71382fd4e08Sratchov 			break;
71482fd4e08Sratchov 	}
71582fd4e08Sratchov 	g->mode = MODE_PRINT;
71682fd4e08Sratchov 	if (*pos != '=') {
71782fd4e08Sratchov 		if (*pos != '\0') {
71882fd4e08Sratchov 			fprintf(stderr, "junk at end of command\n");
71982fd4e08Sratchov 			return 0;
72082fd4e08Sratchov 		}
72182fd4e08Sratchov 		return 1;
72282fd4e08Sratchov 	}
72382fd4e08Sratchov 	pos++;
72482fd4e08Sratchov 	if (i_flag) {
72582fd4e08Sratchov 		printf("can't set values in info mode\n");
72682fd4e08Sratchov 		return 0;
72782fd4e08Sratchov 	}
72882fd4e08Sratchov 	npar = 0;
72982fd4e08Sratchov 	switch (g->desc.type) {
73082fd4e08Sratchov 	case SIOCTL_NUM:
73182fd4e08Sratchov 	case SIOCTL_SW:
73282fd4e08Sratchov 		if (!parse_modeval(&pos, &mode, &val))
73382fd4e08Sratchov 			return 0;
73482fd4e08Sratchov 		for (i = g; i != NULL; i = nextpar(i)) {
73582fd4e08Sratchov 			if (!matchpar(i, astr, aunit))
73682fd4e08Sratchov 				continue;
73782fd4e08Sratchov 			i->mode = mode;
73882fd4e08Sratchov 			i->newval = ftoi(val * i->desc.maxval);
73982fd4e08Sratchov 			npar++;
74082fd4e08Sratchov 		}
74182fd4e08Sratchov 		break;
74249f67e12Sratchov 	case SIOCTL_SEL:
743bd915203Sratchov 		if (*pos == '\0') {
744bd915203Sratchov 			fprintf(stderr, "%s.%s: expects value\n", astr, func);
745bd915203Sratchov 			exit(1);
746bd915203Sratchov 		}
747d9a51c35Sjmc 		/* FALLTHROUGH */
74882fd4e08Sratchov 	case SIOCTL_VEC:
74982fd4e08Sratchov 	case SIOCTL_LIST:
75082fd4e08Sratchov 		for (i = g; i != NULL; i = nextpar(i)) {
75182fd4e08Sratchov 			if (!matchpar(i, astr, aunit))
75282fd4e08Sratchov 				continue;
75382fd4e08Sratchov 			for (e = i; e != NULL; e = nextent(e, 0)) {
75482fd4e08Sratchov 				e->newval = 0;
75582fd4e08Sratchov 				e->mode = MODE_SET;
75682fd4e08Sratchov 			}
75782fd4e08Sratchov 			npar++;
75882fd4e08Sratchov 		}
75982fd4e08Sratchov 		comma = 0;
76082fd4e08Sratchov 		for (;;) {
76182fd4e08Sratchov 			if (*pos == '\0')
76282fd4e08Sratchov 				break;
76382fd4e08Sratchov 			if (comma) {
76482fd4e08Sratchov 				if (*pos != ',')
76582fd4e08Sratchov 					break;
76682fd4e08Sratchov 				pos++;
76782fd4e08Sratchov 			}
76882fd4e08Sratchov 			if (!parse_node(&pos, vstr, &vunit))
76982fd4e08Sratchov 				return 0;
77082fd4e08Sratchov 			if (*pos == ':') {
77182fd4e08Sratchov 				pos++;
77282fd4e08Sratchov 				if (!parse_modeval(&pos, &mode, &val))
77382fd4e08Sratchov 					return 0;
77482fd4e08Sratchov 			} else {
77582fd4e08Sratchov 				val = 1.;
77682fd4e08Sratchov 				mode = MODE_SET;
77782fd4e08Sratchov 			}
77882fd4e08Sratchov 			nent = 0;
77982fd4e08Sratchov 			for (i = g; i != NULL; i = nextpar(i)) {
78082fd4e08Sratchov 				if (!matchpar(i, astr, aunit))
78182fd4e08Sratchov 					continue;
78282fd4e08Sratchov 				for (e = i; e != NULL; e = nextent(e, 0)) {
78382fd4e08Sratchov 					if (matchent(e, vstr, vunit)) {
78482fd4e08Sratchov 						e->newval = ftoi(val * e->desc.maxval);
78582fd4e08Sratchov 						e->mode = mode;
78682fd4e08Sratchov 						nent++;
78782fd4e08Sratchov 					}
78882fd4e08Sratchov 				}
78982fd4e08Sratchov 			}
790*4aaef610Sratchov 			if (*pos == '(') {
791*4aaef610Sratchov 				while (*pos != 0) {
792*4aaef610Sratchov 					if (*pos++ == ')')
793*4aaef610Sratchov 						break;
794*4aaef610Sratchov 				}
795*4aaef610Sratchov 			}
79682fd4e08Sratchov 			if (nent == 0) {
79782fd4e08Sratchov 				/* XXX: use print_node()-like routine */
79882fd4e08Sratchov 				fprintf(stderr, "%s[%d]: invalid value\n", vstr, vunit);
799f707c874Sratchov 				print_par(g, 0);
80082fd4e08Sratchov 				exit(1);
80182fd4e08Sratchov 			}
80282fd4e08Sratchov 			comma = 1;
80382fd4e08Sratchov 		}
80482fd4e08Sratchov 	}
80582fd4e08Sratchov 	if (npar == 0) {
80682fd4e08Sratchov 		fprintf(stderr, "%s: invalid parameter\n", line);
80782fd4e08Sratchov 		exit(1);
80882fd4e08Sratchov 	}
80982fd4e08Sratchov 	if (*pos != '\0') {
81082fd4e08Sratchov 		printf("%s: junk at end of command\n", pos);
81182fd4e08Sratchov 		exit(1);
81282fd4e08Sratchov 	}
81382fd4e08Sratchov 	return 1;
81482fd4e08Sratchov }
81582fd4e08Sratchov 
81682fd4e08Sratchov /*
81782fd4e08Sratchov  * write the controls with the ``set'' flag on the device
81882fd4e08Sratchov  */
81982fd4e08Sratchov void
commit(void)82082fd4e08Sratchov commit(void)
82182fd4e08Sratchov {
82282fd4e08Sratchov 	struct info *i;
82382fd4e08Sratchov 	int val;
82482fd4e08Sratchov 
82582fd4e08Sratchov 	for (i = infolist; i != NULL; i = i->next) {
82682fd4e08Sratchov 		val = 0xdeadbeef;
82782fd4e08Sratchov 		switch (i->mode) {
82882fd4e08Sratchov 		case MODE_IGNORE:
82982fd4e08Sratchov 		case MODE_PRINT:
83082fd4e08Sratchov 			continue;
83182fd4e08Sratchov 		case MODE_SET:
83282fd4e08Sratchov 			val = i->newval;
83382fd4e08Sratchov 			break;
83482fd4e08Sratchov 		case MODE_ADD:
83582fd4e08Sratchov 			val = i->curval + i->newval;
83682fd4e08Sratchov 			if (val > i->desc.maxval)
83782fd4e08Sratchov 				val = i->desc.maxval;
83882fd4e08Sratchov 			break;
83982fd4e08Sratchov 		case MODE_SUB:
84082fd4e08Sratchov 			val = i->curval - i->newval;
84182fd4e08Sratchov 			if (val < 0)
84282fd4e08Sratchov 				val = 0;
84382fd4e08Sratchov 			break;
84482fd4e08Sratchov 		case MODE_TOGGLE:
84582fd4e08Sratchov 			val = i->curval ? 0 : i->desc.maxval;
84682fd4e08Sratchov 		}
84782fd4e08Sratchov 		sioctl_setval(hdl, i->ctladdr, val);
84882fd4e08Sratchov 		i->curval = val;
84982fd4e08Sratchov 	}
85082fd4e08Sratchov }
85182fd4e08Sratchov 
85282fd4e08Sratchov /*
85382fd4e08Sratchov  * print all parameters
85482fd4e08Sratchov  */
85582fd4e08Sratchov void
list(void)85682fd4e08Sratchov list(void)
85782fd4e08Sratchov {
85882fd4e08Sratchov 	struct info *p, *g;
85982fd4e08Sratchov 
86082fd4e08Sratchov 	for (g = infolist; g != NULL; g = nextfunc(g)) {
86182fd4e08Sratchov 		if (g->mode == MODE_IGNORE)
86282fd4e08Sratchov 			continue;
86382fd4e08Sratchov 		if (i_flag) {
86482fd4e08Sratchov 			if (v_flag) {
86582fd4e08Sratchov 				for (p = g; p != NULL; p = nextpar(p))
866f707c874Sratchov 					print_par(p, 0);
86782fd4e08Sratchov 			} else
868f707c874Sratchov 				print_par(g, 1);
86982fd4e08Sratchov 		} else {
87082fd4e08Sratchov 			if (v_flag || !ismono(g)) {
87182fd4e08Sratchov 				for (p = g; p != NULL; p = nextpar(p))
872f707c874Sratchov 					print_par(p, 0);
87382fd4e08Sratchov 			} else
874f707c874Sratchov 				print_par(g, 1);
87582fd4e08Sratchov 		}
87682fd4e08Sratchov 	}
87782fd4e08Sratchov }
87882fd4e08Sratchov 
87982fd4e08Sratchov /*
88082fd4e08Sratchov  * register a new knob/button, called from the poll() loop.  this may be
88182fd4e08Sratchov  * called when label string changes, in which case we update the
882e8de1577Sratchov  * existing label widget rather than inserting a new one.
88382fd4e08Sratchov  */
88482fd4e08Sratchov void
ondesc(void * arg,struct sioctl_desc * d,int curval)88582fd4e08Sratchov ondesc(void *arg, struct sioctl_desc *d, int curval)
88682fd4e08Sratchov {
88782fd4e08Sratchov 	struct info *i, **pi;
88882fd4e08Sratchov 	int cmp;
88982fd4e08Sratchov 
89082fd4e08Sratchov 	if (d == NULL)
89182fd4e08Sratchov 		return;
89282fd4e08Sratchov 
89382fd4e08Sratchov 	/*
89482fd4e08Sratchov 	 * delete control
89582fd4e08Sratchov 	 */
89682fd4e08Sratchov 	for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
89782fd4e08Sratchov 		if (d->addr == i->desc.addr) {
89882fd4e08Sratchov 			if (m_flag)
899f707c874Sratchov 				print_ent(i, "deleted");
90082fd4e08Sratchov 			*pi = i->next;
90182fd4e08Sratchov 			free(i);
90282fd4e08Sratchov 			break;
90382fd4e08Sratchov 		}
90482fd4e08Sratchov 	}
90582fd4e08Sratchov 
906a7cecdf4Sratchov 	switch (d->type) {
907a7cecdf4Sratchov 	case SIOCTL_NUM:
908a7cecdf4Sratchov 	case SIOCTL_SW:
909a7cecdf4Sratchov 	case SIOCTL_VEC:
910a7cecdf4Sratchov 	case SIOCTL_LIST:
91149f67e12Sratchov 	case SIOCTL_SEL:
912a7cecdf4Sratchov 		break;
913a7cecdf4Sratchov 	default:
91482fd4e08Sratchov 		return;
915a7cecdf4Sratchov 	}
91682fd4e08Sratchov 
91782fd4e08Sratchov 	/*
91882fd4e08Sratchov 	 * find the right position to insert the new widget
91982fd4e08Sratchov 	 */
92082fd4e08Sratchov 	for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
92182fd4e08Sratchov 		cmp = cmpdesc(d, &i->desc);
9220bc434f8Sratchov 		if (cmp <= 0)
92382fd4e08Sratchov 			break;
92482fd4e08Sratchov 	}
92582fd4e08Sratchov 	i = malloc(sizeof(struct info));
92682fd4e08Sratchov 	if (i == NULL) {
92782fd4e08Sratchov 		perror("malloc");
92882fd4e08Sratchov 		exit(1);
92982fd4e08Sratchov 	}
93082fd4e08Sratchov 	i->desc = *d;
93182fd4e08Sratchov 	i->ctladdr = d->addr;
93282fd4e08Sratchov 	i->curval = i->newval = curval;
93382fd4e08Sratchov 	i->mode = MODE_IGNORE;
93482fd4e08Sratchov 	i->next = *pi;
93582fd4e08Sratchov 	*pi = i;
93682fd4e08Sratchov 	if (m_flag)
937f707c874Sratchov 		print_ent(i, "added");
93882fd4e08Sratchov }
93982fd4e08Sratchov 
94082fd4e08Sratchov /*
94182fd4e08Sratchov  * update a knob/button state, called from the poll() loop
94282fd4e08Sratchov  */
94382fd4e08Sratchov void
onctl(void * arg,unsigned addr,unsigned val)94482fd4e08Sratchov onctl(void *arg, unsigned addr, unsigned val)
94582fd4e08Sratchov {
94649f67e12Sratchov 	struct info *i, *j;
94782fd4e08Sratchov 
94849f67e12Sratchov 	i = infolist;
94949f67e12Sratchov 	for (;;) {
95049f67e12Sratchov 		if (i == NULL)
95149f67e12Sratchov 			return;
95249f67e12Sratchov 		if (i->ctladdr == addr)
95349f67e12Sratchov 			break;
95449f67e12Sratchov 		i = i->next;
95549f67e12Sratchov 	}
95649f67e12Sratchov 
95749f67e12Sratchov 	if (i->curval == val) {
95849f67e12Sratchov 		print_ent(i, "eq");
95949f67e12Sratchov 		return;
96049f67e12Sratchov 	}
96149f67e12Sratchov 
96249f67e12Sratchov 	if (i->desc.type == SIOCTL_SEL) {
96349f67e12Sratchov 		for (j = infolist; j != NULL; j = j->next) {
96449f67e12Sratchov 			if (strcmp(i->desc.group, j->desc.group) != 0 ||
96549f67e12Sratchov 			    strcmp(i->desc.node0.name, j->desc.node0.name) != 0 ||
96649f67e12Sratchov 			    strcmp(i->desc.func, j->desc.func) != 0 ||
96749f67e12Sratchov 			    i->desc.node0.unit != j->desc.node0.unit)
96882fd4e08Sratchov 				continue;
96949f67e12Sratchov 			j->curval = (i->ctladdr == j->ctladdr);
97049f67e12Sratchov 		}
97149f67e12Sratchov 	} else
97282fd4e08Sratchov 		i->curval = val;
97349f67e12Sratchov 
97482fd4e08Sratchov 	if (m_flag)
975f707c874Sratchov 		print_ent(i, "changed");
97682fd4e08Sratchov }
97782fd4e08Sratchov 
97882fd4e08Sratchov int
main(int argc,char ** argv)97982fd4e08Sratchov main(int argc, char **argv)
98082fd4e08Sratchov {
98182fd4e08Sratchov 	char *devname = SIO_DEVANY;
98282fd4e08Sratchov 	int i, c, d_flag = 0;
98382fd4e08Sratchov 	struct info *g;
98482fd4e08Sratchov 	struct pollfd *pfds;
98582fd4e08Sratchov 	int nfds, revents;
98682fd4e08Sratchov 
9873b691801Sratchov 	while ((c = getopt(argc, argv, "df:imnqv")) != -1) {
98882fd4e08Sratchov 		switch (c) {
98982fd4e08Sratchov 		case 'd':
99082fd4e08Sratchov 			d_flag = 1;
99182fd4e08Sratchov 			break;
99282fd4e08Sratchov 		case 'f':
99382fd4e08Sratchov 			devname = optarg;
99482fd4e08Sratchov 			break;
99582fd4e08Sratchov 		case 'i':
99682fd4e08Sratchov 			i_flag = 1;
99782fd4e08Sratchov 			break;
99882fd4e08Sratchov 		case 'm':
99982fd4e08Sratchov 			m_flag = 1;
100082fd4e08Sratchov 			break;
100126791b1eSratchov 		case 'n':
100226791b1eSratchov 			n_flag = 1;
100326791b1eSratchov 			break;
10043b691801Sratchov 		case 'q':
10053b691801Sratchov 			q_flag = 1;
10063b691801Sratchov 			break;
100782fd4e08Sratchov 		case 'v':
100882fd4e08Sratchov 			v_flag++;
100982fd4e08Sratchov 			break;
101082fd4e08Sratchov 		default:
101182fd4e08Sratchov 			fprintf(stderr, "usage: sndioctl "
10123b691801Sratchov 			    "[-dimnqv] [-f device] [command ...]\n");
101382fd4e08Sratchov 			exit(1);
101482fd4e08Sratchov 		}
101582fd4e08Sratchov 	}
101682fd4e08Sratchov 	argc -= optind;
101782fd4e08Sratchov 	argv += optind;
101882fd4e08Sratchov 
101982fd4e08Sratchov 	hdl = sioctl_open(devname, SIOCTL_READ | SIOCTL_WRITE, 0);
102082fd4e08Sratchov 	if (hdl == NULL) {
102182fd4e08Sratchov 		fprintf(stderr, "%s: can't open control device\n", devname);
102282fd4e08Sratchov 		exit(1);
102382fd4e08Sratchov 	}
10245d6ea990Smestre 
10255d6ea990Smestre 	if (pledge("stdio audio", NULL) == -1) {
10265d6ea990Smestre 		fprintf(stderr, "%s: pledge: %s\n", getprogname(),
10275d6ea990Smestre 		    strerror(errno));
10285d6ea990Smestre 		exit(1);
10295d6ea990Smestre 	}
10305d6ea990Smestre 
103182fd4e08Sratchov 	if (!sioctl_ondesc(hdl, ondesc, NULL)) {
103282fd4e08Sratchov 		fprintf(stderr, "%s: can't get device description\n", devname);
103382fd4e08Sratchov 		exit(1);
103482fd4e08Sratchov 	}
103582fd4e08Sratchov 	sioctl_onval(hdl, onctl, NULL);
103682fd4e08Sratchov 
103782fd4e08Sratchov 	if (d_flag) {
103882fd4e08Sratchov 		if (argc > 0) {
103982fd4e08Sratchov 			fprintf(stderr,
104082fd4e08Sratchov 			    "commands are not allowed with -d option\n");
104182fd4e08Sratchov 			exit(1);
104282fd4e08Sratchov 		}
104382fd4e08Sratchov 		dump();
104482fd4e08Sratchov 	} else {
104582fd4e08Sratchov 		if (argc == 0) {
104682fd4e08Sratchov 			for (g = infolist; g != NULL; g = nextfunc(g))
104782fd4e08Sratchov 				g->mode = MODE_PRINT;
104882fd4e08Sratchov 		} else {
104982fd4e08Sratchov 			for (i = 0; i < argc; i++) {
105082fd4e08Sratchov 				if (!cmd(argv[i]))
105182fd4e08Sratchov 					return 1;
105282fd4e08Sratchov 			}
105382fd4e08Sratchov 		}
105482fd4e08Sratchov 		commit();
10553b691801Sratchov 		if (!q_flag)
105682fd4e08Sratchov 			list();
105782fd4e08Sratchov 	}
105882fd4e08Sratchov 	if (m_flag) {
105982fd4e08Sratchov 		pfds = malloc(sizeof(struct pollfd) * sioctl_nfds(hdl));
106082fd4e08Sratchov 		if (pfds == NULL) {
106182fd4e08Sratchov 			perror("malloc");
106282fd4e08Sratchov 			exit(1);
106382fd4e08Sratchov 		}
106482fd4e08Sratchov 		for (;;) {
106574b220efSratchov                 	fflush(stdout);
106682fd4e08Sratchov 			nfds = sioctl_pollfd(hdl, pfds, POLLIN);
106782fd4e08Sratchov 			if (nfds == 0)
106882fd4e08Sratchov 				break;
106982fd4e08Sratchov 			while (poll(pfds, nfds, -1) < 0) {
107082fd4e08Sratchov 				if (errno != EINTR) {
107182fd4e08Sratchov 					perror("poll");
107282fd4e08Sratchov 					exit(1);
107382fd4e08Sratchov 				}
107482fd4e08Sratchov 			}
107582fd4e08Sratchov 			revents = sioctl_revents(hdl, pfds);
107682fd4e08Sratchov 			if (revents & POLLHUP) {
107782fd4e08Sratchov 				fprintf(stderr, "disconnected\n");
107882fd4e08Sratchov 				break;
107982fd4e08Sratchov 			}
108082fd4e08Sratchov 		}
108182fd4e08Sratchov 		free(pfds);
108282fd4e08Sratchov 	}
108382fd4e08Sratchov 	sioctl_close(hdl);
108482fd4e08Sratchov 	return 0;
108582fd4e08Sratchov }
1086