mixerctl.c revision 1.18 1 /* $NetBSD: mixerctl.c,v 1.18 2003/06/23 13:05:49 agc 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 <sys/cdefs.h>
39
40 #ifndef lint
41 __RCSID("$NetBSD: mixerctl.c,v 1.18 2003/06/23 13:05:49 agc Exp $");
42 #endif
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <fcntl.h>
47 #include <err.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <sys/types.h>
51 #include <sys/ioctl.h>
52 #include <sys/audioio.h>
53
54 #include <paths.h>
55
56 FILE *out = stdout;
57 int vflag = 0;
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 static char *
72 catstr(char *p, char *q)
73 {
74 char *r = malloc(strlen(p) + strlen(q) + 2);
75 strcpy(r, p);
76 strcat(r, ".");
77 strcat(r, q);
78 return r;
79 }
80
81 static struct field *
82 findfield(char *name)
83 {
84 int i;
85 for(i = 0; fields[i].name; i++)
86 if (strcmp(fields[i].name, name) == 0)
87 return &fields[i];
88 return 0;
89 }
90
91 static void
92 prfield(struct field *p, char *sep, int prvalset)
93 {
94 mixer_ctrl_t *m;
95 int i, n;
96
97 if (sep)
98 fprintf(out, "%s%s", p->name, sep);
99 m = p->valp;
100 switch(m->type) {
101 case AUDIO_MIXER_ENUM:
102 for(i = 0; i < p->infp->un.e.num_mem; i++)
103 if (p->infp->un.e.member[i].ord == m->un.ord)
104 fprintf(out, "%s",
105 p->infp->un.e.member[i].label.name);
106 if (prvalset) {
107 fprintf(out, " [ ");
108 for(i = 0; i < p->infp->un.e.num_mem; i++)
109 fprintf(out, "%s ",
110 p->infp->un.e.member[i].label.name);
111 fprintf(out, "]");
112 }
113 break;
114 case AUDIO_MIXER_SET:
115 for(n = i = 0; i < p->infp->un.s.num_mem; i++)
116 if (m->un.mask & p->infp->un.s.member[i].mask)
117 fprintf(out, "%s%s", n++ ? "," : "",
118 p->infp->un.s.member[i].label.name);
119 if (prvalset) {
120 fprintf(out, " { ");
121 for(i = 0; i < p->infp->un.s.num_mem; i++)
122 fprintf(out, "%s ",
123 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 if (p->infp->un.v.delta)
136 fprintf(out, " delta=%d", p->infp->un.v.delta);
137 }
138 break;
139 default:
140 printf("\n");
141 errx(1, "Invalid format.");
142 }
143 }
144
145 static int
146 rdfield(struct field *p, char *q)
147 {
148 mixer_ctrl_t *m;
149 int v, v0, v1, mask;
150 int i;
151 char *s;
152
153 m = p->valp;
154 switch(m->type) {
155 case AUDIO_MIXER_ENUM:
156 for(i = 0; i < p->infp->un.e.num_mem; i++)
157 if (strcmp(p->infp->un.e.member[i].label.name, q) == 0)
158 break;
159 if (i < p->infp->un.e.num_mem)
160 m->un.ord = p->infp->un.e.member[i].ord;
161 else {
162 warnx("Bad enum value %s", q);
163 return 0;
164 }
165 break;
166 case AUDIO_MIXER_SET:
167 mask = 0;
168 for(v = 0; q && *q; q = s) {
169 s = strchr(q, ',');
170 if (s)
171 *s++ = 0;
172 for(i = 0; i < p->infp->un.s.num_mem; i++)
173 if (strcmp(p->infp->un.s.member[i].label.name,
174 q) == 0)
175 break;
176 if (i < p->infp->un.s.num_mem) {
177 mask |= p->infp->un.s.member[i].mask;
178 } else {
179 warnx("Bad set value %s", q);
180 return 0;
181 }
182 }
183 m->un.mask = mask;
184 break;
185 case AUDIO_MIXER_VALUE:
186 if (m->un.value.num_channels == 1) {
187 if (sscanf(q, "%d", &v) == 1) {
188 m->un.value.level[0] = v;
189 } else {
190 warnx("Bad number %s", q);
191 return 0;
192 }
193 } else {
194 if (sscanf(q, "%d,%d", &v0, &v1) == 2) {
195 m->un.value.level[0] = v0;
196 m->un.value.level[1] = v1;
197 } else if (sscanf(q, "%d", &v) == 1) {
198 m->un.value.level[0] = m->un.value.level[1] = v;
199 } else {
200 warnx("Bad numbers %s", q);
201 return 0;
202 }
203 }
204 break;
205 default:
206 errx(1, "Invalid format.");
207 }
208 p->changed = 1;
209 return 1;
210 }
211
212 static int
213 incfield(struct field *p, int inc)
214 {
215 mixer_ctrl_t *m;
216 int i, v;
217
218 m = p->valp;
219 switch(m->type) {
220 case AUDIO_MIXER_ENUM:
221 m->un.ord += inc;
222 if (m->un.ord < 0)
223 m->un.ord = p->infp->un.e.num_mem-1;
224 if (m->un.ord >= p->infp->un.e.num_mem)
225 m->un.ord = 0;
226 break;
227 case AUDIO_MIXER_SET:
228 m->un.mask += inc;
229 if (m->un.mask < 0)
230 m->un.mask = (1 << p->infp->un.s.num_mem) - 1;
231 if (m->un.mask >= (1 << p->infp->un.s.num_mem))
232 m->un.mask = 0;
233 warnx("Can't ++/-- %s", p->name);
234 return 0;
235 case AUDIO_MIXER_VALUE:
236 if (p->infp->un.v.delta)
237 inc *= p->infp->un.v.delta;
238 for (i = 0; i < m->un.value.num_channels; i++) {
239 v = m->un.value.level[i];
240 v += inc;
241 if (v < AUDIO_MIN_GAIN)
242 v = AUDIO_MIN_GAIN;
243 if (v > AUDIO_MAX_GAIN)
244 v = AUDIO_MAX_GAIN;
245 m->un.value.level[i] = v;
246 }
247 break;
248 default:
249 errx(1, "Invalid format.");
250 }
251 p->changed = 1;
252 return 1;
253 }
254
255 static void
256 wrarg(int fd, char *arg, char *sep)
257 {
258 char *q;
259 struct field *p;
260 mixer_ctrl_t val;
261 int incdec, r;
262
263 q = strchr(arg, '=');
264 if (q == NULL) {
265 int l = strlen(arg);
266 incdec = 0;
267 if (l > 2 && arg[l-2] == '+' && arg[l-1] == '+')
268 incdec = 1;
269 else if (l > 2 && arg[l-2] == '-' && arg[l-1] == '-')
270 incdec = -1;
271 else {
272 warnx("No `=' in %s", arg);
273 return;
274 }
275 arg[l-2] = 0;
276 } else
277 *q++ = 0;
278
279 p = findfield(arg);
280 if (p == NULL) {
281 warnx("field %s does not exist", arg);
282 return;
283 }
284
285 val = *p->valp;
286 if (q != NULL)
287 r = rdfield(p, q);
288 else
289 r = incfield(p, incdec);
290 if (r) {
291 if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
292 warn("AUDIO_MIXER_WRITE");
293 else if (sep) {
294 *p->valp = val;
295 prfield(p, ": ", 0);
296 ioctl(fd, AUDIO_MIXER_READ, p->valp);
297 printf(" -> ");
298 prfield(p, 0, 0);
299 printf("\n");
300 }
301 }
302 }
303
304 static void
305 prarg(int fd, char *arg, char *sep)
306 {
307 struct field *p;
308
309 p = findfield(arg);
310 if (p == NULL)
311 warnx("field %s does not exist", arg);
312 else
313 prfield(p, sep, vflag), fprintf(out, "\n");
314 }
315
316 int
317 main(int argc, char **argv)
318 {
319 int fd, i, j, ch, pos;
320 int aflag = 0, wflag = 0;
321 char *file;
322 char *sep = "=";
323 mixer_devinfo_t dinfo;
324 int ndev;
325
326 file = getenv("MIXERDEVICE");
327 if (file == NULL)
328 file = _PATH_MIXER;
329
330 prog = *argv;
331
332 while ((ch = getopt(argc, argv, "ad:f:nvw")) != -1) {
333 switch(ch) {
334 case 'a':
335 aflag++;
336 break;
337 case 'w':
338 wflag++;
339 break;
340 case 'v':
341 vflag++;
342 break;
343 case 'n':
344 sep = 0;
345 break;
346 case 'f': /* compatibility */
347 case 'd':
348 file = optarg;
349 break;
350 case '?':
351 default:
352 usage:
353 fprintf(out, "%s [-d file] [-v] [-n] name ...\n", prog);
354 fprintf(out, "%s [-d file] [-v] [-n] -w name=value ...\n",prog);
355 fprintf(out, "%s [-d file] [-v] [-n] -a\n", prog);
356 exit(0);
357 }
358 }
359 argc -= optind;
360 argv += optind;
361
362 fd = open(file, O_RDWR);
363 /* Try with mixer0. */
364 if (fd < 0 && file == _PATH_MIXER) {
365 file = _PATH_MIXER0;
366 fd = open(file, O_RDWR);
367 }
368
369 if (fd < 0)
370 err(1, "%s", file);
371
372 for(ndev = 0; ; ndev++) {
373 dinfo.index = ndev;
374 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
375 break;
376 }
377 rfields = calloc(ndev, sizeof *rfields);
378 fields = calloc(ndev, sizeof *fields);
379 infos = calloc(ndev, sizeof *infos);
380 values = calloc(ndev, sizeof *values);
381
382 for(i = 0; i < ndev; i++) {
383 infos[i].index = i;
384 ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
385 }
386
387 for(i = 0; i < ndev; i++) {
388 rfields[i].name = infos[i].label.name;
389 rfields[i].valp = &values[i];
390 rfields[i].infp = &infos[i];
391 }
392
393 for(i = 0; i < ndev; i++) {
394 values[i].dev = i;
395 values[i].type = infos[i].type;
396 if (infos[i].type != AUDIO_MIXER_CLASS) {
397 values[i].un.value.num_channels = 2;
398 if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
399 values[i].un.value.num_channels = 1;
400 if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
401 err(1, "AUDIO_MIXER_READ");
402 }
403 }
404 }
405
406 for(j = i = 0; i < ndev; i++) {
407 if (infos[i].type != AUDIO_MIXER_CLASS &&
408 infos[i].type != -1) {
409 fields[j++] = rfields[i];
410 for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
411 pos = infos[pos].next) {
412 fields[j] = rfields[pos];
413 fields[j].name = catstr(rfields[i].name,
414 infos[pos].label.name);
415 infos[pos].type = -1;
416 j++;
417 }
418 }
419 }
420
421 for(i = 0; i < j; i++) {
422 int cls = fields[i].infp->mixer_class;
423 if (cls >= 0 && cls < ndev)
424 fields[i].name = catstr(infos[cls].label.name,
425 fields[i].name);
426 }
427
428 if (argc == 0 && aflag && !wflag) {
429 for(i = 0; fields[i].name; i++) {
430 prfield(&fields[i], sep, vflag);
431 fprintf(out, "\n");
432 }
433 } else if (argc > 0 && !aflag) {
434 while(argc--) {
435 if (wflag)
436 wrarg(fd, *argv, sep);
437 else
438 prarg(fd, *argv, sep);
439 argv++;
440 }
441 } else
442 goto usage;
443 exit(0);
444 }
445