1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-14 10:09:48 +00:00

- 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.
This commit is contained in:
Ariff Abdullah 2006-03-31 10:27:47 +00:00
parent cd8a592bb3
commit 9c271eba78
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=157330

View File

@ -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 @@ vchan_destroy(struct pcm_channel *c)
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 @@ vchan_destroy(struct pcm_channel *c)
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;
}