diff options
author | orion <orion@FreeBSD.org> | 2002-08-18 14:17:06 +0000 |
---|---|---|
committer | orion <orion@FreeBSD.org> | 2002-08-18 14:17:06 +0000 |
commit | 5dddb64180563b6e7335a2839aa6cb48ba8855d9 (patch) | |
tree | 4eadfe559063ed9765d3308a640c52be50283c75 /sys/dev/sound | |
parent | 6da980591b6309b8a6764e2727a485f5d32023cb (diff) | |
download | FreeBSD-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.c | 68 |
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; |