#include <sys/mman.h>
#include <sys/types.h>
#include <sys/ioctl.h>
-#ifdef __OpenBSD__
-#include <soundcard.h>
-#else
#include <sys/soundcard.h>
-#endif
#include "qemu-common.h"
-#include "host-utils.h"
-#include "qemu-char.h"
+#include "qemu/main-loop.h"
+#include "qemu/host-utils.h"
#include "audio.h"
+#include "trace.h"
#define AUDIO_CAP "oss"
#include "audio_int.h"
+#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY
+#define USE_DSP_POLICY
+#endif
+
+typedef struct OSSConf {
+ int try_mmap;
+ int nfrags;
+ int fragsize;
+ const char *devpath_out;
+ const char *devpath_in;
+ int exclusive;
+ int policy;
+} OSSConf;
+
typedef struct OSSVoiceOut {
HWVoiceOut hw;
void *pcm_buf;
int fragsize;
int mmapped;
int pending;
+ OSSConf *conf;
} OSSVoiceOut;
typedef struct OSSVoiceIn {
int fd;
int nfrags;
int fragsize;
+ OSSConf *conf;
} OSSVoiceIn;
-static struct {
- int try_mmap;
- int nfrags;
- int fragsize;
- const char *devpath_out;
- const char *devpath_in;
- int debug;
- int exclusive;
- int policy;
-} conf = {
- .try_mmap = 0,
- .nfrags = 4,
- .fragsize = 4096,
- .devpath_out = "/dev/dsp",
- .devpath_in = "/dev/dsp",
- .debug = 0,
- .exclusive = 0,
- .policy = 5
-};
-
struct oss_params {
int freq;
audfmt_e fmt;
audio_run ("oss_poll_in");
}
-static int oss_poll_out (HWVoiceOut *hw)
+static void oss_poll_out (HWVoiceOut *hw)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
- return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
+ qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
}
-static int oss_poll_in (HWVoiceIn *hw)
+static void oss_poll_in (HWVoiceIn *hw)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
- return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
+ qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
}
static int oss_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
-static int aud_to_ossfmt (audfmt_e fmt)
+static int aud_to_ossfmt (audfmt_e fmt, int endianness)
{
switch (fmt) {
case AUD_FMT_S8:
return AFMT_U8;
case AUD_FMT_S16:
- return AFMT_S16_LE;
+ if (endianness) {
+ return AFMT_S16_BE;
+ }
+ else {
+ return AFMT_S16_LE;
+ }
case AUD_FMT_U16:
- return AFMT_U16_LE;
+ if (endianness) {
+ return AFMT_U16_BE;
+ }
+ else {
+ return AFMT_U16_LE;
+ }
default:
dolog ("Internal logic error: Bad audio format %d\n", fmt);
}
#endif
+#ifdef USE_DSP_POLICY
+static int oss_get_version (int fd, int *version, const char *typ)
+{
+ if (ioctl (fd, OSS_GETVERSION, &version)) {
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ /*
+ * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION
+ * since 7.x, but currently only on the mixer device (or in
+ * the Linuxolator), and in the native version that part of
+ * the code is in fact never reached so the ioctl fails anyway.
+ * Until this is fixed, just check the errno and if its what
+ * FreeBSD's sound drivers return atm assume they are new enough.
+ */
+ if (errno == EINVAL) {
+ *version = 0x040000;
+ return 0;
+ }
+#endif
+ oss_logerr2 (errno, typ, "Failed to get OSS version\n");
+ return -1;
+ }
+ return 0;
+}
+#endif
+
static int oss_open (int in, struct oss_params *req,
- struct oss_params *obt, int *pfd)
+ struct oss_params *obt, int *pfd, OSSConf* conf)
{
int fd;
- int version;
- int oflags = conf.exclusive ? O_EXCL : 0;
+ int oflags = conf->exclusive ? O_EXCL : 0;
audio_buf_info abinfo;
int fmt, freq, nchannels;
- const char *dspname = in ? conf.devpath_in : conf.devpath_out;
+ int setfragment = 1;
+ const char *dspname = in ? conf->devpath_in : conf->devpath_out;
const char *typ = in ? "ADC" : "DAC";
/* Kludge needed to have working mmap on Linux */
- oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
+ oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
fd = open (dspname, oflags | O_NONBLOCK);
if (-1 == fd) {
goto err;
}
- if (ioctl (fd, OSS_GETVERSION, &version)) {
- oss_logerr2 (errno, typ, "Failed to get OSS version\n");
- version = 0;
- }
+#ifdef USE_DSP_POLICY
+ if (conf->policy >= 0) {
+ int version;
- if (conf.debug) {
- dolog ("OSS version = %#x\n", version);
- }
+ if (!oss_get_version (fd, &version, typ)) {
+ trace_oss_version(version);
-#ifdef SNDCTL_DSP_POLICY
- if (conf.policy >= 0 && version >= 0x040000) {
- int policy = conf.policy;
- if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
- oss_logerr2 (errno, typ, "Failed to set timing policy to %d\n",
- conf.policy);
- goto err;
+ if (version >= 0x040000) {
+ int policy = conf->policy;
+ if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
+ oss_logerr2 (errno, typ,
+ "Failed to set timing policy to %d\n",
+ conf->policy);
+ goto err;
+ }
+ setfragment = 0;
+ }
}
}
- else
#endif
- {
+
+ if (setfragment) {
int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize);
if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
}
if (abinfo.bytes > bufsize) {
- if (conf.debug) {
- dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
- abinfo.bytes, bufsize);
- }
+ trace_oss_invalid_available_size(abinfo.bytes, bufsize);
abinfo.bytes = bufsize;
}
if (abinfo.bytes < 0) {
- if (conf.debug) {
- dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
- abinfo.bytes, bufsize);
- }
+ trace_oss_invalid_available_size(abinfo.bytes, bufsize);
return 0;
}
}
}
else {
- qemu_free (oss->pcm_buf);
+ g_free (oss->pcm_buf);
}
oss->pcm_buf = NULL;
}
}
-static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
struct oss_params req, obt;
int fd;
audfmt_e effective_fmt;
struct audsettings obt_as;
+ OSSConf *conf = drv_opaque;
oss->fd = -1;
- req.fmt = aud_to_ossfmt (as->fmt);
+ req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
- req.fragsize = conf.fragsize;
- req.nfrags = conf.nfrags;
+ req.fragsize = conf->fragsize;
+ req.nfrags = conf->nfrags;
- if (oss_open (0, &req, &obt, &fd)) {
+ if (oss_open (0, &req, &obt, &fd, conf)) {
return -1;
}
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->mmapped = 0;
- if (conf.try_mmap) {
+ if (conf->try_mmap) {
oss->pcm_buf = mmap (
NULL,
hw->samples << hw->info.shift,
}
oss->fd = fd;
+ oss->conf = conf;
return 0;
}
va_end (ap);
ldebug ("enabling voice\n");
- if (poll_mode && oss_poll_out (hw)) {
+ if (poll_mode) {
+ oss_poll_out (hw);
poll_mode = 0;
}
hw->poll_mode = poll_mode;
return 0;
}
-static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
struct oss_params req, obt;
int fd;
audfmt_e effective_fmt;
struct audsettings obt_as;
+ OSSConf *conf = drv_opaque;
oss->fd = -1;
- req.fmt = aud_to_ossfmt (as->fmt);
+ req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
- req.fragsize = conf.fragsize;
- req.nfrags = conf.nfrags;
- if (oss_open (1, &req, &obt, &fd)) {
+ req.fragsize = conf->fragsize;
+ req.nfrags = conf->nfrags;
+ if (oss_open (1, &req, &obt, &fd, conf)) {
return -1;
}
}
oss->fd = fd;
+ oss->conf = conf;
return 0;
}
oss_anal_close (&oss->fd);
- if (oss->pcm_buf) {
- qemu_free (oss->pcm_buf);
- oss->pcm_buf = NULL;
- }
+ g_free(oss->pcm_buf);
+ oss->pcm_buf = NULL;
}
static int oss_run_in (HWVoiceIn *hw)
hw->info.align + 1);
}
read_samples += nread >> hwshift;
- hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
- &nominal_volume);
+ hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
}
if (bufs[i].len - nread) {
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
- va_list ap;
- int poll_mode;
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
- va_start (ap, cmd);
- poll_mode = va_arg (ap, int);
- va_end (ap);
-
switch (cmd) {
case VOICE_ENABLE:
- if (poll_mode && oss_poll_in (hw)) {
- poll_mode = 0;
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ if (poll_mode) {
+ oss_poll_in (hw);
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
}
- hw->poll_mode = poll_mode;
break;
case VOICE_DISABLE:
return 0;
}
+static OSSConf glob_conf = {
+ .try_mmap = 0,
+ .nfrags = 4,
+ .fragsize = 4096,
+ .devpath_out = "/dev/dsp",
+ .devpath_in = "/dev/dsp",
+ .exclusive = 0,
+ .policy = 5
+};
+
static void *oss_audio_init (void)
{
- return &conf;
+ OSSConf *conf = g_malloc(sizeof(OSSConf));
+ *conf = glob_conf;
+
+ if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
+ access(conf->devpath_out, R_OK | W_OK) < 0) {
+ g_free(conf);
+ return NULL;
+ }
+ return conf;
}
static void oss_audio_fini (void *opaque)
{
- (void) opaque;
+ g_free(opaque);
}
static struct audio_option oss_options[] = {
{
.name = "FRAGSIZE",
.tag = AUD_OPT_INT,
- .valp = &conf.fragsize,
+ .valp = &glob_conf.fragsize,
.descr = "Fragment size in bytes"
},
{
.name = "NFRAGS",
.tag = AUD_OPT_INT,
- .valp = &conf.nfrags,
+ .valp = &glob_conf.nfrags,
.descr = "Number of fragments"
},
{
.name = "MMAP",
.tag = AUD_OPT_BOOL,
- .valp = &conf.try_mmap,
+ .valp = &glob_conf.try_mmap,
.descr = "Try using memory mapped access"
},
{
.name = "DAC_DEV",
.tag = AUD_OPT_STR,
- .valp = &conf.devpath_out,
+ .valp = &glob_conf.devpath_out,
.descr = "Path to DAC device"
},
{
.name = "ADC_DEV",
.tag = AUD_OPT_STR,
- .valp = &conf.devpath_in,
+ .valp = &glob_conf.devpath_in,
.descr = "Path to ADC device"
},
{
.name = "EXCLUSIVE",
.tag = AUD_OPT_BOOL,
- .valp = &conf.exclusive,
+ .valp = &glob_conf.exclusive,
.descr = "Open device in exclusive mode (vmix wont work)"
},
-#ifdef SNDCTL_DSP_POLICY
+#ifdef USE_DSP_POLICY
{
.name = "POLICY",
.tag = AUD_OPT_INT,
- .valp = &conf.policy,
+ .valp = &glob_conf.policy,
.descr = "Set the timing policy of the device, -1 to use fragment mode",
},
#endif
- {
- .name = "DEBUG",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.debug,
- .descr = "Turn on some debugging messages"
- },
{ /* End of list */ }
};