xref: /netbsd-src/usr.bin/mixerctl/mixerctl.c (revision 4472dbe5e3bd91ef2540bada7a7ca7384627ff9b)
1 /*	$NetBSD: mixerctl.c,v 1.14 1998/11/25 22:17:08 augustss Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Lennart Augustsson (augustss@netbsd.org) and Chuck Cranor.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <fcntl.h>
41 #include <err.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <sys/types.h>
45 #include <sys/ioctl.h>
46 #include <sys/audioio.h>
47 
48 #define MIXER "/dev/mixer0"
49 #define OLD_MIXER "/dev/mixer"
50 
51 char *catstr __P((char *p, char *q));
52 struct field *findfield __P((char *name));
53 void prfield __P((struct field *p, char *sep, int prvalset));
54 int rdfield __P((struct field *p, char *q));
55 int main(int argc, char **argv);
56 
57 FILE *out = stdout;
58 
59 char *prog;
60 
61 struct field {
62 	char *name;
63 	mixer_ctrl_t *valp;
64 	mixer_devinfo_t *infp;
65 	char changed;
66 } *fields, *rfields;
67 
68 mixer_ctrl_t *values;
69 mixer_devinfo_t *infos;
70 
71 char *
72 catstr(p, q)
73 	char *p;
74         char *q;
75 {
76 	char *r = malloc(strlen(p) + strlen(q) + 2);
77 	strcpy(r, p);
78 	strcat(r, ".");
79 	strcat(r, q);
80 	return r;
81 }
82 
83 struct field *
84 findfield(name)
85 	char *name;
86 {
87 	int i;
88 	for(i = 0; fields[i].name; i++)
89 		if (strcmp(fields[i].name, name) == 0)
90 			return &fields[i];
91 	return 0;
92 }
93 
94 void
95 prfield(p, sep, prvalset)
96 	struct field *p;
97         char *sep;
98         int prvalset;
99 {
100 	mixer_ctrl_t *m;
101 	int i, n;
102 
103 	if (sep)
104 		fprintf(out, "%s%s", p->name, sep);
105 	m = p->valp;
106 	switch(m->type) {
107 	case AUDIO_MIXER_ENUM:
108 		for(i = 0; i < p->infp->un.e.num_mem; i++)
109 			if (p->infp->un.e.member[i].ord == m->un.ord)
110 				fprintf(out, "%s",
111 					p->infp->un.e.member[i].label.name);
112 		if (prvalset) {
113 			fprintf(out, "  [ ");
114 			for(i = 0; i < p->infp->un.e.num_mem; i++)
115 				fprintf(out, "%s ", p->infp->un.e.member[i].label.name);
116 			fprintf(out, "]");
117 		}
118 		break;
119 	case AUDIO_MIXER_SET:
120 		for(n = i = 0; i < p->infp->un.s.num_mem; i++)
121 			if (m->un.mask & p->infp->un.s.member[i].mask)
122 				fprintf(out, "%s%s", n++ ? "," : "",
123 					p->infp->un.s.member[i].label.name);
124 		if (prvalset) {
125 			fprintf(out, "  { ");
126 			for(i = 0; i < p->infp->un.s.num_mem; i++)
127 				fprintf(out, "%s ", p->infp->un.s.member[i].label.name);
128 			fprintf(out, "}");
129 		}
130 		break;
131 	case AUDIO_MIXER_VALUE:
132 		if (m->un.value.num_channels == 1)
133 			fprintf(out, "%d", m->un.value.level[0]);
134 		else
135 			fprintf(out, "%d,%d", m->un.value.level[0],
136 			       m->un.value.level[1]);
137 		if (prvalset)
138 			fprintf(out, " %s", p->infp->un.v.units.name);
139 		break;
140 	default:
141 		printf("\n");
142 		errx(1, "Invalid format.");
143 	}
144 }
145 
146 int
147 rdfield(p, q)
148 	struct field *p;
149         char *q;
150 {
151 	mixer_ctrl_t *m;
152 	int v, v0, v1, mask;
153 	int i;
154 	char *s;
155 
156 	m = p->valp;
157 	switch(m->type) {
158 	case AUDIO_MIXER_ENUM:
159 		for(i = 0; i < p->infp->un.e.num_mem; i++)
160 			if (strcmp(p->infp->un.e.member[i].label.name, q) == 0)
161 				break;
162 		if (i < p->infp->un.e.num_mem)
163 			m->un.ord = p->infp->un.e.member[i].ord;
164 		else {
165 			warnx("Bad enum value %s", q);
166 			return 0;
167 		}
168 		break;
169 	case AUDIO_MIXER_SET:
170 		mask = 0;
171 		for(v = 0; q && *q; q = s) {
172 			s = strchr(q, ',');
173 			if (s)
174 				*s++ = 0;
175 			for(i = 0; i < p->infp->un.s.num_mem; i++)
176 				if (strcmp(p->infp->un.s.member[i].label.name, q) == 0)
177 					break;
178 			if (i < p->infp->un.s.num_mem) {
179 				mask |= p->infp->un.s.member[i].mask;
180 			} else {
181 				warnx("Bad set value %s", q);
182 				return 0;
183 			}
184 		}
185 		m->un.mask = mask;
186 		break;
187 	case AUDIO_MIXER_VALUE:
188 		if (m->un.value.num_channels == 1) {
189 			if (sscanf(q, "%d", &v) == 1) {
190 				m->un.value.level[0] = v;
191 			} else {
192 				warnx("Bad number %s", q);
193 				return 0;
194 			}
195 		} else {
196 			if (sscanf(q, "%d,%d", &v0, &v1) == 2) {
197 				m->un.value.level[0] = v0;
198 				m->un.value.level[1] = v1;
199 			} else if (sscanf(q, "%d", &v) == 1) {
200 				m->un.value.level[0] = m->un.value.level[1] = v;
201 			} else {
202 				warnx("Bad numbers %s", q);
203 				return 0;
204 			}
205 		}
206 		break;
207 	default:
208 		errx(1, "Invalid format.");
209 	}
210 	p->changed = 1;
211 	return 1;
212 }
213 
214 int
215 main(argc, argv)
216 	int argc;
217         char **argv;
218 {
219 	int fd, i, j, ch, pos;
220 	int aflag = 0, wflag = 0, vflag = 0;
221 	char *file;
222 	char *sep = "=";
223 	mixer_devinfo_t dinfo;
224 	mixer_ctrl_t val;
225 	int ndev;
226 
227 	file = getenv("MIXERDEVICE");
228 	if (file == 0)
229 		file = MIXER;
230 
231 	prog = *argv;
232 
233 	while ((ch = getopt(argc, argv, "af:nvw")) != -1) {
234 		switch(ch) {
235 		case 'a':
236 			aflag++;
237 			break;
238 		case 'w':
239 			wflag++;
240 			break;
241 		case 'v':
242 			vflag++;
243 			break;
244 		case 'n':
245 			sep = 0;
246 			break;
247 		case 'f':
248 			file = optarg;
249 			break;
250 		case '?':
251 		default:
252 		usage:
253 		fprintf(out, "%s [-f file] [-v] [-n] name ...\n", prog);
254 		fprintf(out, "%s [-f file] [-v] [-n] -w name=value ...\n", prog);
255 		fprintf(out, "%s [-f file] [-v] [-n] -a\n", prog);
256 		exit(0);
257 		}
258 	}
259 	argc -= optind;
260 	argv += optind;
261 
262 	fd = open(file, O_RDWR);
263 #ifdef OLD_MIXER
264         /* Allow the non-unit device to be used. */
265         if (fd < 0 && file == MIXER) {
266         	file = OLD_MIXER;
267                 fd = open(file, O_RDWR);
268         }
269 #endif
270 	if (fd < 0)
271 		err(1, "%s", file);
272 
273 	for(ndev = 0; ; ndev++) {
274 		dinfo.index = ndev;
275 		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
276 			break;
277 	}
278 	rfields = calloc(ndev, sizeof *rfields);
279 	fields = calloc(ndev, sizeof *fields);
280 	infos = calloc(ndev, sizeof *infos);
281 	values = calloc(ndev, sizeof *values);
282 
283 	for(i = 0; i < ndev; i++) {
284 		infos[i].index = i;
285 		ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
286 	}
287 
288 	for(i = 0; i < ndev; i++) {
289 		rfields[i].name = infos[i].label.name;
290 		rfields[i].valp = &values[i];
291 		rfields[i].infp = &infos[i];
292 	}
293 
294 	for(i = 0; i < ndev; i++) {
295 		values[i].dev = i;
296 		values[i].type = infos[i].type;
297 		if (infos[i].type != AUDIO_MIXER_CLASS) {
298 			values[i].un.value.num_channels = 2;
299 			if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
300 				values[i].un.value.num_channels = 1;
301 				if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
302 					err(1, "AUDIO_MIXER_READ");
303 			}
304 		}
305 	}
306 
307 	for(j = i = 0; i < ndev; i++) {
308 		if (infos[i].type != AUDIO_MIXER_CLASS &&
309 		    infos[i].type != -1) {
310 			fields[j++] = rfields[i];
311 			for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
312 			    pos = infos[pos].next) {
313 				fields[j] = rfields[pos];
314 				fields[j].name = catstr(rfields[i].name,
315 							infos[pos].label.name);
316 				infos[pos].type = -1;
317 				j++;
318 			}
319 		}
320 	}
321 
322 	for(i = 0; i < j; i++) {
323 		int cls = fields[i].infp->mixer_class;
324 		if (cls >= 0 && cls < ndev)
325 			fields[i].name = catstr(infos[cls].label.name,
326 						fields[i].name);
327 	}
328 
329 	if (argc == 0 && aflag && !wflag) {
330 		for(i = 0; fields[i].name; i++) {
331 			prfield(&fields[i], sep, vflag);
332 			fprintf(out, "\n");
333 		}
334 	} else if (argc > 0 && !aflag) {
335 		struct field *p;
336 		if (wflag) {
337 			while(argc--) {
338 				char *q;
339 
340 				q = strchr(*argv, '=');
341 				if (q) {
342 					*q++ = 0;
343 					p = findfield(*argv);
344 					if (p == 0)
345 						warnx("field %s does not exist", *argv);
346 					else {
347 						val = *p->valp;
348 						if (rdfield(p, q)) {
349 							if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
350 								warn("AUDIO_MIXER_WRITE");
351 							else if (sep) {
352 								*p->valp = val;
353 								prfield(p, ": ", 0);
354 								ioctl(fd, AUDIO_MIXER_READ, p->valp);
355 								printf(" -> ");
356 								prfield(p, 0, 0);
357 								printf("\n");
358 							}
359 						}
360 					}
361 				} else {
362 					warnx("No `=' in %s", *argv);
363 				}
364 				argv++;
365 			}
366 		} else {
367 			while(argc--) {
368 				p = findfield(*argv);
369 				if (p == 0)
370 					warnx("field %s does not exist", *argv);
371 				else
372 					prfield(p, sep, vflag), fprintf(out, "\n");
373 				argv++;
374 			}
375 		}
376 	} else
377 		goto usage;
378 	exit(0);
379 }
380