On Sun, Jun 19, 2016 at 04:22:31PM +0200, Alexandre Ratchov wrote: > This diff reimplements audioctl with less lines, using the recently > added ioctls and simplifies its output. audioctl is the last > remaining user of few old ioctls and this diff would permit to > simplify the audio(4) driver soon. > > - 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 > - document all audio driver variables in the man page > - document difference between /dev/audioctl0 and /dev/audio0 > - give an example of how to test hardware capabilities with > audioctl > - stop using symlinks in /dev, most other software doesn't use > them. > > OK?
OK semarie@ I reviewed the audioctl.c result file (and not the diff) as it is a rewrite. Just one point about the man page: Test if the hardware supports 24-bit encoding and 44100Hz sample rate: $ audioctl -f /dev/audio0 encoding=s24 rate=44100 "Test" is somehow incorrect as if the device supports it, the values will be changed: so it is more than just testing. > Index: audioctl.1 > =================================================================== > RCS file: /cvs/src/usr.bin/audioctl/audioctl.1,v > retrieving revision 1.28 > diff -u -p -u -p -r1.28 audioctl.1 > --- audioctl.1 29 Jan 2016 10:45:38 -0000 1.28 > +++ audioctl.1 19 Jun 2016 14:19:39 -0000 > @@ -31,42 +31,34 @@ > .Os > .Sh NAME > .Nm audioctl > -.Nd control audio device > +.Nd get or set audio driver variables > .Sh SYNOPSIS > .Nm audioctl > -.Op Fl an > .Op Fl f Ar file > .Nm audioctl > .Op Fl n > .Op Fl f Ar file > .Ar name ... > .Nm audioctl > -.Op Fl n > +.Op Fl nq > .Op Fl f Ar file > .Ar name Ns = Ns Ar value ... > .Sh DESCRIPTION > The > .Nm > -command displays or sets various audio system driver variables. > -If a list of variables is present on the command line, > -.Nm > -prints the current value of those variables for the specified device. > -By default, > -.Nm > -operates on the > -.Pa /dev/audioctl > -device. > -.Pp > +utility retrieves or sets > +.Xr audio 4 > +driver variables. > The options are as follows: > -.Bl -tag -width "name=valueXX" > -.It Fl a > -Print all device variables and their current values. > -This is the default, if no parameters are given to > -.Nm . > +.Bl -tag -width Ds > .It Fl f Ar file > -Specify an alternative audio control device. > +Specifies the audio control device or the audio device. > +The default is > +.Pa /dev/audioctl0 . > .It Fl n > Suppress printing of the variable name. > +.It Fl q > +Suppress all output when setting a variable. > .It Ar name Ns = Ns Ar value > Attempt to set the specified variable > .Ar name > @@ -74,22 +66,84 @@ to > .Ar value . > .El > .Pp > +If the audio control device is used, then values are only stored in the > +.Xr audio 4 > +driver; they will be submitted to the hardware the next time the > +device is opened for playback or recording. > +If the audio device is used instead of the control device, > +then values are negotiated with the hardware immediately; this requires > +exclusive access to the device. > Variables may only be changed if the device is not opened for > playback or recording by another process. > -.Sh ENVIRONMENT > -.Bl -tag -width AUDIOCTLDEVICE > -.It Ev AUDIOCTLDEVICE > -Audio control device to use. > +.Pp > +The following variable names are available: > +.Bl -column "record.channels" > +.It Sy Name Ta Sy Meaning > +.It name Ta device name as shown by > +.Xr dmesg 8 > +.It mode Ta current device mode ( > +.Va play , > +.Va record > +or both) > +.It pause Ta set if not attempting to start > +.It active Ta set if playing or recording > +.It nblks Ta number of blocks (in frames) in the play buffer > +.It blksz Ta number of frames per block > +.It rate Ta sample rate in Hz > +.It encoding Ta current sample format > +.It play.channels Ta number of play channels > +.It play.bytes Ta bytes played since playback started > +.It play.errors Ta bytes inserted during underruns > +.It record.channels Ta number of recording channels > +.It record.bytes Ta bytes recorded since device started > +.It record.errors Ta bytes dropped during overruns > .El > +.Pp > +Encoding names use the following scheme: signedness > +.Po > +.Va s > +or > +.Va u > +.Pc > +followed > +by the precision in bits, the byte-order > +.Po > +.Va le > +or > +.Va be > +.Pc , > +the number of > +bytes per sample, and the alignment > +.Po > +.Va msb > +or > +.Va lsb > +.Pc . > +Only the signedness and the precision are mandatory. > +Examples: > +.Va u8 , s16le , s24le3 , s24le4lsb . > .Sh FILES > -.Bl -tag -width /dev/audioctl > -.It Pa /dev/audioctl > -default audio control device > +.Bl -tag -width /dev/audioctl0 > +.It Pa /dev/audioctlN > +audio control devices > +.It Pa /dev/audioN > +audio devices > .El > .Sh EXAMPLES > -To set the playing sampling rate to 11025 you can enter: > +Display the number of bytes of silence inserted during play buffer > +underruns since device started: > +.Bd -literal -offset indent > +$ audioctl play.errors > +.Ed > +.Pp > +Test if the hardware supports 24-bit encoding and 44100Hz sample rate: > +.Bd -literal -offset indent > +$ audioctl -f /dev/audio0 encoding=s24 rate=44100 > +.Ed > .Pp > -.Dl $ audioctl play.rate=11025 > +Note the use of > +.Pa /dev/audio0 , > +to force negotiation with the hardware. > .Sh SEE ALSO > .Xr aucat 1 , > .Xr cdio 1 , > Index: audioctl.c > =================================================================== > RCS file: /cvs/src/usr.bin/audioctl/audioctl.c,v > retrieving revision 1.30 > diff -u -p -u -p -r1.30 audioctl.c > --- audioctl.c 29 Jan 2016 10:23:56 -0000 1.30 > +++ audioctl.c 19 Jun 2016 14:19:39 -0000 > @@ -1,436 +1,278 @@ > -/* $OpenBSD: audioctl.c,v 1.28 2015/05/26 18:17:12 ratchov Exp $ */ > -/* $NetBSD: audioctl.c,v 1.14 1998/04/27 16:55:23 augustss Exp $ */ > - > +/* $OpenBSD$ */ > /* > - * Copyright (c) 1997 The NetBSD Foundation, Inc. > - * All rights reserved. > - * > - * Author: Lennart Augustsson > + * Copyright (c) 2016 Alexandre Ratchov <a...@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 } > + {"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} > }; > > -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 } > -}; > - > -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; > - > - 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 "s" or "u" (signedness) */ > + if (*p == 's') > + ap->sig = 1; > + else if (*p == 'u') > + ap->sig = 0; > + else > + return 0; > + p++; > + > + /* 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" (alignement) */ > + 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; > - } > - 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; > - 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; > + switch (p->type) { > + case NUM: > + printf("%u", *(unsigned int *)addr); > + break; > + case STR: > + printf("%s", (char *)addr); > + break; > + 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; > } > > -- Sebastien Marie