diff options
author | 2016-06-21 06:26:50 +0000 | |
---|---|---|
committer | 2016-06-21 06:26:50 +0000 | |
commit | 79067d65389fdbf23a48bea274d2b1e12455514c (patch) | |
tree | 585fb1103f33346d576071fc7f6a714f4210fb81 /usr.bin/audioctl | |
parent | Disable DSA_FLAG_NO_EXP_CONSTTIME, always enable constant-time behavior. (diff) | |
download | wireguard-openbsd-79067d65389fdbf23a48bea274d2b1e12455514c.tar.xz wireguard-openbsd-79067d65389fdbf23a48bea274d2b1e12455514c.zip |
Reimplement audioctl using new api in a simper way.
- group all encoding parameters in a signle string, ex. "s16le",
this way we use the same naming scheme as aucat, sndiod and many
ports.
- remove "properties" as they are not used any longer
- remove the list of encodings as there's no benefit in having it.
We don't have lists for other parameters (sample rates, channel
numbers) either.
- add -q option, to look like sysctl
- remove unused -a option
- stop using symlinks in /dev, most other software doesn't use
them.
ok semarie@
Diffstat (limited to 'usr.bin/audioctl')
-rw-r--r-- | usr.bin/audioctl/audioctl.c | 594 |
1 files changed, 218 insertions, 376 deletions
diff --git a/usr.bin/audioctl/audioctl.c b/usr.bin/audioctl/audioctl.c index 1dd5b63f1b0..2511aec00c6 100644 --- a/usr.bin/audioctl/audioctl.c +++ b/usr.bin/audioctl/audioctl.c @@ -1,436 +1,278 @@ -/* $OpenBSD: audioctl.c,v 1.30 2016/01/29 10:23:56 ratchov Exp $ */ -/* $NetBSD: audioctl.c,v 1.14 1998/04/27 16:55:23 augustss Exp $ */ - +/* $OpenBSD: audioctl.c,v 1.31 2016/06/21 06:26:50 ratchov Exp $ */ /* - * Copyright (c) 1997 The NetBSD Foundation, Inc. - * All rights reserved. - * - * Author: Lennart Augustsson + * Copyright (c) 2016 Alexandre Ratchov <alex@caoua.org> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * audioctl(1) - a program to control audio device. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/audioio.h> +#include <fcntl.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> -#include <fcntl.h> -#include <err.h> #include <unistd.h> #include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/audioio.h> - -struct field *findfield(char *name); -void prfield(struct field *p, const char *sep); -void rdfield(struct field *p, char *q); -void getinfo(int fd); -void usage(void); -int main(int argc, char **argv); - -FILE *out = stdout; - -audio_device_t adev; - -audio_info_t info; - -char encbuf[1000]; - -int properties, fullduplex; +#include <err.h> -struct audio_pos getpos; +/* + * Default bytes per sample for the given bits per sample. + */ +#define BPS(bits) (((bits) <= 8) ? 1 : (((bits) <= 16) ? 2 : 4)) -unsigned int block_size; +struct audio_device rname; +struct audio_status rstatus; +struct audio_swpar rpar, wpar; +struct audio_pos rpos; struct field { - const char *name; - void *valp; - int format; -#define STRING 1 -#define INT 2 -#define UINT 3 -#define P_R 4 -#define UCHAR 6 -#define ENC 7 -#define PROPS 8 -#define XINT 9 - char flags; -#define READONLY 1 -#define ALIAS 2 -#define SET 4 - u_int oldval; + char *name; + void *raddr, *waddr; +#define MODE 0 +#define NUM 1 +#define STR 2 +#define ENC 3 + int type; + int set; } fields[] = { - { "name", &adev.name, STRING, READONLY }, - { "encodings", encbuf, STRING, READONLY }, - { "properties", &properties, PROPS, READONLY }, - { "hiwat", &info.hiwat, UINT, 0 }, - { "mode", &info.mode, P_R, READONLY }, - { "rate", &info.play.sample_rate, UINT, 0 }, - { "precision", &info.play.precision, UINT, 0 }, - { "bps", &info.play.bps, UINT, 0 }, - { "msb", &info.play.msb, UINT, 0 }, - { "encoding", &info.play.encoding, ENC, 0 }, - { "pause", &info.play.pause, UCHAR, 0 }, - { "active", &info.play.active, UCHAR, READONLY }, - { "block_size", &block_size, UINT, 0 }, - { "play.channels", &info.play.channels, UINT, 0 }, - { "play.bytes", &getpos.play_pos, UINT, READONLY }, - { "play.errors", &getpos.play_xrun, UINT, READONLY }, - { "record.channels", &info.record.channels, UINT, 0 }, - { "record.bytes", &getpos.rec_pos, UINT, READONLY }, - { "record.errors", &getpos.rec_xrun, UINT, READONLY }, - { 0 } -}; - -struct { - const char *ename; - u_int eno; -} encs[] = { - { AudioEmulaw, AUDIO_ENCODING_ULAW }, - { "ulaw", AUDIO_ENCODING_ULAW }, - { AudioEalaw, AUDIO_ENCODING_ALAW }, - { AudioEslinear, AUDIO_ENCODING_SLINEAR }, - { "linear", AUDIO_ENCODING_SLINEAR }, - { AudioEulinear, AUDIO_ENCODING_ULINEAR }, - { AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE }, - { "linear_le", AUDIO_ENCODING_SLINEAR_LE }, - { AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE }, - { AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE }, - { "linear_be", AUDIO_ENCODING_SLINEAR_BE }, - { AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE }, - { 0 } + {"name", &rname.name, NULL, STR}, + {"mode", &rstatus.mode, NULL, MODE}, + {"pause", &rstatus.pause, NULL, NUM}, + {"active", &rstatus.active, NULL, NUM}, + {"nblks", &rpar.nblks, &wpar.nblks, NUM}, + {"blksz", &rpar.round, &wpar.round, NUM}, + {"rate", &rpar.rate, &wpar.rate, NUM}, + {"encoding", &rpar, &wpar, ENC}, + {"play.channels", &rpar.pchan, &wpar.pchan, NUM}, + {"play.bytes", &rpos.play_pos, NULL, NUM}, + {"play.errors", &rpos.play_xrun, NULL, NUM}, + {"record.channels", &rpar.rchan, &wpar.rchan, NUM}, + {"record.bytes", &rpos.rec_pos, NULL, NUM}, + {"record.errors", &rpos.rec_xrun, NULL, NUM}, + {NULL, NULL, 0} }; -static struct { - const char *name; - u_int prop; -} props[] = { - { "full_duplex", AUDIO_PROP_FULLDUPLEX }, - { "mmap", AUDIO_PROP_MMAP }, - { "independent", AUDIO_PROP_INDEPENDENT }, - { 0 } -}; +const char usagestr[] = "usage: audioctl [-nq] [-f path] [name=[value]] ...\n"; -struct field * -findfield(char *name) -{ - int i; - for (i = 0; fields[i].name; i++) - if (strcmp(fields[i].name, name) == 0) - return &fields[i]; - return (0); -} - -static void -prval(u_int format, void *valp) +/* + * parse encoding string (examples: s8, u8, s16, s16le, s24be ...) + * and fill enconding fields of audio_swpar structure + */ +int +strtoenc(struct audio_swpar *ap, char *p) { - u_int v; - const char *cm; - int i; + /* expect "s" or "u" (signedness) */ + if (*p == 's') + ap->sig = 1; + else if (*p == 'u') + ap->sig = 0; + else + return 0; + p++; - switch (format) { - case STRING: - fprintf(out, "%s", (char *)valp); - break; - case INT: - fprintf(out, "%d", *(int *)valp); - break; - case UINT: - fprintf(out, "%u", *(u_int *)valp); - break; - case XINT: - fprintf(out, "0x%x", *(u_int *)valp); - break; - case UCHAR: - fprintf(out, "%u", *(u_char *)valp); - break; - case P_R: - v = *(u_int *)valp; - cm = ""; - if (v & AUMODE_PLAY) { - fprintf(out, "play"); - cm = ","; - } - if (v & AUMODE_RECORD) - fprintf(out, "%srecord", cm); - break; - case ENC: - v = *(u_int *)valp; - for (i = 0; encs[i].ename; i++) - if (encs[i].eno == v) - break; - if (encs[i].ename) - fprintf(out, "%s", encs[i].ename); - else - fprintf(out, "%u", v); - break; - case PROPS: - v = *(u_int *)valp; - for (cm = "", i = 0; props[i].name; i++) { - if (v & props[i].prop) { - fprintf(out, "%s%s", cm, props[i].name); - cm = ","; - } - } - break; - default: - errx(1, "Invalid print format."); + /* expect 1-2 decimal digits (bits per sample) */ + ap->bits = 0; + while (*p >= '0' && *p <= '9') { + ap->bits = (ap->bits * 10) + *p++ - '0'; + if (ap->bits > 32) + return 0; } -} + if (ap->bits < 8) + return 0; -void -prfield(struct field *p, const char *sep) -{ - if (sep) { - fprintf(out, "%s", p->name); - if (p->flags & SET) { - fprintf(out, "%s", ": "); - prval(p->format, &p->oldval); - fprintf(out, " -> "); - } else - fprintf(out, "%s", sep); - } - prval(p->format, p->valp); - fprintf(out, "\n"); + /* set defaults as next tokens are optional */ + ap->bps = BPS(ap->bits); + ap->le = (BYTE_ORDER == LITTLE_ENDIAN); + ap->msb = 1; + if (*p == '\0') + return 1; + + /* expect "le" or "be" (endianness) */ + if (p[0] == 'l' && p[1] == 'e') + ap->le = 1; + else if (p[0] == 'b' && p[1] == 'e') + ap->le = 0; + else + return 0; + p += 2; + if (*p == '\0') + return 1; + + /* expect 1 decimal digit (number of bytes) */ + if (*p < '0' || *p > '9') + return 0; + ap->bps = *p - '0'; + if (ap->bps < ((ap->bits + 7) >> 3) || ap->bps > 4) + return 0; + if (*++p == '\0') + return 1; + + /* expect "msb" or "lsb" (alignment) */ + if (p[0] == 'm' && p[1] == 's' && p[2] == 'b') + ap->msb = 1; + else if (p[0] == 'l' && p[1] == 's' && p[2] == 'b') + ap->msb = 0; + else if (*p == '\0') + return 1; + p += 3; + if (*p == '\0') + return 1; + + /* must be no additional junk */ + return 0; } void -rdfield(struct field *p, char *q) +print_val(struct field *p, void *addr) { - int i; - u_int u; + int mode; + struct audio_swpar *ap; - switch (p->format) { - case UINT: - p->oldval = *(u_int *)p->valp; - if (sscanf(q, "%u", (unsigned int *)p->valp) != 1) { - warnx("Bad number %s", q); - return; - } + switch (p->type) { + case NUM: + printf("%u", *(unsigned int *)addr); break; - case UCHAR: - *(char *)&p->oldval = *(u_char *)p->valp; - if (sscanf(q, "%u", &u) != 1) { - warnx("Bad number %s", q); - return; - } - *(u_char *)p->valp = u; + case STR: + printf("%s", (char *)addr); break; - case XINT: - p->oldval = *(u_int *)p->valp; - if (sscanf(q, "0x%x", (unsigned int *)p->valp) != 1 && - sscanf(q, "%x", (unsigned int *)p->valp) != 1) { - warnx("Bad number %s", q); - return; + case MODE: + mode = *(unsigned int *)addr; + if (mode & AUMODE_PLAY) + printf("play"); + if (mode & AUMODE_RECORD) { + if (mode & AUMODE_PLAY) + printf(","); + printf("record"); } break; case ENC: - p->oldval = *(u_int *)p->valp; - for (i = 0; encs[i].ename; i++) - if (strcmp(encs[i].ename, q) == 0) - break; - if (encs[i].ename) - *(u_int*)p->valp = encs[i].eno; - else { - warnx("Unknown encoding: %s", q); - return; + ap = addr; + printf("%s%u", ap->sig ? "s" : "u", ap->bits); + if (ap->bps == 1) + break; + printf("%s", ap->le ? "le" : "be"); + if (ap->bps != BPS(ap->bits) || ap->bits < ap->bps * 8) { + printf("%u", ap->bps); + if (ap->bits < ap->bps * 8) + printf("%s", ap->msb ? "msb" : "lsb"); } - break; - default: - errx(1, "Invalid read format."); } - p->flags |= SET; } void -getinfo(int fd) +parse_val(struct field *f, void *addr, char *p) { - int pos = 0, i = 0; + const char *strerr; - if (ioctl(fd, AUDIO_GETDEV, &adev) < 0) - err(1, "AUDIO_GETDEV"); - for (;;) { - audio_encoding_t enc; - enc.index = i++; - if (ioctl(fd, AUDIO_GETENC, &enc) < 0) - break; - if (pos) - encbuf[pos++] = ','; - snprintf(encbuf+pos, sizeof(encbuf)-pos, "%s:%d:%d:%d%s", - enc.name, enc.precision, enc.bps, enc.msb, - enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : ""); - pos += strlen(encbuf+pos); + switch (f->type) { + case NUM: + *(unsigned int *)addr = strtonum(p, 0, UINT_MAX, &strerr); + if (strerr) + errx(1, "%s: %s", p, strerr); + break; + case ENC: + if (!strtoenc((struct audio_swpar *)addr, p)) + errx(1, "%s: bad encoding", p); } - if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0) - err(1, "AUDIO_GETFD"); - if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0) - err(1, "AUDIO_GETPROPS"); - if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0) - err(1, "AUDIO_GETPROPS"); - if (ioctl(fd, AUDIO_GETINFO, &info) < 0) - err(1, "AUDIO_GETINFO"); - if (ioctl(fd, AUDIO_GETPOS, &getpos) < 0) - err(1, "AUDIO_GETPOS"); - block_size = info.play.block_size / - (info.play.channels * info.play.bps); -} - -void -usage(void) -{ - extern char *__progname; /* from crt0.o */ - - fprintf(stderr, - "usage: %s [-an] [-f file]\n" - " %s [-n] [-f file] name ...\n" - " %s [-n] [-f file] name=value ...\n", - __progname, __progname, __progname); - - exit(1); } int main(int argc, char **argv) { - int fd, i, ch; - int aflag = 0, canwrite, writeinfo = 0; - struct stat dstat, ostat; - struct field *p; - const char *file; - const char *sep = "="; - - if ((file = getenv("AUDIOCTLDEVICE")) == 0 || *file == '\0') - file = "/dev/audioctl"; - - while ((ch = getopt(argc, argv, "af:nw")) != -1) { - switch (ch) { - case 'a': - aflag = 1; - break; - case 'w': - /* backward compatibility */ + struct field *f; + char *lhs, *rhs, *path = "/dev/audioctl0"; + int fd, c, set = 0, print_names = 1, quiet = 0; + + while ((c = getopt(argc, argv, "anf:q")) != -1) { + switch (c) { + case 'a': /* ignored, compat */ break; case 'n': - sep = 0; + print_names = 0; break; case 'f': - file = optarg; + path = optarg; + break; + case 'q': + quiet = 1; break; default: - usage(); + fputs(usagestr, stderr); + return 1; } } argc -= optind; argv += optind; - if (argc == 0) - aflag = 1; - - if ((fd = open(file, O_RDWR)) < 0) { - if ((fd = open(file, O_RDONLY)) < 0) - err(1, "%s", file); - canwrite = 0; - } else - canwrite = 1; - - /* Check if stdout is the same device as the audio device. */ - if (fstat(fd, &dstat) < 0) - err(1, "fstat au"); - if (fstat(STDOUT_FILENO, &ostat) < 0) - err(1, "fstat stdout"); - if (S_ISCHR(dstat.st_mode) && S_ISCHR(ostat.st_mode) && - major(dstat.st_dev) == major(ostat.st_dev) && - minor(dstat.st_dev) == minor(ostat.st_dev)) - /* We can't write to stdout so use stderr */ - out = stderr; - - if (!argc && !aflag) - usage(); - - getinfo(fd); - - if (aflag) { - for (i = 0; fields[i].name; i++) { - if (!(fields[i].flags & ALIAS)) { - prfield(&fields[i], sep); - } + fd = open(path, O_RDWR); + if (fd < 0) + err(1, "%s", path); + if (ioctl(fd, AUDIO_GETSTATUS, &rstatus) < 0) + err(1, "AUDIO_GETSTATUS"); + if (ioctl(fd, AUDIO_GETDEV, &rname) < 0) + err(1, "AUDIO_GETDEV"); + if (ioctl(fd, AUDIO_GETPAR, &rpar) < 0) + err(1, "AUDIO_GETPAR"); + if (ioctl(fd, AUDIO_GETPOS, &rpos) < 0) + err(1, "AUDIO_GETPOS"); + if (argc == 0) { + for (f = fields; f->name != NULL; f++) { + printf("%s=", f->name); + print_val(f, f->raddr); + printf("\n"); } - } else { - while (argc--) { - char *q; - - if ((q = strchr(*argv, '=')) != NULL) { - *q++ = 0; - p = findfield(*argv); - if (p == 0) - warnx("field `%s' does not exist", *argv); - else { - if (!canwrite) - errx(1, "%s: permission denied", - *argv); - if (p->flags & READONLY) - warnx("`%s' is read only", *argv); - else { - rdfield(p, q); - if (p->valp == &fullduplex) - if (ioctl(fd, AUDIO_SETFD, - &fullduplex) < 0) - err(1, "set failed"); - } - writeinfo = 1; - } - } else { - p = findfield(*argv); - if (p == 0) - warnx("field %s does not exist", *argv); - else { - prfield(p, sep); - } - } - argv++; + } + AUDIO_INITPAR(&wpar); + for (; argc > 0; argc--, argv++) { + lhs = *argv; + rhs = strchr(*argv, '='); + if (rhs) + *rhs++ = '\0'; + for (f = fields;; f++) { + if (f->name == NULL) + errx(1, "%s: unknown parameter", lhs); + if (strcmp(f->name, lhs) == 0) + break; } - if (writeinfo) { - info.record.sample_rate = info.play.sample_rate; - info.record.encoding = info.play.encoding; - info.record.precision = info.play.precision; - info.record.bps = info.play.bps; - info.record.msb = info.play.msb; - info.record.block_size = block_size * - info.record.bps * info.record.channels; - info.play.block_size = block_size * - info.play.bps * info.play.channels; - if (ioctl(fd, AUDIO_SETINFO, &info) < 0) - err(1, "set failed"); + if (rhs) { + if (f->waddr == NULL) + errx(1, "%s: is read only", f->name); + parse_val(f, f->waddr, rhs); + f->set = 1; + set = 1; + } else { + if (print_names) + printf("%s=", f->name); + print_val(f, f->raddr); + printf("\n"); } - getinfo(fd); - for (i = 0; fields[i].name; i++) { - if (fields[i].flags & SET) { - prfield(&fields[i], sep); - } + } + if (!set) + return 0; + if (ioctl(fd, AUDIO_SETPAR, &wpar) < 0) + err(1, "AUDIO_SETPAR"); + if (ioctl(fd, AUDIO_GETPAR, &wpar) < 0) + err(1, "AUDIO_GETPAR"); + for (f = fields; f->name != NULL; f++) { + if (!f->set || quiet) + continue; + if (print_names) { + printf("%s: ", f->name); + print_val(f, f->raddr); + printf(" -> "); } + print_val(f, f->waddr); + printf("\n"); } - exit(0); + return 0; } |