Subject: Re: gus driver will hang with new audio patches
To: None <gnats-bugs@NetBSD.ORG, current-users@NetBSD.ORG>
From: John Kohl <jtk@kolvir.arlington.ma.us>
List: current-users
Date: 02/10/1996 18:21:19
I found and fixed the problem with the latest audio changes causing the
GUS driver to loop. I'm happy to say now that I can get uninterrupted
44kHz x stereo x 16 bit playback on my machine!
The root cause is that the GUS driver uses on-board memory as an
additional buffer and does its own buffer admission rate control. The
GUS driver can get into a situation where it doesn't acknowledge a
buffer from the audio layer until it has room for it on the card. Due
to timing races, the audio layer may end up thinking the buffer it's
currently got from the user should be played again.
The fix is for the audio layer to keep track of which buffers were real
and which were silence buffers. The simplest solution I came up with to
this is to use separate DMA callback routines for silence and
non-silence output. The sc->sc_silence flag becomes unnecessary.
Herewith, my patches.
===================================================================
RCS file: RCS/audio.c,v
retrieving revision 1.1.1.4
diff -c -r1.1.1.4 audio.c
*** audio.c 1996/02/09 00:11:58 1.1.1.4
--- audio.c 1996/02/10 23:20:47
***************
*** 139,145 ****
void audiostartr __P((struct audio_softc *));
void audiostartp __P((struct audio_softc *));
void audio_rint __P((struct audio_softc *));
! void audio_pint __P((struct audio_softc *));
int audio_calc_blksize __P((struct audio_softc *));
void audio_silence_fill __P((struct audio_softc *, u_char *, int));
--- 139,147 ----
void audiostartr __P((struct audio_softc *));
void audiostartp __P((struct audio_softc *));
void audio_rint __P((struct audio_softc *));
! void audio_pint __P((struct audio_softc *, int));
! void audio_rpint __P((struct audio_softc *));
! void audio_spint __P((struct audio_softc *));
int audio_calc_blksize __P((struct audio_softc *));
void audio_silence_fill __P((struct audio_softc *, u_char *, int));
***************
*** 1222,1230 ****
if (sc->pr.nblk > 0) {
u_char *hp = sc->pr.hp;
- sc->sc_silence = 0;
if (rval = sc->hw_if->start_output(sc->hw_hdl, hp, sc->sc_blksize,
! audio_pint, (void *)sc)) {
DPRINTF(("audiostartp: failed: %d\n", rval));
}
else {
--- 1228,1235 ----
if (sc->pr.nblk > 0) {
u_char *hp = sc->pr.hp;
if (rval = sc->hw_if->start_output(sc->hw_hdl, hp, sc->sc_blksize,
! audio_rpint, (void *)sc)) {
DPRINTF(("audiostartp: failed: %d\n", rval));
}
else {
***************
*** 1238,1251 ****
}
/*
* Called from HW driver module on completion of dma output.
* Start output of new block, wrap in ring buffer if needed.
* If no more buffers to play, output zero instead.
* Do a wakeup if necessary.
*/
void
! audio_pint(sc)
struct audio_softc *sc;
{
u_char *hp;
int cc = sc->sc_blksize;
--- 1243,1278 ----
}
/*
+ * Use this routine as DMA callback if we played user data. We need to
+ * account for user data and silence separately.
+ */
+ void
+ audio_rpint(sc)
+ struct audio_softc *sc;
+ {
+ audio_pint(sc, 0); /* 'twas a real audio block */
+ }
+
+ /*
+ * Use this routine as DMA callback if we played silence.
+ */
+ void
+ audio_spint(sc)
+ struct audio_softc *sc;
+ {
+ audio_pint(sc, 1); /* 'twas a silence block */
+ }
+
+ /*
* Called from HW driver module on completion of dma output.
* Start output of new block, wrap in ring buffer if needed.
* If no more buffers to play, output zero instead.
* Do a wakeup if necessary.
*/
void
! audio_pint(sc, silence)
struct audio_softc *sc;
+ int silence;
{
u_char *hp;
int cc = sc->sc_blksize;
***************
*** 1259,1265 ****
* always fails and the output is always silence after the
* first block.
*/
! if (!sc->sc_silence)
cb->nblk--;
if (cb->nblk > 0) {
hp = cb->hp;
--- 1286,1292 ----
* always fails and the output is always silence after the
* first block.
*/
! if (!silence)
cb->nblk--;
if (cb->nblk > 0) {
hp = cb->hp;
***************
*** 1277,1283 ****
Dprintf("audio_pint: hp=0x%x cc=%d\n", hp, cc);
#endif
if (err = hw->start_output(sc->hw_hdl, hp, cc,
! audio_pint, (void *)sc)) {
DPRINTF(("audio_pint restart failed: %d\n", err));
audio_clear(sc);
}
--- 1304,1310 ----
Dprintf("audio_pint: hp=0x%x cc=%d\n", hp, cc);
#endif
if (err = hw->start_output(sc->hw_hdl, hp, cc,
! audio_rpint, (void *)sc)) {
DPRINTF(("audio_pint restart failed: %d\n", err));
audio_clear(sc);
}
***************
*** 1299,1308 ****
Dprintf("audio_pint: drops=%d auzero %d 0x%x\n", cb->cb_drops, cc, *(int *)auzero_block);
#endif
psilence:
- sc->sc_silence = 1;
if (err = hw->start_output(sc->hw_hdl,
auzero_block, cc,
! audio_pint, (void *)sc)) {
DPRINTF(("audio_pint zero failed: %d\n", err));
audio_clear(sc);
}
--- 1326,1334 ----
Dprintf("audio_pint: drops=%d auzero %d 0x%x\n", cb->cb_drops, cc, *(int *)auzero_block);
#endif
psilence:
if (err = hw->start_output(sc->hw_hdl,
auzero_block, cc,
! audio_spint, (void *)sc)) {
DPRINTF(("audio_pint zero failed: %d\n", err));
audio_clear(sc);
}
===================================================================
RCS file: RCS/audiovar.h,v
retrieving revision 1.1
diff -c -r1.1 audiovar.h
*** audiovar.h 1996/02/10 21:40:52 1.1
--- audiovar.h 1996/02/10 22:47:46
***************
*** 118,122 ****
int sc_rblks; /* number of phantom record blocks */
int sc_pencoding; /* current encoding; play */
int sc_rencoding; /* current encoding; record */
- int sc_silence; /* currently outputting audio_zero */
};
--- 118,121 ----