Index: audio/ossaudio.c =================================================================== RCS file: /cvsroot/qemu/qemu/audio/ossaudio.c,v retrieving revision 1.9 diff -a -u -r1.9 ossaudio.c --- audio/ossaudio.c 20 Nov 2005 16:24:34 -0000 1.9 +++ audio/ossaudio.c 30 Jan 2006 15:31:31 -0000 @@ -30,23 +30,58 @@ #define AUDIO_CAP "oss" #include "audio_int.h" +/* milliseconds of idle input or output prior to device being released... */ +#define OSS_RELEASE_MSEC 1500 + +/* statics we use before they are defined */ +static int oss_init_in(HWVoiceIn *hw, audsettings_t *as); +static void oss_fini_in(HWVoiceIn *hw); +static int oss_init_out(HWVoiceOut *hw, audsettings_t *as); +static void oss_fini_out(HWVoiceOut *hw); +static int oss_ctl_out(HWVoiceOut *hw, int cmd, ...); + +/* import and MACROs for calling noaudio PCM ops; this avoids having to + * globally export entrypoints into noaudio driver */ +extern struct audio_driver no_audio_driver; +#define no_init_in(hw, as) no_audio_driver.pcm_ops->init_in(hw, as) +#define no_fini_in(hw) no_audio_driver.pcm_ops->fini_in(hw) +#define no_init_out(hw, as) no_audio_driver.pcm_ops->init_out(hw, as) +#define no_fini_out(hw) no_audio_driver.pcm_ops->fini_out(hw) +#define no_run_in(hw) no_audio_driver.pcm_ops->run_in(hw) +#define no_run_out(hw) no_audio_driver.pcm_ops->run_out(hw) + typedef struct OSSVoiceOut { HWVoiceOut hw; + int64_t old_ticks; +/* NOTE: the first portion of this structure is identical to NoVoiceOut in + * noaudio.c, since no_run_out() is used if the audio device cannot open; + * if NoVoiceOut changes, this above portion must change here as well */ void *pcm_buf; int fd; int nfrags; int fragsize; int mmapped; int old_optr; + audsettings_t saved_as; + QEMUTimer *rel_timer; + int released; + int pending_cmd; } OSSVoiceOut; typedef struct OSSVoiceIn { HWVoiceIn hw; + int64_t old_ticks; +/* NOTE: the first portion of this structure is identical to NoVoiceIn in + * noaudio.c, since no_run_in() is used if the audio device cannot open; + * if NoVoiceIn changes, this above portion must change here as well */ void *pcm_buf; int fd; int nfrags; int fragsize; int old_optr; + audsettings_t saved_as; + QEMUTimer *rel_timer; + int released; } OSSVoiceIn; static struct { @@ -194,7 +229,7 @@ #endif static int oss_open (int in, struct oss_params *req, - struct oss_params *obt, int *pfd) + struct oss_params *obt, int *pfd, int *released) { int fd; int mmmmssss; @@ -205,9 +240,11 @@ fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK); if (-1 == fd) { + *released = (int) (errno == EBUSY); oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname); return -1; } + *released = 0; freq = req->freq; nchannels = req->nchannels; @@ -290,6 +327,18 @@ return 0; } + /* if the audio device was released (or was busy when the last open was + * attempted, then we try to open it again; if it failed for other reasons, + * then we'll just use noaudio's run_out */ + if (oss->released) + oss_init_out(hw, &oss->saved_as); + if (oss->fd < 0) + return no_run_out(hw); /* permanent failure, or busy for now */ + else { + qemu_mod_timer(oss->rel_timer, qemu_get_clock(rt_clock) + + OSS_RELEASE_MSEC); + } + bufsize = hw->samples << hw->info.shift; if (oss->mmapped) { @@ -386,6 +435,7 @@ } hw->rpos = rpos; + oss->old_ticks = qemu_get_clock(vm_clock); return decr; } @@ -395,7 +445,12 @@ OSSVoiceOut *oss = (OSSVoiceOut *) hw; ldebug ("oss_fini\n"); - oss_anal_close (&oss->fd); + if (oss->fd < 0) + no_fini_out(hw); + else { + oss_anal_close (&oss->fd); + oss->released = 1; + } if (oss->pcm_buf) { if (oss->mmapped) { @@ -410,6 +465,9 @@ } oss->pcm_buf = NULL; } + + if (oss->rel_timer != NULL) + qemu_del_timer(oss->rel_timer); } static int oss_init_out (HWVoiceOut *hw, audsettings_t *as) @@ -423,6 +481,7 @@ audsettings_t obt_as; oss->fd = -1; + oss->saved_as = *as; req.fmt = aud_to_ossfmt (as->fmt); req.freq = as->freq; @@ -430,14 +489,14 @@ req.fragsize = conf.fragsize; req.nfrags = conf.nfrags; - if (oss_open (0, &req, &obt, &fd)) { - return -1; + if (oss_open (0, &req, &obt, &fd, &oss->released)) { + return no_init_out(hw, as); } err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); if (err) { oss_anal_close (&fd); - return -1; + return no_init_out(hw, as); } obt_as.freq = obt.freq; @@ -514,11 +573,19 @@ 1 << hw->info.shift ); oss_anal_close (&fd); - return -1; + return no_init_out(hw, as); } } oss->fd = fd; + if (oss->rel_timer == NULL) { + oss->rel_timer = qemu_new_timer(rt_clock, + (QEMUTimerCB *) oss_fini_out, oss); + } + if (oss->pending_cmd) + oss_ctl_out((HWVoiceOut *) hw, oss->pending_cmd); + qemu_mod_timer(oss->rel_timer, qemu_get_clock(rt_clock) + + OSS_RELEASE_MSEC); return 0; } @@ -531,6 +598,12 @@ return 0; } + if (oss->fd < 0) { + oss->pending_cmd = cmd; + return 0; + } else + oss->pending_cmd = 0; + switch (cmd) { case VOICE_ENABLE: ldebug ("enabling voice\n"); @@ -568,20 +641,21 @@ audsettings_t obt_as; oss->fd = -1; + oss->saved_as = *as; req.fmt = aud_to_ossfmt (as->fmt); req.freq = as->freq; req.nchannels = as->nchannels; req.fragsize = conf.fragsize; req.nfrags = conf.nfrags; - if (oss_open (1, &req, &obt, &fd)) { - return -1; + if (oss_open (1, &req, &obt, &fd, &oss->released)) { + return no_init_in(hw, as); } err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); if (err) { oss_anal_close (&fd); - return -1; + return no_init_in(hw, as); } obt_as.freq = obt.freq; @@ -607,10 +681,16 @@ dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", hw->samples, 1 << hw->info.shift); oss_anal_close (&fd); - return -1; + return no_init_in(hw, as); } oss->fd = fd; + if (oss->rel_timer == NULL) { + oss->rel_timer = qemu_new_timer(rt_clock, + (QEMUTimerCB *) oss_fini_in, oss); + } + qemu_mod_timer(oss->rel_timer, qemu_get_clock(rt_clock) + + OSS_RELEASE_MSEC); return 0; } @@ -618,12 +698,20 @@ { OSSVoiceIn *oss = (OSSVoiceIn *) hw; - oss_anal_close (&oss->fd); + if (oss->fd < 0) + no_fini_in(hw); + else { + oss_anal_close (&oss->fd); + oss->released = 1; + } if (oss->pcm_buf) { qemu_free (oss->pcm_buf); oss->pcm_buf = NULL; } + + if (oss->rel_timer != NULL) + qemu_del_timer(oss->rel_timer); } static int oss_run_in (HWVoiceIn *hw) @@ -654,6 +742,20 @@ bufs[0].len = dead << hwshift; } + if (bufs[0].len || bufs[1].len) { + + /* if the audio device was released (or was busy when the last open was + * attempted, then we try to open it again; if it failed for other + * reasons, then we'll just use noaudio's run_in */ + if (oss->released) + oss_init_in(hw, &oss->saved_as); + if (oss->fd < 0) + return no_run_in(hw); /* permanent failure, or busy for now */ + else { + qemu_mod_timer(oss->rel_timer, qemu_get_clock(rt_clock) + + OSS_RELEASE_MSEC); + } + } for (i = 0; i < 2; ++i) { ssize_t nread;