Module Name:    src
Committed By:   isaki
Date:           Wed May  8 14:36:12 UTC 2019

Modified Files:
        src/usr.bin/audiocfg: audiocfg.1 audiodev.c audiodev.h main.c

Log Message:
Update respond to isaki-audio2 branch.
- Extend list command to display supported hardware formats.
- Add set command to set hardware format.
- Use correct /dev/audioctl instead of /dev/audio.


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/usr.bin/audiocfg/audiocfg.1
cvs rdiff -u -r1.6 -r1.7 src/usr.bin/audiocfg/audiodev.c
cvs rdiff -u -r1.4 -r1.5 src/usr.bin/audiocfg/audiodev.h
cvs rdiff -u -r1.7 -r1.8 src/usr.bin/audiocfg/main.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/audiocfg/audiocfg.1
diff -u src/usr.bin/audiocfg/audiocfg.1:1.3 src/usr.bin/audiocfg/audiocfg.1:1.4
--- src/usr.bin/audiocfg/audiocfg.1:1.3	Wed Sep  1 09:17:31 2010
+++ src/usr.bin/audiocfg/audiocfg.1	Wed May  8 14:36:12 2019
@@ -1,4 +1,4 @@
-.\"	$NetBSD: audiocfg.1,v 1.3 2010/09/01 09:17:31 wiz Exp $
+.\"	$NetBSD: audiocfg.1,v 1.4 2019/05/08 14:36:12 isaki Exp $
 .\"
 .\" Copyright (c) 2010 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -40,6 +40,14 @@
 .Cm default
 .Ar index
 .Nm
+.Cm set
+.Ar index
+.Ar direction
+.Ar encoding
+.Ar precision
+.Ar channels
+.Ar sample_rate
+.Nm
 .Cm test
 .Ar index
 .Sh DESCRIPTION
@@ -51,6 +59,25 @@ Run with
 .Dq Cm list ,
 it lists the available audio devices and shows the currently selected
 default audio device.
+.Pp
+Run with
+.Dq Cm set ,
+if sets the hardware format.
+The
+.Ar direction
+is represented by either of 'p'(playback) or 'r'(record), or both of them,
+indicates direction you want to set to.
+The remaining parameters
+.Ar encoding ,
+.Ar precision ,
+.Ar channels
+and
+.Ar sample_rate
+indicate the hardware format you want to set to.
+These parameters must be selected from the candidates displayed by
+.Nm
+.Cm list .
+.Pp
 Called with
 .Dq Cm test ,
 it plays a tone of 2 seconds for each channel of the device with the index

Index: src/usr.bin/audiocfg/audiodev.c
diff -u src/usr.bin/audiocfg/audiodev.c:1.6 src/usr.bin/audiocfg/audiodev.c:1.7
--- src/usr.bin/audiocfg/audiodev.c:1.6	Sat Mar  5 22:10:39 2016
+++ src/usr.bin/audiocfg/audiodev.c	Wed May  8 14:36:12 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: audiodev.c,v 1.6 2016/03/05 22:10:39 mrg Exp $ */
+/* $NetBSD: audiodev.c,v 1.7 2019/05/08 14:36:12 isaki Exp $ */
 
 /*
  * Copyright (c) 2010 Jared D. McNeill <[email protected]>
@@ -31,6 +31,7 @@
 #include <sys/stat.h>
 #include <sys/drvctlio.h>
 
+#include <errno.h>
 #include <fcntl.h>
 #include <paths.h>
 #include <stdio.h>
@@ -47,51 +48,23 @@ static TAILQ_HEAD(audiodevhead, audiodev
 
 #define AUDIODEV_SAMPLE_RATE	44100
 
-static unsigned int
-audiodev_probe_pchans(struct audiodev *adev)
-{
-	audio_info_t info;
-	unsigned int nchans = 0, n;
-	int error;
-
-	AUDIO_INITINFO(&info);
-	info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
-	info.play.precision = 16;
-	info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
-	info.play.channels = 1;
-	info.mode = AUMODE_PLAY;
-	error = ioctl(adev->fd, AUDIO_SETINFO, &info);
-	if (error == -1)
-		return 0;
-	nchans = 1;
-
-	for (n = 2; n <= 16; n += 2) {
-		info.play.channels = n;
-		error = ioctl(adev->fd, AUDIO_SETINFO, &info);
-		if (error == -1)
-			break;
-		nchans = info.play.channels;
-	}
-
-	return nchans;
-}
-
 static int
 audiodev_getinfo(struct audiodev *adev)
 {
 	struct stat st;
+	struct audiofmt *f;
+	audio_format_query_t query;
+	int i;
 
-	if (stat(adev->path, &st) == -1)
+	if (stat(adev->ctlpath, &st) == -1)
 		return -1;
 	adev->dev = st.st_rdev;
 
-	if (stat(_PATH_AUDIO, &st) != -1 && st.st_rdev == adev->dev)
+	if (stat(_PATH_AUDIOCTL, &st) != -1 && st.st_rdev == adev->dev)
 		adev->defaultdev = true;
 
-	adev->fd = open(adev->path, O_RDWR);
+	adev->fd = open(adev->ctlpath, O_RDONLY);
 	if (adev->fd == -1) {
-		adev->fd = open(adev->path, O_WRONLY);
-		if (adev->fd == -1)
 			return -1;
 	}
 	if (ioctl(adev->fd, AUDIO_GETDEV, &adev->audio_device) == -1) {
@@ -99,7 +72,29 @@ audiodev_getinfo(struct audiodev *adev)
 		return -1;
 	}
 
-	adev->pchan = audiodev_probe_pchans(adev);
+	for (i = 0; ;i++) {
+		memset(&query, 0, sizeof(query));
+		query.index = i;
+		if (ioctl(adev->fd, AUDIO_QUERYFORMAT, &query) == -1) {
+			if (errno == ENODEV) {
+				/* QUERYFORMAT not supported. */
+				break;
+			}
+			if (errno == EINVAL)
+				break;
+			close(adev->fd);
+			return -1;
+		}
+
+		f = calloc(1, sizeof(*f));
+		f->fmt = query.fmt;
+		TAILQ_INSERT_TAIL(&adev->formats, f, next);
+	}
+
+	if (ioctl(adev->fd, AUDIO_GETFORMAT, &adev->info) == -1) {
+		close(adev->fd);
+		return -1;
+	}
 
 	return 0;
 }
@@ -115,8 +110,10 @@ audiodev_add(const char *pdev, const cha
 
 	strlcpy(adev->pxname, pdev, sizeof(adev->pxname));
 	strlcpy(adev->xname, dev, sizeof(adev->xname));
-	snprintf(adev->path, sizeof(adev->path) - 1, "/dev/%s", dev);
+	snprintf(adev->path, sizeof(adev->path), "/dev/%s", dev);
+	snprintf(adev->ctlpath, sizeof(adev->ctlpath), "/dev/audioctl%d", unit);
 	adev->unit = unit;
+	TAILQ_INIT(&adev->formats);
 
 	if (audiodev_getinfo(adev) == -1) {
 		free(adev);
@@ -124,8 +121,16 @@ audiodev_add(const char *pdev, const cha
 	}
 
 #ifdef DEBUG
-	printf("[%c] %s: %s\n", adev->defaultdev ? '*' : ' ',
-	    adev->path, adev->audio_device.name);
+	printf("DEBUG: [%c] %s(%s): %s\n", adev->defaultdev ? '*' : ' ',
+	    adev->path, adev->ctlpath, adev->audio_device.name);
+	struct audiofmt *f;
+	TAILQ_FOREACH(f, &adev->formats, next) {
+		printf("DEBUG: enc%d, %d/%d, %dch\n",
+		    f->fmt.encoding,
+		    f->fmt.validbits,
+		    f->fmt.precision,
+		    f->fmt.channels);
+	}
 #endif
 
 	TAILQ_INSERT_TAIL(&audiodevlist, adev, next);
@@ -207,13 +212,13 @@ audiodev_set_default(struct audiodev *ad
 	char audioctlpath[PATH_MAX+1];
 	char mixerpath[PATH_MAX+1];
 
-	snprintf(audiopath, sizeof(audiopath) - 1,
+	snprintf(audiopath, sizeof(audiopath),
 	    _PATH_AUDIO "%u", adev->unit);
-	snprintf(soundpath, sizeof(soundpath) - 1,
+	snprintf(soundpath, sizeof(soundpath),
 	    _PATH_SOUND "%u", adev->unit);
-	snprintf(audioctlpath, sizeof(audioctlpath) - 1,
+	snprintf(audioctlpath, sizeof(audioctlpath),
 	    _PATH_AUDIOCTL "%u", adev->unit);
-	snprintf(mixerpath, sizeof(mixerpath) - 1,
+	snprintf(mixerpath, sizeof(mixerpath),
 	    _PATH_MIXER "%u", adev->unit);
 
 	unlink(_PATH_AUDIO);
@@ -242,33 +247,92 @@ audiodev_set_default(struct audiodev *ad
 }
 
 int
+audiodev_set_param(struct audiodev *adev, int mode,
+	const char *encname, unsigned int prec, unsigned int ch, unsigned int freq)
+{
+	struct audio_info ai;
+	int setmode;
+	u_int enc;
+
+	setmode = 0;
+	ai = adev->info;
+
+	for (enc = 0; enc < encoding_max; enc++) {
+		if (strcmp(encname, encoding_names[enc]) == 0)
+			break;
+	}
+	if (enc >= encoding_max) {
+		fprintf(stderr, "unknown encoding name: %s\n", encname);
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((ai.mode & mode & AUMODE_PLAY)) {
+		setmode |= AUMODE_PLAY;
+		ai.play.encoding = enc;
+		ai.play.precision = prec;
+		ai.play.channels = ch;
+		ai.play.sample_rate = freq;
+	}
+	if ((ai.mode & mode & AUMODE_RECORD)) {
+		setmode |= AUMODE_RECORD;
+		ai.record.encoding = enc;
+		ai.record.precision = prec;
+		ai.record.channels = ch;
+		ai.record.sample_rate = freq;
+	}
+
+	if (setmode == 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	ai.mode = setmode;
+	printf("setting %s to %s:%u, %uch, %uHz\n",
+	    adev->xname, encname, prec, ch, freq);
+	if (ioctl(adev->fd, AUDIO_SETFORMAT, &ai) == -1) {
+		perror("ioctl AUDIO_SETFORMAT");
+		return -1;
+	}
+	return 0;
+}
+
+int
 audiodev_test(struct audiodev *adev, unsigned int chanmask)
 {
 	audio_info_t info;
 	int16_t *buf;
 	size_t buflen;
 	off_t off;
-	int rv = 0;
+	int fd;
+	int rv = -1;
+
+	fd = open(adev->path, O_WRONLY);
+	if (fd == -1) {
+		perror("open");
+		return -1;
+	}
 
 	AUDIO_INITINFO(&info);
 	info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
-	info.play.channels = adev->pchan;
+	info.play.channels = adev->info.play.channels;
 	info.play.precision = 16;
 	info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
 	info.mode = AUMODE_PLAY;
-	if (ioctl(adev->fd, AUDIO_SETINFO, &info) == -1) {
+	if (ioctl(fd, AUDIO_SETINFO, &info) == -1) {
 		perror("ioctl AUDIO_SETINFO");
-		return -1;
+		goto abort;
 	}
-	if (ioctl(adev->fd, AUDIO_GETINFO, &info) == -1) {
+	if (ioctl(fd, AUDIO_GETINFO, &info) == -1) {
 		perror("ioctl AUDIO_GETINFO");
-		return -1;
+		goto abort;
 	}
 
 	dtmf_new(&buf, &buflen, info.play.sample_rate, 2,
-	    adev->pchan, chanmask, 350.0, 440.0);
-	if (buf == NULL)
-		return -1;
+	    adev->info.play.channels, chanmask, 350.0, 440.0);
+	if (buf == NULL) {
+		goto abort;
+	}
 
 	off = 0;
 	while (buflen > 0) {
@@ -278,10 +342,9 @@ audiodev_test(struct audiodev *adev, uns
 		wlen = info.play.buffer_size;
 		if (wlen > buflen)
 			wlen = buflen;
-		ret = write(adev->fd, (char *)buf + off, wlen);
+		ret = write(fd, (char *)buf + off, wlen);
 		if (ret == -1) {
 			perror("write");
-			rv = -1;
 			goto done;
 		}
 		wlen = ret;
@@ -289,11 +352,16 @@ audiodev_test(struct audiodev *adev, uns
 		buflen -= wlen;
 	}
 
-	if (ioctl(adev->fd, AUDIO_DRAIN) == -1)
+	if (ioctl(fd, AUDIO_DRAIN) == -1) {
 		perror("ioctl AUDIO_DRAIN");
+		goto done;
+	}
 
+	rv = 0;
 done:
 	free(buf);
+abort:
+	close(fd);
 
 	return rv;
 }

Index: src/usr.bin/audiocfg/audiodev.h
diff -u src/usr.bin/audiocfg/audiodev.h:1.4 src/usr.bin/audiocfg/audiodev.h:1.5
--- src/usr.bin/audiocfg/audiodev.h:1.4	Sun Aug 11 06:31:00 2013
+++ src/usr.bin/audiocfg/audiodev.h	Wed May  8 14:36:12 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: audiodev.h,v 1.4 2013/08/11 06:31:00 dholland Exp $ */
+/* $NetBSD: audiodev.h,v 1.5 2019/05/08 14:36:12 isaki Exp $ */
 
 /*
  * Copyright (c) 2010 Jared D. McNeill <[email protected]>
@@ -35,19 +35,25 @@
 
 #include <stdbool.h>
 
+struct audiofmt {
+	struct audio_format fmt;
+	TAILQ_ENTRY(audiofmt) next;
+};
+
 struct audiodev {
 	char pxname[16];	/* hw (parent) device */
 	char xname[16];		/* audio(4) device */
 	uint16_t unit;
 	char path[PATH_MAX+1];
+	char ctlpath[PATH_MAX+1];
 
 	int fd;
 	dev_t dev;
 	bool defaultdev;
 
-	unsigned pchan;
-
 	audio_device_t audio_device;
+	TAILQ_HEAD(, audiofmt) formats;
+	struct audio_info info;
 
 	TAILQ_ENTRY(audiodev) next;
 };
@@ -56,6 +62,12 @@ int			audiodev_refresh(void);
 unsigned int		audiodev_count(void);
 struct audiodev *	audiodev_get(unsigned int);
 int			audiodev_set_default(struct audiodev *);
+int			audiodev_set_param(struct audiodev *, int,
+				const char *, unsigned int, unsigned int,
+				unsigned int);
 int			audiodev_test(struct audiodev *, unsigned int);
 
+extern const char *	encoding_names[];
+extern u_int		encoding_max;
+
 #endif /* !_HAVE_AUDIODEV_H */

Index: src/usr.bin/audiocfg/main.c
diff -u src/usr.bin/audiocfg/main.c:1.7 src/usr.bin/audiocfg/main.c:1.8
--- src/usr.bin/audiocfg/main.c:1.7	Mon Aug 12 14:03:18 2013
+++ src/usr.bin/audiocfg/main.c	Wed May  8 14:36:12 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: main.c,v 1.7 2013/08/12 14:03:18 joerg Exp $ */
+/* $NetBSD: main.c,v 1.8 2019/05/08 14:36:12 isaki Exp $ */
 
 /*
  * Copyright (c) 2010 Jared D. McNeill <[email protected]>
@@ -40,15 +40,43 @@
 __dead static void
 usage(const char *p)
 {
-	fprintf(stderr, "usage: %s list\n", p);
+	fprintf(stderr, "usage: %s list [<index>]\n", p);
 	fprintf(stderr, "       %s default <index>\n", p);
+	fprintf(stderr, "       %s set  <index> [p|r] <enc> <prec> <ch> <freq>\n",
+	    p);
 	fprintf(stderr, "       %s test <index>\n", p);
 	exit(EXIT_FAILURE);
 }
 
+const char *encoding_names[] = {
+	"none",
+	AudioEmulaw,
+	AudioEalaw,
+	"pcm16",
+	"pcm8",
+	AudioEadpcm,
+	AudioEslinear_le,
+	AudioEslinear_be,
+	AudioEulinear_le,
+	AudioEulinear_be,
+	AudioEslinear,
+	AudioEulinear,
+	AudioEmpeg_l1_stream,
+	AudioEmpeg_l1_packets,
+	AudioEmpeg_l1_system,
+	AudioEmpeg_l2_stream,
+	AudioEmpeg_l2_packets,
+	AudioEmpeg_l2_system,
+	AudioEac3,
+};
+u_int encoding_max = __arraycount(encoding_names);
+
 static void
 print_audiodev(struct audiodev *adev, int i)
 {
+	struct audiofmt *f;
+	int j;
+
 	assert(adev != NULL);
 
 	printf("%u: [%c] %s @ %s: ",
@@ -57,8 +85,54 @@ print_audiodev(struct audiodev *adev, in
 	printf("%s", adev->audio_device.name);
 	if (strlen(adev->audio_device.version) > 0)
 		printf(" %s", adev->audio_device.version);
-	printf(", %u playback channel%s\n",
-	    adev->pchan, adev->pchan == 1 ? "" : "s");
+	printf("\n");
+	printf("       playback: ");
+	if ((adev->info.mode & AUMODE_PLAY))
+		printf("%uch, %uHz\n",
+		    adev->info.play.channels, adev->info.play.sample_rate);
+	else
+		printf("unavailable\n");
+	printf("       record:   ");
+	if ((adev->info.mode & AUMODE_RECORD))
+		printf("%uch, %uHz\n",
+		    adev->info.record.channels, adev->info.record.sample_rate);
+	else
+		printf("unavailable\n");
+
+	TAILQ_FOREACH(f, &adev->formats, next) {
+		printf("       ");
+		if (f->fmt.priority < 0)
+			printf("(  ) ");
+		else if ((f->fmt.mode & (AUMODE_PLAY | AUMODE_RECORD))
+		    == (AUMODE_PLAY | AUMODE_RECORD))
+			printf("(PR) ");
+		else if ((f->fmt.mode & AUMODE_PLAY))
+			printf("(P-) ");
+		else if ((f->fmt.mode & AUMODE_RECORD))
+			printf("(-R) ");
+
+		if (f->fmt.encoding < encoding_max)
+			printf("%s", encoding_names[f->fmt.encoding]);
+		else
+			printf("unknown_encoding_%d", f->fmt.encoding);
+		printf(" %d/%d, %dch, ",
+		    f->fmt.validbits,
+		    f->fmt.precision,
+		    f->fmt.channels);
+		if (f->fmt.frequency_type == 0) {
+			printf("%d-%dHz",
+			    f->fmt.frequency[0],
+			    f->fmt.frequency[1]);
+		} else {
+			for (j = 0; j < (int)f->fmt.frequency_type; j++) {
+				printf("%s%d",
+				    (j == 0) ? "{ " : ", ",
+				    f->fmt.frequency[j]);
+			}
+			printf(" }");
+		}
+		printf("\n");
+	}
 }
 
 int
@@ -66,6 +140,12 @@ main(int argc, char *argv[])
 {
 	struct audiodev *adev;
 	unsigned int n, i;
+	unsigned int j;
+	const char *enc;
+	unsigned int prec;
+	unsigned int ch;
+	unsigned int freq;
+	int mode;
 
 	if (audiodev_refresh() == -1)
 		return EXIT_FAILURE;
@@ -74,10 +154,17 @@ main(int argc, char *argv[])
 		usage(argv[0]);
 		/* NOTREACHED */
 
-	if (strcmp(argv[1], "list") == 0) {
+	if (strcmp(argv[1], "list") == 0 && argc == 2) {
 		n = audiodev_count();
 		for (i = 0; i < n; i++)
 			print_audiodev(audiodev_get(i), i);
+	} else if (strcmp(argv[1], "list") == 0 && argc == 3) {
+		errno = 0;
+		i = strtoul(argv[2], NULL, 10);
+		if (errno)
+			usage(argv[0]);
+			/* NOTREACHED */
+		print_audiodev(audiodev_get(i), i);
 	} else if (strcmp(argv[1], "default") == 0 && argc == 3) {
 		if (*argv[2] < '0' || *argv[2] > '9')
 			usage(argv[0]);
@@ -97,6 +184,51 @@ main(int argc, char *argv[])
 			perror("couldn't set default device");
 			return EXIT_FAILURE;
 		}
+	} else if (strcmp(argv[1], "set") == 0 && argc == 8) {
+		/* XXX bad commandline... */
+		/* audiocfg set <index> [p|r] <enc> <prec> <ch> <freq> */
+		if (*argv[2] < '0' || *argv[2] > '9')
+			usage(argv[0]);
+			/* NOTREACHED */
+		errno = 0;
+		i = strtoul(argv[2], NULL, 10);
+		if (errno)
+			usage(argv[0]);
+			/* NOTREACHED */
+		adev = audiodev_get(i);
+		if (adev == NULL) {
+			fprintf(stderr, "no such device\n");
+			return EXIT_FAILURE;
+		}
+
+		mode = 0;
+		for (j = 0; j < strlen(argv[3]); j++) {
+			if (argv[3][j] == 'p')
+				mode |= AUMODE_PLAY;
+			else if (argv[3][j] == 'r')
+				mode |= AUMODE_RECORD;
+			else
+				usage(argv[0]);
+		}
+		enc = argv[4];
+		prec = strtoul(argv[5], NULL, 10);
+		if (errno)
+			usage(argv[0]);
+		errno = 0;
+		ch = strtoul(argv[6], NULL, 10);
+		if (errno)
+			usage(argv[0]);
+			/* NOTREACHED */
+		errno = 0;
+		freq = strtoul(argv[7], NULL, 10);
+		if (errno)
+			usage(argv[0]);
+			/* NOTREACHED */
+
+		if (audiodev_set_param(adev, mode, enc, prec, ch, freq) == -1) {
+			perror("couldn't set parameter");
+			return EXIT_FAILURE;
+		}
 	} else if (strcmp(argv[1], "test") == 0 && argc == 3) {
 		if (*argv[2] < '0' || *argv[2] > '9')
 			usage(argv[0]);
@@ -112,7 +244,7 @@ main(int argc, char *argv[])
 			return EXIT_FAILURE;
 		}
 		print_audiodev(adev, i);
-		for (i = 0; i < adev->pchan; i++) {
+		for (i = 0; i < adev->info.play.channels; i++) {
 			printf("  testing channel %d...", i);
 			fflush(stdout);
 			if (audiodev_test(adev, 1 << i) == -1)

Reply via email to