simpler audioctl

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

simpler audioctl

Alexandre Ratchov-2
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?

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 <[hidden email]>
  *
- * 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;
 }

Reply | Threaded
Open this post in threaded view
|

Re: simpler audioctl

Sebastien Marie-2
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 <[hidden email]>
>   *
> - * 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

Reply | Threaded
Open this post in threaded view
|

Re: simpler audioctl

Alexandre Ratchov-2
On Sun, Jun 19, 2016 at 05:56:38PM +0200, Sebastien Marie wrote:
>
> 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.

Correct, what about the following:

        Request the hardware to use signed 24-bit samples and
        44100Hz sample rate:

                $ audioctl -f /dev/audio0 encoding=s24 rate=44100

        Note the use of /dev/audio0 to force negotiation with the
        hardware.  If above parameters are not supported by the
        hardware, then supported ones will be selected instead.