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