Annotation of src/usr.bin/audioctl/audioctl.c, Revision 1.47
1.47 ! jmc 1: /* $OpenBSD: audioctl.c,v 1.46 2023/01/08 08:27:17 jmc 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;
1.45 ratchov 46: int show;
1.31 ratchov 47: int set;
1.1 provos 48: } fields[] = {
1.31 ratchov 49: {"name", &rname.name, NULL, STR},
50: {"mode", &rstatus.mode, NULL, MODE},
51: {"pause", &rstatus.pause, NULL, NUM},
52: {"active", &rstatus.active, NULL, NUM},
53: {"nblks", &rpar.nblks, &wpar.nblks, NUM},
54: {"blksz", &rpar.round, &wpar.round, NUM},
55: {"rate", &rpar.rate, &wpar.rate, NUM},
56: {"encoding", &rpar, &wpar, ENC},
57: {"play.channels", &rpar.pchan, &wpar.pchan, NUM},
58: {"play.bytes", &rpos.play_pos, NULL, NUM},
59: {"play.errors", &rpos.play_xrun, NULL, NUM},
60: {"record.channels", &rpar.rchan, &wpar.rchan, NUM},
61: {"record.bytes", &rpos.rec_pos, NULL, NUM},
62: {"record.errors", &rpos.rec_xrun, NULL, NUM},
63: {NULL, NULL, 0}
1.1 provos 64: };
65:
1.32 ratchov 66: const char usagestr[] =
1.47 ! jmc 67: "usage: audioctl [-nq] [-f file] [-w wait] [name[=value] ...]\n";
1.1 provos 68:
1.45 ratchov 69: int fd, show_names = 1, quiet = 0, wait_sec = 0;
1.39 ratchov 70:
1.31 ratchov 71: /*
72: * parse encoding string (examples: s8, u8, s16, s16le, s24be ...)
1.44 jmc 73: * and fill encoding fields of audio_swpar structure
1.31 ratchov 74: */
75: int
76: strtoenc(struct audio_swpar *ap, char *p)
1.1 provos 77: {
1.31 ratchov 78: /* expect "s" or "u" (signedness) */
79: if (*p == 's')
80: ap->sig = 1;
81: else if (*p == 'u')
82: ap->sig = 0;
83: else
84: return 0;
85: p++;
86:
87: /* expect 1-2 decimal digits (bits per sample) */
88: ap->bits = 0;
89: while (*p >= '0' && *p <= '9') {
90: ap->bits = (ap->bits * 10) + *p++ - '0';
91: if (ap->bits > 32)
92: return 0;
93: }
94: if (ap->bits < 8)
95: return 0;
1.1 provos 96:
1.31 ratchov 97: /* set defaults as next tokens are optional */
98: ap->bps = BPS(ap->bits);
99: ap->le = (BYTE_ORDER == LITTLE_ENDIAN);
100: ap->msb = 1;
101: if (*p == '\0')
102: return 1;
103:
104: /* expect "le" or "be" (endianness) */
105: if (p[0] == 'l' && p[1] == 'e')
106: ap->le = 1;
107: else if (p[0] == 'b' && p[1] == 'e')
108: ap->le = 0;
109: else
110: return 0;
111: p += 2;
112: if (*p == '\0')
113: return 1;
114:
115: /* expect 1 decimal digit (number of bytes) */
116: if (*p < '0' || *p > '9')
117: return 0;
118: ap->bps = *p - '0';
119: if (ap->bps < ((ap->bits + 7) >> 3) || ap->bps > 4)
120: return 0;
121: if (*++p == '\0')
122: return 1;
123:
124: /* expect "msb" or "lsb" (alignment) */
125: if (p[0] == 'm' && p[1] == 's' && p[2] == 'b')
126: ap->msb = 1;
127: else if (p[0] == 'l' && p[1] == 's' && p[2] == 'b')
128: ap->msb = 0;
129: else if (*p == '\0')
130: return 1;
131: p += 3;
132: if (*p == '\0')
133: return 1;
1.1 provos 134:
1.31 ratchov 135: /* must be no additional junk */
136: return 0;
1.20 ratchov 137: }
138:
139: void
1.41 ratchov 140: print_field(struct field *p, void *addr)
1.1 provos 141: {
1.31 ratchov 142: int mode;
143: struct audio_swpar *ap;
1.1 provos 144:
1.31 ratchov 145: switch (p->type) {
146: case NUM:
147: printf("%u", *(unsigned int *)addr);
148: break;
149: case STR:
150: printf("%s", (char *)addr);
151: break;
152: case MODE:
153: mode = *(unsigned int *)addr;
154: if (mode & AUMODE_PLAY)
155: printf("play");
156: if (mode & AUMODE_RECORD) {
157: if (mode & AUMODE_PLAY)
158: printf(",");
159: printf("record");
1.20 ratchov 160: }
1.1 provos 161: break;
162: case ENC:
1.31 ratchov 163: ap = addr;
164: printf("%s%u", ap->sig ? "s" : "u", ap->bits);
165: if (ap->bps == 1)
166: break;
167: printf("%s", ap->le ? "le" : "be");
168: if (ap->bps != BPS(ap->bits) || ap->bits < ap->bps * 8) {
169: printf("%u", ap->bps);
170: if (ap->bits < ap->bps * 8)
171: printf("%s", ap->msb ? "msb" : "lsb");
1.20 ratchov 172: }
1.1 provos 173: }
174: }
175:
176: void
1.41 ratchov 177: parse_field(struct field *f, void *addr, char *p)
1.1 provos 178: {
1.31 ratchov 179: const char *strerr;
1.1 provos 180:
1.31 ratchov 181: switch (f->type) {
182: case NUM:
183: *(unsigned int *)addr = strtonum(p, 0, UINT_MAX, &strerr);
184: if (strerr)
185: errx(1, "%s: %s", p, strerr);
186: break;
187: case ENC:
188: if (!strtoenc((struct audio_swpar *)addr, p))
189: errx(1, "%s: bad encoding", p);
1.1 provos 190: }
191: }
192:
1.39 ratchov 193: void
194: audio_main(int argc, char **argv)
1.1 provos 195: {
1.31 ratchov 196: struct field *f;
1.39 ratchov 197: char *lhs, *rhs;
198: int set = 0;
1.31 ratchov 199:
200: if (argc == 0) {
1.45 ratchov 201: for (f = fields; f->name != NULL; f++)
202: f->show = 1;
1.31 ratchov 203: }
204: AUDIO_INITPAR(&wpar);
205: for (; argc > 0; argc--, argv++) {
206: lhs = *argv;
207: rhs = strchr(*argv, '=');
208: if (rhs)
209: *rhs++ = '\0';
210: for (f = fields;; f++) {
211: if (f->name == NULL)
212: errx(1, "%s: unknown parameter", lhs);
213: if (strcmp(f->name, lhs) == 0)
214: break;
1.11 vincent 215: }
1.31 ratchov 216: if (rhs) {
217: if (f->waddr == NULL)
218: errx(1, "%s: is read only", f->name);
1.41 ratchov 219: parse_field(f, f->waddr, rhs);
1.31 ratchov 220: f->set = 1;
221: set = 1;
1.45 ratchov 222: } else
223: f->show = 1;
224: }
225:
226: if (set && wait_sec)
227: errx(1, "Can't set variables wait_secically");
228:
229: while (1) {
230: if (ioctl(fd, AUDIO_GETSTATUS, &rstatus) == -1)
231: err(1, "AUDIO_GETSTATUS");
232: if (ioctl(fd, AUDIO_GETDEV, &rname) == -1)
233: err(1, "AUDIO_GETDEV");
234: if (ioctl(fd, AUDIO_GETPAR, &rpar) == -1)
235: err(1, "AUDIO_GETPAR");
236: if (ioctl(fd, AUDIO_GETPOS, &rpos) == -1)
237: err(1, "AUDIO_GETPOS");
238: for (f = fields; f->name != NULL; f++) {
239: if (!f->show)
240: continue;
1.40 ratchov 241: if (show_names)
1.31 ratchov 242: printf("%s=", f->name);
1.41 ratchov 243: print_field(f, f->raddr);
1.31 ratchov 244: printf("\n");
1.30 ratchov 245: }
1.45 ratchov 246:
247: if (wait_sec == 0)
248: break;
249:
250: /* ioctls are fast, we neglect drift from real-time clock */
251: sleep(wait_sec);
1.31 ratchov 252: }
1.45 ratchov 253:
1.39 ratchov 254: if (!set)
255: return;
1.45 ratchov 256:
1.37 deraadt 257: if (ioctl(fd, AUDIO_SETPAR, &wpar) == -1)
1.31 ratchov 258: err(1, "AUDIO_SETPAR");
1.37 deraadt 259: if (ioctl(fd, AUDIO_GETPAR, &wpar) == -1)
1.31 ratchov 260: err(1, "AUDIO_GETPAR");
261: for (f = fields; f->name != NULL; f++) {
262: if (!f->set || quiet)
263: continue;
1.40 ratchov 264: if (show_names) {
1.31 ratchov 265: printf("%s: ", f->name);
1.41 ratchov 266: print_field(f, f->raddr);
1.31 ratchov 267: printf(" -> ");
1.1 provos 268: }
1.41 ratchov 269: print_field(f, f->waddr);
1.31 ratchov 270: printf("\n");
1.11 vincent 271: }
1.39 ratchov 272: }
273:
274: int
275: main(int argc, char **argv)
276: {
277: char *path = "/dev/audioctl0";
1.45 ratchov 278: const char *errstr;
1.42 ratchov 279: int c;
1.39 ratchov 280:
1.45 ratchov 281: while ((c = getopt(argc, argv, "anf:qw:")) != -1) {
1.39 ratchov 282: switch (c) {
283: case 'a': /* ignored, compat */
284: break;
285: case 'n':
1.40 ratchov 286: show_names = 0;
1.39 ratchov 287: break;
288: case 'f':
289: path = optarg;
290: break;
291: case 'q':
292: quiet = 1;
1.45 ratchov 293: break;
294: case 'w':
295: wait_sec = strtonum(optarg, 1, INT_MAX, &errstr);
296: if (errstr != NULL)
297: errx(1, "wait is %s: %s", errstr, optarg);
1.39 ratchov 298: break;
299: default:
300: fputs(usagestr, stderr);
301: return 1;
302: }
303: }
304: argc -= optind;
305: argv += optind;
306:
307: if (unveil(path, "w") == -1)
1.43 beck 308: err(1, "unveil %s", path);
1.39 ratchov 309: if (unveil(NULL, NULL) == -1)
310: err(1, "unveil");
311:
312: fd = open(path, O_WRONLY);
313: if (fd == -1)
314: err(1, "%s", path);
315:
316: audio_main(argc, argv);
317:
1.34 jsg 318: close(fd);
1.43 beck 319: return 0;
1.1 provos 320: }