Annotation of src/usr.bin/mixerctl/mixerctl.c, Revision 1.12
1.12 ! todd 1: /* $OpenBSD: mixerctl.c,v 1.11 2003/05/15 04:16:57 jsyn Exp $ */
1.3 provos 2: /* $NetBSD: mixerctl.c,v 1.11 1998/04/27 16:55:23 augustss Exp $ */
1.1 provos 3:
4: /*
5: * Copyright (c) 1997 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * Author: Lennart Augustsson, with some code and ideas from Chuck Cranor.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the NetBSD
21: * Foundation, Inc. and its contributors.
22: * 4. Neither the name of The NetBSD Foundation nor the names of its
23: * contributors may be used to endorse or promote products derived
24: * from this software without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36: * POSSIBILITY OF SUCH DAMAGE.
37: */
1.9 pvalchev 38:
39: /*
40: * mixerctl(1) - a program to control audio mixing.
41: */
42:
1.1 provos 43: #include <stdio.h>
44: #include <stdlib.h>
45: #include <fcntl.h>
46: #include <err.h>
47: #include <unistd.h>
48: #include <string.h>
49: #include <sys/types.h>
50: #include <sys/ioctl.h>
51: #include <sys/audioio.h>
52:
1.7 millert 53: char *catstr(char *p, char *q);
54: struct field *findfield(char *name);
55: void prfield(struct field *p, char *sep, int prvalset);
56: int rdfield(struct field *p, char *q);
1.1 provos 57: int main(int argc, char **argv);
1.9 pvalchev 58: void usage(void);
1.1 provos 59:
60: FILE *out = stdout;
61:
62: struct field {
63: char *name;
64: mixer_ctrl_t *valp;
65: mixer_devinfo_t *infp;
66: char changed;
67: } *fields, *rfields;
68:
69: mixer_ctrl_t *values;
70: mixer_devinfo_t *infos;
71:
72: char *
1.9 pvalchev 73: catstr(char *p, char *q)
1.1 provos 74: {
1.8 deraadt 75: int len;
76: char *r;
77:
78: len = strlen(p) + 1 + strlen(q) + 1;
1.9 pvalchev 79: if ((r = malloc(len)) == NULL)
80: err(1, "malloc()");
1.8 deraadt 81: strlcpy(r, p, len);
82: strlcat(r, ".", len);
83: strlcat(r, q, len);
1.9 pvalchev 84: return (r);
1.1 provos 85: }
86:
87: struct field *
1.9 pvalchev 88: findfield(char *name)
1.1 provos 89: {
90: int i;
91: for(i = 0; fields[i].name; i++)
92: if (strcmp(fields[i].name, name) == 0)
93: return &fields[i];
1.9 pvalchev 94: return (0);
1.1 provos 95: }
96:
97: void
1.9 pvalchev 98: prfield(struct field *p, char *sep, int prvalset)
1.1 provos 99: {
100: mixer_ctrl_t *m;
101: int i, n;
102:
103: if (sep)
104: fprintf(out, "%s%s", p->name, sep);
105: m = p->valp;
106: switch(m->type) {
107: case AUDIO_MIXER_ENUM:
108: for(i = 0; i < p->infp->un.e.num_mem; i++)
109: if (p->infp->un.e.member[i].ord == m->un.ord)
110: fprintf(out, "%s",
111: p->infp->un.e.member[i].label.name);
112: if (prvalset) {
113: fprintf(out, " [ ");
114: for(i = 0; i < p->infp->un.e.num_mem; i++)
115: fprintf(out, "%s ", p->infp->un.e.member[i].label.name);
116: fprintf(out, "]");
117: }
118: break;
119: case AUDIO_MIXER_SET:
120: for(n = i = 0; i < p->infp->un.s.num_mem; i++)
121: if (m->un.mask & p->infp->un.s.member[i].mask)
122: fprintf(out, "%s%s", n++ ? "," : "",
123: p->infp->un.s.member[i].label.name);
124: if (prvalset) {
125: fprintf(out, " { ");
126: for(i = 0; i < p->infp->un.s.num_mem; i++)
127: fprintf(out, "%s ", p->infp->un.s.member[i].label.name);
128: fprintf(out, "}");
129: }
130: break;
131: case AUDIO_MIXER_VALUE:
132: if (m->un.value.num_channels == 1)
133: fprintf(out, "%d", m->un.value.level[0]);
134: else
1.6 mickey 135: fprintf(out, "%d,%d", m->un.value.level[0],
1.1 provos 136: m->un.value.level[1]);
137: if (prvalset)
138: fprintf(out, " %s", p->infp->un.v.units.name);
139: break;
140: default:
141: errx(1, "Invalid format.");
142: }
143: }
144:
145: int
1.9 pvalchev 146: rdfield(struct field *p, char *q)
1.1 provos 147: {
148: mixer_ctrl_t *m;
149: int v, v0, v1, mask;
150: int i;
151: char *s;
152:
153: m = p->valp;
154: switch(m->type) {
155: case AUDIO_MIXER_ENUM:
156: for(i = 0; i < p->infp->un.e.num_mem; i++)
157: if (strcmp(p->infp->un.e.member[i].label.name, q) == 0)
158: break;
159: if (i < p->infp->un.e.num_mem)
160: m->un.ord = p->infp->un.e.member[i].ord;
1.9 pvalchev 161: else
162: errx(1, "Bad enum value %s", q);
1.1 provos 163: break;
164: case AUDIO_MIXER_SET:
165: mask = 0;
166: for(v = 0; q && *q; q = s) {
1.9 pvalchev 167: if (s = strchr(q, ','))
1.1 provos 168: *s++ = 0;
1.4 millert 169: for (i = 0; i < p->infp->un.s.num_mem; i++)
1.1 provos 170: if (strcmp(p->infp->un.s.member[i].label.name, q) == 0)
171: break;
1.9 pvalchev 172: if (i < p->infp->un.s.num_mem)
1.1 provos 173: mask |= p->infp->un.s.member[i].mask;
1.9 pvalchev 174: else
175: errx(1, "Bad set value %s", q);
1.1 provos 176: }
177: m->un.mask = mask;
178: break;
179: case AUDIO_MIXER_VALUE:
180: if (m->un.value.num_channels == 1) {
181: if (sscanf(q, "%d", &v) == 1) {
1.4 millert 182: switch (*q) {
183: case '+':
184: case '-':
185: m->un.value.level[0] += v;
186: break;
187: default:
188: m->un.value.level[0] = v;
189: break;
190: }
1.9 pvalchev 191: } else
192: errx(1, "Bad number %s", q);
1.1 provos 193: } else {
194: if (sscanf(q, "%d,%d", &v0, &v1) == 2) {
1.4 millert 195: switch (*q) {
196: case '+':
197: case '-':
198: m->un.value.level[0] += v0;
199: break;
200: default:
201: m->un.value.level[0] = v0;
202: break;
203: }
204: s = strchr(q, ',') + 1;
205: switch (*s) {
206: case '+':
207: case '-':
208: m->un.value.level[1] += v1;
209: break;
210: default:
211: m->un.value.level[1] = v1;
212: break;
213: }
1.1 provos 214: } else if (sscanf(q, "%d", &v) == 1) {
1.4 millert 215: switch (*q) {
216: case '+':
217: case '-':
218: m->un.value.level[0] += v;
219: m->un.value.level[1] += v;
220: break;
221: default:
1.9 pvalchev 222: m->un.value.level[0] = v;
223: m->un.value.level[1] = v;
1.4 millert 224: break;
225: }
1.9 pvalchev 226: } else
227: errx(1, "Bad numbers %s", q);
1.1 provos 228: }
229: break;
230: default:
231: errx(1, "Invalid format.");
232: }
233: p->changed = 1;
1.9 pvalchev 234: return (1);
1.1 provos 235: }
236:
237: int
1.9 pvalchev 238: main(int argc, char **argv)
1.1 provos 239: {
240: int fd, i, j, ch, pos;
1.10 jfb 241: int aflag = 0, qflag = 0, wflag = 0, vflag = 0;
1.3 provos 242: char *file;
1.1 provos 243: char *sep = "=";
244: mixer_devinfo_t dinfo;
245: mixer_ctrl_t val;
1.9 pvalchev 246: int ndev = 0;
1.1 provos 247:
1.9 pvalchev 248: if ((file = getenv("MIXERDEVICE")) == 0 || *file == '\0')
249: file = "/dev/mixer";
1.1 provos 250:
1.10 jfb 251: while ((ch = getopt(argc, argv, "af:nqvw")) != -1) {
1.1 provos 252: switch(ch) {
253: case 'a':
254: aflag++;
255: break;
256: case 'w':
257: wflag++;
258: break;
259: case 'v':
260: vflag++;
261: break;
262: case 'n':
263: sep = 0;
264: break;
265: case 'f':
266: file = optarg;
267: break;
1.10 jfb 268: case 'q':
269: qflag = 1;
270: break;
1.1 provos 271: case '?':
272: default:
1.9 pvalchev 273: usage();
1.1 provos 274: }
275: }
276: argc -= optind;
277: argv += optind;
1.6 mickey 278:
1.9 pvalchev 279: if ((fd = open(file, wflag ? O_RDWR : O_RDONLY)) < 0)
1.1 provos 280: err(1, "%s", file);
281:
1.9 pvalchev 282: for(;;) {
283: dinfo.index = ndev++;
284: if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
285: break;
1.1 provos 286: }
1.5 mickey 287:
1.9 pvalchev 288: if (!ndev)
1.5 mickey 289: errx(1, "no mixer devices configured");
290:
1.9 pvalchev 291: if ((rfields = calloc(ndev, sizeof *rfields)) == NULL ||
292: (fields = calloc(ndev, sizeof *fields)) == NULL ||
293: (infos = calloc(ndev, sizeof *infos)) == NULL ||
294: (values = calloc(ndev, sizeof *values)) == NULL)
295: err(1, "calloc()");
1.1 provos 296:
297: for(i = 0; i < ndev; i++) {
298: infos[i].index = i;
299: ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
300: }
301:
302: for(i = 0; i < ndev; i++) {
303: rfields[i].name = infos[i].label.name;
304: rfields[i].valp = &values[i];
305: rfields[i].infp = &infos[i];
306: }
307:
308: for(i = 0; i < ndev; i++) {
309: values[i].dev = i;
310: values[i].type = infos[i].type;
311: if (infos[i].type != AUDIO_MIXER_CLASS) {
312: values[i].un.value.num_channels = 2;
313: if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
314: values[i].un.value.num_channels = 1;
315: if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
316: err(1, "AUDIO_MIXER_READ");
317: }
318: }
319: }
320:
321: for(j = i = 0; i < ndev; i++) {
322: if (infos[i].type != AUDIO_MIXER_CLASS &&
323: infos[i].type != -1) {
324: fields[j++] = rfields[i];
325: for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
326: pos = infos[pos].next) {
327: fields[j] = rfields[pos];
1.6 mickey 328: fields[j].name = catstr(rfields[i].name,
1.1 provos 329: infos[pos].label.name);
330: infos[pos].type = -1;
331: j++;
332: }
333: }
334: }
335:
336: for(i = 0; i < j; i++) {
337: int cls = fields[i].infp->mixer_class;
338: if (cls >= 0 && cls < ndev)
1.6 mickey 339: fields[i].name = catstr(infos[cls].label.name,
1.1 provos 340: fields[i].name);
341: }
342:
1.9 pvalchev 343: if (!argc && aflag && !wflag) {
1.1 provos 344: for(i = 0; fields[i].name; i++) {
345: prfield(&fields[i], sep, vflag);
346: fprintf(out, "\n");
347: }
348: } else if (argc > 0 && !aflag) {
349: struct field *p;
350: if (wflag) {
351: while(argc--) {
352: char *q;
353:
1.9 pvalchev 354: if (q = strchr(*argv, '=')) {
1.1 provos 355: *q++ = 0;
356: p = findfield(*argv);
357: if (p == 0)
358: warnx("field %s does not exist", *argv);
359: else {
360: val = *p->valp;
361: if (rdfield(p, q)) {
362: if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
363: warn("AUDIO_MIXER_WRITE");
1.10 jfb 364: else if (sep && !qflag) {
1.1 provos 365: *p->valp = val;
366: prfield(p, ": ", 0);
367: ioctl(fd, AUDIO_MIXER_READ, p->valp);
368: printf(" -> ");
369: prfield(p, 0, 0);
370: printf("\n");
371: }
372: }
373: }
1.9 pvalchev 374: } else
1.1 provos 375: warnx("No `=' in %s", *argv);
376: argv++;
377: }
378: } else {
379: while(argc--) {
380: p = findfield(*argv);
381: if (p == 0)
382: warnx("field %s does not exist", *argv);
383: else
384: prfield(p, sep, vflag), fprintf(out, "\n");
385: argv++;
386: }
387: }
388: } else
1.9 pvalchev 389: usage();
1.1 provos 390: exit(0);
1.9 pvalchev 391: }
392:
393: void
394: usage(void)
395: {
396: extern char *__progname; /* from crt0.o */
397:
398: fprintf(stderr,
1.11 jsyn 399: "usage: %s [-f file] [-n] [-v] -a\n"
400: " %s [-f file] [-n] [-v] name ...\n"
1.12 ! todd 401: " %s [-f file] [-n] [-q] -w name=value ...\n",
1.11 jsyn 402: __progname, __progname, __progname);
1.9 pvalchev 403:
404: exit(1);
1.1 provos 405: }