mixerctl.c revision 1.8 1 /* $NetBSD: mixerctl.c,v 1.8 1997/10/16 23:28:17 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 break;
134 default:
135 printf("\n");
136 errx(1, "Invalid format.");
137 }
138 }
139
140 int
141 rdfield(p, q)
142 struct field *p;
143 char *q;
144 {
145 mixer_ctrl_t *m;
146 int v, v0, v1, mask;
147 int i;
148 char *s;
149
150 m = p->valp;
151 switch(m->type) {
152 case AUDIO_MIXER_ENUM:
153 for(i = 0; i < p->infp->un.e.num_mem; i++)
154 if (strcmp(p->infp->un.e.member[i].label.name, q) == 0)
155 break;
156 if (i < p->infp->un.e.num_mem)
157 m->un.ord = p->infp->un.e.member[i].ord;
158 else {
159 warnx("Bad enum value %s", q);
160 return 0;
161 }
162 break;
163 case AUDIO_MIXER_SET:
164 mask = 0;
165 for(v = 0; q && *q; q = s) {
166 s = strchr(q, ',');
167 if (s)
168 *s++ = 0;
169 for(i = 0; i < p->infp->un.s.num_mem; i++)
170 if (strcmp(p->infp->un.s.member[i].label.name, q) == 0)
171 break;
172 if (i < p->infp->un.s.num_mem) {
173 mask |= p->infp->un.s.member[i].mask;
174 } else {
175 warnx("Bad set value %s", q);
176 return 0;
177 }
178 }
179 m->un.mask = mask;
180 break;
181 case AUDIO_MIXER_VALUE:
182 if (m->un.value.num_channels == 1) {
183 if (sscanf(q, "%d", &v) == 1) {
184 m->un.value.level[0] = v;
185 } else {
186 warnx("Bad number %s", q);
187 return 0;
188 }
189 } else {
190 if (sscanf(q, "%d,%d", &v0, &v1) == 2) {
191 m->un.value.level[0] = v0;
192 m->un.value.level[1] = v1;
193 } else if (sscanf(q, "%d", &v) == 1) {
194 m->un.value.level[0] = m->un.value.level[1] = v;
195 } else {
196 warnx("Bad numbers %s", q);
197 return 0;
198 }
199 }
200 break;
201 default:
202 errx(1, "Invalid format.");
203 }
204 p->changed = 1;
205 return 1;
206 }
207
208 int
209 main(argc, argv)
210 int argc;
211 char **argv;
212 {
213 int fd, i, j, ch, pos;
214 int aflag = 0, wflag = 0, vflag = 0;
215 char *file = "/dev/mixer";
216 char *sep = "=";
217 mixer_devinfo_t dinfo;
218 mixer_ctrl_t val;
219 int ndev;
220
221 prog = *argv;
222
223 while ((ch = getopt(argc, argv, "af:nvw")) != -1) {
224 switch(ch) {
225 case 'a':
226 aflag++;
227 break;
228 case 'w':
229 wflag++;
230 break;
231 case 'v':
232 vflag++;
233 break;
234 case 'n':
235 sep = 0;
236 break;
237 case 'f':
238 file = optarg;
239 break;
240 case '?':
241 default:
242 usage:
243 fprintf(out, "%s [-f file] [-v] [-n] name ...\n", prog);
244 fprintf(out, "%s [-f file] [-v] [-n] -w name=value ...\n", prog);
245 fprintf(out, "%s [-f file] [-v] [-n] -a\n", prog);
246 exit(0);
247 }
248 }
249 argc -= optind;
250 argv += optind;
251
252 fd = open(file, O_RDWR);
253 if (fd < 0)
254 err(1, "%s", file);
255
256 for(ndev = 0; ; ndev++) {
257 dinfo.index = ndev;
258 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
259 break;
260 }
261 rfields = calloc(ndev, sizeof *rfields);
262 fields = calloc(ndev, sizeof *fields);
263 infos = calloc(ndev, sizeof *infos);
264 values = calloc(ndev, sizeof *values);
265
266 for(i = 0; i < ndev; i++) {
267 infos[i].index = i;
268 ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
269 }
270
271 for(i = 0; i < ndev; i++) {
272 rfields[i].name = infos[i].label.name;
273 rfields[i].valp = &values[i];
274 rfields[i].infp = &infos[i];
275 }
276
277 for(i = 0; i < ndev; i++) {
278 values[i].dev = i;
279 values[i].type = infos[i].type;
280 if (infos[i].type != AUDIO_MIXER_CLASS) {
281 values[i].un.value.num_channels = 2;
282 if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
283 values[i].un.value.num_channels = 1;
284 if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
285 err(1, "AUDIO_MIXER_READ");
286 }
287 }
288 }
289
290 for(j = i = 0; i < ndev; i++) {
291 if (infos[i].type != AUDIO_MIXER_CLASS &&
292 infos[i].type != -1) {
293 fields[j++] = rfields[i];
294 for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
295 pos = infos[pos].next) {
296 fields[j] = rfields[pos];
297 fields[j].name = catstr(rfields[i].name,
298 infos[pos].label.name);
299 infos[pos].type = -1;
300 j++;
301 }
302 }
303 }
304
305 for(i = 0; i < j; i++) {
306 int cls = fields[i].infp->mixer_class;
307 if (cls >= 0 && cls < ndev)
308 fields[i].name = catstr(infos[cls].label.name,
309 fields[i].name);
310 }
311
312 if (argc == 0 && aflag && !wflag) {
313 for(i = 0; fields[i].name; i++) {
314 prfield(&fields[i], sep, vflag);
315 fprintf(out, "\n");
316 }
317 } else if (argc > 0 && !aflag) {
318 struct field *p;
319 if (wflag) {
320 while(argc--) {
321 char *q;
322
323 q = strchr(*argv, '=');
324 if (q) {
325 *q++ = 0;
326 p = findfield(*argv);
327 if (p == 0)
328 warnx("field %s does not exist", *argv);
329 else {
330 val = *p->valp;
331 if (rdfield(p, q)) {
332 if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
333 warn("AUDIO_MIXER_WRITE");
334 else if (sep) {
335 *p->valp = val;
336 prfield(p, ": ", 0);
337 ioctl(fd, AUDIO_MIXER_READ, p->valp);
338 printf(" -> ");
339 prfield(p, 0, 0);
340 printf("\n");
341 }
342 }
343 }
344 } else {
345 warnx("No `=' in %s", *argv);
346 }
347 argv++;
348 }
349 } else {
350 while(argc--) {
351 p = findfield(*argv);
352 if (p == 0)
353 warnx("field %s does not exist", *argv);
354 else
355 prfield(p, sep, vflag), fprintf(out, "\n");
356 argv++;
357 }
358 }
359 } else
360 goto usage;
361 exit(0);
362 }
363