Annotation of src/usr.bin/audioctl/audioctl.c, Revision 1.30
1.29 ratchov 1: /* $OpenBSD: audioctl.c,v 1.28 2015/05/26 18:17:12 ratchov Exp $ */
1.3 provos 2: /* $NetBSD: audioctl.c,v 1.14 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
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: *
19: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29: * POSSIBILITY OF SUCH DAMAGE.
30: */
31:
1.7 pvalchev 32: /*
33: * audioctl(1) - a program to control audio device.
34: */
35:
1.1 provos 36: #include <stdio.h>
1.3 provos 37: #include <stdlib.h>
1.1 provos 38: #include <fcntl.h>
39: #include <err.h>
40: #include <unistd.h>
41: #include <string.h>
42: #include <sys/types.h>
43: #include <sys/stat.h>
44: #include <sys/ioctl.h>
45: #include <sys/audioio.h>
46:
1.6 millert 47: struct field *findfield(char *name);
1.12 moritz 48: void prfield(struct field *p, const char *sep);
1.6 millert 49: void rdfield(struct field *p, char *q);
50: void getinfo(int fd);
51: void usage(void);
52: int main(int argc, char **argv);
1.1 provos 53:
54: FILE *out = stdout;
55:
56: audio_device_t adev;
57:
58: audio_info_t info;
59:
60: char encbuf[1000];
61:
1.29 ratchov 62: int properties, fullduplex;
1.1 provos 63:
1.29 ratchov 64: struct audio_pos getpos;
1.28 ratchov 65:
1.30 ! ratchov 66: unsigned int block_size;
! 67:
1.1 provos 68: struct field {
1.12 moritz 69: const char *name;
1.1 provos 70: void *valp;
71: int format;
72: #define STRING 1
73: #define INT 2
74: #define UINT 3
75: #define P_R 4
76: #define UCHAR 6
77: #define ENC 7
78: #define PROPS 8
79: #define XINT 9
80: char flags;
81: #define READONLY 1
82: #define ALIAS 2
83: #define SET 4
1.22 krw 84: u_int oldval;
1.1 provos 85: } fields[] = {
86: { "name", &adev.name, STRING, READONLY },
87: { "encodings", encbuf, STRING, READONLY },
88: { "properties", &properties, PROPS, READONLY },
89: { "hiwat", &info.hiwat, UINT, 0 },
90: { "mode", &info.mode, P_R, READONLY },
1.30 ! ratchov 91: { "rate", &info.play.sample_rate, UINT, 0 },
! 92: { "precision", &info.play.precision, UINT, 0 },
! 93: { "bps", &info.play.bps, UINT, 0 },
! 94: { "msb", &info.play.msb, UINT, 0 },
! 95: { "encoding", &info.play.encoding, ENC, 0 },
! 96: { "pause", &info.play.pause, UCHAR, 0 },
! 97: { "active", &info.play.active, UCHAR, READONLY },
! 98: { "block_size", &block_size, UINT, 0 },
1.1 provos 99: { "play.channels", &info.play.channels, UINT, 0 },
1.29 ratchov 100: { "play.bytes", &getpos.play_pos, UINT, READONLY },
101: { "play.errors", &getpos.play_xrun, UINT, READONLY },
1.1 provos 102: { "record.channels", &info.record.channels, UINT, 0 },
1.29 ratchov 103: { "record.bytes", &getpos.rec_pos, UINT, READONLY },
104: { "record.errors", &getpos.rec_xrun, UINT, READONLY },
1.1 provos 105: { 0 }
106: };
107:
108: struct {
1.12 moritz 109: const char *ename;
110: u_int eno;
1.1 provos 111: } encs[] = {
112: { AudioEmulaw, AUDIO_ENCODING_ULAW },
113: { "ulaw", AUDIO_ENCODING_ULAW },
114: { AudioEalaw, AUDIO_ENCODING_ALAW },
115: { AudioEslinear, AUDIO_ENCODING_SLINEAR },
116: { "linear", AUDIO_ENCODING_SLINEAR },
117: { AudioEulinear, AUDIO_ENCODING_ULINEAR },
118: { AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE },
119: { "linear_le", AUDIO_ENCODING_SLINEAR_LE },
120: { AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE },
121: { AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE },
122: { "linear_be", AUDIO_ENCODING_SLINEAR_BE },
123: { AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE },
124: { 0 }
125: };
126:
127: static struct {
1.12 moritz 128: const char *name;
1.1 provos 129: u_int prop;
130: } props[] = {
131: { "full_duplex", AUDIO_PROP_FULLDUPLEX },
132: { "mmap", AUDIO_PROP_MMAP },
133: { "independent", AUDIO_PROP_INDEPENDENT },
134: { 0 }
135: };
136:
137: struct field *
1.7 pvalchev 138: findfield(char *name)
1.1 provos 139: {
140: int i;
1.15 sobrado 141: for (i = 0; fields[i].name; i++)
1.1 provos 142: if (strcmp(fields[i].name, name) == 0)
143: return &fields[i];
1.7 pvalchev 144: return (0);
1.1 provos 145: }
146:
1.23 deraadt 147: static void
1.20 ratchov 148: prval(u_int format, void *valp)
1.1 provos 149: {
150: u_int v;
1.12 moritz 151: const char *cm;
1.1 provos 152: int i;
153:
1.20 ratchov 154: switch (format) {
1.1 provos 155: case STRING:
1.20 ratchov 156: fprintf(out, "%s", (char *)valp);
1.1 provos 157: break;
158: case INT:
1.20 ratchov 159: fprintf(out, "%d", *(int *)valp);
1.1 provos 160: break;
161: case UINT:
1.20 ratchov 162: fprintf(out, "%u", *(u_int *)valp);
1.1 provos 163: break;
164: case XINT:
1.20 ratchov 165: fprintf(out, "0x%x", *(u_int *)valp);
1.1 provos 166: break;
167: case UCHAR:
1.20 ratchov 168: fprintf(out, "%u", *(u_char *)valp);
1.1 provos 169: break;
170: case P_R:
1.20 ratchov 171: v = *(u_int *)valp;
1.1 provos 172: cm = "";
173: if (v & AUMODE_PLAY) {
1.27 ratchov 174: fprintf(out, "play");
1.1 provos 175: cm = ",";
176: }
177: if (v & AUMODE_RECORD)
178: fprintf(out, "%srecord", cm);
179: break;
180: case ENC:
1.20 ratchov 181: v = *(u_int *)valp;
1.15 sobrado 182: for (i = 0; encs[i].ename; i++)
1.1 provos 183: if (encs[i].eno == v)
184: break;
185: if (encs[i].ename)
186: fprintf(out, "%s", encs[i].ename);
187: else
188: fprintf(out, "%u", v);
189: break;
190: case PROPS:
1.20 ratchov 191: v = *(u_int *)valp;
1.1 provos 192: for (cm = "", i = 0; props[i].name; i++) {
193: if (v & props[i].prop) {
194: fprintf(out, "%s%s", cm, props[i].name);
195: cm = ",";
196: }
197: }
198: break;
199: default:
200: errx(1, "Invalid print format.");
201: }
202: }
203:
204: void
1.20 ratchov 205: prfield(struct field *p, const char *sep)
206: {
207: if (sep) {
208: fprintf(out, "%s", p->name);
209: if (p->flags & SET) {
210: fprintf(out, "%s", ": ");
211: prval(p->format, &p->oldval);
212: fprintf(out, " -> ");
213: } else
214: fprintf(out, "%s", sep);
215: }
216: prval(p->format, p->valp);
217: fprintf(out, "\n");
218: }
219:
220: void
1.7 pvalchev 221: rdfield(struct field *p, char *q)
1.1 provos 222: {
223: int i;
224: u_int u;
225:
1.15 sobrado 226: switch (p->format) {
1.1 provos 227: case UINT:
1.20 ratchov 228: p->oldval = *(u_int *)p->valp;
229: if (sscanf(q, "%u", (unsigned int *)p->valp) != 1) {
1.1 provos 230: warnx("Bad number %s", q);
1.20 ratchov 231: return;
232: }
1.1 provos 233: break;
234: case UCHAR:
1.20 ratchov 235: *(char *)&p->oldval = *(u_char *)p->valp;
236: if (sscanf(q, "%u", &u) != 1) {
1.1 provos 237: warnx("Bad number %s", q);
1.20 ratchov 238: return;
239: }
240: *(u_char *)p->valp = u;
1.1 provos 241: break;
242: case XINT:
1.20 ratchov 243: p->oldval = *(u_int *)p->valp;
1.1 provos 244: if (sscanf(q, "0x%x", (unsigned int *)p->valp) != 1 &&
1.20 ratchov 245: sscanf(q, "%x", (unsigned int *)p->valp) != 1) {
1.1 provos 246: warnx("Bad number %s", q);
1.20 ratchov 247: return;
248: }
1.1 provos 249: break;
250: case ENC:
1.20 ratchov 251: p->oldval = *(u_int *)p->valp;
1.15 sobrado 252: for (i = 0; encs[i].ename; i++)
1.1 provos 253: if (strcmp(encs[i].ename, q) == 0)
254: break;
255: if (encs[i].ename)
256: *(u_int*)p->valp = encs[i].eno;
1.20 ratchov 257: else {
1.1 provos 258: warnx("Unknown encoding: %s", q);
1.20 ratchov 259: return;
260: }
1.1 provos 261: break;
262: default:
263: errx(1, "Invalid read format.");
264: }
265: p->flags |= SET;
266: }
267:
268: void
1.7 pvalchev 269: getinfo(int fd)
1.1 provos 270: {
1.7 pvalchev 271: int pos = 0, i = 0;
1.1 provos 272:
273: if (ioctl(fd, AUDIO_GETDEV, &adev) < 0)
274: err(1, "AUDIO_GETDEV");
1.15 sobrado 275: for (;;) {
1.9 deraadt 276: audio_encoding_t enc;
277: enc.index = i++;
278: if (ioctl(fd, AUDIO_GETENC, &enc) < 0)
279: break;
280: if (pos)
281: encbuf[pos++] = ',';
1.21 jakemsr 282: snprintf(encbuf+pos, sizeof(encbuf)-pos, "%s:%d:%d:%d%s",
283: enc.name, enc.precision, enc.bps, enc.msb,
1.9 deraadt 284: enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : "");
285: pos += strlen(encbuf+pos);
1.1 provos 286: }
287: if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0)
288: err(1, "AUDIO_GETFD");
289: if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0)
290: err(1, "AUDIO_GETPROPS");
1.29 ratchov 291: if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0)
292: err(1, "AUDIO_GETPROPS");
1.1 provos 293: if (ioctl(fd, AUDIO_GETINFO, &info) < 0)
294: err(1, "AUDIO_GETINFO");
1.29 ratchov 295: if (ioctl(fd, AUDIO_GETPOS, &getpos) < 0)
296: err(1, "AUDIO_GETPOS");
1.30 ! ratchov 297: block_size = info.play.block_size /
! 298: (info.play.channels * info.play.bps);
1.1 provos 299: }
300:
301: void
1.7 pvalchev 302: usage(void)
1.1 provos 303: {
1.7 pvalchev 304: extern char *__progname; /* from crt0.o */
305:
306: fprintf(stderr,
1.16 deraadt 307: "usage: %s [-an] [-f file]\n"
1.15 sobrado 308: " %s [-n] [-f file] name ...\n"
309: " %s [-n] [-f file] name=value ...\n",
310: __progname, __progname, __progname);
1.7 pvalchev 311:
1.1 provos 312: exit(1);
313: }
314:
315: int
1.7 pvalchev 316: main(int argc, char **argv)
1.1 provos 317: {
318: int fd, i, ch;
1.11 vincent 319: int aflag = 0, canwrite, writeinfo = 0;
1.1 provos 320: struct stat dstat, ostat;
1.11 vincent 321: struct field *p;
1.12 moritz 322: const char *file;
323: const char *sep = "=";
1.1 provos 324:
1.7 pvalchev 325: if ((file = getenv("AUDIOCTLDEVICE")) == 0 || *file == '\0')
1.3 provos 326: file = "/dev/audioctl";
1.1 provos 327:
328: while ((ch = getopt(argc, argv, "af:nw")) != -1) {
1.15 sobrado 329: switch (ch) {
1.1 provos 330: case 'a':
1.25 deraadt 331: aflag = 1;
1.1 provos 332: break;
333: case 'w':
1.11 vincent 334: /* backward compatibility */
1.1 provos 335: break;
336: case 'n':
337: sep = 0;
338: break;
339: case 'f':
340: file = optarg;
341: break;
342: default:
343: usage();
344: }
345: }
346: argc -= optind;
347: argv += optind;
1.16 deraadt 348:
349: if (argc == 0)
1.25 deraadt 350: aflag = 1;
1.11 vincent 351:
352: if ((fd = open(file, O_RDWR)) < 0) {
353: if ((fd = open(file, O_RDONLY)) < 0)
354: err(1, "%s", file);
355: canwrite = 0;
356: } else
357: canwrite = 1;
1.1 provos 358:
359: /* Check if stdout is the same device as the audio device. */
360: if (fstat(fd, &dstat) < 0)
361: err(1, "fstat au");
362: if (fstat(STDOUT_FILENO, &ostat) < 0)
363: err(1, "fstat stdout");
364: if (S_ISCHR(dstat.st_mode) && S_ISCHR(ostat.st_mode) &&
365: major(dstat.st_dev) == major(ostat.st_dev) &&
366: minor(dstat.st_dev) == minor(ostat.st_dev))
367: /* We can't write to stdout so use stderr */
368: out = stderr;
369:
1.11 vincent 370: if (!argc && !aflag)
371: usage();
372:
373: getinfo(fd);
1.1 provos 374:
1.11 vincent 375: if (aflag) {
376: for (i = 0; fields[i].name; i++) {
1.1 provos 377: if (!(fields[i].flags & ALIAS)) {
378: prfield(&fields[i], sep);
379: }
380: }
1.11 vincent 381: } else {
382: while (argc--) {
383: char *q;
1.1 provos 384:
1.11 vincent 385: if ((q = strchr(*argv, '=')) != NULL) {
386: *q++ = 0;
387: p = findfield(*argv);
388: if (p == 0)
389: warnx("field `%s' does not exist", *argv);
390: else {
391: if (!canwrite)
392: errx(1, "%s: permission denied",
393: *argv);
394: if (p->flags & READONLY)
395: warnx("`%s' is read only", *argv);
1.1 provos 396: else {
1.11 vincent 397: rdfield(p, q);
398: if (p->valp == &fullduplex)
399: if (ioctl(fd, AUDIO_SETFD,
400: &fullduplex) < 0)
401: err(1, "set failed");
1.1 provos 402: }
1.11 vincent 403: writeinfo = 1;
1.1 provos 404: }
1.11 vincent 405: } else {
1.1 provos 406: p = findfield(*argv);
1.11 vincent 407: if (p == 0)
408: warnx("field %s does not exist", *argv);
409: else {
1.1 provos 410: prfield(p, sep);
411: }
1.11 vincent 412: }
413: argv++;
414: }
1.30 ! ratchov 415: if (writeinfo) {
! 416: info.record.sample_rate = info.play.sample_rate;
! 417: info.record.encoding = info.play.encoding;
! 418: info.record.precision = info.play.precision;
! 419: info.record.bps = info.play.bps;
! 420: info.record.msb = info.play.msb;
! 421: info.record.block_size = block_size *
! 422: info.record.bps * info.record.channels;
! 423: info.play.block_size = block_size *
! 424: info.play.bps * info.play.channels;
! 425: if (ioctl(fd, AUDIO_SETINFO, &info) < 0)
! 426: err(1, "set failed");
! 427: }
1.20 ratchov 428: getinfo(fd);
429: for (i = 0; fields[i].name; i++) {
430: if (fields[i].flags & SET) {
431: prfield(&fields[i], sep);
1.1 provos 432: }
433: }
1.11 vincent 434: }
1.1 provos 435: exit(0);
436: }