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