mixerctl.c revision 1.14 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 (at) 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