summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2006-03-31 10:27:47 +0000
committerariff <ariff@FreeBSD.org>2006-03-31 10:27:47 +0000
commit1aa8a677cca892a5127360be96a110c293d93f1d (patch)
tree5e73c2e1f6f0aed233703634404a974a2056bd3b
parent69fb696b657ca9767e91ff4ad17e2aad0822b1d7 (diff)
downloadFreeBSD-src-1aa8a677cca892a5127360be96a110c293d93f1d.zip
FreeBSD-src-1aa8a677cca892a5127360be96a110c293d93f1d.tar.gz
- Increase snddev refcount earlier while accessing through sysctl interface
to avoid possible device unregister race (impossible to reproduce, yet possible). - Extra sanity check to ensure proper parent channel is being selected. - Reset parent channel once all of its children gone.
-rw-r--r--sys/dev/sound/pcm/vchan.c61
1 files changed, 34 insertions, 27 deletions
diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c
index 0627be6..bc99714 100644
--- a/sys/dev/sound/pcm/vchan.c
+++ b/sys/dev/sound/pcm/vchan.c
@@ -289,36 +289,50 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
d = oidp->oid_arg1;
if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
return EINVAL;
+ if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
+ pcm_inprog(d, -1);
+ return EINPROGRESS;
+ }
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
CHN_LOCK(c);
if (c->direction == PCMDIR_PLAY) {
if (c->flags & CHN_F_VIRTUAL) {
+ /* Sanity check */
+ if (ch != NULL && ch != c->parentchannel) {
+ CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
if (req->newptr != NULL &&
(c->flags & CHN_F_BUSY)) {
CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
return EBUSY;
}
- if (ch == NULL)
- ch = c->parentchannel;
+ } else if (c->flags & CHN_F_HAS_VCHAN) {
+ /* No way!! */
+ if (ch != NULL) {
+ CHN_UNLOCK(c);
+ pcm_inprog(d, -1);
+ return EINVAL;
+ }
+ ch = c;
+ newspd = ch->speed;
}
}
CHN_UNLOCK(c);
}
- if (ch != NULL) {
- CHN_LOCK(ch);
- newspd = ch->speed;
- CHN_UNLOCK(ch);
+ if (ch == NULL) {
+ pcm_inprog(d, -1);
+ return EINVAL;
}
err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
if (err == 0 && req->newptr != NULL) {
- if (ch == NULL || newspd < 1 ||
- newspd < feeder_rate_ratemin ||
- newspd > feeder_rate_ratemax)
- return EINVAL;
- if (pcm_inprog(d, 1) != 1) {
+ if (newspd < 1 || newspd < feeder_rate_ratemin ||
+ newspd > feeder_rate_ratemax) {
pcm_inprog(d, -1);
- return EINPROGRESS;
+ return EINVAL;
}
CHN_LOCK(ch);
caps = chn_getcaps(ch);
@@ -349,8 +363,8 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
}
} else
CHN_UNLOCK(ch);
- pcm_inprog(d, -1);
}
+ pcm_inprog(d, -1);
return err;
}
#endif
@@ -522,7 +536,8 @@ vchan_destroy(struct pcm_channel *c)
struct snddev_info *d = parent->parentsnddev;
struct pcmchan_children *pce;
struct snddev_channel *sce;
- int err, last;
+ uint32_t spd;
+ int err;
CHN_LOCK(parent);
if (!(parent->flags & CHN_F_BUSY)) {
@@ -567,10 +582,11 @@ gotch:
SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
free(pce, M_DEVBUF);
- last = SLIST_EMPTY(&parent->children);
- if (last) {
- parent->flags &= ~CHN_F_BUSY;
- parent->flags &= ~CHN_F_HAS_VCHAN;
+ if (SLIST_EMPTY(&parent->children)) {
+ parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
+ spd = parent->speed;
+ if (chn_reset(parent, parent->format) == 0)
+ chn_setspeed(parent, spd);
}
/* remove us from our grandparent's channel list */
@@ -581,15 +597,6 @@ gotch:
if (!err)
err = pcm_chn_destroy(c);
-#if 0
- if (!err && last) {
- CHN_LOCK(parent);
- chn_reset(parent, chn_getcaps(parent)->fmtlist[0]);
- chn_setspeed(parent, chn_getcaps(parent)->minspeed);
- CHN_UNLOCK(parent);
- }
-#endif
-
return err;
}
OpenPOWER on IntegriCloud