--- /home/mat/dsp.c.1.66 Fri Oct 24 18:07:23 2003 +++ /usr/src/sys/dev/sound/pcm/dsp.c Sat Oct 25 00:24:49 2003 @@ -321,7 +321,7 @@ struct pcm_channel *rdch, *wrch; struct snddev_info *d; intrmask_t s; - int exit; + int refs; s = spltty(); d = dsp_get_info(i_dev); @@ -329,53 +329,57 @@ rdch = i_dev->si_drv1; wrch = i_dev->si_drv2; - exit = 0; + refs = 0; - /* decrement refcount for each channel, exit if nonzero */ if (rdch) { CHN_LOCK(rdch); - if (pcm_chnref(rdch, -1) > 0) { - CHN_UNLOCK(rdch); - exit = 1; - } + refs += pcm_chnref(rdch, -1); + CHN_UNLOCK(rdch); } if (wrch) { CHN_LOCK(wrch); - if (pcm_chnref(wrch, -1) > 0) { - CHN_UNLOCK(wrch); - exit = 1; - } - } - if (exit) { - pcm_unlock(d); - splx(s); - return 0; + refs += pcm_chnref(wrch, -1); + CHN_UNLOCK(wrch); } - /* both refcounts are zero, abort and release */ + /* + * If there are no more references, release the channels. + */ + if ((rdch || wrch) && refs == 0) { - if (pcm_getfakechan(d)) - pcm_getfakechan(d)->flags = 0; + if (pcm_getfakechan(d)) + pcm_getfakechan(d)->flags = 0; - i_dev->si_drv1 = NULL; - i_dev->si_drv2 = NULL; + i_dev->si_drv1 = NULL; + i_dev->si_drv2 = NULL; - dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT); - pcm_unlock(d); + dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT); - if (rdch) { - chn_abort(rdch); /* won't sleep */ - rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(rdch, 0); - pcm_chnrelease(rdch); - } - if (wrch) { - chn_flush(wrch); /* may sleep */ - wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(wrch, 0); - pcm_chnrelease(wrch); - } + pcm_unlock(d); + if (rdch) { + CHN_LOCK(rdch); + chn_abort(rdch); /* won't sleep */ + rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); + chn_reset(rdch, 0); + pcm_chnrelease(rdch); + } + if (wrch) { + CHN_LOCK(wrch); + /* + * XXX: Maybe the right behaviour is to abort on non_block. + * It seems that mplayer flushes the audio queue by quickly + * closing and re-opening. In FBSD, there's a long pause + * while the audio queue flushes that I presume isn't there in + * linux. + */ + chn_flush(wrch); /* may sleep */ + wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); + chn_reset(wrch, 0); + pcm_chnrelease(wrch); + } + } else + pcm_unlock(d); splx(s); return 0; }