Annotation of src/usr.bin/mixerctl/mixerctl.c, Revision 1.9
1.9 ! pvalchev 1: /* $OpenBSD: mixerctl.c,v 1.8 2002/05/29 18:33:39 deraadt 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;
241: int aflag = 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:
251: while ((ch = getopt(argc, argv, "af:nvw")) != -1) {
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;
268: case '?':
269: default:
1.9 ! pvalchev 270: usage();
1.1 provos 271: }
272: }
273: argc -= optind;
274: argv += optind;
1.6 mickey 275:
1.9 ! pvalchev 276: if ((fd = open(file, wflag ? O_RDWR : O_RDONLY)) < 0)
1.1 provos 277: err(1, "%s", file);
278:
1.9 ! pvalchev 279: for(;;) {
! 280: dinfo.index = ndev++;
! 281: if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
! 282: break;
1.1 provos 283: }
1.5 mickey 284:
1.9 ! pvalchev 285: if (!ndev)
1.5 mickey 286: errx(1, "no mixer devices configured");
287:
1.9 ! pvalchev 288: if ((rfields = calloc(ndev, sizeof *rfields)) == NULL ||
! 289: (fields = calloc(ndev, sizeof *fields)) == NULL ||
! 290: (infos = calloc(ndev, sizeof *infos)) == NULL ||
! 291: (values = calloc(ndev, sizeof *values)) == NULL)
! 292: err(1, "calloc()");
1.1 provos 293:
294: for(i = 0; i < ndev; i++) {
295: infos[i].index = i;
296: ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
297: }
298:
299: for(i = 0; i < ndev; i++) {
300: rfields[i].name = infos[i].label.name;
301: rfields[i].valp = &values[i];
302: rfields[i].infp = &infos[i];
303: }
304:
305: for(i = 0; i < ndev; i++) {
306: values[i].dev = i;
307: values[i].type = infos[i].type;
308: if (infos[i].type != AUDIO_MIXER_CLASS) {
309: values[i].un.value.num_channels = 2;
310: if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
311: values[i].un.value.num_channels = 1;
312: if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
313: err(1, "AUDIO_MIXER_READ");
314: }
315: }
316: }
317:
318: for(j = i = 0; i < ndev; i++) {
319: if (infos[i].type != AUDIO_MIXER_CLASS &&
320: infos[i].type != -1) {
321: fields[j++] = rfields[i];
322: for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
323: pos = infos[pos].next) {
324: fields[j] = rfields[pos];
1.6 mickey 325: fields[j].name = catstr(rfields[i].name,
1.1 provos 326: infos[pos].label.name);
327: infos[pos].type = -1;
328: j++;
329: }
330: }
331: }
332:
333: for(i = 0; i < j; i++) {
334: int cls = fields[i].infp->mixer_class;
335: if (cls >= 0 && cls < ndev)
1.6 mickey 336: fields[i].name = catstr(infos[cls].label.name,
1.1 provos 337: fields[i].name);
338: }
339:
1.9 ! pvalchev 340: if (!argc && aflag && !wflag) {
1.1 provos 341: for(i = 0; fields[i].name; i++) {
342: prfield(&fields[i], sep, vflag);
343: fprintf(out, "\n");
344: }
345: } else if (argc > 0 && !aflag) {
346: struct field *p;
347: if (wflag) {
348: while(argc--) {
349: char *q;
350:
1.9 ! pvalchev 351: if (q = strchr(*argv, '=')) {
1.1 provos 352: *q++ = 0;
353: p = findfield(*argv);
354: if (p == 0)
355: warnx("field %s does not exist", *argv);
356: else {
357: val = *p->valp;
358: if (rdfield(p, q)) {
359: if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
360: warn("AUDIO_MIXER_WRITE");
361: else if (sep) {
362: *p->valp = val;
363: prfield(p, ": ", 0);
364: ioctl(fd, AUDIO_MIXER_READ, p->valp);
365: printf(" -> ");
366: prfield(p, 0, 0);
367: printf("\n");
368: }
369: }
370: }
1.9 ! pvalchev 371: } else
1.1 provos 372: warnx("No `=' in %s", *argv);
373: argv++;
374: }
375: } else {
376: while(argc--) {
377: p = findfield(*argv);
378: if (p == 0)
379: warnx("field %s does not exist", *argv);
380: else
381: prfield(p, sep, vflag), fprintf(out, "\n");
382: argv++;
383: }
384: }
385: } else
1.9 ! pvalchev 386: usage();
1.1 provos 387: exit(0);
1.9 ! pvalchev 388: }
! 389:
! 390: void
! 391: usage(void)
! 392: {
! 393: extern char *__progname; /* from crt0.o */
! 394:
! 395: fprintf(stderr,
! 396: "usage: %s [-f file] [-n] [-v] name ...\n"
! 397: " %s [-f file] [-n] [-v] -w name=value ...\n"
! 398: " %s [-f file] [-n] [-v] -a\n", __progname,
! 399: __progname, __progname);
! 400:
! 401: exit(1);
1.1 provos 402: }