xref: /freebsd/usr.sbin/mixer/mixer.c (revision c22be0b181e954a4cc00ed4f5c7df974f3061c3c)
1903873ceSHans Petter Selasky /*-
2903873ceSHans Petter Selasky  * Copyright (c) 2021 Christos Margiolis <christos@FreeBSD.org>
3ae0de421SAndrey A. Chernov  *
4903873ceSHans Petter Selasky  * Permission is hereby granted, free of charge, to any person obtaining a copy
5903873ceSHans Petter Selasky  * of this software and associated documentation files (the "Software"), to deal
6903873ceSHans Petter Selasky  * in the Software without restriction, including without limitation the rights
7903873ceSHans Petter Selasky  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8903873ceSHans Petter Selasky  * copies of the Software, and to permit persons to whom the Software is
9903873ceSHans Petter Selasky  * furnished to do so, subject to the following conditions:
10ae0de421SAndrey A. Chernov  *
11903873ceSHans Petter Selasky  * The above copyright notice and this permission notice shall be included in
12903873ceSHans Petter Selasky  * all copies or substantial portions of the Software.
13ae0de421SAndrey A. Chernov  *
14903873ceSHans Petter Selasky  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15903873ceSHans Petter Selasky  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16903873ceSHans Petter Selasky  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17903873ceSHans Petter Selasky  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18903873ceSHans Petter Selasky  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19903873ceSHans Petter Selasky  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20903873ceSHans Petter Selasky  * THE SOFTWARE.
21ae0de421SAndrey A. Chernov  */
22ae0de421SAndrey A. Chernov 
239aac2759SChristos Margiolis #include <sys/sysctl.h>
249aac2759SChristos Margiolis #include <sys/wait.h>
259aac2759SChristos Margiolis 
26cd94e9c3SPhilippe Charnier #include <err.h>
27903873ceSHans Petter Selasky #include <errno.h>
28da3d4469SHans Petter Selasky #include <mixer.h>
29cd94e9c3SPhilippe Charnier #include <stdio.h>
30eddcf96dSJohn-Mark Gurney #include <stdlib.h>
31903873ceSHans Petter Selasky #include <string.h>
32eddcf96dSJohn-Mark Gurney #include <unistd.h>
33ae0de421SAndrey A. Chernov 
34f250ff5fSHans Petter Selasky enum {
35f250ff5fSHans Petter Selasky 	C_VOL = 0,
36f250ff5fSHans Petter Selasky 	C_MUT,
37f250ff5fSHans Petter Selasky 	C_SRC,
38f250ff5fSHans Petter Selasky };
39ae0de421SAndrey A. Chernov 
40903873ceSHans Petter Selasky static void usage(void) __dead2;
41903873ceSHans Petter Selasky static void initctls(struct mixer *);
42903873ceSHans Petter Selasky static void printall(struct mixer *, int);
43903873ceSHans Petter Selasky static void printminfo(struct mixer *, int);
44903873ceSHans Petter Selasky static void printdev(struct mixer *, int);
45903873ceSHans Petter Selasky static void printrecsrc(struct mixer *, int); /* XXX: change name */
469aac2759SChristos Margiolis static int set_dunit(struct mixer *, int, char *);
47903873ceSHans Petter Selasky /* Control handlers */
48903873ceSHans Petter Selasky static int mod_volume(struct mix_dev *, void *);
49903873ceSHans Petter Selasky static int mod_mute(struct mix_dev *, void *);
50903873ceSHans Petter Selasky static int mod_recsrc(struct mix_dev *, void *);
51903873ceSHans Petter Selasky static int print_volume(struct mix_dev *, void *);
52903873ceSHans Petter Selasky static int print_mute(struct mix_dev *, void *);
53903873ceSHans Petter Selasky static int print_recsrc(struct mix_dev *, void *);
54ae0de421SAndrey A. Chernov 
55ae0de421SAndrey A. Chernov int
main(int argc,char * argv[])56ae0de421SAndrey A. Chernov main(int argc, char *argv[])
57ae0de421SAndrey A. Chernov {
58903873ceSHans Petter Selasky 	struct mixer *m;
59903873ceSHans Petter Selasky 	mix_ctl_t *cp;
609aac2759SChristos Margiolis 	char *name = NULL, buf[NAME_MAX], *vctl = NULL;
61da3d4469SHans Petter Selasky 	char *p, *q, *devstr, *ctlstr, *valstr = NULL;
62f250ff5fSHans Petter Selasky 	int dunit, i, n, pall = 1, shorthand;
63903873ceSHans Petter Selasky 	int aflag = 0, dflag = 0, oflag = 0, sflag = 0;
648fc722a5SHans Petter Selasky 	int ch;
65ae0de421SAndrey A. Chernov 
669aac2759SChristos Margiolis 	while ((ch = getopt(argc, argv, "ad:f:hosV:")) != -1) {
67903873ceSHans Petter Selasky 		switch (ch) {
68903873ceSHans Petter Selasky 		case 'a':
69903873ceSHans Petter Selasky 			aflag = 1;
7035ebab0aSJung-uk Kim 			break;
71903873ceSHans Petter Selasky 		case 'd':
7275be886eSChristos Margiolis 			if (strncmp(optarg, "pcm", 3) == 0)
7375be886eSChristos Margiolis 				optarg += 3;
7409ba0701SChristos Margiolis 			errno = 0;
75903873ceSHans Petter Selasky 			dunit = strtol(optarg, NULL, 10);
76903873ceSHans Petter Selasky 			if (errno == EINVAL || errno == ERANGE)
7709ba0701SChristos Margiolis 				err(1, "strtol(%s)", optarg);
78903873ceSHans Petter Selasky 			dflag = 1;
7935ebab0aSJung-uk Kim 			break;
80903873ceSHans Petter Selasky 		case 'f':
81903873ceSHans Petter Selasky 			name = optarg;
82903873ceSHans Petter Selasky 			break;
83903873ceSHans Petter Selasky 		case 'o':
84903873ceSHans Petter Selasky 			oflag = 1;
85903873ceSHans Petter Selasky 			break;
86903873ceSHans Petter Selasky 		case 's':
87e68adf0bSJung-uk Kim 			sflag = 1;
8835ebab0aSJung-uk Kim 			break;
899aac2759SChristos Margiolis 		case 'V':
909aac2759SChristos Margiolis 			vctl = optarg;
919aac2759SChristos Margiolis 			break;
92da3d4469SHans Petter Selasky 		case 'h': /* FALLTHROUGH */
93903873ceSHans Petter Selasky 		case '?':
94903873ceSHans Petter Selasky 		default:
95903873ceSHans Petter Selasky 			usage();
9635ebab0aSJung-uk Kim 		}
9735ebab0aSJung-uk Kim 	}
98903873ceSHans Petter Selasky 	argc -= optind;
99903873ceSHans Petter Selasky 	argv += optind;
10035ebab0aSJung-uk Kim 
101903873ceSHans Petter Selasky 	/* Print all mixers and exit. */
102903873ceSHans Petter Selasky 	if (aflag) {
103903873ceSHans Petter Selasky 		if ((n = mixer_get_nmixers()) < 0)
10409ba0701SChristos Margiolis 			errx(1, "no mixers present in the system");
105903873ceSHans Petter Selasky 		for (i = 0; i < n; i++) {
106e3b94b37SChristos Margiolis 			(void)mixer_get_path(buf, sizeof(buf), i);
107903873ceSHans Petter Selasky 			if ((m = mixer_open(buf)) == NULL)
1080e807985SChristos Margiolis 				continue;
109903873ceSHans Petter Selasky 			initctls(m);
110903873ceSHans Petter Selasky 			if (sflag)
111903873ceSHans Petter Selasky 				printrecsrc(m, oflag);
112903873ceSHans Petter Selasky 			else {
113903873ceSHans Petter Selasky 				printall(m, oflag);
114903873ceSHans Petter Selasky 				if (oflag)
115903873ceSHans Petter Selasky 					printf("\n");
116493f8e95SMike Pritchard 			}
117903873ceSHans Petter Selasky 			(void)mixer_close(m);
118493f8e95SMike Pritchard 		}
119493f8e95SMike Pritchard 		return (0);
120493f8e95SMike Pritchard 	}
121493f8e95SMike Pritchard 
122903873ceSHans Petter Selasky 	if ((m = mixer_open(name)) == NULL)
12309ba0701SChristos Margiolis 		errx(1, "%s: no such mixer", name);
124903873ceSHans Petter Selasky 
125903873ceSHans Petter Selasky 	initctls(m);
126903873ceSHans Petter Selasky 
1275daa7cf4SChristos Margiolis 	if (dflag) {
1289aac2759SChristos Margiolis 		if (set_dunit(m, dunit, vctl) < 0)
129903873ceSHans Petter Selasky 			goto parse;
1305daa7cf4SChristos Margiolis 		else {
1315daa7cf4SChristos Margiolis 			/*
1325daa7cf4SChristos Margiolis 			 * Open current mixer since we changed the default
1335daa7cf4SChristos Margiolis 			 * unit, otherwise we'll print and apply changes to the
1345daa7cf4SChristos Margiolis 			 * old one.
1355daa7cf4SChristos Margiolis 			 */
1365daa7cf4SChristos Margiolis 			(void)mixer_close(m);
1375daa7cf4SChristos Margiolis 			if ((m = mixer_open(NULL)) == NULL)
1385daa7cf4SChristos Margiolis 				errx(1, "cannot open default mixer");
1395daa7cf4SChristos Margiolis 			initctls(m);
1405daa7cf4SChristos Margiolis 		}
1415daa7cf4SChristos Margiolis 	}
142903873ceSHans Petter Selasky 	if (sflag) {
143903873ceSHans Petter Selasky 		printrecsrc(m, oflag);
144903873ceSHans Petter Selasky 		(void)mixer_close(m);
145903873ceSHans Petter Selasky 		return (0);
146903873ceSHans Petter Selasky 	}
147903873ceSHans Petter Selasky 
148903873ceSHans Petter Selasky parse:
149903873ceSHans Petter Selasky 	while (argc > 0) {
150*c22be0b1SMark Johnston 		char *orig;
151*c22be0b1SMark Johnston 
152*c22be0b1SMark Johnston 		if ((orig = p = strdup(*argv)) == NULL)
153903873ceSHans Petter Selasky 			err(1, "strdup(%s)", *argv);
154f250ff5fSHans Petter Selasky 
155f250ff5fSHans Petter Selasky 		/* Check if we're using the shorthand syntax for volume setting. */
156f250ff5fSHans Petter Selasky 		shorthand = 0;
157f250ff5fSHans Petter Selasky 		for (q = p; *q != '\0'; q++) {
158f250ff5fSHans Petter Selasky 			if (*q == '=') {
159f250ff5fSHans Petter Selasky 				q++;
160f250ff5fSHans Petter Selasky 				shorthand = ((*q >= '0' && *q <= '9') ||
161f250ff5fSHans Petter Selasky 				    *q == '+' || *q == '-' || *q == '.');
162f250ff5fSHans Petter Selasky 				break;
163f250ff5fSHans Petter Selasky 			} else if (*q == '.')
164f250ff5fSHans Petter Selasky 				break;
165f250ff5fSHans Petter Selasky 		}
166f250ff5fSHans Petter Selasky 
167903873ceSHans Petter Selasky 		/* Split the string into device, control and value. */
168da3d4469SHans Petter Selasky 		devstr = strsep(&p, ".=");
169903873ceSHans Petter Selasky 		if ((m->dev = mixer_get_dev_byname(m, devstr)) == NULL) {
170903873ceSHans Petter Selasky 			warnx("%s: no such device", devstr);
171903873ceSHans Petter Selasky 			goto next;
172903873ceSHans Petter Selasky 		}
173903873ceSHans Petter Selasky 		/* Input: `dev`. */
174903873ceSHans Petter Selasky 		if (p == NULL) {
175903873ceSHans Petter Selasky 			printdev(m, 1);
176903873ceSHans Petter Selasky 			pall = 0;
177903873ceSHans Petter Selasky 			goto next;
178f250ff5fSHans Petter Selasky 		} else if (shorthand) {
179f250ff5fSHans Petter Selasky 			/*
180f250ff5fSHans Petter Selasky 			 * Input: `dev=N` -> shorthand for `dev.volume=N`.
181f250ff5fSHans Petter Selasky 			 *
182f250ff5fSHans Petter Selasky 			 * We don't care what the rest of the string contains as
183f250ff5fSHans Petter Selasky 			 * long as we're sure the very beginning is right,
184f250ff5fSHans Petter Selasky 			 * mod_volume() will take care of parsing it properly.
185f250ff5fSHans Petter Selasky 			 */
186da3d4469SHans Petter Selasky 			cp = mixer_get_ctl(m->dev, C_VOL);
187da3d4469SHans Petter Selasky 			cp->mod(cp->parent_dev, p);
188da3d4469SHans Petter Selasky 			goto next;
189da3d4469SHans Petter Selasky 		}
190903873ceSHans Petter Selasky 		ctlstr = strsep(&p, "=");
191903873ceSHans Petter Selasky 		if ((cp = mixer_get_ctl_byname(m->dev, ctlstr)) == NULL) {
192903873ceSHans Petter Selasky 			warnx("%s.%s: no such control", devstr, ctlstr);
193903873ceSHans Petter Selasky 			goto next;
194903873ceSHans Petter Selasky 		}
195903873ceSHans Petter Selasky 		/* Input: `dev.control`. */
196903873ceSHans Petter Selasky 		if (p == NULL) {
197903873ceSHans Petter Selasky 			(void)cp->print(cp->parent_dev, cp->name);
198903873ceSHans Petter Selasky 			pall = 0;
199903873ceSHans Petter Selasky 			goto next;
200903873ceSHans Petter Selasky 		}
201903873ceSHans Petter Selasky 		valstr = p;
202903873ceSHans Petter Selasky 		/* Input: `dev.control=val`. */
203903873ceSHans Petter Selasky 		cp->mod(cp->parent_dev, valstr);
204903873ceSHans Petter Selasky next:
205*c22be0b1SMark Johnston 		free(orig);
206e68adf0bSJung-uk Kim 		argc--;
207e68adf0bSJung-uk Kim 		argv++;
208eddcf96dSJohn-Mark Gurney 	}
209ae0de421SAndrey A. Chernov 
210903873ceSHans Petter Selasky 	if (pall)
211903873ceSHans Petter Selasky 		printall(m, oflag);
212903873ceSHans Petter Selasky 	(void)mixer_close(m);
213903873ceSHans Petter Selasky 
214903873ceSHans Petter Selasky 	return (0);
215eddcf96dSJohn-Mark Gurney }
216eddcf96dSJohn-Mark Gurney 
217903873ceSHans Petter Selasky static void __dead2
usage(void)218903873ceSHans Petter Selasky usage(void)
219903873ceSHans Petter Selasky {
2209aac2759SChristos Margiolis 	fprintf(stderr, "usage: %1$s [-f device] [-d pcmN | N "
2219aac2759SChristos Margiolis 	    "[-V voss_device:mode]] [-os] [dev[.control[=value]]] ...\n"
22275be886eSChristos Margiolis 	    "       %1$s [-os] -a\n"
223aa92785aSHans Petter Selasky 	    "       %1$s -h\n", getprogname());
224903873ceSHans Petter Selasky 	exit(1);
225903873ceSHans Petter Selasky }
226903873ceSHans Petter Selasky 
227903873ceSHans Petter Selasky static void
initctls(struct mixer * m)228903873ceSHans Petter Selasky initctls(struct mixer *m)
229903873ceSHans Petter Selasky {
230903873ceSHans Petter Selasky 	struct mix_dev *dp;
231903873ceSHans Petter Selasky 	int rc = 0;
232903873ceSHans Petter Selasky 
233903873ceSHans Petter Selasky 	TAILQ_FOREACH(dp, &m->devs, devs) {
234903873ceSHans Petter Selasky 		rc += mixer_add_ctl(dp, C_VOL, "volume", mod_volume, print_volume);
235903873ceSHans Petter Selasky 		rc += mixer_add_ctl(dp, C_MUT, "mute", mod_mute, print_mute);
236903873ceSHans Petter Selasky 		rc += mixer_add_ctl(dp, C_SRC, "recsrc", mod_recsrc, print_recsrc);
237903873ceSHans Petter Selasky 	}
238903873ceSHans Petter Selasky 	if (rc) {
239903873ceSHans Petter Selasky 		(void)mixer_close(m);
24009ba0701SChristos Margiolis 		errx(1, "cannot make mixer controls");
241903873ceSHans Petter Selasky 	}
242903873ceSHans Petter Selasky }
243903873ceSHans Petter Selasky 
244903873ceSHans Petter Selasky static void
printall(struct mixer * m,int oflag)245903873ceSHans Petter Selasky printall(struct mixer *m, int oflag)
246903873ceSHans Petter Selasky {
247903873ceSHans Petter Selasky 	struct mix_dev *dp;
248903873ceSHans Petter Selasky 
249903873ceSHans Petter Selasky 	printminfo(m, oflag);
250903873ceSHans Petter Selasky 	TAILQ_FOREACH(dp, &m->devs, devs) {
251903873ceSHans Petter Selasky 		m->dev = dp;
252903873ceSHans Petter Selasky 		printdev(m, oflag);
253903873ceSHans Petter Selasky 	}
254903873ceSHans Petter Selasky }
255903873ceSHans Petter Selasky 
256903873ceSHans Petter Selasky static void
printminfo(struct mixer * m,int oflag)257903873ceSHans Petter Selasky printminfo(struct mixer *m, int oflag)
258903873ceSHans Petter Selasky {
259903873ceSHans Petter Selasky 	int playrec = MIX_MODE_PLAY | MIX_MODE_REC;
260903873ceSHans Petter Selasky 
261903873ceSHans Petter Selasky 	if (oflag)
262903873ceSHans Petter Selasky 		return;
26363dcf7fdSHans Petter Selasky 	printf("%s:", m->mi.name);
26463dcf7fdSHans Petter Selasky 	if (*m->ci.longname != '\0')
26563dcf7fdSHans Petter Selasky 		printf(" <%s>", m->ci.longname);
26663dcf7fdSHans Petter Selasky 	if (*m->ci.hw_info != '\0')
26763dcf7fdSHans Petter Selasky 		printf(" %s", m->ci.hw_info);
26863dcf7fdSHans Petter Selasky 
26963dcf7fdSHans Petter Selasky 	if (m->mode != 0)
270903873ceSHans Petter Selasky 		printf(" (");
271903873ceSHans Petter Selasky 	if (m->mode & MIX_MODE_PLAY)
272903873ceSHans Petter Selasky 		printf("play");
273903873ceSHans Petter Selasky 	if ((m->mode & playrec) == playrec)
274903873ceSHans Petter Selasky 		printf("/");
275903873ceSHans Petter Selasky 	if (m->mode & MIX_MODE_REC)
276903873ceSHans Petter Selasky 		printf("rec");
27763dcf7fdSHans Petter Selasky 	if (m->mode != 0)
278903873ceSHans Petter Selasky 		printf(")");
27963dcf7fdSHans Petter Selasky 
280903873ceSHans Petter Selasky 	if (m->f_default)
281903873ceSHans Petter Selasky 		printf(" (default)");
282903873ceSHans Petter Selasky 	printf("\n");
283903873ceSHans Petter Selasky }
284903873ceSHans Petter Selasky 
285903873ceSHans Petter Selasky static void
printdev(struct mixer * m,int oflag)286903873ceSHans Petter Selasky printdev(struct mixer *m, int oflag)
287903873ceSHans Petter Selasky {
288903873ceSHans Petter Selasky 	struct mix_dev *d = m->dev;
289903873ceSHans Petter Selasky 	mix_ctl_t *cp;
290903873ceSHans Petter Selasky 
291903873ceSHans Petter Selasky 	if (!oflag) {
29260bdfe98SHans Petter Selasky 		printf("    %-10s= %.2f:%.2f    ",
29360bdfe98SHans Petter Selasky 		    d->name, d->vol.left, d->vol.right);
294903873ceSHans Petter Selasky 		if (!MIX_ISREC(m, d->devno))
295903873ceSHans Petter Selasky 			printf(" pbk");
296903873ceSHans Petter Selasky 		if (MIX_ISREC(m, d->devno))
297903873ceSHans Petter Selasky 			printf(" rec");
298903873ceSHans Petter Selasky 		if (MIX_ISRECSRC(m, d->devno))
299903873ceSHans Petter Selasky 			printf(" src");
300903873ceSHans Petter Selasky 		if (MIX_ISMUTE(m, d->devno))
301903873ceSHans Petter Selasky 			printf(" mute");
302903873ceSHans Petter Selasky 		printf("\n");
303903873ceSHans Petter Selasky 	} else {
304903873ceSHans Petter Selasky 		TAILQ_FOREACH(cp, &d->ctls, ctls) {
305903873ceSHans Petter Selasky 			(void)cp->print(cp->parent_dev, cp->name);
306903873ceSHans Petter Selasky 		}
307903873ceSHans Petter Selasky 	}
308903873ceSHans Petter Selasky }
309903873ceSHans Petter Selasky 
310903873ceSHans Petter Selasky static void
printrecsrc(struct mixer * m,int oflag)311903873ceSHans Petter Selasky printrecsrc(struct mixer *m, int oflag)
312903873ceSHans Petter Selasky {
313903873ceSHans Petter Selasky 	struct mix_dev *dp;
314903873ceSHans Petter Selasky 	int n = 0;
315903873ceSHans Petter Selasky 
316903873ceSHans Petter Selasky 	if (!m->recmask)
317903873ceSHans Petter Selasky 		return;
318903873ceSHans Petter Selasky 	if (!oflag)
319903873ceSHans Petter Selasky 		printf("%s: ", m->mi.name);
320903873ceSHans Petter Selasky 	TAILQ_FOREACH(dp, &m->devs, devs) {
321903873ceSHans Petter Selasky 		if (MIX_ISRECSRC(m, dp->devno)) {
322903873ceSHans Petter Selasky 			if (n++ && !oflag)
323903873ceSHans Petter Selasky 				printf(", ");
324903873ceSHans Petter Selasky 			printf("%s", dp->name);
325903873ceSHans Petter Selasky 			if (oflag)
326903873ceSHans Petter Selasky 				printf(".%s=+%s",
327da3d4469SHans Petter Selasky 				    mixer_get_ctl(dp, C_SRC)->name, n ? " " : "");
328903873ceSHans Petter Selasky 		}
329903873ceSHans Petter Selasky 	}
330903873ceSHans Petter Selasky 	printf("\n");
331903873ceSHans Petter Selasky }
332903873ceSHans Petter Selasky 
333903873ceSHans Petter Selasky static int
set_dunit(struct mixer * m,int dunit,char * vctl)3349aac2759SChristos Margiolis set_dunit(struct mixer *m, int dunit, char *vctl)
335903873ceSHans Petter Selasky {
3369aac2759SChristos Margiolis 	const char *opt;
3379aac2759SChristos Margiolis 	char *dev, *mode;
3389aac2759SChristos Margiolis 	char buf[32];
3399aac2759SChristos Margiolis 	size_t size;
3409aac2759SChristos Margiolis 	int n, rc;
3419aac2759SChristos Margiolis 
3429aac2759SChristos Margiolis 	/*
3439aac2759SChristos Margiolis 	 * Issue warning in case of hw.snd.basename_clone being unset. Omit the
3449aac2759SChristos Margiolis 	 * check and warning if the -V flag is used, since the user is most
3459aac2759SChristos Margiolis 	 * likely to be aware of this, and the warning might be confusing.
3469aac2759SChristos Margiolis 	 */
3479aac2759SChristos Margiolis 	if (vctl == NULL) {
3489aac2759SChristos Margiolis 		size = sizeof(int);
3499aac2759SChristos Margiolis 		if (sysctlbyname("hw.snd.basename_clone", &n, &size,
3509aac2759SChristos Margiolis 		    NULL, 0) < 0) {
3519aac2759SChristos Margiolis 			warn("hw.snd.basename_clone failed");
3529aac2759SChristos Margiolis 			return (-1);
3539aac2759SChristos Margiolis 		}
3549aac2759SChristos Margiolis 		if (n == 0) {
3559aac2759SChristos Margiolis 			warnx("warning: hw.snd.basename_clone not set. "
3569aac2759SChristos Margiolis 			    "/dev/dsp is managed externally and does not "
3579aac2759SChristos Margiolis 			    "change with the default unit change here.");
3589aac2759SChristos Margiolis 		}
3599aac2759SChristos Margiolis 	}
360903873ceSHans Petter Selasky 
361903873ceSHans Petter Selasky 	if ((n = mixer_get_dunit()) < 0) {
362903873ceSHans Petter Selasky 		warn("cannot get default unit");
363903873ceSHans Petter Selasky 		return (-1);
364903873ceSHans Petter Selasky 	}
3655c2b216aSMark Johnston 	if (mixer_set_dunit(m, dunit) < 0) {
36609ba0701SChristos Margiolis 		warn("cannot set default unit to %d", dunit);
367903873ceSHans Petter Selasky 		return (-1);
368903873ceSHans Petter Selasky 	}
3695c2b216aSMark Johnston 	printf("default_unit: %d -> %d\n", n, dunit);
370903873ceSHans Petter Selasky 
3719aac2759SChristos Margiolis 	/* Hot-swap in case virtual_oss exists and is running. */
3729aac2759SChristos Margiolis 	if (vctl != NULL) {
3739aac2759SChristos Margiolis 		dev = strsep(&vctl, ":");
3749aac2759SChristos Margiolis 		mode = vctl;
3759aac2759SChristos Margiolis 		if (dev == NULL || mode == NULL) {
3769aac2759SChristos Margiolis 			warnx("voss_device:mode tuple incomplete");
3779aac2759SChristos Margiolis 			return (-1);
3789aac2759SChristos Margiolis 		}
3799aac2759SChristos Margiolis 		if (strcmp(mode, "all") == 0)
3809aac2759SChristos Margiolis 			opt = "-f";
3819aac2759SChristos Margiolis 		else if (strcmp(mode, "play") == 0)
3829aac2759SChristos Margiolis 			opt = "-P";
3839aac2759SChristos Margiolis 		else if (strcmp(mode, "rec") == 0)
3849aac2759SChristos Margiolis 			opt = "-R";
3859aac2759SChristos Margiolis 		else {
3869aac2759SChristos Margiolis 			warnx("please use one of the following modes: "
3879aac2759SChristos Margiolis 			    "all, play, rec");
3889aac2759SChristos Margiolis 			return (-1);
3899aac2759SChristos Margiolis 		}
3909aac2759SChristos Margiolis 		snprintf(buf, sizeof(buf), "/dev/dsp%d", dunit);
3919aac2759SChristos Margiolis 		switch (fork()) {
3929aac2759SChristos Margiolis 		case -1:
3939aac2759SChristos Margiolis 			warn("fork");
3949aac2759SChristos Margiolis 			break;
3959aac2759SChristos Margiolis 		case 0:
3969aac2759SChristos Margiolis 			rc = execl("/usr/local/sbin/virtual_oss_cmd",
3979aac2759SChristos Margiolis 			    "virtual_oss_cmd", dev, opt, buf, NULL);
3989aac2759SChristos Margiolis 			if (rc < 0)
3999aac2759SChristos Margiolis 				warn("virtual_oss_cmd");
4009aac2759SChristos Margiolis 			_exit(0);
4019aac2759SChristos Margiolis 		default:
4029aac2759SChristos Margiolis 			if (wait(NULL) < 0)
4039aac2759SChristos Margiolis 				warn("wait");
4049aac2759SChristos Margiolis 			break;
4059aac2759SChristos Margiolis 		}
4069aac2759SChristos Margiolis 	}
4079aac2759SChristos Margiolis 
408903873ceSHans Petter Selasky 	return (0);
409903873ceSHans Petter Selasky }
410903873ceSHans Petter Selasky 
411903873ceSHans Petter Selasky static int
mod_volume(struct mix_dev * d,void * p)412903873ceSHans Petter Selasky mod_volume(struct mix_dev *d, void *p)
413903873ceSHans Petter Selasky {
414903873ceSHans Petter Selasky 	struct mixer *m;
415903873ceSHans Petter Selasky 	mix_ctl_t *cp;
416903873ceSHans Petter Selasky 	mix_volume_t v;
417903873ceSHans Petter Selasky 	const char *val;
4184014365eSKyle Evans 	char *endp, lstr[8], rstr[8];
419903873ceSHans Petter Selasky 	float lprev, rprev, lrel, rrel;
420903873ceSHans Petter Selasky 	int n;
421903873ceSHans Petter Selasky 
422903873ceSHans Petter Selasky 	m = d->parent_mixer;
423903873ceSHans Petter Selasky 	cp = mixer_get_ctl(m->dev, C_VOL);
424903873ceSHans Petter Selasky 	val = p;
425903873ceSHans Petter Selasky 	n = sscanf(val, "%7[^:]:%7s", lstr, rstr);
426903873ceSHans Petter Selasky 	if (n == EOF) {
427903873ceSHans Petter Selasky 		warnx("invalid volume value: %s", val);
428903873ceSHans Petter Selasky 		return (-1);
429903873ceSHans Petter Selasky 	}
430e68adf0bSJung-uk Kim 	lrel = rrel = 0;
431903873ceSHans Petter Selasky 	if (n > 0) {
432e68adf0bSJung-uk Kim 		if (*lstr == '+' || *lstr == '-')
4334014365eSKyle Evans 			lrel = 1;
4344014365eSKyle Evans 		v.left = strtof(lstr, &endp);
4354014365eSKyle Evans 		if (*endp != '\0' && (*endp != '%' || *(endp + 1) != '\0')) {
4364014365eSKyle Evans 			warnx("invalid volume value: %s", lstr);
4374014365eSKyle Evans 			return (-1);
4384014365eSKyle Evans 		}
439903873ceSHans Petter Selasky 
4404014365eSKyle Evans 		if (*endp == '%')
441903873ceSHans Petter Selasky 			v.left /= 100.0f;
44222baecf2SMatthew N. Dodd 	}
443903873ceSHans Petter Selasky 	if (n > 1) {
444e68adf0bSJung-uk Kim 		if (*rstr == '+' || *rstr == '-')
44522baecf2SMatthew N. Dodd 			rrel = 1;
4464014365eSKyle Evans 		v.right = strtof(rstr, &endp);
4474014365eSKyle Evans 		if (*endp != '\0' && (*endp != '%' || *(endp + 1) != '\0')) {
4484014365eSKyle Evans 			warnx("invalid volume value: %s", rstr);
4494014365eSKyle Evans 			return (-1);
4504014365eSKyle Evans 		}
45122baecf2SMatthew N. Dodd 
4524014365eSKyle Evans 		if (*endp == '%')
453903873ceSHans Petter Selasky 			v.right /= 100.0f;
454eddcf96dSJohn-Mark Gurney 	}
455903873ceSHans Petter Selasky 	switch (n) {
456eddcf96dSJohn-Mark Gurney 	case 1:
457903873ceSHans Petter Selasky 		v.right = v.left; /* FALLTHROUGH */
4584014365eSKyle Evans 		rrel = lrel;
459eddcf96dSJohn-Mark Gurney 	case 2:
46022baecf2SMatthew N. Dodd 		if (lrel)
461903873ceSHans Petter Selasky 			v.left += m->dev->vol.left;
46222baecf2SMatthew N. Dodd 		if (rrel)
463903873ceSHans Petter Selasky 			v.right += m->dev->vol.right;
46422baecf2SMatthew N. Dodd 
465903873ceSHans Petter Selasky 		if (v.left < MIX_VOLMIN)
466903873ceSHans Petter Selasky 			v.left = MIX_VOLMIN;
467903873ceSHans Petter Selasky 		else if (v.left > MIX_VOLMAX)
468903873ceSHans Petter Selasky 			v.left = MIX_VOLMAX;
469903873ceSHans Petter Selasky 		if (v.right < MIX_VOLMIN)
470903873ceSHans Petter Selasky 			v.right = MIX_VOLMIN;
471903873ceSHans Petter Selasky 		else if (v.right > MIX_VOLMAX)
472903873ceSHans Petter Selasky 			v.right = MIX_VOLMAX;
473eddcf96dSJohn-Mark Gurney 
474903873ceSHans Petter Selasky 		lprev = m->dev->vol.left;
475903873ceSHans Petter Selasky 		rprev = m->dev->vol.right;
476903873ceSHans Petter Selasky 		if (mixer_set_vol(m, v) < 0)
477903873ceSHans Petter Selasky 			warn("%s.%s=%.2f:%.2f",
478903873ceSHans Petter Selasky 			    m->dev->name, cp->name, v.left, v.right);
479903873ceSHans Petter Selasky 		else
480903873ceSHans Petter Selasky 			printf("%s.%s: %.2f:%.2f -> %.2f:%.2f\n",
481903873ceSHans Petter Selasky 			   m->dev->name, cp->name, lprev, rprev, v.left, v.right);
482903873ceSHans Petter Selasky 	}
483eddcf96dSJohn-Mark Gurney 
484903873ceSHans Petter Selasky 	return (0);
485903873ceSHans Petter Selasky }
486eddcf96dSJohn-Mark Gurney 
487903873ceSHans Petter Selasky static int
mod_mute(struct mix_dev * d,void * p)488903873ceSHans Petter Selasky mod_mute(struct mix_dev *d, void *p)
489903873ceSHans Petter Selasky {
490903873ceSHans Petter Selasky 	struct mixer *m;
491903873ceSHans Petter Selasky 	mix_ctl_t *cp;
492903873ceSHans Petter Selasky 	const char *val;
493903873ceSHans Petter Selasky 	int n, opt = -1;
494903873ceSHans Petter Selasky 
495903873ceSHans Petter Selasky 	m = d->parent_mixer;
496903873ceSHans Petter Selasky 	cp = mixer_get_ctl(m->dev, C_MUT);
497903873ceSHans Petter Selasky 	val = p;
4988ca73331SChristos Margiolis 	if (strncmp(val, "off", strlen(val)) == 0 ||
4998ca73331SChristos Margiolis 	    strncmp(val, "0", strlen(val)) == 0)
500903873ceSHans Petter Selasky 		opt = MIX_UNMUTE;
5018ca73331SChristos Margiolis 	else if (strncmp(val, "on", strlen(val)) == 0 ||
5028ca73331SChristos Margiolis 	    strncmp(val, "1", strlen(val)) == 0)
503903873ceSHans Petter Selasky 		opt = MIX_MUTE;
5048ca73331SChristos Margiolis 	else if (strncmp(val, "toggle", strlen(val)) == 0 ||
5058ca73331SChristos Margiolis 	    strncmp(val, "^", strlen(val)) == 0)
506903873ceSHans Petter Selasky 		opt = MIX_TOGGLEMUTE;
507cc7479d7SChristos Margiolis 	else {
508cc7479d7SChristos Margiolis 		warnx("%s: no such modifier", val);
509903873ceSHans Petter Selasky 		return (-1);
510eddcf96dSJohn-Mark Gurney 	}
511903873ceSHans Petter Selasky 	n = MIX_ISMUTE(m, m->dev->devno);
512903873ceSHans Petter Selasky 	if (mixer_set_mute(m, opt) < 0)
513cc7479d7SChristos Margiolis 		warn("%s.%s=%s", m->dev->name, cp->name, val);
514903873ceSHans Petter Selasky 	else
515cc7479d7SChristos Margiolis 		printf("%s.%s: %s -> %s\n",
516cc7479d7SChristos Margiolis 		    m->dev->name, cp->name,
517cc7479d7SChristos Margiolis 		    n ? "on" : "off",
518cc7479d7SChristos Margiolis 		    MIX_ISMUTE(m, m->dev->devno) ? "on" : "off");
519903873ceSHans Petter Selasky 
520903873ceSHans Petter Selasky 	return (0);
521eddcf96dSJohn-Mark Gurney }
522eddcf96dSJohn-Mark Gurney 
523903873ceSHans Petter Selasky static int
mod_recsrc(struct mix_dev * d,void * p)524903873ceSHans Petter Selasky mod_recsrc(struct mix_dev *d, void *p)
525903873ceSHans Petter Selasky {
526903873ceSHans Petter Selasky 	struct mixer *m;
527903873ceSHans Petter Selasky 	mix_ctl_t *cp;
528903873ceSHans Petter Selasky 	const char *val;
529903873ceSHans Petter Selasky 	int n, opt = -1;
530903873ceSHans Petter Selasky 
531903873ceSHans Petter Selasky 	m = d->parent_mixer;
532903873ceSHans Petter Selasky 	cp = mixer_get_ctl(m->dev, C_SRC);
533903873ceSHans Petter Selasky 	val = p;
5348ca73331SChristos Margiolis 	if (strncmp(val, "add", strlen(val)) == 0 ||
5358ca73331SChristos Margiolis 	    strncmp(val, "+", strlen(val)) == 0)
536903873ceSHans Petter Selasky 		opt = MIX_ADDRECSRC;
5378ca73331SChristos Margiolis 	else if (strncmp(val, "remove", strlen(val)) == 0 ||
5388ca73331SChristos Margiolis 	    strncmp(val, "-", strlen(val)) == 0)
539903873ceSHans Petter Selasky 		opt = MIX_REMOVERECSRC;
5408ca73331SChristos Margiolis 	else if (strncmp(val, "set", strlen(val)) == 0 ||
5418ca73331SChristos Margiolis 	    strncmp(val, "=", strlen(val)) == 0)
542903873ceSHans Petter Selasky 		opt = MIX_SETRECSRC;
5438ca73331SChristos Margiolis 	else if (strncmp(val, "toggle", strlen(val)) == 0 ||
5448ca73331SChristos Margiolis 	    strncmp(val, "^", strlen(val)) == 0)
545903873ceSHans Petter Selasky 		opt = MIX_TOGGLERECSRC;
546cc7479d7SChristos Margiolis 	else {
547cc7479d7SChristos Margiolis 		warnx("%s: no such modifier", val);
548903873ceSHans Petter Selasky 		return (-1);
549903873ceSHans Petter Selasky 	}
550903873ceSHans Petter Selasky 	n = MIX_ISRECSRC(m, m->dev->devno);
551903873ceSHans Petter Selasky 	if (mixer_mod_recsrc(m, opt) < 0)
552cc7479d7SChristos Margiolis 		warn("%s.%s=%s", m->dev->name, cp->name, val);
553903873ceSHans Petter Selasky 	else
554cc7479d7SChristos Margiolis 		printf("%s.%s: %s -> %s\n",
555cc7479d7SChristos Margiolis 		    m->dev->name, cp->name,
556cc7479d7SChristos Margiolis 		    n ? "add" : "remove",
557cc7479d7SChristos Margiolis 		    MIX_ISRECSRC(m, m->dev->devno) ? "add" : "remove");
558903873ceSHans Petter Selasky 
559903873ceSHans Petter Selasky 	return (0);
56068a1b905SMaxim Sobolev }
56168a1b905SMaxim Sobolev 
562903873ceSHans Petter Selasky static int
print_volume(struct mix_dev * d,void * p)563903873ceSHans Petter Selasky print_volume(struct mix_dev *d, void *p)
564903873ceSHans Petter Selasky {
565903873ceSHans Petter Selasky 	struct mixer *m = d->parent_mixer;
566903873ceSHans Petter Selasky 	const char *ctl_name = p;
567903873ceSHans Petter Selasky 
568903873ceSHans Petter Selasky 	printf("%s.%s=%.2f:%.2f\n",
569903873ceSHans Petter Selasky 	    m->dev->name, ctl_name, m->dev->vol.left, m->dev->vol.right);
570903873ceSHans Petter Selasky 
571903873ceSHans Petter Selasky 	return (0);
572ae0de421SAndrey A. Chernov }
573ae0de421SAndrey A. Chernov 
574903873ceSHans Petter Selasky static int
print_mute(struct mix_dev * d,void * p)575903873ceSHans Petter Selasky print_mute(struct mix_dev *d, void *p)
576903873ceSHans Petter Selasky {
577903873ceSHans Petter Selasky 	struct mixer *m = d->parent_mixer;
578903873ceSHans Petter Selasky 	const char *ctl_name = p;
57935ebab0aSJung-uk Kim 
580cc7479d7SChristos Margiolis 	printf("%s.%s=%s\n", m->dev->name, ctl_name,
581cc7479d7SChristos Margiolis 	    MIX_ISMUTE(m, m->dev->devno) ? "on" : "off");
582903873ceSHans Petter Selasky 
583903873ceSHans Petter Selasky 	return (0);
584903873ceSHans Petter Selasky }
585903873ceSHans Petter Selasky 
586903873ceSHans Petter Selasky static int
print_recsrc(struct mix_dev * d,void * p)587903873ceSHans Petter Selasky print_recsrc(struct mix_dev *d, void *p)
588903873ceSHans Petter Selasky {
589903873ceSHans Petter Selasky 	struct mixer *m = d->parent_mixer;
590903873ceSHans Petter Selasky 	const char *ctl_name = p;
591903873ceSHans Petter Selasky 
592903873ceSHans Petter Selasky 	if (!MIX_ISRECSRC(m, m->dev->devno))
593903873ceSHans Petter Selasky 		return (-1);
594cc7479d7SChristos Margiolis 	printf("%s.%s=add\n", m->dev->name, ctl_name);
595eddcf96dSJohn-Mark Gurney 
596e68adf0bSJung-uk Kim 	return (0);
597ae0de421SAndrey A. Chernov }
598