aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/sb16.c50
-rw-r--r--oss.c423
2 files changed, 275 insertions, 198 deletions
diff --git a/hw/sb16.c b/hw/sb16.c
index 472d4c5669..260718cdc0 100644
--- a/hw/sb16.c
+++ b/hw/sb16.c
@@ -26,7 +26,10 @@
#define MIN(a, b) ((a)>(b)?(b):(a))
#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0])))
-#define log(...) fprintf (stderr, "sb16: " __VA_ARGS__)
+#define log(...) do { \
+ fprintf (stderr, "sb16: " __VA_ARGS__); \
+ fputc ('\n', stderr); \
+} while (0)
/* #define DEBUG_SB16 */
#ifdef DEBUG_SB16
@@ -44,6 +47,8 @@
#define IO_WRITE_PROTO(name) \
void name (void *opaque, uint32_t nport, uint32_t val)
+static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
+
static struct {
int ver_lo;
int ver_hi;
@@ -76,7 +81,7 @@ typedef struct SB16State {
int v2x6;
uint8_t in_data[10];
- uint8_t out_data[10];
+ uint8_t out_data[50];
int left_till_irq;
@@ -223,6 +228,20 @@ static void command (SB16State *dsp, uint8_t cmd)
/* IMS uses those when probing for sound devices */
return;
+ case 0x04:
+ dsp->needed_bytes = 1;
+ break;
+
+ case 0x05:
+ case 0x0e:
+ dsp->needed_bytes = 2;
+ break;
+
+ case 0x0f:
+ dsp->needed_bytes = 1;
+ dsp_out_data (dsp, 0);
+ break;
+
case 0x10:
dsp->needed_bytes = 1;
break;
@@ -274,8 +293,8 @@ static void command (SB16State *dsp, uint8_t cmd)
uint8_t d0;
d0 = 4;
- if (dsp->fmt_signed) d0 |= 16;
- if (dsp->fmt_stereo) d0 |= 32;
+ /* if (dsp->fmt_signed) d0 |= 16; */
+ /* if (dsp->fmt_stereo) d0 |= 32; */
dma_cmd (cmd == 0x90 ? 0xc4 : 0xc0, d0, -1);
cmd = -1;
break;
@@ -324,6 +343,14 @@ static void command (SB16State *dsp, uint8_t cmd)
dsp_out_data(dsp, sb.ver_hi);
return;
+ case 0xe3:
+ {
+ int i;
+ for (i = sizeof (e3) - 1; i >= 0; --i)
+ dsp_out_data (dsp, e3[i]);
+ return;
+ }
+
case 0xf2:
dsp_out_data(dsp, 0xaa);
dsp->mixer_regs[0x82] |= dsp->mixer_regs[0x80];
@@ -360,6 +387,11 @@ static void complete (SB16State *dsp)
}
else {
switch (dsp->cmd) {
+ case 0x05:
+ case 0x04:
+ case 0x0e:
+ case 0x0f:
+ break;
case 0x10:
break;
@@ -425,8 +457,10 @@ static IO_WRITE_PROTO (dsp_write)
iport = nport - sb.port;
+ ldebug ("write %#x %#x\n", nport, iport);
switch (iport) {
case 0x6:
+ control (0);
if (0 == val)
dsp->v2x6 = 0;
else if ((1 == val) && (0 == dsp->v2x6)) {
@@ -477,7 +511,7 @@ static IO_READ_PROTO (dsp_read)
if (dsp->out_data_len) {
retval = dsp->out_data[--dsp->out_data_len];
} else {
- log("empty output buffer\n");
+ log("empty output buffer");
goto error;
}
break;
@@ -487,7 +521,7 @@ static IO_READ_PROTO (dsp_read)
break;
case 0xd: /* timer interrupt clear */
- log("timer interrupt clear\n");
+ log("timer interrupt clear");
goto error;
case 0xe: /* data available status | irq 8 ack */
@@ -669,7 +703,7 @@ static int magic_of_irq (int irq)
case 10:
return 8;
default:
- log ("bad irq %d\n", irq);
+ log ("bad irq %d", irq);
return 2;
}
}
@@ -687,7 +721,7 @@ static int irq_of_magic (int magic)
case 8:
return 10;
default:
- log ("bad irq magic %d\n", magic);
+ log ("bad irq magic %d", magic);
return 2;
}
}
diff --git a/oss.c b/oss.c
index 81de93b5f3..f0a74bde07 100644
--- a/oss.c
+++ b/oss.c
@@ -24,6 +24,7 @@
#include "vl.h"
#ifndef _WIN32
+#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
@@ -32,6 +33,7 @@
#include <stdlib.h>
#include <limits.h>
#include <inttypes.h>
+#include <sys/mman.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
@@ -90,25 +92,38 @@ static inline uint32_t lsbindex (uint32_t u)
ldebug ("ioctl " #args " = %d\n", ret); \
} while (0)
-static int audio_fd = -1;
-static int freq;
-static int conf_nfrags = 4;
-static int conf_fragsize;
-static int nfrags;
-static int fragsize;
-static int bufsize;
-static int nchannels;
-static int fmt;
-static int rpos;
-static int wpos;
-static int atom;
-static int live;
-static int leftover;
-static int bytes_per_second;
-static void *buf;
-static enum {DONT, DSP, TID} estimate = TID;
-
-static void (*copy_fn)(void *, void *, int);
+static struct {
+ int fd;
+ int freq;
+ int bits16;
+ int nchannels;
+ int rpos;
+ int wpos;
+ int live;
+ int oss_fmt;
+ int bytes_per_second;
+ int is_mapped;
+ void *buf;
+ int bufsize;
+ int nfrags;
+ int fragsize;
+ int old_optr;
+ int leftover;
+ uint64_t old_ticks;
+ void (*copy_fn)(void *, void *, int);
+} oss = { .fd = -1 };
+
+static struct {
+ int try_mmap;
+ int nfrags;
+ int fragsize;
+} conf = {
+ .try_mmap = 0,
+ .nfrags = 4,
+ .fragsize = 4096
+};
+
+static enum {DONT, DSP, TID} est = DONT;
static void copy_no_conversion (void *dst, void *src, int size)
{
@@ -141,70 +156,124 @@ static void pab (struct audio_buf_info *abinfo)
rpos, wpos, live);
}
-void AUD_reset (int rfreq, int rnchannels, audfmt_e rfmt)
+static void do_open ()
{
- int fmt_;
- int bits16;
+ int mmmmssss;
+ audio_buf_info abinfo;
+ int fmt, freq, nchannels;
- if (-1 == audio_fd) {
- AUD_open (rfreq, rnchannels, rfmt);
- return;
+ if (oss.buf) {
+ if (-1 == munmap (oss.buf, oss.bufsize)) {
+ ERRFail ("failed to unmap audio buffer %p %d",
+ oss.buf, oss.bufsize);
+ }
+ oss.buf = NULL;
}
- switch (rfmt) {
- case AUD_FMT_U8:
- bits16 = 0;
- fmt_ = AFMT_U8;
- copy_fn = copy_no_conversion;
- atom = 1;
- break;
+ if (-1 != oss.fd)
+ close (oss.fd);
- case AUD_FMT_S8:
- Fail ("can not play 8bit signed");
+ oss.fd = open ("/dev/dsp", O_RDWR | O_NONBLOCK);
+ if (-1 == oss.fd) {
+ ERRFail ("can not open /dev/dsp");
+ }
- case AUD_FMT_S16:
- bits16 = 1;
- fmt_ = AFMT_S16_LE;
- copy_fn = copy_no_conversion;
- atom = 2;
- break;
+ fmt = oss.oss_fmt;
+ freq = oss.freq;
+ nchannels = oss.nchannels;
+
+ IOCTL ((oss.fd, SNDCTL_DSP_RESET, 1));
+ IOCTL ((oss.fd, SNDCTL_DSP_SAMPLESIZE, &fmt));
+ IOCTL ((oss.fd, SNDCTL_DSP_CHANNELS, &nchannels));
+ IOCTL ((oss.fd, SNDCTL_DSP_SPEED, &freq));
+ IOCTL ((oss.fd, SNDCTL_DSP_NONBLOCK));
+
+ mmmmssss = (conf.nfrags << 16) | conf.fragsize;
+ IOCTL ((oss.fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss));
+
+ if ((oss.oss_fmt != fmt)
+ || (oss.nchannels != nchannels)
+ || (oss.freq != freq)) {
+ Fail ("failed to set audio parameters\n"
+ "parameter | requested value | obtained value\n"
+ "format | %10d | %10d\n"
+ "channels | %10d | %10d\n"
+ "frequency | %10d | %10d\n",
+ oss.oss_fmt, fmt,
+ oss.nchannels, nchannels,
+ oss.freq, freq);
+ }
- case AUD_FMT_U16:
- bits16 = 1;
- fmt_ = AFMT_S16_LE;
- copy_fn = copy_u16_to_s16;
- atom = 2;
- break;
+ IOCTL ((oss.fd, SNDCTL_DSP_GETOSPACE, &abinfo));
- default:
- abort ();
- }
+ oss.nfrags = abinfo.fragstotal;
+ oss.fragsize = abinfo.fragsize;
+ oss.bufsize = oss.nfrags * oss.fragsize;
+ oss.old_optr = 0;
- if ((fmt_ == fmt) && (bits16 + 1 == nchannels) && (rfreq == freq))
- return;
+ oss.bytes_per_second = (freq << (nchannels >> 1)) << oss.bits16;
+
+ linfo ("bytes per second %d\n", oss.bytes_per_second);
+
+ linfo ("fragments %d, fragstotal %d, fragsize %d, bytes %d, bufsize %d\n",
+ abinfo.fragments,
+ abinfo.fragstotal,
+ abinfo.fragsize,
+ abinfo.bytes,
+ oss.bufsize);
+
+ oss.buf = MAP_FAILED;
+ oss.is_mapped = 0;
+
+ if (conf.try_mmap) {
+ oss.buf = mmap (NULL, oss.bufsize, PROT_WRITE, MAP_SHARED, oss.fd, 0);
+ if (MAP_FAILED == oss.buf) {
+ int err;
+
+ err = errno;
+ log ("failed to mmap audio, size %d, fd %d\n"
+ "syserr: %s\n",
+ oss.bufsize, oss.fd, strerror (err));
+ }
else {
- AUD_open (rfreq, rnchannels, rfmt);
+ est = TID;
+ oss.is_mapped = 1;
+ }
+ }
+
+ if (MAP_FAILED == oss.buf) {
+ est = TID;
+ oss.buf = mmap (NULL, oss.bufsize, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (MAP_FAILED == oss.buf) {
+ ERRFail ("mmap audio buf, size %d", oss.bufsize);
+ }
+ }
+
+ oss.rpos = 0;
+ oss.wpos = 0;
+ oss.live = 0;
+
+ if (oss.is_mapped) {
+ int trig;
+
+ trig = 0;
+ IOCTL ((oss.fd, SNDCTL_DSP_SETTRIGGER, &trig));
+ trig = PCM_ENABLE_OUTPUT;
+ IOCTL ((oss.fd, SNDCTL_DSP_SETTRIGGER, &trig));
}
}
-void AUD_open (int rfreq, int rnchannels, audfmt_e rfmt)
+static void maybe_open (int req_freq, int req_nchannels,
+ audfmt_e req_fmt, int force_open)
{
- int fmt_;
- int mmmmssss;
- struct audio_buf_info abinfo;
- int _fmt;
- int _freq;
- int _nchannels;
- int bits16;
+ int oss_fmt, bits16;
- bits16 = 0;
-
- switch (rfmt) {
+ switch (req_fmt) {
case AUD_FMT_U8:
bits16 = 0;
- fmt_ = AFMT_U8;
- copy_fn = copy_no_conversion;
- atom = 1;
+ oss_fmt = AFMT_U8;
+ oss.copy_fn = copy_no_conversion;
break;
case AUD_FMT_S8:
@@ -212,111 +281,42 @@ void AUD_open (int rfreq, int rnchannels, audfmt_e rfmt)
case AUD_FMT_S16:
bits16 = 1;
- fmt_ = AFMT_S16_LE;
- copy_fn = copy_no_conversion;
- atom = 2;
+ oss_fmt = AFMT_S16_LE;
+ oss.copy_fn = copy_no_conversion;
break;
case AUD_FMT_U16:
bits16 = 1;
- fmt_ = AFMT_S16_LE;
- copy_fn = copy_u16_to_s16;
- atom = 2;
+ oss_fmt = AFMT_S16_LE;
+ oss.copy_fn = copy_u16_to_s16;
break;
default:
abort ();
}
- if (buf) {
- free (buf);
- buf = 0;
- }
-
- if (-1 != audio_fd)
- close (audio_fd);
-
- audio_fd = open ("/dev/dsp", O_WRONLY | O_NONBLOCK);
- if (-1 == audio_fd) {
- ERRFail ("can not open /dev/dsp");
- }
-
- _fmt = fmt_;
- _freq = rfreq;
- _nchannels = rnchannels;
-
- IOCTL ((audio_fd, SNDCTL_DSP_RESET, 1));
- IOCTL ((audio_fd, SNDCTL_DSP_SAMPLESIZE, &_fmt));
- IOCTL ((audio_fd, SNDCTL_DSP_CHANNELS, &_nchannels));
- IOCTL ((audio_fd, SNDCTL_DSP_SPEED, &_freq));
- IOCTL ((audio_fd, SNDCTL_DSP_NONBLOCK));
-
- /* from oss.pdf:
-
- The argument to this call is an integer encoded as 0xMMMMSSSS (in
- hex). The 16 least significant bits determine the fragment
- size. The size is 2^SSSS. For examp le SSSS=0008 gives fragment
- size of 256 bytes (2^8). The minimum is 16 bytes (SSSS=4) and the
- maximum is total_buffer_size/2. Some devices or processor
- architectures may require larger fragments - in this case the
- requested fragment size is automatically increased.
-
- So ahem... 4096 = 2^12, and grand total 0x0004000c
- */
-
- mmmmssss = (conf_nfrags << 16) | conf_fragsize;
- IOCTL ((audio_fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss));
-
- linfo ("_fmt = %d, fmt = %d\n"
- "_channels = %d, rnchannels = %d\n"
- "_freq = %d, freq = %d\n",
- _fmt, fmt_,
- _nchannels, rnchannels,
- _freq, rfreq);
-
- if (_fmt != fmt_) {
- Fail ("format %d != %d", _fmt, fmt_);
- }
-
- if (_nchannels != rnchannels) {
- Fail ("channels %d != %d", _nchannels, rnchannels);
+ if (force_open
+ || (-1 == oss.fd)
+ || (oss_fmt != oss.oss_fmt)
+ || (req_nchannels != oss.nchannels)
+ || (req_freq != oss.freq)
+ || (bits16 != oss.bits16)) {
+ oss.oss_fmt = oss_fmt;
+ oss.nchannels = req_nchannels;
+ oss.freq = req_freq;
+ oss.bits16 = bits16;
+ do_open ();
}
+}
- if (_freq != rfreq) {
- Fail ("freq %d != %d", _freq, rfreq);
- }
-
- IOCTL ((audio_fd, SNDCTL_DSP_GETOSPACE, &abinfo));
-
- nfrags = abinfo.fragstotal;
- fragsize = abinfo.fragsize;
- freq = _freq;
- fmt = _fmt;
- nchannels = rnchannels;
- atom <<= nchannels >> 1;
- bufsize = nfrags * fragsize;
-
- bytes_per_second = (freq << (nchannels >> 1)) << bits16;
-
- linfo ("bytes per second %d\n", bytes_per_second);
-
- linfo ("fragments %d, fragstotal %d, fragsize %d, bytes %d, bufsize %d\n",
- abinfo.fragments,
- abinfo.fragstotal,
- abinfo.fragsize,
- abinfo.bytes,
- bufsize);
-
- if (NULL == buf) {
- buf = malloc (bufsize);
- if (NULL == buf) {
- abort ();
- }
- }
+void AUD_reset (int req_freq, int req_nchannels, audfmt_e req_fmt)
+{
+ maybe_open (req_freq, req_nchannels, req_fmt, 0);
+}
- rpos = 0;
- wpos = 0;
- live = 0;
+void AUD_open (int req_freq, int req_nchannels, audfmt_e req_fmt)
+{
+ maybe_open (req_freq, req_nchannels, req_fmt, 1);
}
int AUD_write (void *in_buf, int size)
@@ -324,27 +324,27 @@ int AUD_write (void *in_buf, int size)
int to_copy, temp;
uint8_t *in, *out;
- to_copy = MIN (bufsize - live, size);
+ to_copy = MIN (oss.bufsize - oss.live, size);
temp = to_copy;
in = in_buf;
- out = buf;
+ out = oss.buf;
while (temp) {
int copy;
- copy = MIN (temp, bufsize - wpos);
- copy_fn (out + wpos, in, copy);
+ copy = MIN (temp, oss.bufsize - oss.wpos);
+ oss.copy_fn (out + oss.wpos, in, copy);
- wpos += copy;
- if (wpos == bufsize) {
- wpos = 0;
+ oss.wpos += copy;
+ if (oss.wpos == oss.bufsize) {
+ oss.wpos = 0;
}
temp -= copy;
in += copy;
- live += copy;
+ oss.live += copy;
}
return to_copy;
@@ -356,10 +356,34 @@ void AUD_run (void)
int bytes;
struct audio_buf_info abinfo;
- if (0 == live)
+ if (0 == oss.live)
return;
- res = ioctl (audio_fd, SNDCTL_DSP_GETOSPACE, &abinfo);
+ if (oss.is_mapped) {
+ count_info info;
+
+ res = ioctl (oss.fd, SNDCTL_DSP_GETOPTR, &info);
+ if (-1 == res) {
+ int err;
+
+ err = errno;
+ lwarn ("SNDCTL_DSP_GETOPTR failed with %s\n", strerror (err));
+ return;
+ }
+
+ if (info.ptr > oss.old_optr) {
+ bytes = info.ptr - oss.old_optr;
+ }
+ else {
+ bytes = oss.bufsize + info.ptr - oss.old_optr;
+ }
+
+ oss.old_optr = info.ptr;
+ oss.live -= bytes;
+ return;
+ }
+
+ res = ioctl (oss.fd, SNDCTL_DSP_GETOSPACE, &abinfo);
if (-1 == res) {
int err;
@@ -369,7 +393,7 @@ void AUD_run (void)
}
bytes = abinfo.bytes;
- bytes = MIN (live, bytes);
+ bytes = MIN (oss.live, bytes);
#if 0
bytes = (bytes / fragsize) * fragsize;
#endif
@@ -377,9 +401,9 @@ void AUD_run (void)
while (bytes) {
int left, play, written;
- left = bufsize - rpos;
+ left = oss.bufsize - oss.rpos;
play = MIN (left, bytes);
- written = write (audio_fd, (void *) ((uint32_t) buf + rpos), play);
+ written = write (oss.fd, (void *) ((uint32_t) oss.buf + oss.rpos), play);
if (-1 == written) {
if (EAGAIN == errno || EINTR == errno) {
@@ -391,12 +415,12 @@ void AUD_run (void)
}
play = written;
- live -= play;
- rpos += play;
+ oss.live -= play;
+ oss.rpos += play;
bytes -= play;
- if (rpos == bufsize) {
- rpos = 0;
+ if (oss.rpos == oss.bufsize) {
+ oss.rpos = 0;
}
}
}
@@ -406,7 +430,7 @@ static int get_dsp_bytes (void)
int res;
struct count_info info;
- res = ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &info);
+ res = ioctl (oss.fd, SNDCTL_DSP_GETOPTR, &info);
if (-1 == res) {
int err;
@@ -420,22 +444,22 @@ static int get_dsp_bytes (void)
}
}
-void AUD_adjust_estimate (int _leftover)
+void AUD_adjust_estimate (int leftover)
{
- leftover = _leftover;
+ oss.leftover = leftover;
}
int AUD_get_free (void)
{
int free, elapsed;
- free = bufsize - live;
+ free = oss.bufsize - oss.live;
if (0 == free)
return 0;
elapsed = free;
- switch (estimate) {
+ switch (est) {
case DONT:
break;
@@ -456,16 +480,15 @@ int AUD_get_free (void)
case TID:
{
- static uint64_t old_ticks;
uint64_t ticks, delta;
uint64_t ua_elapsed;
uint64_t al_elapsed;
ticks = qemu_get_clock(rt_clock);
- delta = ticks - old_ticks;
- old_ticks = ticks;
+ delta = ticks - oss.old_ticks;
+ oss.old_ticks = ticks;
- ua_elapsed = (delta * bytes_per_second) / 1000;
+ ua_elapsed = (delta * oss.bytes_per_second) / 1000;
al_elapsed = ua_elapsed & ~3ULL;
ldebug ("tid elapsed %llu bytes\n", ua_elapsed);
@@ -475,7 +498,7 @@ int AUD_get_free (void)
else
elapsed = al_elapsed;
- elapsed += leftover;
+ elapsed += oss.leftover;
}
}
@@ -490,27 +513,47 @@ int AUD_get_free (void)
int AUD_get_live (void)
{
- return live;
+ return oss.live;
}
int AUD_get_buffer_size (void)
{
- return bufsize;
+ return oss.bufsize;
+}
+
+#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE"
+#define QC_OSS_NFRAGS "QEMU_OSS_NFRAGS"
+#define QC_OSS_MMAP "QEMU_OSS_MMAP"
+
+static int get_conf_val (const char *key, int defval)
+{
+ int val = defval;
+ char *strval;
+
+ strval = getenv (key);
+ if (strval) {
+ val = atoi (strval);
+ }
+
+ return val;
}
void AUD_init (void)
{
int fsp;
- int _fragsize = 4096;
DEREF (pab);
- fsp = _fragsize;
+ conf.fragsize = get_conf_val (QC_OSS_FRAGSIZE, conf.fragsize);
+ conf.nfrags = get_conf_val (QC_OSS_NFRAGS, conf.nfrags);
+ conf.try_mmap = get_conf_val (QC_OSS_MMAP, conf.try_mmap);
+
+ fsp = conf.fragsize;
if (0 != (fsp & (fsp - 1))) {
Fail ("fragment size %d is not power of 2", fsp);
}
- conf_fragsize = lsbindex (fsp);
+ conf.fragsize = lsbindex (fsp);
}
#else