1270631Sjfv/*
2270631Sjfv *	This is an example of a mixer program for Linux
3292095Ssmh *
4270631Sjfv *	updated 1/1/93 to add stereo, level query, broken
5270631Sjfv *      	devmask kludge - cmetz@thor.tjhsst.edu
6270631Sjfv *
7270631Sjfv * (C) Craig Metz and Hannu Savolainen 1993.
8270631Sjfv *
9270631Sjfv * You may do anything you wish with this program.
10270631Sjfv *
11270631Sjfv * ditto for my modifications (John-Mark Gurney, 1997)
12270631Sjfv */
13270631Sjfv
14270631Sjfv#include <sys/cdefs.h>
15270631Sjfv__FBSDID("$FreeBSD: releng/10.3/usr.sbin/mixer/mixer.c 230611 2012-01-27 09:15:55Z mav $");
16270631Sjfv
17270631Sjfv#include <err.h>
18270631Sjfv#include <fcntl.h>
19270631Sjfv#include <libgen.h>
20270631Sjfv#include <limits.h>
21270631Sjfv#include <stdio.h>
22270631Sjfv#include <string.h>
23270631Sjfv#include <stdlib.h>
24270631Sjfv#include <unistd.h>
25270631Sjfv#include <sys/soundcard.h>
26270631Sjfv
27270631Sjfvstatic const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
28270631Sjfv
29270631Sjfvstatic void	usage(int devmask, int recmask);
30270631Sjfvstatic int	res_name(const char *name, int mask);
31270631Sjfvstatic void	print_recsrc(int recsrc, int recmask, int sflag);
32270631Sjfv
33270631Sjfvstatic void
34270631Sjfvusage(int devmask, int recmask)
35270631Sjfv{
36270631Sjfv	int	i, n;
37270631Sjfv
38270631Sjfv	printf("usage: mixer [-f device] [-s | -S] [dev [+|-][voll[:[+|-]volr]] ...\n"
39270631Sjfv	    "       mixer [-f device] [-s | -S] recsrc ...\n"
40292095Ssmh	    "       mixer [-f device] [-s | -S] {^|+|-|=}rec rdev ...\n");
41270631Sjfv	if (devmask != 0) {
42270631Sjfv		printf(" devices: ");
43270631Sjfv		for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
44270631Sjfv			if ((1 << i) & devmask)  {
45270631Sjfv				if (n)
46270631Sjfv					printf(", ");
47270631Sjfv				printf("%s", names[i]);
48270631Sjfv				n++;
49270631Sjfv			}
50270631Sjfv	}
51270631Sjfv	if (recmask != 0) {
52270631Sjfv		printf("\n rec devices: ");
53270631Sjfv		for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
54270631Sjfv			if ((1 << i) & recmask)  {
55270631Sjfv				if (n)
56270631Sjfv					printf(", ");
57270631Sjfv				printf("%s", names[i]);
58270631Sjfv				n++;
59270631Sjfv			}
60270631Sjfv	}
61270631Sjfv	printf("\n");
62270631Sjfv	exit(1);
63270631Sjfv}
64270631Sjfv
65292100Ssmhstatic int
66292095Ssmhres_name(const char *name, int mask)
67292100Ssmh{
68270631Sjfv	int	foo;
69270631Sjfv
70292100Ssmh	for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
71292100Ssmh		if ((1 << foo) & mask && strcmp(names[foo], name) == 0)
72292100Ssmh			break;
73292100Ssmh
74292100Ssmh	return (foo == SOUND_MIXER_NRDEVICES ? -1 : foo);
75292100Ssmh}
76292100Ssmh
77292100Ssmhstatic void
78292100Ssmhprint_recsrc(int recsrc, int recmask, int sflag)
79292100Ssmh{
80292100Ssmh	int	i, n;
81292100Ssmh
82292100Ssmh	if (recmask == 0)
83270631Sjfv		return;
84270631Sjfv
85270631Sjfv	if (!sflag)
86270631Sjfv		printf("Recording source: ");
87270631Sjfv
88270631Sjfv	for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
89270631Sjfv		if ((1 << i) & recsrc) {
90270631Sjfv			if (sflag)
91270631Sjfv				printf("%srec ", n ? " +" : "=");
92270631Sjfv			else if (n)
93270631Sjfv				printf(", ");
94270631Sjfv			printf("%s", names[i]);
95270631Sjfv			n++;
96270631Sjfv		}
97270631Sjfv	if (!sflag)
98270631Sjfv		printf("\n");
99270631Sjfv}
100270631Sjfv
101292100Ssmhint
102292100Ssmhmain(int argc, char *argv[])
103292100Ssmh{
104292100Ssmh	char	mixer[PATH_MAX] = "/dev/mixer";
105292100Ssmh	char	lstr[5], rstr[5];
106292100Ssmh	char	*name, *eptr;
107292100Ssmh	int	devmask = 0, recmask = 0, recsrc = 0, orecsrc;
108292100Ssmh	int	dusage = 0, drecsrc = 0, sflag = 0, Sflag = 0;
109292100Ssmh	int	l, r, lrel, rrel;
110292100Ssmh	int	ch, foo, bar, baz, dev, m, n, t;
111292100Ssmh
112292100Ssmh	if ((name = strdup(basename(argv[0]))) == NULL)
113292100Ssmh		err(1, "strdup()");
114292100Ssmh	if (strncmp(name, "mixer", 5) == 0 && name[5] != '\0') {
115292100Ssmh		n = strtol(name + 5, &eptr, 10) - 1;
116292100Ssmh		if (n > 0 && *eptr == '\0')
117292100Ssmh			snprintf(mixer, PATH_MAX - 1, "/dev/mixer%d", n);
118292100Ssmh	}
119292100Ssmh	free(name);
120292100Ssmh	name = mixer;
121292100Ssmh
122292100Ssmh	n = 1;
123292100Ssmh	for (;;) {
124292100Ssmh		if (n >= argc || *argv[n] != '-')
125292100Ssmh			break;
126292100Ssmh		if (strlen(argv[n]) != 2) {
127292100Ssmh			if (strcmp(argv[n] + 1, "rec") != 0)
128292100Ssmh				dusage = 1;
129292100Ssmh			break;
130292100Ssmh		}
131292100Ssmh		ch = *(argv[n] + 1);
132292100Ssmh		if (ch == 'f' && n < argc - 1) {
133292100Ssmh			name = argv[n + 1];
134292100Ssmh			n += 2;
135292100Ssmh		} else if (ch == 's') {
136292100Ssmh			sflag = 1;
137292100Ssmh			n++;
138292100Ssmh		} else if (ch == 'S') {
139292100Ssmh			Sflag = 1;
140292100Ssmh			n++;
141292100Ssmh		} else {
142292100Ssmh			dusage = 1;
143292100Ssmh			break;
144292100Ssmh		}
145292100Ssmh	}
146292100Ssmh	if (sflag && Sflag)
147292100Ssmh		dusage = 1;
148292100Ssmh
149292100Ssmh	argc -= n - 1;
150292100Ssmh	argv += n - 1;
151292100Ssmh
152292100Ssmh	if ((baz = open(name, O_RDWR)) < 0)
153292100Ssmh		err(1, "%s", name);
154292100Ssmh	if (ioctl(baz, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
155292100Ssmh		err(1, "SOUND_MIXER_READ_DEVMASK");
156292100Ssmh	if (ioctl(baz, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
157292100Ssmh		err(1, "SOUND_MIXER_READ_RECMASK");
158292100Ssmh	if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
159292100Ssmh		err(1, "SOUND_MIXER_READ_RECSRC");
160292100Ssmh	orecsrc = recsrc;
161292100Ssmh
162292100Ssmh	if (argc == 1 && dusage == 0) {
163292100Ssmh		for (foo = 0, n = 0; foo < SOUND_MIXER_NRDEVICES; foo++) {
164292100Ssmh			if (!((1 << foo) & devmask))
165292100Ssmh				continue;
166292100Ssmh			if (ioctl(baz, MIXER_READ(foo),&bar) == -1) {
167292100Ssmh			   	warn("MIXER_READ");
168292100Ssmh				continue;
169292100Ssmh			}
170292100Ssmh			if (Sflag || sflag) {
171292100Ssmh				printf("%s%s%c%d:%d", n ? " " : "",
172292100Ssmh				    names[foo], Sflag ? ':' : ' ',
173292100Ssmh				    bar & 0x7f, (bar >> 8) & 0x7f);
174292100Ssmh				n++;
175292100Ssmh			} else
176292100Ssmh				printf("Mixer %-8s is currently set to "
177292100Ssmh				    "%3d:%d\n", names[foo], bar & 0x7f,
178292100Ssmh				    (bar >> 8) & 0x7f);
179292100Ssmh		}
180292100Ssmh		if (n && recmask)
181292100Ssmh			printf(" ");
182292100Ssmh		print_recsrc(recsrc, recmask, Sflag || sflag);
183292100Ssmh		return (0);
184292100Ssmh	}
185292100Ssmh
186292100Ssmh	argc--;
187292100Ssmh	argv++;
188292100Ssmh
189292100Ssmh	n = 0;
190292100Ssmh	while (argc > 0 && dusage == 0) {
191292100Ssmh		if (strcmp("recsrc", *argv) == 0) {
192292100Ssmh			drecsrc = 1;
193292100Ssmh			argc--;
194292100Ssmh			argv++;
195292100Ssmh			continue;
196292100Ssmh		} else if (strcmp("rec", *argv + 1) == 0) {
197292100Ssmh			if (**argv != '+' && **argv != '-' &&
198292100Ssmh			    **argv != '=' && **argv != '^') {
199292100Ssmh				warnx("unknown modifier: %c", **argv);
200292100Ssmh				dusage = 1;
201292100Ssmh				break;
202292100Ssmh			}
203292100Ssmh			if (argc <= 1) {
204292100Ssmh				warnx("no recording device specified");
205292100Ssmh				dusage = 1;
206292100Ssmh				break;
207292100Ssmh			}
208292100Ssmh			if ((dev = res_name(argv[1], recmask)) == -1) {
209292100Ssmh				warnx("unknown recording device: %s", argv[1]);
210292100Ssmh				dusage = 1;
211292100Ssmh				break;
212292100Ssmh			}
213292100Ssmh			switch (**argv) {
214292100Ssmh			case '+':
215292100Ssmh				recsrc |= (1 << dev);
216292100Ssmh				break;
217292100Ssmh			case '-':
218292100Ssmh				recsrc &= ~(1 << dev);
219292100Ssmh				break;
220292100Ssmh			case '=':
221292100Ssmh				recsrc = (1 << dev);
222292100Ssmh				break;
223292100Ssmh			case '^':
224292100Ssmh				recsrc ^= (1 << dev);
225292100Ssmh				break;
226292100Ssmh			}
227292100Ssmh			drecsrc = 1;
228292100Ssmh			argc -= 2;
229292100Ssmh			argv += 2;
230292100Ssmh			continue;
231292100Ssmh		}
232292100Ssmh
233292100Ssmh		if ((t = sscanf(*argv, "%d:%d", &l, &r)) > 0)
234292100Ssmh			dev = 0;
235292100Ssmh		else if ((dev = res_name(*argv, devmask)) == -1) {
236292100Ssmh			warnx("unknown device: %s", *argv);
237292100Ssmh			dusage = 1;
238292100Ssmh			break;
239292100Ssmh		}
240292100Ssmh
241292100Ssmh		lrel = rrel = 0;
242292100Ssmh		if (argc > 1) {
243292100Ssmh			m = sscanf(argv[1], "%7[^:]:%7s", lstr, rstr);
244292100Ssmh			if (m > 0) {
245292100Ssmh				if (*lstr == '+' || *lstr == '-')
246292100Ssmh					lrel = rrel = 1;
247292100Ssmh				l = strtol(lstr, NULL, 10);
248292100Ssmh			}
249292100Ssmh			if (m > 1) {
250292100Ssmh				if (*rstr == '+' || *rstr == '-')
251292100Ssmh					rrel = 1;
252292100Ssmh				r = strtol(rstr, NULL, 10);
253292100Ssmh			}
254292100Ssmh		}
255292100Ssmh
256292100Ssmh		switch (argc > 1 ? m : t) {
257292100Ssmh		case 0:
258292100Ssmh			if (ioctl(baz, MIXER_READ(dev), &bar) == -1) {
259292100Ssmh				warn("MIXER_READ");
260292100Ssmh				argc--;
261292100Ssmh				argv++;
262292100Ssmh				continue;
263292100Ssmh			}
264292100Ssmh			if (Sflag || sflag) {
265292100Ssmh				printf("%s%s%c%d:%d", n ? " " : "",
266292100Ssmh				    names[dev], Sflag ? ':' : ' ',
267292100Ssmh				    bar & 0x7f, (bar >> 8) & 0x7f);
268292100Ssmh				n++;
269292100Ssmh			} else
270292100Ssmh				printf("Mixer %-8s is currently set to "
271292100Ssmh				    "%3d:%d\n", names[dev], bar & 0x7f,
272292100Ssmh				    (bar >> 8) & 0x7f);
273292100Ssmh
274292100Ssmh			argc--;
275292100Ssmh			argv++;
276292100Ssmh			break;
277292100Ssmh		case 1:
278292100Ssmh			r = l;
279292100Ssmh			/* FALLTHROUGH */
280292100Ssmh		case 2:
281292100Ssmh			if (ioctl(baz, MIXER_READ(dev), &bar) == -1) {
282292100Ssmh				warn("MIXER_READ");
283292100Ssmh				argc--;
284292100Ssmh				argv++;
285292100Ssmh				continue;
286292100Ssmh			}
287292100Ssmh
288292100Ssmh			if (lrel)
289292100Ssmh				l = (bar & 0x7f) + l;
290292100Ssmh			if (rrel)
291292100Ssmh				r = ((bar >> 8) & 0x7f) + r;
292292100Ssmh
293292100Ssmh			if (l < 0)
294292100Ssmh				l = 0;
295292100Ssmh			else if (l > 100)
296292100Ssmh				l = 100;
297292100Ssmh			if (r < 0)
298292100Ssmh				r = 0;
299292100Ssmh			else if (r > 100)
300292100Ssmh				r = 100;
301292100Ssmh
302292100Ssmh			if (!Sflag)
303292100Ssmh				printf("Setting the mixer %s from %d:%d to "
304292100Ssmh				    "%d:%d.\n", names[dev], bar & 0x7f,
305292100Ssmh				    (bar >> 8) & 0x7f, l, r);
306292100Ssmh
307270631Sjfv			l |= r << 8;
308270631Sjfv			if (ioctl(baz, MIXER_WRITE(dev), &l) == -1)
309270631Sjfv				warn("WRITE_MIXER");
310270631Sjfv
311270631Sjfv			argc -= 2;
312270631Sjfv			argv += 2;
313270631Sjfv 			break;
314270631Sjfv		}
315270631Sjfv	}
316270631Sjfv
317270631Sjfv	if (dusage) {
318270631Sjfv		close(baz);
319270631Sjfv		usage(devmask, recmask);
320270631Sjfv		/* NOTREACHED */
321291248Ssmh	}
322291248Ssmh
323270631Sjfv	if (orecsrc != recsrc) {
324270631Sjfv		if (ioctl(baz, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1)
325270631Sjfv			err(1, "SOUND_MIXER_WRITE_RECSRC");
326270631Sjfv		if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
327270631Sjfv			err(1, "SOUND_MIXER_READ_RECSRC");
328270631Sjfv	}
329291248Ssmh
330291248Ssmh	if (drecsrc)
331291248Ssmh		print_recsrc(recsrc, recmask, Sflag || sflag);
332291248Ssmh
333270631Sjfv	close(baz);
334291248Ssmh
335291248Ssmh	return (0);
336270631Sjfv}
337291248Ssmh