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