xref: /netbsd-src/usr.bin/mixerctl/mixerctl.c (revision a5a68ff5f29de57339ca14f6c671c0a87714f1f8)
1 /*	$NetBSD: mixerctl.c,v 1.5 1997/08/24 23:20: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 REGENTS OR CONTRIBUTORS BE
29  * 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 FILE *out = stdout;
48 
49 char *prog;
50 
51 struct field {
52 	char *name;
53 	mixer_ctrl_t *valp;
54 	mixer_devinfo_t *infp;
55 	char changed;
56 } *fields, *rfields;
57 
58 mixer_ctrl_t *values;
59 mixer_devinfo_t *infos;
60 
61 char *
62 catstr(char *p, char *q)
63 {
64 	char *r = malloc(strlen(p) + strlen(q) + 2);
65 	strcpy(r, p);
66 	strcat(r, ".");
67 	strcat(r, q);
68 	return r;
69 }
70 
71 struct field *
72 findfield(char *name)
73 {
74 	int i;
75 	for(i = 0; fields[i].name; i++)
76 		if (strcmp(fields[i].name, name) == 0)
77 			return &fields[i];
78 	return 0;
79 }
80 
81 void
82 prfield(struct field *p, char *sep, int prvalset)
83 {
84 	mixer_ctrl_t *m;
85 	int i, n;
86 
87 	if (sep)
88 		fprintf(out, "%s%s", p->name, sep);
89 	m = p->valp;
90 	switch(m->type) {
91 	case AUDIO_MIXER_ENUM:
92 		for(i = 0; i < p->infp->un.e.num_mem; i++)
93 			if (p->infp->un.e.member[i].ord == m->un.ord)
94 				fprintf(out, "%s",
95 					p->infp->un.e.member[i].label.name);
96 		if (prvalset) {
97 			fprintf(out, "  [ ");
98 			for(i = 0; i < p->infp->un.e.num_mem; i++)
99 				fprintf(out, "%s ", p->infp->un.e.member[i].label.name);
100 			fprintf(out, "]");
101 		}
102 		break;
103 	case AUDIO_MIXER_SET:
104 		for(n = i = 0; i < p->infp->un.s.num_mem; i++)
105 			if (m->un.mask & p->infp->un.s.member[i].mask)
106 				fprintf(out, "%s%s", n++ ? "," : "",
107 					p->infp->un.s.member[i].label.name);
108 		if (prvalset) {
109 			fprintf(out, "  { ");
110 			for(i = 0; i < p->infp->un.s.num_mem; i++)
111 				fprintf(out, "%s ", p->infp->un.s.member[i].label.name);
112 			fprintf(out, "}");
113 		}
114 		break;
115 	case AUDIO_MIXER_VALUE:
116 		if (m->un.value.num_channels == 1)
117 			fprintf(out, "%d", m->un.value.level[0]);
118 		else
119 			fprintf(out, "%d,%d", m->un.value.level[0],
120 			       m->un.value.level[1]);
121 		break;
122 	default:
123 		printf("\n");
124 		errx(1, "Invalid format.");
125 	}
126 }
127 
128 int
129 rdfield(struct field *p, char *q)
130 {
131 	mixer_ctrl_t *m;
132 	int v, v0, v1, mask;
133 	int i;
134 	char *s;
135 
136 	m = p->valp;
137 	switch(m->type) {
138 	case AUDIO_MIXER_ENUM:
139 		for(i = 0; i < p->infp->un.e.num_mem; i++)
140 			if (strcmp(p->infp->un.e.member[i].label.name, q) == 0)
141 				break;
142 		if (i < p->infp->un.e.num_mem)
143 			m->un.ord = p->infp->un.e.member[i].ord;
144 		else {
145 			warnx("Bad enum value %s", q);
146 			return 0;
147 		}
148 		break;
149 	case AUDIO_MIXER_SET:
150 		mask = 0;
151 		for(v = 0; q && *q; q = s) {
152 			s = strchr(q, ',');
153 			if (s)
154 				*s++ = 0;
155 			for(i = 0; i < p->infp->un.s.num_mem; i++)
156 				if (strcmp(p->infp->un.s.member[i].label.name, q) == 0)
157 					break;
158 			if (i < p->infp->un.s.num_mem) {
159 				mask |= p->infp->un.s.member[i].mask;
160 			} else {
161 				warnx("Bad set value %s", q);
162 				return 0;
163 			}
164 		}
165 		m->un.mask = mask;
166 		break;
167 	case AUDIO_MIXER_VALUE:
168 		if (m->un.value.num_channels == 1) {
169 			if (sscanf(q, "%d", &v) == 1) {
170 				m->un.value.level[0] = v;
171 			} else {
172 				warnx("Bad number %s", q);
173 				return 0;
174 			}
175 		} else {
176 			if (sscanf(q, "%d,%d", &v0, &v1) == 2) {
177 				m->un.value.level[0] = v0;
178 				m->un.value.level[1] = v1;
179 			} else if (sscanf(q, "%d", &v) == 1) {
180 				m->un.value.level[0] = m->un.value.level[1] = v;
181 			} else {
182 				warnx("Bad numbers %s", q);
183 				return 0;
184 			}
185 		}
186 		break;
187 	default:
188 		errx(1, "Invalid format.");
189 	}
190 	p->changed = 1;
191 	return 1;
192 }
193 
194 void
195 main(int argc, char **argv)
196 {
197 	int fd, r, i, j, ch, pos;
198 	int aflag = 0, wflag = 0, vflag = 0;
199 	char *file = "/dev/mixer";
200 	char *sep = "=";
201 	mixer_devinfo_t dinfo;
202 	mixer_ctrl_t val;
203 	int ndev;
204 
205 	prog = *argv;
206 
207 	while ((ch = getopt(argc, argv, "af:nvw")) != -1) {
208 		switch(ch) {
209 		case 'a':
210 			aflag++;
211 			break;
212 		case 'w':
213 			wflag++;
214 			break;
215 		case 'v':
216 			vflag++;
217 			break;
218 		case 'n':
219 			sep = 0;
220 			break;
221 		case 'f':
222 			file = optarg;
223 			break;
224 		case '?':
225 		default:
226 		usage:
227 		fprintf(out, "%s [-f file] [-v] [-n] name ...\n", prog);
228 		fprintf(out, "%s [-f file] [-v] [-n] -w name=value ...\n", prog);
229 		fprintf(out, "%s [-f file] [-v] [-n] -a\n", prog);
230 		exit(0);
231 		}
232 	}
233 	argc -= optind;
234 	argv += optind;
235 
236 	fd = open(file, O_RDWR);
237 	if (fd < 0)
238 		err(1, "%s", file);
239 
240 	for(ndev = 0; ; ndev++) {
241 		dinfo.index = ndev;
242 		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
243 			break;
244 	}
245 	rfields = calloc(ndev, sizeof *rfields);
246 	fields = calloc(ndev, sizeof *fields);
247 	infos = calloc(ndev, sizeof *infos);
248 	values = calloc(ndev, sizeof *values);
249 
250 	for(i = 0; i < ndev; i++) {
251 		infos[i].index = i;
252 		ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
253 	}
254 
255 	for(i = 0; i < ndev; i++) {
256 		rfields[i].name = infos[i].label.name;
257 		rfields[i].valp = &values[i];
258 		rfields[i].infp = &infos[i];
259 	}
260 
261 	for(i = 0; i < ndev; i++) {
262 		values[i].dev = i;
263 		values[i].type = infos[i].type;
264 		if (infos[i].type != AUDIO_MIXER_CLASS) {
265 			values[i].un.value.num_channels = 2;
266 			if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
267 				values[i].un.value.num_channels = 1;
268 				if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
269 					err(1, NULL);
270 			}
271 		}
272 	}
273 
274 	for(j = i = 0; i < ndev; i++) {
275 		if (infos[i].type != AUDIO_MIXER_CLASS &&
276 		    infos[i].type != -1) {
277 			fields[j++] = rfields[i];
278 			for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
279 			    pos = infos[pos].next) {
280 				fields[j] = rfields[pos];
281 				fields[j].name = catstr(rfields[i].name,
282 							infos[pos].label.name);
283 				infos[pos].type = -1;
284 				j++;
285 			}
286 		}
287 	}
288 
289 	for(i = 0; i < j; i++) {
290 		if (infos[i].mixer_class >= 0 && infos[i].mixer_class < ndev)
291 			fields[i].name = catstr(infos[fields[i].infp->mixer_class].label.name,
292 						fields[i].name);
293 	}
294 
295 	if (argc == 0 && aflag && !wflag) {
296 		for(i = 0; fields[i].name; i++) {
297 			prfield(&fields[i], sep, vflag);
298 			fprintf(out, "\n");
299 		}
300 	} else if (argc > 0 && !aflag) {
301 		struct field *p;
302 		if (wflag) {
303 			while(argc--) {
304 				char *q;
305 
306 				q = strchr(*argv, '=');
307 				if (q) {
308 					*q++ = 0;
309 					p = findfield(*argv);
310 					if (p == 0)
311 						warnx("field %s does not exist", *argv);
312 					else {
313 						val = *p->valp;
314 						if (rdfield(p, q)) {
315 							if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
316 								warn(NULL);
317 							else if (sep) {
318 								*p->valp = val;
319 								prfield(p, ": ", 0);
320 								ioctl(fd, AUDIO_MIXER_READ, p->valp);
321 								printf(" -> ");
322 								prfield(p, 0, 0);
323 								printf("\n");
324 							}
325 						}
326 					}
327 				} else {
328 					warnx("No `=' in %s", *argv);
329 				}
330 				argv++;
331 			}
332 		} else {
333 			while(argc--) {
334 				p = findfield(*argv);
335 				if (p == 0)
336 					warnx("field %s does not exist", *argv);
337 				else
338 					prfield(p, sep, vflag), fprintf(out, "\n");
339 				argv++;
340 			}
341 		}
342 	} else
343 		goto usage;
344 	exit(0);
345 }
346