ossaudio: If the user's channel count is rejected, use the hardware count

This commit is contained in:
nia 2020-04-15 16:39:06 +00:00
parent 7354445c97
commit 87706eec7d
2 changed files with 109 additions and 15 deletions

View File

@ -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);
@ -1048,6 +1047,54 @@ mixer_ioctl(int fd, unsigned long com, void *argp)
return 0;
}
/*
* 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.

View File

@ -1,4 +1,4 @@
/* $NetBSD: ossaudio.c,v 1.80 2020/04/15 15:25:33 nia Exp $ */
/* $NetBSD: ossaudio.c,v 1.81 2020/04/15 16:39:06 nia Exp $ */
/*-
* Copyright (c) 1997, 2008 The NetBSD Foundation, Inc.
@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ossaudio.c,v 1.80 2020/04/15 15:25:33 nia Exp $");
__KERNEL_RCSID(0, "$NetBSD: ossaudio.c,v 1.81 2020/04/15 16:39:06 nia Exp $");
#include <sys/param.h>
#include <sys/proc.h>
@ -68,6 +68,7 @@ static int opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, in
static int enum_to_ord(struct audiodevinfo *di, int enm);
static int enum_to_mask(struct audiodevinfo *di, int enm);
static void setchannels(file_t *, int, int);
static void setblocksize(file_t *, struct audio_info *);
#ifdef AUDIO_DEBUG
@ -487,15 +488,13 @@ oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t
__func__, error));
goto out;
}
tmpinfo.play.channels =
tmpinfo.record.channels = idat;
DPRINTF(("%s: SNDCTL_DSP_CHANNELS > %d\n", __func__, idat));
error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
if (error) {
DPRINTF(("%s: AUDIO_SETINFO %d\n",
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
__func__, error));
goto out;
}
setchannels(fp, tmpinfo.mode, idat);
/* FALLTHROUGH */
case OSS_SOUND_PCM_READ_CHANNELS:
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
@ -1531,6 +1530,54 @@ oss_ioctl_sequencer(struct lwp *l, const struct oss_sys_ioctl_args *uap, registe
return error;
}
/*
* 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(file_t *fp, int mode, int nchannels)
{
struct audio_info tmpinfo, hwfmt;
int (*ioctlf)(file_t *, u_long, void *);
ioctlf = fp->f_ops->fo_ioctl;
if (ioctlf(fp, AUDIO_GETFORMAT, &hwfmt) < 0) {
hwfmt.record.channels = hwfmt.play.channels = 2;
}
if (mode & AUMODE_PLAY) {
AUDIO_INITINFO(&tmpinfo);
tmpinfo.play.channels = nchannels;
if (ioctlf(fp, AUDIO_SETINFO, &tmpinfo) < 0) {
AUDIO_INITINFO(&tmpinfo);
tmpinfo.play.channels = hwfmt.play.channels;
(void)ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
}
}
if (mode & AUMODE_RECORD) {
AUDIO_INITINFO(&tmpinfo);
tmpinfo.record.channels = nchannels;
if (ioctlf(fp, AUDIO_SETINFO, &tmpinfo) < 0) {
AUDIO_INITINFO(&tmpinfo);
tmpinfo.record.channels = hwfmt.record.channels;
(void)ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
}
}
}
/*
* Check that the blocksize is a power of 2 as OSS wants.
* If not, set it to be.