Annotation of src/usr.bin/mixerctl/mixerctl.c, Revision 1.19
1.19 ! otto 1: /* $OpenBSD: mixerctl.c,v 1.18 2005/01/04 18:22:09 millert 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.19 ! otto 43: #include <sys/types.h>
! 44: #include <sys/ioctl.h>
! 45: #include <sys/audioio.h>
! 46:
! 47: #include <err.h>
! 48: #include <fcntl.h>
1.1 provos 49: #include <stdio.h>
50: #include <stdlib.h>
1.19 ! otto 51: #include <string.h>
1.1 provos 52: #include <unistd.h>
53:
1.19 ! otto 54: void catstr(char *, char *, char *);
! 55: struct field *findfield(char *);
! 56: void prfield(struct field *, char *, int);
! 57: void rdfield(int, struct field *, char *);
! 58: __dead void usage(void);
1.1 provos 59:
1.19 ! otto 60: #define FIELD_NAME_MAX 64
1.1 provos 61:
62: struct field {
1.19 ! otto 63: char name[FIELD_NAME_MAX];
1.1 provos 64: mixer_ctrl_t *valp;
65: mixer_devinfo_t *infp;
66: } *fields, *rfields;
67:
68: mixer_ctrl_t *values;
69: mixer_devinfo_t *infos;
70:
1.19 ! otto 71: void
! 72: catstr(char *p, char *q, char *out)
1.1 provos 73: {
1.19 ! otto 74: char tmp[FIELD_NAME_MAX];
1.8 deraadt 75:
1.19 ! otto 76: snprintf(tmp, FIELD_NAME_MAX, "%s.%s", p, q);
! 77: strlcpy(out, tmp, FIELD_NAME_MAX);
1.1 provos 78: }
79:
80: struct field *
1.9 pvalchev 81: findfield(char *name)
1.1 provos 82: {
83: int i;
1.19 ! otto 84: for (i = 0; fields[i].name[0] != '\0'; i++)
1.1 provos 85: if (strcmp(fields[i].name, name) == 0)
86: return &fields[i];
1.9 pvalchev 87: return (0);
1.1 provos 88: }
89:
1.19 ! otto 90: #define e_member_name un.e.member[i].label.name
! 91: #define s_member_name un.s.member[i].label.name
! 92:
1.1 provos 93: void
1.9 pvalchev 94: prfield(struct field *p, char *sep, int prvalset)
1.1 provos 95: {
96: mixer_ctrl_t *m;
97: int i, n;
98:
99: if (sep)
1.19 ! otto 100: printf("%s%s", p->name, sep);
1.1 provos 101: m = p->valp;
1.19 ! otto 102: switch (m->type) {
1.1 provos 103: case AUDIO_MIXER_ENUM:
1.18 millert 104: for (i = 0; i < p->infp->un.e.num_mem; i++)
1.1 provos 105: if (p->infp->un.e.member[i].ord == m->un.ord)
1.19 ! otto 106: printf("%s",
! 107: p->infp->e_member_name);
1.1 provos 108: if (prvalset) {
1.19 ! otto 109: printf(" [ ");
1.18 millert 110: for (i = 0; i < p->infp->un.e.num_mem; i++)
1.19 ! otto 111: printf("%s ", p->infp->e_member_name);
! 112: printf("]");
1.1 provos 113: }
114: break;
115: case AUDIO_MIXER_SET:
1.18 millert 116: for (n = i = 0; i < p->infp->un.s.num_mem; i++)
1.1 provos 117: if (m->un.mask & p->infp->un.s.member[i].mask)
1.19 ! otto 118: printf("%s%s", n++ ? "," : "",
! 119: p->infp->s_member_name);
1.1 provos 120: if (prvalset) {
1.19 ! otto 121: printf(" { ");
1.18 millert 122: for (i = 0; i < p->infp->un.s.num_mem; i++)
1.19 ! otto 123: printf("%s ", p->infp->s_member_name);
! 124: printf("}");
1.1 provos 125: }
126: break;
127: case AUDIO_MIXER_VALUE:
128: if (m->un.value.num_channels == 1)
1.19 ! otto 129: printf("%d", m->un.value.level[0]);
1.1 provos 130: else
1.19 ! otto 131: printf("%d,%d", m->un.value.level[0],
1.1 provos 132: m->un.value.level[1]);
133: if (prvalset)
1.19 ! otto 134: printf(" %s", p->infp->un.v.units.name);
1.1 provos 135: break;
136: default:
137: errx(1, "Invalid format.");
138: }
139: }
140:
1.19 ! otto 141: void
! 142: rdfield(int fd, struct field *p, char *q)
1.1 provos 143: {
1.19 ! otto 144: mixer_ctrl_t *m, oldval;
1.1 provos 145: int v, v0, v1, mask;
146: int i;
147: char *s;
148:
1.19 ! otto 149: oldval = *p->valp;
1.1 provos 150: m = p->valp;
1.19 ! otto 151:
! 152: switch (m->type) {
1.1 provos 153: case AUDIO_MIXER_ENUM:
1.18 millert 154: for (i = 0; i < p->infp->un.e.num_mem; i++)
1.19 ! otto 155: if (strcmp(p->infp->e_member_name, q) == 0)
1.1 provos 156: break;
157: if (i < p->infp->un.e.num_mem)
158: m->un.ord = p->infp->un.e.member[i].ord;
1.9 pvalchev 159: else
160: errx(1, "Bad enum value %s", q);
1.1 provos 161: break;
162: case AUDIO_MIXER_SET:
163: mask = 0;
1.18 millert 164: for (v = 0; q && *q; q = s) {
1.19 ! otto 165: if ((s = strchr(q, ',')) != NULL)
1.1 provos 166: *s++ = 0;
1.4 millert 167: for (i = 0; i < p->infp->un.s.num_mem; i++)
1.19 ! otto 168: if (strcmp(p->infp->s_member_name, q) == 0)
1.1 provos 169: break;
1.9 pvalchev 170: if (i < p->infp->un.s.num_mem)
1.1 provos 171: mask |= p->infp->un.s.member[i].mask;
1.9 pvalchev 172: else
173: errx(1, "Bad set value %s", q);
1.1 provos 174: }
175: m->un.mask = mask;
176: break;
177: case AUDIO_MIXER_VALUE:
178: if (m->un.value.num_channels == 1) {
179: if (sscanf(q, "%d", &v) == 1) {
1.4 millert 180: switch (*q) {
181: case '+':
182: case '-':
183: m->un.value.level[0] += v;
184: break;
185: default:
186: m->un.value.level[0] = v;
187: break;
188: }
1.9 pvalchev 189: } else
190: errx(1, "Bad number %s", q);
1.1 provos 191: } else {
192: if (sscanf(q, "%d,%d", &v0, &v1) == 2) {
1.4 millert 193: switch (*q) {
194: case '+':
195: case '-':
196: m->un.value.level[0] += v0;
197: break;
198: default:
199: m->un.value.level[0] = v0;
200: break;
201: }
202: s = strchr(q, ',') + 1;
203: switch (*s) {
204: case '+':
205: case '-':
206: m->un.value.level[1] += v1;
207: break;
208: default:
209: m->un.value.level[1] = v1;
210: break;
211: }
1.1 provos 212: } else if (sscanf(q, "%d", &v) == 1) {
1.4 millert 213: switch (*q) {
214: case '+':
215: case '-':
216: m->un.value.level[0] += v;
217: m->un.value.level[1] += v;
218: break;
219: default:
1.9 pvalchev 220: m->un.value.level[0] = v;
221: m->un.value.level[1] = v;
1.4 millert 222: break;
223: }
1.9 pvalchev 224: } else
225: errx(1, "Bad numbers %s", q);
1.1 provos 226: }
227: break;
228: default:
229: errx(1, "Invalid format.");
230: }
1.19 ! otto 231:
! 232: if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0) {
! 233: warn("AUDIO_MIXER_WRITE");
! 234: } else {
! 235: *p->valp = oldval;
! 236: prfield(p, ": ", 0);
! 237: if (ioctl(fd, AUDIO_MIXER_READ, p->valp) < 0) {
! 238: warn("AUDIO_MIXER_READ");
! 239: } else {
! 240: printf(" -> ");
! 241: prfield(p, NULL, 0);
! 242: printf("\n");
! 243: }
! 244: }
1.1 provos 245: }
246:
247: int
1.9 pvalchev 248: main(int argc, char **argv)
1.1 provos 249: {
250: int fd, i, j, ch, pos;
1.14 tedu 251: int aflag = 0, qflag = 0, vflag = 0;
1.3 provos 252: char *file;
1.1 provos 253: char *sep = "=";
254: mixer_devinfo_t dinfo;
1.16 tedu 255: int ndev;
1.1 provos 256:
1.9 pvalchev 257: if ((file = getenv("MIXERDEVICE")) == 0 || *file == '\0')
258: file = "/dev/mixer";
1.1 provos 259:
1.10 jfb 260: while ((ch = getopt(argc, argv, "af:nqvw")) != -1) {
1.1 provos 261: switch(ch) {
262: case 'a':
263: aflag++;
264: break;
265: case 'w':
1.14 tedu 266: /* compat */
1.1 provos 267: break;
268: case 'v':
269: vflag++;
270: break;
271: case 'n':
272: sep = 0;
273: break;
274: case 'f':
275: file = optarg;
276: break;
1.10 jfb 277: case 'q':
278: qflag = 1;
279: break;
1.1 provos 280: case '?':
281: default:
1.9 pvalchev 282: usage();
1.1 provos 283: }
284: }
285: argc -= optind;
286: argv += optind;
1.6 mickey 287:
1.14 tedu 288: if ((fd = open(file, O_RDWR)) == -1)
289: if ((fd = open(file, O_RDONLY)) == -1)
290: err(1, "%s", file);
1.1 provos 291:
1.18 millert 292: for (ndev = 0; ; ndev++) {
1.16 tedu 293: dinfo.index = ndev;
1.14 tedu 294: if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
295: break;
1.1 provos 296: }
1.5 mickey 297:
1.9 pvalchev 298: if (!ndev)
1.5 mickey 299: errx(1, "no mixer devices configured");
300:
1.9 pvalchev 301: if ((rfields = calloc(ndev, sizeof *rfields)) == NULL ||
302: (fields = calloc(ndev, sizeof *fields)) == NULL ||
303: (infos = calloc(ndev, sizeof *infos)) == NULL ||
304: (values = calloc(ndev, sizeof *values)) == NULL)
305: err(1, "calloc()");
1.1 provos 306:
1.18 millert 307: for (i = 0; i < ndev; i++) {
1.1 provos 308: infos[i].index = i;
1.17 millert 309: if (ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]) < 0) {
1.19 ! otto 310: ndev--;
! 311: i--;
1.17 millert 312: continue;
313: }
1.1 provos 314: }
315:
1.18 millert 316: for (i = 0; i < ndev; i++) {
1.19 ! otto 317: strlcpy(rfields[i].name, infos[i].label.name, FIELD_NAME_MAX);
1.1 provos 318: rfields[i].valp = &values[i];
319: rfields[i].infp = &infos[i];
320: }
321:
1.18 millert 322: for (i = 0; i < ndev; i++) {
1.1 provos 323: values[i].dev = i;
324: values[i].type = infos[i].type;
325: if (infos[i].type != AUDIO_MIXER_CLASS) {
326: values[i].un.value.num_channels = 2;
327: if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
328: values[i].un.value.num_channels = 1;
329: if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
330: err(1, "AUDIO_MIXER_READ");
331: }
332: }
333: }
334:
1.18 millert 335: for (j = i = 0; i < ndev; i++) {
1.1 provos 336: if (infos[i].type != AUDIO_MIXER_CLASS &&
337: infos[i].type != -1) {
338: fields[j++] = rfields[i];
1.18 millert 339: for (pos = infos[i].next; pos != AUDIO_MIXER_LAST;
1.1 provos 340: pos = infos[pos].next) {
341: fields[j] = rfields[pos];
1.19 ! otto 342: catstr(rfields[i].name, infos[pos].label.name,
! 343: fields[j].name);
1.1 provos 344: infos[pos].type = -1;
345: j++;
346: }
347: }
348: }
349:
1.18 millert 350: for (i = 0; i < j; i++) {
1.1 provos 351: int cls = fields[i].infp->mixer_class;
352: if (cls >= 0 && cls < ndev)
1.19 ! otto 353: catstr(infos[cls].label.name, fields[i].name,
! 354: fields[i].name);
1.1 provos 355: }
356:
1.14 tedu 357: if (!argc && aflag) {
1.19 ! otto 358: for (i = 0; fields[i].name[0] != '\0'; i++) {
1.1 provos 359: prfield(&fields[i], sep, vflag);
1.19 ! otto 360: printf("\n");
1.1 provos 361: }
362: } else if (argc > 0 && !aflag) {
363: struct field *p;
1.14 tedu 364:
1.19 ! otto 365: while (argc--) {
1.14 tedu 366: char *q;
367:
1.19 ! otto 368: ch = 0;
! 369: if ((q = strchr(*argv, '=')) != NULL) {
! 370: *q++ = '\0';
! 371: ch = 1;
! 372: }
! 373:
! 374: if ((p = findfield(*argv)) == NULL) {
! 375: warnx("field %s does not exist", *argv);
! 376: } else if (ch) {
! 377: rdfield(fd, p, q);
1.14 tedu 378: } else {
1.19 ! otto 379: prfield(p, sep, vflag);
! 380: printf("\n");
1.1 provos 381: }
1.19 ! otto 382:
1.17 millert 383: argv++;
1.1 provos 384: }
385: } else
1.9 pvalchev 386: usage();
1.1 provos 387: exit(0);
1.9 pvalchev 388: }
389:
1.19 ! otto 390: __dead void
1.9 pvalchev 391: usage(void)
392: {
393: extern char *__progname; /* from crt0.o */
394:
395: fprintf(stderr,
1.13 jmc 396: "usage: %s [-nv] [-f file] -a\n"
397: " %s [-nv] [-f file] name [...]\n"
1.14 tedu 398: " %s [-q] [-f file] name=value [...]\n",
1.11 jsyn 399: __progname, __progname, __progname);
1.9 pvalchev 400:
401: exit(1);
1.1 provos 402: }