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