diff options
| author | Ariff Abdullah <ariff@FreeBSD.org> | 2006-04-04 17:43:49 +0000 |
|---|---|---|
| committer | Ariff Abdullah <ariff@FreeBSD.org> | 2006-04-04 17:43:49 +0000 |
| commit | fa4c64ea60009d02aa4b0dbd0433bba6e5edc7cd (patch) | |
| tree | b1935f6877e4396fe959f840a506ce7c2c14b486 /sys/dev/sound | |
| parent | c8949cb9c2c120c11f969da69b47a488eac2f41a (diff) | |
Notes
Diffstat (limited to 'sys/dev/sound')
| -rw-r--r-- | sys/dev/sound/pcm/dsp.c | 156 | ||||
| -rw-r--r-- | sys/dev/sound/pcm/mixer.c | 14 | ||||
| -rw-r--r-- | sys/dev/sound/pcm/sound.c | 676 | ||||
| -rw-r--r-- | sys/dev/sound/pcm/sound.h | 19 | ||||
| -rw-r--r-- | sys/dev/sound/pcm/vchan.c | 82 |
5 files changed, 513 insertions, 434 deletions
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c index 603a510b2310..48153350864b 100644 --- a/sys/dev/sound/pcm/dsp.c +++ b/sys/dev/sound/pcm/dsp.c @@ -101,7 +101,7 @@ dsp_set_flags(struct cdev *dev, u_int32_t flags) } /* - * return the channels channels associated with an open device instance. + * return the channels associated with an open device instance. * set the priority if the device is simplex and one direction (only) is * specified. * lock channels specified. @@ -170,11 +170,18 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) struct snddev_info *d; u_int32_t fmt; int devtype; - int rdref; int error; + int chnum; + + if (i_dev == NULL || td == NULL) + return ENODEV; + + if ((flags & (FREAD | FWRITE)) == 0) + return EINVAL; d = dsp_get_info(i_dev); devtype = PCMDEV(i_dev); + chnum = -1; /* decide default format */ switch (devtype) { @@ -196,34 +203,24 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) case SND_DEV_DSPREC: fmt = AFMT_U8; - if (mode & FWRITE) { + if (flags & FWRITE) return EINVAL; - } + chnum = PCMCHAN(i_dev); break; default: panic("impossible devtype %d", devtype); } - rdref = 0; - /* lock snddev so nobody else can monkey with it */ pcm_lock(d); rdch = i_dev->si_drv1; wrch = i_dev->si_drv2; - if ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && (rdch || wrch)) { - /* we're a simplex device and already open, no go */ - pcm_unlock(d); - return EBUSY; - } - - if (((flags & FREAD) && rdch) || ((flags & FWRITE) && wrch)) { - /* - * device already open in one or both directions that - * the opener wants; we can't handle this. - */ + if (rdch || wrch || ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && + (flags & (FREAD | FWRITE)) == (FREAD | FWRITE))) { + /* simplex or not, better safe than sorry. */ pcm_unlock(d); return EBUSY; } @@ -237,67 +234,48 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) if (flags & FREAD) { /* open for read */ pcm_unlock(d); - if (devtype == SND_DEV_DSPREC) - rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, PCMCHAN(i_dev)); - else - rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, -1); - if (!rdch) { - /* no channel available, exit */ - return EBUSY; - } - /* got a channel, already locked for us */ - if (chn_reset(rdch, fmt) || - (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED))) { - pcm_chnrelease(rdch); - pcm_lock(d); - i_dev->si_drv1 = NULL; - pcm_unlock(d); - return ENODEV; + error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, chnum); + if (error != 0 && error != EBUSY && chnum != -1 && (flags & FWRITE)) + error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, -1); + + if (error == 0 && (chn_reset(rdch, fmt) || + (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED)))) + error = ENODEV; + + if (error != 0) { + if (rdch) + pcm_chnrelease(rdch); + return error; } 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 - */ - pcm_lock(d); + pcm_lock(d); } if (flags & FWRITE) { /* open for write */ pcm_unlock(d); - wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1); - error = 0; + error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, chnum); + if (error != 0 && error != EBUSY && chnum != -1 && (flags & FREAD)) + error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, -1); - if (!wrch) - error = EBUSY; /* XXX Right return code? */ - else if (chn_reset(wrch, fmt) || - (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED))) + if (error == 0 && (chn_reset(wrch, fmt) || + (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED)))) error = ENODEV; if (error != 0) { - if (wrch) { - /* - * Free play channel - */ + if (wrch) pcm_chnrelease(wrch); - pcm_lock(d); - i_dev->si_drv2 = NULL; - pcm_unlock(d); - } - if (rdref) { + if (rdch) { /* * Lock, deref and release previously created record channel */ CHN_LOCK(rdch); pcm_chnref(rdch, -1); pcm_chnrelease(rdch); - pcm_lock(d); - i_dev->si_drv1 = NULL; - pcm_unlock(d); } return error; @@ -330,38 +308,11 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) wrch = i_dev->si_drv2; pcm_unlock(d); - refs = 0; - - if (rdch) { - CHN_LOCK(rdch); - refs += pcm_chnref(rdch, -1); - CHN_UNLOCK(rdch); - } - if (wrch) { - CHN_LOCK(wrch); - refs += pcm_chnref(wrch, -1); - CHN_UNLOCK(wrch); - } - - /* - * If there are no more references, release the channels. - */ - if ((rdch || wrch) && refs == 0) { - - pcm_lock(d); - - 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); - + if (rdch || wrch) { + refs = 0; if (rdch) { CHN_LOCK(rdch); + refs += pcm_chnref(rdch, -1); chn_abort(rdch); /* won't sleep */ rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); chn_reset(rdch, 0); @@ -369,6 +320,7 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) } if (wrch) { CHN_LOCK(wrch); + refs += pcm_chnref(wrch, -1); /* * XXX: Maybe the right behaviour is to abort on non_block. * It seems that mplayer flushes the audio queue by quickly @@ -381,6 +333,23 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) chn_reset(wrch, 0); pcm_chnrelease(wrch); } + + pcm_lock(d); + if (rdch) + i_dev->si_drv1 = NULL; + if (wrch) + i_dev->si_drv2 = NULL; + /* + * If there are no more references, release the channels. + */ + if (refs == 0 && i_dev->si_drv1 == NULL && + i_dev->si_drv2 == NULL) { + if (pcm_getfakechan(d)) + pcm_getfakechan(d)->flags = 0; + /* What is this?!? */ + dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT); + } + pcm_unlock(d); } return 0; } @@ -445,8 +414,15 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * */ d = dsp_get_info(i_dev); - if (IOCGROUP(cmd) == 'M') - return mixer_ioctl(d->mixer_dev, cmd, arg, mode, td); + if (IOCGROUP(cmd) == 'M') { + /* + * This is at least, a bug to bug compatible with OSS. + */ + if (d->mixer_dev != NULL) + return mixer_ioctl(d->mixer_dev, cmd, arg, -1, td); + else + return EBADF; + } getchns(i_dev, &rdch, &wrch, 0); @@ -1162,8 +1138,8 @@ dsp_clone(void *arg, struct ucred *cred, char *name, int namelen, struct snddev_info *pcm_dev; struct snddev_channel *pcm_chan; int i, unit, devtype; - int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO}; - char *devnames[3] = {"dsp", "dspW", "audio"}; + static int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO}; + static char *devnames[3] = {"dsp", "dspW", "audio"}; if (*dev != NULL) return; diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c index cf8403f8a6bc..8e4ab84691d1 100644 --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -269,10 +269,14 @@ int mixer_uninit(device_t dev) { int i; + struct snddev_info *d; struct snd_mixer *m; struct cdev *pdev; + d = device_get_softc(dev); pdev = mixer_get_devt(dev); + if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL) + return EBADF; m = pdev->si_drv1; snd_mtxlock(m->lock); @@ -294,6 +298,8 @@ mixer_uninit(device_t dev) snd_mtxfree(m->lock); kobj_delete((kobj_t)m, M_MIXER); + d->mixer_dev = NULL; + return 0; } @@ -465,10 +471,16 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread int v = -1, j = cmd & 0xff; m = i_dev->si_drv1; - if (mode != -1 && !m->busy) + + if (m == NULL) return EBADF; snd_mtxlock(m->lock); + if (mode != -1 && !m->busy) { + snd_mtxunlock(m->lock); + return EBADF; + } + if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { if (j == SOUND_MIXER_RECSRC) ret = mixer_setrecsrc(m, *arg_i); diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c index 0311a290f25e..21381c358884 100644 --- a/sys/dev/sound/pcm/sound.c +++ b/sys/dev/sound/pcm/sound.c @@ -53,7 +53,7 @@ static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); struct sysctl_ctx_list * snd_sysctl_tree(device_t dev) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); return &d->sysctl_tree; } @@ -61,7 +61,7 @@ snd_sysctl_tree(device_t dev) struct sysctl_oid * snd_sysctl_tree_top(device_t dev) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); return d->sysctl_tree_top; } @@ -158,50 +158,161 @@ pcm_getfakechan(struct snddev_info *d) return d->fakechan; } -/* return a locked channel */ -struct pcm_channel * -pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) +static int +pcm_setvchans(struct snddev_info *d, int newcnt) +{ + struct snddev_channel *sce = NULL; + struct pcm_channel *c = NULL; + int err = 0, vcnt, dcnt, i; + + pcm_inprog(d, 1); + + if (!(d->flags & SD_F_AUTOVCHAN)) { + err = EINVAL; + goto setvchans_out; + } + + vcnt = d->vchancount; + dcnt = d->playcount + d->reccount; + + if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) { + err = E2BIG; + goto setvchans_out; + } + + dcnt += vcnt; + + if (newcnt > vcnt) { + /* add new vchans - find a parent channel first */ + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + CHN_LOCK(c); + if (c->direction == PCMDIR_PLAY && + ((c->flags & CHN_F_HAS_VCHAN) || + (vcnt == 0 && + !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) + goto addok; + CHN_UNLOCK(c); + } + err = EBUSY; + goto setvchans_out; +addok: + c->flags |= CHN_F_BUSY; + while (err == 0 && newcnt > vcnt) { + if (dcnt > PCMMAXCHAN) { + device_printf(d->dev, "%s: Maximum channel reached.\n", __func__); + break; + } + err = vchan_create(c); + if (err == 0) { + vcnt++; + dcnt++; + } else if (err == E2BIG && newcnt > vcnt) + device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err); + } + if (vcnt == 0) + c->flags &= ~CHN_F_BUSY; + CHN_UNLOCK(c); + } else if (newcnt < vcnt) { +#define ORPHAN_CDEVT(cdevt) \ + ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \ + (cdevt)->si_drv2 == NULL)) + while (err == 0 && newcnt < vcnt) { + i = 0; + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + CHN_LOCK(c); + if (c->direction == PCMDIR_PLAY && + (c->flags & CHN_F_VIRTUAL) && + (i++ == newcnt)) { + if (!(c->flags & CHN_F_BUSY) && + ORPHAN_CDEVT(sce->dsp_devt) && + ORPHAN_CDEVT(sce->dspW_devt) && + ORPHAN_CDEVT(sce->audio_devt) && + ORPHAN_CDEVT(sce->dspr_devt)) + goto remok; + /* + * Either we're busy, or our cdev + * has been stolen by dsp_clone(). + * Skip, and increase newcnt. + */ + if (!(c->flags & CHN_F_BUSY)) + device_printf(d->dev, + "%s: <%s> somebody steal my cdev!\n", + __func__, c->name); + newcnt++; + } + CHN_UNLOCK(c); + } + if (vcnt != newcnt) + err = EBUSY; + break; +remok: + CHN_UNLOCK(c); + err = vchan_destroy(c); + if (err == 0) + vcnt--; + else + device_printf(d->dev, + "%s: WARNING: vchan_destroy() failed!", + __func__); + } + } + +setvchans_out: + pcm_inprog(d, -1); + return err; +} + +/* return error status and a locked channel */ +int +pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, + pid_t pid, int chnum) { struct pcm_channel *c; - struct snddev_channel *sce; + struct snddev_channel *sce; int err; +retry_chnalloc: + err = ENODEV; /* scan for a free channel */ SLIST_FOREACH(sce, &d->channels, link) { c = sce->channel; CHN_LOCK(c); - if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { - if (chnum == -1 || c->num == chnum) { + if (c->direction == direction && !(c->flags & CHN_F_BUSY)) { + if (chnum < 0 || sce->chan_num == chnum) { c->flags |= CHN_F_BUSY; c->pid = pid; - return c; + *ch = c; + return 0; } } + if (sce->chan_num == chnum) { + if (c->direction != direction) + err = EOPNOTSUPP; + else if (c->flags & CHN_F_BUSY) + err = EBUSY; + else + err = EINVAL; + CHN_UNLOCK(c); + return err; + } else if (c->direction == direction && (c->flags & CHN_F_BUSY)) + err = EBUSY; CHN_UNLOCK(c); } /* no channel available */ - if (direction == PCMDIR_PLAY) { - if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { - /* try to create a vchan */ - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - CHN_LOCK(c); - if ((c->flags & CHN_F_HAS_VCHAN) && - !SLIST_EMPTY(&c->children)) { - err = vchan_create(c); - CHN_UNLOCK(c); - if (!err) - return pcm_chnalloc(d, direction, pid, -1); - else - device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); - } else - CHN_UNLOCK(c); - } + if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 && + d->vchancount < snd_maxautovchans && + d->devcount <= PCMMAXCHAN) { + err = pcm_setvchans(d, d->vchancount + 1); + if (err == 0) { + chnum = -2; + goto retry_chnalloc; } } - return NULL; + return err; } /* release a locked channel and unlock it */ @@ -245,65 +356,10 @@ pcm_inprog(struct snddev_info *d, int delta) static void pcm_setmaxautovchans(struct snddev_info *d, int num) { - struct pcm_channel *c, *ch; - struct snddev_channel *sce; - int err, done; - - /* - * XXX WOAH... NEED SUPER CLEANUP!!! - * Robust, yet confusing. Understanding these will - * cause your brain spinning like a Doki Doki Dynamo. - */ - if (num > 0 && d->vchancount == 0) { - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - CHN_LOCK(c); - if ((c->direction == PCMDIR_PLAY) && - !(c->flags & CHN_F_BUSY) && - SLIST_EMPTY(&c->children)) { - c->flags |= CHN_F_BUSY; - err = vchan_create(c); - if (err) { - c->flags &= ~CHN_F_BUSY; - device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); - } - CHN_UNLOCK(c); - return; - } - CHN_UNLOCK(c); - } - return; - } - if (num == 0 && d->vchancount > 0) { - /* - * XXX Keep retrying... - */ - for (done = 0; done < 1024; done++) { - ch = NULL; - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - CHN_LOCK(c); - if (c->direction == PCMDIR_PLAY && - !(c->flags & CHN_F_BUSY) && - (c->flags & CHN_F_VIRTUAL)) { - ch = c; - break; - } - CHN_UNLOCK(c); - } - if (ch != NULL) { - CHN_UNLOCK(ch); - snd_mtxlock(d->lock); - err = vchan_destroy(ch); - if (err) - device_printf(d->dev, "vchan_destroy(%s) == %d\n", - ch->name, err); - snd_mtxunlock(d->lock); - } else - return; - } - return; - } + if (num > 0 && d->vchancount == 0) + pcm_setvchans(d, 1); + else if (num == 0 && d->vchancount > 0) + pcm_setvchans(d, 0); } #ifdef USING_DEVFS @@ -338,18 +394,14 @@ sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) v = snd_maxautovchans; error = sysctl_handle_int(oidp, &v, sizeof(v), req); if (error == 0 && req->newptr != NULL) { - if (v < 0 || v >= SND_MAXVCHANS || pcm_devclass == NULL) - return EINVAL; - if (v != snd_maxautovchans) { + if (v < 0 || v > PCMMAXCHAN) + return E2BIG; + if (pcm_devclass != NULL && v != snd_maxautovchans) { for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); if (!d) continue; - if (d->flags & SD_F_AUTOVCHAN) { - if (pcm_inprog(d, 1) == 1) - pcm_setmaxautovchans(d, v); - pcm_inprog(d, -1); - } + pcm_setmaxautovchans(d, v); } } snd_maxautovchans = v; @@ -362,9 +414,11 @@ SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, struct pcm_channel * pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) { - struct pcm_channel *ch; + struct snddev_channel *sce; + struct pcm_channel *ch, *c; char *dirs; - int direction, err, *pnum; + uint32_t flsearch = 0; + int direction, err, rpnum, *pnum; switch(dir) { case PCMDIR_PLAY: @@ -383,6 +437,7 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c dirs = "virtual"; direction = PCMDIR_PLAY; pnum = &d->vchancount; + flsearch = CHN_F_VIRTUAL; break; default: @@ -401,14 +456,54 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c } snd_mtxlock(d->lock); - ch->num = (*pnum)++; + ch->num = 0; + rpnum = 0; + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + if (direction != c->direction || + (c->flags & CHN_F_VIRTUAL) != flsearch) + continue; + if (ch->num == c->num) + ch->num++; + else { +#if 0 + device_printf(d->dev, + "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n", + __func__, dirs, ch->num, c->num); +#endif + goto retry_num_search; + } + rpnum++; + } + goto retry_num_search_out; +retry_num_search: + rpnum = 0; + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + if (direction != c->direction || + (c->flags & CHN_F_VIRTUAL) != flsearch) + continue; + if (ch->num == c->num) { + ch->num++; + goto retry_num_search; + } + rpnum++; + } +retry_num_search_out: + if (*pnum != rpnum) { + device_printf(d->dev, + "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n", + __func__, dirs, *pnum, rpnum); + *pnum = rpnum; + } + (*pnum)++; snd_mtxunlock(d->lock); ch->pid = -1; ch->parentsnddev = d; ch->parentchannel = parent; ch->dev = d->dev; - snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); + snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); err = chn_init(ch, devinfo, dir, direction); if (err) { @@ -447,14 +542,16 @@ pcm_chn_destroy(struct pcm_channel *ch) int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) { - struct snddev_channel *sce, *tmp, *after; - int device = device_get_unit(d->dev); + struct snddev_channel *sce, *tmp, *after; + unsigned rdevcount; + int device = device_get_unit(d->dev); + size_t namelen; /* * Note it's confusing nomenclature. * dev_t * device -> pcm_device - * unit -> pcm_channel + * unit -> pcm_channel * channel -> snddev_channel * device_t * unit -> pcm_device @@ -467,54 +564,101 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) snd_mtxlock(d->lock); sce->channel = ch; - sce->chan_num= d->devcount++; - if (SLIST_EMPTY(&d->channels)) { + sce->chan_num = 0; + rdevcount = 0; + after = NULL; + SLIST_FOREACH(tmp, &d->channels, link) { + if (sce->chan_num == tmp->chan_num) + sce->chan_num++; + else { +#if 0 + device_printf(d->dev, + "%s: cdev numbering screwed (Expect: %d, Got: %d)\n", + __func__, sce->chan_num, tmp->chan_num); +#endif + goto retry_chan_num_search; + } + after = tmp; + rdevcount++; + } + goto retry_chan_num_search_out; +retry_chan_num_search: + /* + * Look for possible channel numbering collision. This may not + * be optimized, but it will ensure that no collision occured. + * Can be considered cheap since none of the locking/unlocking + * operations involved. + */ + rdevcount = 0; + after = NULL; + SLIST_FOREACH(tmp, &d->channels, link) { + if (sce->chan_num == tmp->chan_num) { + sce->chan_num++; + goto retry_chan_num_search; + } + if (sce->chan_num > tmp->chan_num) + after = tmp; + rdevcount++; + } +retry_chan_num_search_out: + /* + * Don't overflow PCMMKMINOR / PCMMAXCHAN. + */ + if (sce->chan_num > PCMMAXCHAN) { + snd_mtxunlock(d->lock); + device_printf(d->dev, + "%s: WARNING: sce->chan_num overflow! (%d)\n", + __func__, sce->chan_num); + free(sce, M_DEVBUF); + return E2BIG; + } + if (d->devcount != rdevcount) { + device_printf(d->dev, + "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n", + __func__, d->devcount, rdevcount); + d->devcount = rdevcount; + } + d->devcount++; + if (after == NULL) { SLIST_INSERT_HEAD(&d->channels, sce, link); } else { - /* - * Micro optimization, channel ordering: - * hw,hw,hw,vch,vch,vch,rec - */ - after = NULL; - if (ch->flags & CHN_F_VIRTUAL) { - /* virtual channel to the end */ - SLIST_FOREACH(tmp, &d->channels, link) { - if (tmp->channel->direction == PCMDIR_REC) - break; - after = tmp; - } - } else { - if (ch->direction == PCMDIR_REC) { - SLIST_FOREACH(tmp, &d->channels, link) { - after = tmp; - } - } else { - SLIST_FOREACH(tmp, &d->channels, link) { - if (tmp->channel->direction == PCMDIR_REC) - break; - if (!(tmp->channel->flags & CHN_F_VIRTUAL)) - after = tmp; - } - } - } - if (after == NULL) { - SLIST_INSERT_HEAD(&d->channels, sce, link); - } else { - SLIST_INSERT_AFTER(after, sce, link); + SLIST_INSERT_AFTER(after, sce, link); + } +#if 0 + if (1) { + int cnum = 0; + SLIST_FOREACH(tmp, &d->channels, link) { + if (cnum != tmp->chan_num) + device_printf(d->dev, + "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n", + __func__, cnum, tmp->chan_num); + cnum++; } } +#endif + + namelen = strlen(ch->name); + if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */ + snprintf(ch->name + namelen, + CHN_NAMELEN - namelen, ":dsp%d.%d", + device, sce->chan_num); + } snd_mtxunlock(d->lock); - sce->dsp_devt= make_dev(&dsp_cdevsw, + + /* + * I will revisit these someday, and nuke it mercilessly.. + */ + sce->dsp_devt = make_dev(&dsp_cdevsw, PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", device, sce->chan_num); - sce->dspW_devt= make_dev(&dsp_cdevsw, + sce->dspW_devt = make_dev(&dsp_cdevsw, PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", device, sce->chan_num); - sce->audio_devt= make_dev(&dsp_cdevsw, + sce->audio_devt = make_dev(&dsp_cdevsw, PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", device, sce->chan_num); @@ -531,7 +675,7 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) { - struct snddev_channel *sce; + struct snddev_channel *sce; #if 0 int ourlock; @@ -573,9 +717,9 @@ gotit: int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); struct pcm_channel *ch; - int err; + int err; ch = pcm_chn_create(d, NULL, cls, dir, devinfo); if (!ch) { @@ -590,28 +734,14 @@ pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) return err; } - CHN_LOCK(ch); - if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && - ch->direction == PCMDIR_PLAY && d->vchancount == 0) { - ch->flags |= CHN_F_BUSY; - err = vchan_create(ch); - if (err) { - ch->flags &= ~CHN_F_BUSY; - CHN_UNLOCK(ch); - device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); - return err; - } - } - CHN_UNLOCK(ch); - return err; } static int pcm_killchan(device_t dev) { - struct snddev_info *d = device_get_softc(dev); - struct snddev_channel *sce; + struct snddev_info *d = device_get_softc(dev); + struct snddev_channel *sce; struct pcm_channel *ch; int error = 0; @@ -627,26 +757,28 @@ pcm_killchan(device_t dev) int pcm_setstatus(device_t dev, char *str) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); snd_mtxlock(d->lock); strncpy(d->status, str, SND_STATUSLEN); snd_mtxunlock(d->lock); + if (snd_maxautovchans > 0) + pcm_setvchans(d, 1); return 0; } -u_int32_t +uint32_t pcm_getflags(device_t dev) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); return d->flags; } void -pcm_setflags(device_t dev, u_int32_t val) +pcm_setflags(device_t dev, uint32_t val) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); d->flags = val; } @@ -654,7 +786,7 @@ pcm_setflags(device_t dev, u_int32_t val) void * pcm_getdevinfo(device_t dev) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); return d->devinfo; } @@ -662,7 +794,7 @@ pcm_getdevinfo(device_t dev) unsigned int pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); int sz, x; sz = 0; @@ -692,7 +824,7 @@ pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned i int pcm_register(device_t dev, void *devinfo, int numplay, int numrec) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); if (pcm_veto_load) { device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); @@ -720,7 +852,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) SLIST_INIT(&d->channels); - if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) + if ((numplay == 0 || numrec == 0) && numplay != numrec) d->flags |= SD_F_SIMPLEX; d->fakechan = fkchan_setup(dev); @@ -739,12 +871,12 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); #endif if (numplay > 0) { - vchan_initsys(dev); d->flags |= SD_F_AUTOVCHAN; + vchan_initsys(dev); } sndstat_register(dev, d->status, sndstat_prepare_pcm); - return 0; + return 0; no: snd_mtxfree(d->lock); return ENXIO; @@ -753,8 +885,9 @@ no: int pcm_unregister(device_t dev) { - struct snddev_info *d = device_get_softc(dev); - struct snddev_channel *sce; + struct snddev_info *d = device_get_softc(dev); + struct snddev_channel *sce; + struct pcmchan_children *pce; struct pcm_channel *ch; if (sndstat_acquire() != 0) { @@ -780,7 +913,7 @@ pcm_unregister(device_t dev) } } - if (mixer_uninit(dev)) { + if (mixer_uninit(dev) == EBUSY) { device_printf(dev, "unregister: mixer busy\n"); snd_mtxunlock(d->lock); sndstat_release(); @@ -788,26 +921,66 @@ pcm_unregister(device_t dev) } SLIST_FOREACH(sce, &d->channels, link) { - if (sce->dsp_devt) + if (sce->dsp_devt) { destroy_dev(sce->dsp_devt); - if (sce->dspW_devt) + sce->dsp_devt = NULL; + } + if (sce->dspW_devt) { destroy_dev(sce->dspW_devt); - if (sce->audio_devt) + sce->dspW_devt = NULL; + } + if (sce->audio_devt) { destroy_dev(sce->audio_devt); - if (sce->dspr_devt) + sce->audio_devt = NULL; + } + if (sce->dspr_devt) { destroy_dev(sce->dspr_devt); + sce->dspr_devt = NULL; + } + d->devcount--; + ch = sce->channel; + if (ch == NULL) + continue; + pce = SLIST_FIRST(&ch->children); + while (pce != NULL) { +#if 0 + device_printf(d->dev, "<%s> removing <%s>\n", + ch->name, (pce->channel != NULL) ? + pce->channel->name : "unknown"); +#endif + SLIST_REMOVE(&ch->children, pce, pcmchan_children, link); + free(pce, M_DEVBUF); + pce = SLIST_FIRST(&ch->children); + } } #ifdef SND_DYNSYSCTL d->sysctl_tree_top = NULL; sysctl_ctx_free(&d->sysctl_tree); #endif + +#if 0 + SLIST_FOREACH(sce, &d->channels, link) { + ch = sce->channel; + if (ch == NULL) + continue; + if (!SLIST_EMPTY(&ch->children)) + device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n", + __func__, ch->name); + } +#endif while (!SLIST_EMPTY(&d->channels)) pcm_killchan(dev); chn_kill(d->fakechan); fkchan_kill(d->fakechan); +#if 0 + device_printf(d->dev, "%s: devcount=%u, playcount=%u, " + "reccount=%u, vchancount=%u\n", + __func__, d->devcount, d->playcount, d->reccount, + d->vchancount); +#endif snd_mtxunlock(d->lock); snd_mtxfree(d->lock); sndstat_unregister(dev); @@ -820,11 +993,11 @@ pcm_unregister(device_t dev) static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) { - struct snddev_info *d; - struct snddev_channel *sce; + struct snddev_info *d; + struct snddev_channel *sce; struct pcm_channel *c; struct pcm_feeder *f; - int pc, rc, vc; + int pc, rc, vc; if (verbose < 1) return 0; @@ -862,6 +1035,10 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) SLIST_FOREACH(sce, &d->channels, link) { c = sce->channel; + + KASSERT(c->bufhard != NULL && c->bufsoft != NULL, + ("hosed pcm channel setup")); + sbuf_printf(s, "\n\t"); /* it would be better to indent child channels */ @@ -877,32 +1054,22 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) sbuf_printf(s, ", pid %d", c->pid); sbuf_printf(s, "\n\t"); - if (c->bufhard != NULL && c->bufsoft != NULL) { - sbuf_printf(s, "interrupts %d, ", c->interrupts); - if (c->direction == PCMDIR_REC) - sbuf_printf(s, "overruns %d, hfree %d, sfree %d", - c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); -#if 0 - sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); -#endif - else - sbuf_printf(s, "underruns %d, ready %d", - c->xruns, sndbuf_getready(c->bufsoft)); -#if 0 - sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, sndbuf_getready(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); -#endif - sbuf_printf(s, "\n\t"); - } + sbuf_printf(s, "interrupts %d, ", c->interrupts); + if (c->direction == PCMDIR_REC) + sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", + c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), + sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), + sndbuf_getblkcnt(c->bufhard), + sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), + sndbuf_getblkcnt(c->bufsoft)); + else + sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", + c->xruns, sndbuf_getready(c->bufsoft), + sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), + sndbuf_getblkcnt(c->bufhard), + sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), + sndbuf_getblkcnt(c->bufsoft)); + sbuf_printf(s, "\n\t"); sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); sbuf_printf(s, " -> "); @@ -937,113 +1104,16 @@ int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; - struct snddev_channel *sce; - struct pcm_channel *c; - int err, newcnt, cnt; + int err, newcnt; - /* - * XXX WOAH... NEED SUPER CLEANUP!!! - * Robust, yet confusing. Understanding these will - * cause your brain spinning like a Doki Doki Dynamo. - */ d = oidp->oid_arg1; - if (!(d->flags & SD_F_AUTOVCHAN)) { - pcm_inprog(d, -1); - return EINVAL; - } - - cnt = 0; - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - CHN_LOCK(c); - if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) { - cnt++; - if (req->newptr != NULL && c->flags & CHN_F_BUSY) { - /* Better safe than sorry */ - CHN_UNLOCK(c); - return EBUSY; - } - } - CHN_UNLOCK(c); - } - - newcnt = cnt; - + newcnt = d->vchancount; err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); - if (err == 0 && req->newptr != NULL) { - - if (newcnt < 0 || newcnt > SND_MAXVCHANS) - return E2BIG; - - if (pcm_inprog(d, 1) != 1) { - pcm_inprog(d, -1); - return EINPROGRESS; - } + if (err == 0 && req->newptr != NULL && d->vchancount != newcnt) + err = pcm_setvchans(d, newcnt); - if (newcnt > cnt) { - /* add new vchans - find a parent channel first */ - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - CHN_LOCK(c); - /* not a candidate if not a play channel */ - if (c->direction != PCMDIR_PLAY) - goto next; - /* not a candidate if a virtual channel */ - if (c->flags & CHN_F_VIRTUAL) - goto next; - /* not a candidate if it's in use */ - if (!(c->flags & CHN_F_BUSY) || - !(SLIST_EMPTY(&c->children))) - /* - * if we get here we're a nonvirtual - * play channel, and either - * 1) not busy - * 2) busy with children, not directly - * open - * - * thus we can add children - */ - goto addok; -next: - CHN_UNLOCK(c); - } - pcm_inprog(d, -1); - return EBUSY; -addok: - c->flags |= CHN_F_BUSY; - while (err == 0 && newcnt > cnt) { - err = vchan_create(c); - if (err == 0) - cnt++; - } - CHN_UNLOCK(c); - } else if (newcnt < cnt) { - snd_mtxlock(d->lock); - while (err == 0 && newcnt < cnt) { - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - CHN_LOCK(c); - if (c->direction == PCMDIR_PLAY && - (c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) - goto remok; - - CHN_UNLOCK(c); - } - snd_mtxunlock(d->lock); - pcm_inprog(d, -1); - return EINVAL; -remok: - CHN_UNLOCK(c); - err = vchan_destroy(c); - if (err == 0) - cnt--; - } - snd_mtxunlock(d->lock); - } - pcm_inprog(d, -1); - } return err; } #endif diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h index af539a66bf0b..7613b300c642 100644 --- a/sys/dev/sound/pcm/sound.h +++ b/sys/dev/sound/pcm/sound.h @@ -123,11 +123,15 @@ nomenclature: [etc.] */ -#define PCMMINOR(x) (minor(x)) -#define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16) -#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4) -#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f) -#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) +#define PCMMAXCHAN 0xff +#define PCMMAXDEV 0x0f +#define PCMMAXUNIT 0x0f +#define PCMMINOR(x) (minor(x)) +#define PCMCHAN(x) ((PCMMINOR(x) >> 16) & PCMMAXCHAN) +#define PCMUNIT(x) ((PCMMINOR(x) >> 4) & PCMMAXUNIT) +#define PCMDEV(x) (PCMMINOR(x) & PCMMAXDEV) +#define PCMMKMINOR(u, d, c) ((((c) & PCMMAXCHAN) << 16) | \ + (((u) & PCMMAXUNIT) << 4) | ((d) & PCMMAXDEV)) #define SD_F_SIMPLEX 0x00000001 #define SD_F_AUTOVCHAN 0x00000002 @@ -156,7 +160,8 @@ nomenclature: struct pcm_channel *fkchan_setup(device_t dev); int fkchan_kill(struct pcm_channel *c); -#define SND_MAXVCHANS 255 +/* XXX Flawed definition. I'll fix it someday. */ +#define SND_MAXVCHANS PCMMAXCHAN /* * Minor numbers for the sound driver. @@ -207,7 +212,7 @@ struct sysctl_ctx_list *snd_sysctl_tree(device_t dev); struct sysctl_oid *snd_sysctl_tree_top(device_t dev); struct pcm_channel *pcm_getfakechan(struct snddev_info *d); -struct pcm_channel *pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum); +int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int chnum); int pcm_chnrelease(struct pcm_channel *c); int pcm_chnref(struct pcm_channel *c, int ref); int pcm_inprog(struct snddev_info *d, int delta); diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c index 9d6fd597c269..fba208b708b9 100644 --- a/sys/dev/sound/pcm/vchan.c +++ b/sys/dev/sound/pcm/vchan.c @@ -290,36 +290,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); @@ -350,8 +364,8 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) } } else CHN_UNLOCK(ch); - pcm_inprog(d, -1); } + pcm_inprog(d, -1); return err; } #endif @@ -507,8 +521,8 @@ vchan_create(struct pcm_channel *parent) parent->flags &= ~CHN_F_HAS_VCHAN; CHN_UNLOCK(parent); free(pce, M_DEVBUF); - pcm_chn_remove(d, child); - pcm_chn_destroy(child); + if (pcm_chn_remove(d, child) == 0) + pcm_chn_destroy(child); CHN_LOCK(parent); return err; } @@ -524,7 +538,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)) { @@ -546,24 +561,34 @@ vchan_destroy(struct pcm_channel *c) gotch: SLIST_FOREACH(sce, &d->channels, link) { if (sce->channel == c) { - if (sce->dsp_devt) + if (sce->dsp_devt) { destroy_dev(sce->dsp_devt); - if (sce->dspW_devt) + sce->dsp_devt = NULL; + } + if (sce->dspW_devt) { destroy_dev(sce->dspW_devt); - if (sce->audio_devt) + sce->dspW_devt = NULL; + } + if (sce->audio_devt) { destroy_dev(sce->audio_devt); - if (sce->dspr_devt) + sce->audio_devt = NULL; + } + if (sce->dspr_devt) { destroy_dev(sce->dspr_devt); + sce->dspr_devt = NULL; + } + d->devcount--; break; } } 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 */ @@ -574,15 +599,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; } |
