diff options
author | scottl <scottl@FreeBSD.org> | 2003-11-11 05:38:28 +0000 |
---|---|---|
committer | scottl <scottl@FreeBSD.org> | 2003-11-11 05:38:28 +0000 |
commit | b1f6b54a9b03a8c61b8ef229f3f4ba40905bcd45 (patch) | |
tree | b489e6d70819d26baf14c77df12889db048aca11 /sys/dev/sound | |
parent | cc852a80a31de8499d7cb796db663450b8fd1596 (diff) | |
download | FreeBSD-src-b1f6b54a9b03a8c61b8ef229f3f4ba40905bcd45.zip FreeBSD-src-b1f6b54a9b03a8c61b8ef229f3f4ba40905bcd45.tar.gz |
Fix sound LOR problems:
dsp_open: rearrange to only hold one lock at a time
dsp_close: ditto
mixer_hwvol_init: delete locking, the only consumer seems to
be the ess driver and it only call it a creation time, I
think the device will be stable across the sleepable malloc.
cmi interrupt routine: Release locks while caller chn_intr,
either this or do what emu10k1 does which is have no locks
at in the interrupt handler.
Submitted by: mat@cnd.mcgill.ca
Diffstat (limited to 'sys/dev/sound')
-rw-r--r-- | sys/dev/sound/pci/cmi.c | 50 | ||||
-rw-r--r-- | sys/dev/sound/pcm/dsp.c | 169 | ||||
-rw-r--r-- | sys/dev/sound/pcm/mixer.c | 2 |
3 files changed, 113 insertions, 108 deletions
diff --git a/sys/dev/sound/pci/cmi.c b/sys/dev/sound/pci/cmi.c index 52c519a..bc99893 100644 --- a/sys/dev/sound/pci/cmi.c +++ b/sys/dev/sound/pci/cmi.c @@ -516,41 +516,41 @@ cmi_intr(void *data) { struct sc_info *sc = data; u_int32_t intrstat; + u_int32_t toclear; snd_mtxlock(sc->lock); intrstat = cmi_rd(sc, CMPCI_REG_INTR_STATUS, 4); - if ((intrstat & CMPCI_REG_ANY_INTR) == 0) { - goto out; - } + if ((intrstat & CMPCI_REG_ANY_INTR) != 0) { - /* Disable interrupts */ - if (intrstat & CMPCI_REG_CH0_INTR) { - cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); - } + toclear = 0; + if (intrstat & CMPCI_REG_CH0_INTR) { + toclear |= CMPCI_REG_CH0_INTR_ENABLE; + //cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); + } - if (intrstat & CMPCI_REG_CH1_INTR) { - cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); - } + if (intrstat & CMPCI_REG_CH1_INTR) { + toclear |= CMPCI_REG_CH1_INTR_ENABLE; + //cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); + } - /* Signal interrupts to channel */ - if (intrstat & CMPCI_REG_CH0_INTR) { - chn_intr(sc->pch.channel); - } + if (toclear) { + cmi_clr4(sc, CMPCI_REG_INTR_CTRL, toclear); + snd_mtxunlock(sc->lock); - if (intrstat & CMPCI_REG_CH1_INTR) { - chn_intr(sc->rch.channel); - } + /* Signal interrupts to channel */ + if (intrstat & CMPCI_REG_CH0_INTR) { + chn_intr(sc->pch.channel); + } - /* Enable interrupts */ - if (intrstat & CMPCI_REG_CH0_INTR) { - cmi_set4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); - } + if (intrstat & CMPCI_REG_CH1_INTR) { + chn_intr(sc->rch.channel); + } - if (intrstat & CMPCI_REG_CH1_INTR) { - cmi_set4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); - } + snd_mtxlock(sc->lock); + cmi_set4(sc, CMPCI_REG_INTR_CTRL, toclear); -out: + } + } snd_mtxunlock(sc->lock); return; } diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c index 5cff3ad..9fde975 100644 --- a/sys/dev/sound/pcm/dsp.c +++ b/sys/dev/sound/pcm/dsp.c @@ -172,6 +172,8 @@ dsp_open(dev_t i_dev, int flags, int mode, struct thread *td) intrmask_t s; u_int32_t fmt; int devtype; + int rdref; + int error; s = spltty(); d = dsp_get_info(i_dev); @@ -207,6 +209,8 @@ dsp_open(dev_t i_dev, int flags, int mode, struct thread *td) panic("impossible devtype %d", devtype); } + rdref = 0; + /* lock snddev so nobody else can monkey with it */ pcm_lock(d); @@ -249,67 +253,66 @@ dsp_open(dev_t i_dev, int flags, int mode, struct thread *td) return EBUSY; } /* got a channel, already locked for us */ - } - - if (flags & FWRITE) { - /* open for write */ - wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1); - if (!wrch) { - /* no channel available */ - if (flags & FREAD) { - /* just opened a read channel, release it */ - pcm_chnrelease(rdch); - } - /* exit */ - pcm_unlock(d); - splx(s); - return EBUSY; - } - /* got a channel, already locked for us */ - } - - i_dev->si_drv1 = rdch; - i_dev->si_drv2 = wrch; - - /* Bump refcounts, reset and unlock any channels that we just opened, - * and then release device lock. - */ - if (flags & FREAD) { if (chn_reset(rdch, fmt)) { pcm_chnrelease(rdch); i_dev->si_drv1 = NULL; - if (wrch && (flags & FWRITE)) { - pcm_chnrelease(wrch); - i_dev->si_drv2 = NULL; - } pcm_unlock(d); splx(s); return ENODEV; } + if (flags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; pcm_chnref(rdch, 1); CHN_UNLOCK(rdch); + rdref = 1; + /* + * Record channel created, ref'ed and unlocked + */ } + if (flags & FWRITE) { - if (chn_reset(wrch, fmt)) { - pcm_chnrelease(wrch); - i_dev->si_drv2 = NULL; - if (flags & FREAD) { - CHN_LOCK(rdch); - pcm_chnref(rdch, -1); - pcm_chnrelease(rdch); - i_dev->si_drv1 = NULL; - } - pcm_unlock(d); - splx(s); - return ENODEV; + /* open for write */ + wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1); + error = 0; + + if (!wrch) + error = EBUSY; /* XXX Right return code? */ + else if (chn_reset(wrch, fmt)) + error = ENODEV; + + if (error != 0) { + if (wrch) { + /* + * Free play channel + */ + pcm_chnrelease(wrch); + i_dev->si_drv2 = NULL; } - if (flags & O_NONBLOCK) - wrch->flags |= CHN_F_NBIO; - pcm_chnref(wrch, 1); - CHN_UNLOCK(wrch); + if (rdref) { + /* + * Lock, deref and release previously created record channel + */ + CHN_LOCK(rdch); + pcm_chnref(rdch, -1); + pcm_chnrelease(rdch); + i_dev->si_drv1 = NULL; + } + + pcm_unlock(d); + splx(s); + return error; + } + + if (flags & O_NONBLOCK) + wrch->flags |= CHN_F_NBIO; + pcm_chnref(wrch, 1); + CHN_UNLOCK(wrch); } + + i_dev->si_drv1 = rdch; + i_dev->si_drv2 = wrch; + pcm_unlock(d); splx(s); return 0; @@ -321,7 +324,7 @@ dsp_close(dev_t i_dev, int flags, int mode, struct thread *td) 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 +332,57 @@ dsp_close(dev_t i_dev, int flags, int mode, struct thread *td) 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; } diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c index 6eb6117..1c89bb9 100644 --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -319,7 +319,6 @@ mixer_hwvol_init(device_t dev) pdev = mixer_get_devt(dev); m = pdev->si_drv1; - snd_mtxlock(m->lock); m->hwvol_mixer = SOUND_MIXER_VOLUME; m->hwvol_step = 5; @@ -330,7 +329,6 @@ mixer_hwvol_init(device_t dev) OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, sysctl_hw_snd_hwvol_mixer, "A", ""); #endif - snd_mtxunlock(m->lock); return 0; } |