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