summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound
diff options
context:
space:
mode:
authororion <orion@FreeBSD.org>2002-08-18 14:17:06 +0000
committerorion <orion@FreeBSD.org>2002-08-18 14:17:06 +0000
commit5dddb64180563b6e7335a2839aa6cb48ba8855d9 (patch)
tree4eadfe559063ed9765d3308a640c52be50283c75 /sys/dev/sound
parent6da980591b6309b8a6764e2727a485f5d32023cb (diff)
downloadFreeBSD-src-5dddb64180563b6e7335a2839aa6cb48ba8855d9.zip
FreeBSD-src-5dddb64180563b6e7335a2839aa6cb48ba8855d9.tar.gz
Apply reference counting patch. Fixes problem of two applications
opening the device, eg one read only and one write only, and the reference count being non-zero when both exit rendering device permanently busy. PR: kern/35004 Submitted by: Bill Wells MFC after: 3 days
Diffstat (limited to 'sys/dev/sound')
-rw-r--r--sys/dev/sound/pcm/dsp.c68
1 files changed, 33 insertions, 35 deletions
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index b7eb4b9..1d4a660 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -283,40 +283,34 @@ dsp_open(dev_t i_dev, int flags, int mode, struct thread *td)
/* finished with snddev, new channels still locked */
/* bump refcounts, reset and unlock any channels that we just opened */
- if (rdch) {
- if (flags & FREAD) {
- if (chn_reset(rdch, fmt)) {
- pcm_lock(d);
- pcm_chnrelease(rdch);
- if (wrch && (flags & FWRITE))
- pcm_chnrelease(wrch);
- pcm_unlock(d);
- splx(s);
- return ENODEV;
- }
- if (flags & O_NONBLOCK)
- rdch->flags |= CHN_F_NBIO;
- } else
- CHN_LOCK(rdch);
+ if (flags & FREAD) {
+ if (chn_reset(rdch, fmt)) {
+ pcm_lock(d);
+ pcm_chnrelease(rdch);
+ if (wrch && (flags & FWRITE))
+ pcm_chnrelease(wrch);
+ pcm_unlock(d);
+ splx(s);
+ return ENODEV;
+ }
+ if (flags & O_NONBLOCK)
+ rdch->flags |= CHN_F_NBIO;
pcm_chnref(rdch, 1);
CHN_UNLOCK(rdch);
}
- if (wrch) {
- if (flags & FWRITE) {
- if (chn_reset(wrch, fmt)) {
- pcm_lock(d);
- pcm_chnrelease(wrch);
- if (rdch && (flags & FREAD))
- pcm_chnrelease(rdch);
- pcm_unlock(d);
- splx(s);
- return ENODEV;
- }
- if (flags & O_NONBLOCK)
- wrch->flags |= CHN_F_NBIO;
- } else
- CHN_LOCK(wrch);
+ if (flags & FWRITE) {
+ if (chn_reset(wrch, fmt)) {
+ pcm_lock(d);
+ pcm_chnrelease(wrch);
+ if (rdch && (flags & FREAD))
+ pcm_chnrelease(rdch);
+ pcm_unlock(d);
+ splx(s);
+ return ENODEV;
+ }
+ if (flags & O_NONBLOCK)
+ wrch->flags |= CHN_F_NBIO;
pcm_chnref(wrch, 1);
CHN_UNLOCK(wrch);
@@ -345,18 +339,23 @@ dsp_close(dev_t i_dev, int flags, int mode, struct thread *td)
if (rdch) {
CHN_LOCK(rdch);
if (pcm_chnref(rdch, -1) > 0) {
- CHN_UNLOCK(rdch);
exit = 1;
}
+ CHN_UNLOCK(rdch);
}
if (wrch) {
CHN_LOCK(wrch);
if (pcm_chnref(wrch, -1) > 0) {
- CHN_UNLOCK(wrch);
exit = 1;
}
+ CHN_UNLOCK(wrch);
}
+ /* XXX And what happens if one of the channels had 2 references and
+ the other has but one? The latter won't get reset. Can that
+ happen? */
if (exit) {
+ i_dev->si_drv1 = NULL;
+ i_dev->si_drv2 = NULL;
pcm_unlock(d);
splx(s);
return 0;
@@ -367,9 +366,6 @@ dsp_close(dev_t i_dev, int flags, int mode, struct thread *td)
if (pcm_getfakechan(d))
pcm_getfakechan(d)->flags = 0;
- 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);
@@ -385,6 +381,8 @@ dsp_close(dev_t i_dev, int flags, int mode, struct thread *td)
chn_reset(wrch, 0);
pcm_chnrelease(wrch);
}
+ i_dev->si_drv1 = NULL;
+ i_dev->si_drv2 = NULL;
splx(s);
return 0;
OpenPOWER on IntegriCloud