summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound
diff options
context:
space:
mode:
authorscottl <scottl@FreeBSD.org>2003-11-11 05:38:28 +0000
committerscottl <scottl@FreeBSD.org>2003-11-11 05:38:28 +0000
commitb1f6b54a9b03a8c61b8ef229f3f4ba40905bcd45 (patch)
treeb489e6d70819d26baf14c77df12889db048aca11 /sys/dev/sound
parentcc852a80a31de8499d7cb796db663450b8fd1596 (diff)
downloadFreeBSD-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.c50
-rw-r--r--sys/dev/sound/pcm/dsp.c169
-rw-r--r--sys/dev/sound/pcm/mixer.c2
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;
}
OpenPOWER on IntegriCloud