summaryrefslogtreecommitdiff
path: root/lib/libossaudio
diff options
context:
space:
mode:
authornia <nia@NetBSD.org>2020-04-15 16:39:06 +0000
committernia <nia@NetBSD.org>2020-04-15 16:39:06 +0000
commita0ace43f265dcd7bf3c3a896ee856beda1d9b06d (patch)
treee229416dbdd42136f289201fce70008f31ea6319 /lib/libossaudio
parent145badfcce723c3f8f10d8e3bc5cfb4bc2e9159f (diff)
ossaudio: If the user's channel count is rejected, use the hardware count
Diffstat (limited to 'lib/libossaudio')
-rw-r--r--lib/libossaudio/ossaudio.c63
1 files changed, 55 insertions, 8 deletions
diff --git a/lib/libossaudio/ossaudio.c b/lib/libossaudio/ossaudio.c
index d14e9af038b..9bc4497eb7e 100644
--- a/lib/libossaudio/ossaudio.c
+++ b/lib/libossaudio/ossaudio.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ossaudio.c,v 1.40 2020/04/15 15:25:33 nia Exp $ */
+/* $NetBSD: ossaudio.c,v 1.41 2020/04/15 16:39:06 nia Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: ossaudio.c,v 1.40 2020/04/15 15:25:33 nia Exp $");
+__RCSID("$NetBSD: ossaudio.c,v 1.41 2020/04/15 16:39:06 nia Exp $");
/*
* This is an OSS (Linux) sound API emulator.
@@ -63,6 +63,7 @@ __RCSID("$NetBSD: ossaudio.c,v 1.40 2020/04/15 15:25:33 nia Exp $");
static struct audiodevinfo *getdevinfo(int);
+static void setchannels(int, int, int);
static void setblocksize(int, struct audio_info *);
static int audio_ioctl(int, unsigned long, void *);
@@ -350,12 +351,10 @@ audio_ioctl(int fd, unsigned long com, void *argp)
INTARG = idat;
break;
case SNDCTL_DSP_CHANNELS:
- AUDIO_INITINFO(&tmpinfo);
- tmpinfo.play.channels = INTARG;
- (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
- AUDIO_INITINFO(&tmpinfo);
- tmpinfo.record.channels = INTARG;
- (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+ retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
+ if (retval < 0)
+ return retval;
+ setchannels(fd, tmpinfo.mode, INTARG);
/* FALLTHRU */
case SOUND_PCM_READ_CHANNELS:
retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
@@ -1049,6 +1048,54 @@ mixer_ioctl(int fd, unsigned long com, void *argp)
}
/*
+ * When AUDIO_SETINFO fails to set a channel count, the application's chosen
+ * number is out of range of what the kernel allows.
+ *
+ * When this happens, we use the current hardware settings. This is just in
+ * case an application is abusing SNDCTL_DSP_CHANNELS - OSSv4 always sets and
+ * returns a reasonable value, even if it wasn't what the user requested.
+ *
+ * XXX: If a device is opened for both playback and recording, and supports
+ * fewer channels for recording than playback, applications that do both will
+ * behave very strangely. OSS doesn't allow for reporting separate channel
+ * counts for recording and playback. This could be worked around by always
+ * mixing recorded data up to the same number of channels as is being used
+ * for playback.
+ */
+static void
+setchannels(int fd, int mode, int nchannels)
+{
+ struct audio_info tmpinfo, hwfmt;
+
+ if (ioctl(fd, AUDIO_GETFORMAT, &hwfmt) < 0) {
+ errno = 0;
+ hwfmt.record.channels = hwfmt.play.channels = 2;
+ }
+
+ if (mode & AUMODE_PLAY) {
+ AUDIO_INITINFO(&tmpinfo);
+ tmpinfo.play.channels = nchannels;
+ if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) {
+ errno = 0;
+ AUDIO_INITINFO(&tmpinfo);
+ tmpinfo.play.channels = hwfmt.play.channels;
+ (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+ }
+ }
+
+ if (mode & AUMODE_RECORD) {
+ AUDIO_INITINFO(&tmpinfo);
+ tmpinfo.record.channels = nchannels;
+ if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) {
+ errno = 0;
+ AUDIO_INITINFO(&tmpinfo);
+ tmpinfo.record.channels = hwfmt.record.channels;
+ (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+ }
+ }
+}
+
+/*
* Check that the blocksize is a power of 2 as OSS wants.
* If not, set it to be.
*/