mixerctl.c revision 1.10 1 /* $NetBSD: mixerctl.c,v 1.10 1998/04/27 16:52:03 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;
218 char *sep = "=";
219 mixer_devinfo_t dinfo;
220 mixer_ctrl_t val;
221 int ndev;
222
223 file = getenv("MIXERUNIT");
224 if (file == 0)
225 file = "/dev/mixer";
226
227 prog = *argv;
228
229 while ((ch = getopt(argc, argv, "af:nvw")) != -1) {
230 switch(ch) {
231 case 'a':
232 aflag++;
233 break;
234 case 'w':
235 wflag++;
236 break;
237 case 'v':
238 vflag++;
239 break;
240 case 'n':
241 sep = 0;
242 break;
243 case 'f':
244 file = optarg;
245 break;
246 case '?':
247 default:
248 usage:
249 fprintf(out, "%s [-f file] [-v] [-n] name ...\n", prog);
250 fprintf(out, "%s [-f file] [-v] [-n] -w name=value ...\n", prog);
251 fprintf(out, "%s [-f file] [-v] [-n] -a\n", prog);
252 exit(0);
253 }
254 }
255 argc -= optind;
256 argv += optind;
257
258 fd = open(file, O_RDWR);
259 if (fd < 0)
260 err(1, "%s", file);
261
262 for(ndev = 0; ; ndev++) {
263 dinfo.index = ndev;
264 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
265 break;
266 }
267 rfields = calloc(ndev, sizeof *rfields);
268 fields = calloc(ndev, sizeof *fields);
269 infos = calloc(ndev, sizeof *infos);
270 values = calloc(ndev, sizeof *values);
271
272 for(i = 0; i < ndev; i++) {
273 infos[i].index = i;
274 ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
275 }
276
277 for(i = 0; i < ndev; i++) {
278 rfields[i].name = infos[i].label.name;
279 rfields[i].valp = &values[i];
280 rfields[i].infp = &infos[i];
281 }
282
283 for(i = 0; i < ndev; i++) {
284 values[i].dev = i;
285 values[i].type = infos[i].type;
286 if (infos[i].type != AUDIO_MIXER_CLASS) {
287 values[i].un.value.num_channels = 2;
288 if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
289 values[i].un.value.num_channels = 1;
290 if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
291 err(1, "AUDIO_MIXER_READ");
292 }
293 }
294 }
295
296 for(j = i = 0; i < ndev; i++) {
297 if (infos[i].type != AUDIO_MIXER_CLASS &&
298 infos[i].type != -1) {
299 fields[j++] = rfields[i];
300 for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
301 pos = infos[pos].next) {
302 fields[j] = rfields[pos];
303 fields[j].name = catstr(rfields[i].name,
304 infos[pos].label.name);
305 infos[pos].type = -1;
306 j++;
307 }
308 }
309 }
310
311 for(i = 0; i < j; i++) {
312 int cls = fields[i].infp->mixer_class;
313 if (cls >= 0 && cls < ndev)
314 fields[i].name = catstr(infos[cls].label.name,
315 fields[i].name);
316 }
317
318 if (argc == 0 && aflag && !wflag) {
319 for(i = 0; fields[i].name; i++) {
320 prfield(&fields[i], sep, vflag);
321 fprintf(out, "\n");
322 }
323 } else if (argc > 0 && !aflag) {
324 struct field *p;
325 if (wflag) {
326 while(argc--) {
327 char *q;
328
329 q = strchr(*argv, '=');
330 if (q) {
331 *q++ = 0;
332 p = findfield(*argv);
333 if (p == 0)
334 warnx("field %s does not exist", *argv);
335 else {
336 val = *p->valp;
337 if (rdfield(p, q)) {
338 if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
339 warn("AUDIO_MIXER_WRITE");
340 else if (sep) {
341 *p->valp = val;
342 prfield(p, ": ", 0);
343 ioctl(fd, AUDIO_MIXER_READ, p->valp);
344 printf(" -> ");
345 prfield(p, 0, 0);
346 printf("\n");
347 }
348 }
349 }
350 } else {
351 warnx("No `=' in %s", *argv);
352 }
353 argv++;
354 }
355 } else {
356 while(argc--) {
357 p = findfield(*argv);
358 if (p == 0)
359 warnx("field %s does not exist", *argv);
360 else
361 prfield(p, sep, vflag), fprintf(out, "\n");
362 argv++;
363 }
364 }
365 } else
366 goto usage;
367 exit(0);
368 }
369