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