Annotation of src/usr.bin/audioctl/audioctl.c, Revision 1.40
1.40 ! ratchov 1: /* $OpenBSD: audioctl.c,v 1.39 2020/02/01 18:06:19 ratchov Exp $ */
1.1 provos 2: /*
1.31 ratchov 3: * Copyright (c) 2016 Alexandre Ratchov <alex@caoua.org>
1.1 provos 4: *
1.31 ratchov 5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 provos 8: *
1.31 ratchov 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 provos 16: */
1.31 ratchov 17: #include <sys/types.h>
18: #include <sys/ioctl.h>
19: #include <sys/audioio.h>
20: #include <fcntl.h>
21: #include <limits.h>
1.1 provos 22: #include <stdio.h>
1.3 provos 23: #include <stdlib.h>
1.1 provos 24: #include <unistd.h>
25: #include <string.h>
1.31 ratchov 26: #include <err.h>
1.1 provos 27:
1.31 ratchov 28: /*
29: * Default bytes per sample for the given bits per sample.
30: */
31: #define BPS(bits) (((bits) <= 8) ? 1 : (((bits) <= 16) ? 2 : 4))
1.1 provos 32:
1.31 ratchov 33: struct audio_device rname;
34: struct audio_status rstatus;
35: struct audio_swpar rpar, wpar;
36: struct audio_pos rpos;
1.30 ratchov 37:
1.1 provos 38: struct field {
1.31 ratchov 39: char *name;
40: void *raddr, *waddr;
41: #define MODE 0
42: #define NUM 1
43: #define STR 2
44: #define ENC 3
45: int type;
46: int set;
1.1 provos 47: } fields[] = {
1.31 ratchov 48: {"name", &rname.name, NULL, STR},
49: {"mode", &rstatus.mode, NULL, MODE},
50: {"pause", &rstatus.pause, NULL, NUM},
51: {"active", &rstatus.active, NULL, NUM},
52: {"nblks", &rpar.nblks, &wpar.nblks, NUM},
53: {"blksz", &rpar.round, &wpar.round, NUM},
54: {"rate", &rpar.rate, &wpar.rate, NUM},
55: {"encoding", &rpar, &wpar, ENC},
56: {"play.channels", &rpar.pchan, &wpar.pchan, NUM},
57: {"play.bytes", &rpos.play_pos, NULL, NUM},
58: {"play.errors", &rpos.play_xrun, NULL, NUM},
59: {"record.channels", &rpar.rchan, &wpar.rchan, NUM},
60: {"record.bytes", &rpos.rec_pos, NULL, NUM},
61: {"record.errors", &rpos.rec_xrun, NULL, NUM},
62: {NULL, NULL, 0}
1.1 provos 63: };
64:
1.32 ratchov 65: const char usagestr[] =
1.33 jmc 66: "usage: audioctl [-f file]\n"
67: " audioctl [-n] [-f file] name ...\n"
68: " audioctl [-nq] [-f file] name=value ...\n";
1.1 provos 69:
1.40 ! ratchov 70: int fd, show_names = 1, quiet = 0;
1.39 ratchov 71:
1.31 ratchov 72: /*
73: * parse encoding string (examples: s8, u8, s16, s16le, s24be ...)
74: * and fill enconding fields of audio_swpar structure
75: */
76: int
77: strtoenc(struct audio_swpar *ap, char *p)
1.1 provos 78: {
1.31 ratchov 79: /* expect "s" or "u" (signedness) */
80: if (*p == 's')
81: ap->sig = 1;
82: else if (*p == 'u')
83: ap->sig = 0;
84: else
85: return 0;
86: p++;
87:
88: /* expect 1-2 decimal digits (bits per sample) */
89: ap->bits = 0;
90: while (*p >= '0' && *p <= '9') {
91: ap->bits = (ap->bits * 10) + *p++ - '0';
92: if (ap->bits > 32)
93: return 0;
94: }
95: if (ap->bits < 8)
96: return 0;
1.1 provos 97:
1.31 ratchov 98: /* set defaults as next tokens are optional */
99: ap->bps = BPS(ap->bits);
100: ap->le = (BYTE_ORDER == LITTLE_ENDIAN);
101: ap->msb = 1;
102: if (*p == '\0')
103: return 1;
104:
105: /* expect "le" or "be" (endianness) */
106: if (p[0] == 'l' && p[1] == 'e')
107: ap->le = 1;
108: else if (p[0] == 'b' && p[1] == 'e')
109: ap->le = 0;
110: else
111: return 0;
112: p += 2;
113: if (*p == '\0')
114: return 1;
115:
116: /* expect 1 decimal digit (number of bytes) */
117: if (*p < '0' || *p > '9')
118: return 0;
119: ap->bps = *p - '0';
120: if (ap->bps < ((ap->bits + 7) >> 3) || ap->bps > 4)
121: return 0;
122: if (*++p == '\0')
123: return 1;
124:
125: /* expect "msb" or "lsb" (alignment) */
126: if (p[0] == 'm' && p[1] == 's' && p[2] == 'b')
127: ap->msb = 1;
128: else if (p[0] == 'l' && p[1] == 's' && p[2] == 'b')
129: ap->msb = 0;
130: else if (*p == '\0')
131: return 1;
132: p += 3;
133: if (*p == '\0')
134: return 1;
1.1 provos 135:
1.31 ratchov 136: /* must be no additional junk */
137: return 0;
1.20 ratchov 138: }
139:
140: void
1.31 ratchov 141: print_val(struct field *p, void *addr)
1.1 provos 142: {
1.31 ratchov 143: int mode;
144: struct audio_swpar *ap;
1.1 provos 145:
1.31 ratchov 146: switch (p->type) {
147: case NUM:
148: printf("%u", *(unsigned int *)addr);
149: break;
150: case STR:
151: printf("%s", (char *)addr);
152: break;
153: case MODE:
154: mode = *(unsigned int *)addr;
155: if (mode & AUMODE_PLAY)
156: printf("play");
157: if (mode & AUMODE_RECORD) {
158: if (mode & AUMODE_PLAY)
159: printf(",");
160: printf("record");
1.20 ratchov 161: }
1.1 provos 162: break;
163: case ENC:
1.31 ratchov 164: ap = addr;
165: printf("%s%u", ap->sig ? "s" : "u", ap->bits);
166: if (ap->bps == 1)
167: break;
168: printf("%s", ap->le ? "le" : "be");
169: if (ap->bps != BPS(ap->bits) || ap->bits < ap->bps * 8) {
170: printf("%u", ap->bps);
171: if (ap->bits < ap->bps * 8)
172: printf("%s", ap->msb ? "msb" : "lsb");
1.20 ratchov 173: }
1.1 provos 174: }
175: }
176:
177: void
1.31 ratchov 178: parse_val(struct field *f, void *addr, char *p)
1.1 provos 179: {
1.31 ratchov 180: const char *strerr;
1.1 provos 181:
1.31 ratchov 182: switch (f->type) {
183: case NUM:
184: *(unsigned int *)addr = strtonum(p, 0, UINT_MAX, &strerr);
185: if (strerr)
186: errx(1, "%s: %s", p, strerr);
187: break;
188: case ENC:
189: if (!strtoenc((struct audio_swpar *)addr, p))
190: errx(1, "%s: bad encoding", p);
1.1 provos 191: }
192: }
193:
1.39 ratchov 194: void
195: audio_main(int argc, char **argv)
1.1 provos 196: {
1.31 ratchov 197: struct field *f;
1.39 ratchov 198: char *lhs, *rhs;
199: int set = 0;
1.31 ratchov 200:
1.37 deraadt 201: if (ioctl(fd, AUDIO_GETSTATUS, &rstatus) == -1)
1.31 ratchov 202: err(1, "AUDIO_GETSTATUS");
1.37 deraadt 203: if (ioctl(fd, AUDIO_GETDEV, &rname) == -1)
1.31 ratchov 204: err(1, "AUDIO_GETDEV");
1.37 deraadt 205: if (ioctl(fd, AUDIO_GETPAR, &rpar) == -1)
1.31 ratchov 206: err(1, "AUDIO_GETPAR");
1.37 deraadt 207: if (ioctl(fd, AUDIO_GETPOS, &rpos) == -1)
1.31 ratchov 208: err(1, "AUDIO_GETPOS");
209: if (argc == 0) {
210: for (f = fields; f->name != NULL; f++) {
211: printf("%s=", f->name);
212: print_val(f, f->raddr);
213: printf("\n");
1.1 provos 214: }
1.31 ratchov 215: }
216: AUDIO_INITPAR(&wpar);
217: for (; argc > 0; argc--, argv++) {
218: lhs = *argv;
219: rhs = strchr(*argv, '=');
220: if (rhs)
221: *rhs++ = '\0';
222: for (f = fields;; f++) {
223: if (f->name == NULL)
224: errx(1, "%s: unknown parameter", lhs);
225: if (strcmp(f->name, lhs) == 0)
226: break;
1.11 vincent 227: }
1.31 ratchov 228: if (rhs) {
229: if (f->waddr == NULL)
230: errx(1, "%s: is read only", f->name);
231: parse_val(f, f->waddr, rhs);
232: f->set = 1;
233: set = 1;
234: } else {
1.40 ! ratchov 235: if (show_names)
1.31 ratchov 236: printf("%s=", f->name);
237: print_val(f, f->raddr);
238: printf("\n");
1.30 ratchov 239: }
1.31 ratchov 240: }
1.39 ratchov 241: if (!set)
242: return;
1.37 deraadt 243: if (ioctl(fd, AUDIO_SETPAR, &wpar) == -1)
1.31 ratchov 244: err(1, "AUDIO_SETPAR");
1.37 deraadt 245: if (ioctl(fd, AUDIO_GETPAR, &wpar) == -1)
1.31 ratchov 246: err(1, "AUDIO_GETPAR");
247: for (f = fields; f->name != NULL; f++) {
248: if (!f->set || quiet)
249: continue;
1.40 ! ratchov 250: if (show_names) {
1.31 ratchov 251: printf("%s: ", f->name);
252: print_val(f, f->raddr);
253: printf(" -> ");
1.1 provos 254: }
1.31 ratchov 255: print_val(f, f->waddr);
256: printf("\n");
1.11 vincent 257: }
1.39 ratchov 258: }
259:
260: int
261: main(int argc, char **argv)
262: {
263: extern char *__progname;
264: char *path = "/dev/audioctl0";
265: int c, mixer = 0;
266:
267: if (strcmp(__progname, "mixerctl") == 0)
268: mixer = 1;
269:
270: while ((c = getopt(argc, argv, "anf:q")) != -1) {
271: switch (c) {
272: case 'a': /* ignored, compat */
273: break;
274: case 'n':
1.40 ! ratchov 275: show_names = 0;
1.39 ratchov 276: break;
277: case 'f':
278: path = optarg;
279: break;
280: case 'q':
281: quiet = 1;
282: break;
283: default:
284: fputs(usagestr, stderr);
285: return 1;
286: }
287: }
288: argc -= optind;
289: argv += optind;
290:
291: if (unveil(path, "w") == -1)
292: err(1, "unveil");
293: if (unveil(NULL, NULL) == -1)
294: err(1, "unveil");
295:
296: fd = open(path, O_WRONLY);
297: if (fd == -1)
298: err(1, "%s", path);
299:
300: audio_main(argc, argv);
301:
1.34 jsg 302: close(fd);
1.31 ratchov 303: return 0;
1.1 provos 304: }