diff --git a/share/man/man4/snd_uaudio.4 b/share/man/man4/snd_uaudio.4 --- a/share/man/man4/snd_uaudio.4 +++ b/share/man/man4/snd_uaudio.4 @@ -86,16 +86,6 @@ by .An Hiten Pandya Aq Mt hmp@FreeBSD.org . .Sh BUGS -The -.Tn PCM -framework in -.Fx -only supports synchronous device detach. -That means all mixer and DSP character devices belonging to a given -USB audio device must be closed when receiving an error on a DSP read, -a DSP write or a DSP IOCTL request. -Else the USB audio driver will wait for this to happen, preventing -enumeration of new devices on the parenting USB controller. .Pp Some USB audio devices might refuse to work properly unless the sample rate is configured the same for both recording and playback, even if diff --git a/sys/dev/sound/clone.c b/sys/dev/sound/clone.c --- a/sys/dev/sound/clone.c +++ b/sys/dev/sound/clone.c @@ -37,10 +37,7 @@ #include "opt_snd.h" #endif -#if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG) #include -#endif - #include /* @@ -354,23 +351,36 @@ return (pruned); } +static void +snd_clone_gone_cb(void *arg) +{ + struct snd_clone *c; + struct snd_clone_entry *ce = arg; + + SND_CLONE_ASSERT(ce != NULL, ("NULL snd_clone_entry")); + c = ce->parent; + SND_CLONE_ASSERT(c != NULL, ("NULL parent")); + + TAILQ_REMOVE(&c->head, ce, link); + free(ce, M_DEVBUF); + c->refcount--; + c->size--; + + /* All clones have been unref'd, free the manager as well. */ + if (c->refcount == 0) + free(c, M_DEVBUF); +} + void snd_clone_destroy(struct snd_clone *c) { - struct snd_clone_entry *ce, *tmp; + struct snd_clone_entry *ce; SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); - ce = TAILQ_FIRST(&c->head); - while (ce != NULL) { - tmp = TAILQ_NEXT(ce, link); - if (ce->devt != NULL) - destroy_dev(ce->devt); - free(ce, M_DEVBUF); - ce = tmp; + TAILQ_FOREACH(ce, &c->head, link) { + destroy_dev_sched_cb(ce->devt, snd_clone_gone_cb, ce); } - - free(c, M_DEVBUF); } /* diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -799,6 +799,20 @@ return (0); } +static void +mixer_gone_cb(void *arg) +{ + struct snd_mixer *m = arg; + + KASSERT(m != NULL, ("NULL snd_mixer")); + KASSERT(m->type == MIXER_TYPE_PRIMARY, + ("%s(): illegal mixer type=%d", __func__, m->type)); + + snd_mtxfree(m->lock); + kobj_delete((kobj_t)m, M_MIXER); + --mixer_count; +} + int mixer_uninit(device_t dev) { @@ -817,20 +831,7 @@ KASSERT(m->type == MIXER_TYPE_PRIMARY, ("%s(): illegal mixer type=%d", __func__, m->type)); - snd_mtxlock(m->lock); - - if (m->busy) { - snd_mtxunlock(m->lock); - return EBUSY; - } - - /* destroy dev can sleep --hps */ - - snd_mtxunlock(m->lock); - pdev->si_drv1 = NULL; - destroy_dev(pdev); - snd_mtxlock(m->lock); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) @@ -844,13 +845,9 @@ MIXER_UNINIT(m); - snd_mtxfree(m->lock); - kobj_delete((kobj_t)m, M_MIXER); - + destroy_dev_sched_cb(pdev, mixer_gone_cb, m); d->mixer_dev = NULL; - --mixer_count; - return 0; } diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c --- a/sys/dev/sound/pcm/sound.c +++ b/sys/dev/sound/pcm/sound.c @@ -1176,39 +1176,28 @@ CHN_FOREACH(ch, d, channels.pcm) { CHN_LOCK(ch); - if (ch->refcount > 0) { - device_printf(dev, - "unregister: channel %s busy (pid %d)\n", - ch->name, ch->pid); - CHN_UNLOCK(ch); - PCM_RELEASE_QUICK(d); - return (EBUSY); + if (ch->flags & CHN_F_SLEEPING) { + /* + * We are detaching, so do not wait for the timeout in + * chn_read()/chn_write(). Wake up the thread and kill + * the channel immediately. + */ + CHN_BROADCAST(&ch->intr_cv); + ch->flags |= CHN_F_DEAD; } + chn_abort(ch); CHN_UNLOCK(ch); } if (d->clones != NULL) { - if (snd_clone_busy(d->clones) != 0) { - device_printf(dev, "unregister: clone busy\n"); - PCM_RELEASE_QUICK(d); - return (EBUSY); - } else { - PCM_LOCK(d); - (void)snd_clone_disable(d->clones); - PCM_UNLOCK(d); - } - } - - if (mixer_uninit(dev) == EBUSY) { - device_printf(dev, "unregister: mixer busy\n"); PCM_LOCK(d); - if (d->clones != NULL) - (void)snd_clone_enable(d->clones); - PCM_RELEASE(d); + (void)snd_clone_disable(d->clones); PCM_UNLOCK(d); - return (EBUSY); + snd_clone_destroy(d->clones); } + (void)mixer_uninit(dev); + /* remove /dev/sndstat entry first */ sndstat_unregister(dev); @@ -1217,15 +1206,6 @@ d->flags &= ~SD_F_REGISTERED; PCM_UNLOCK(d); - /* - * No lock being held, so this thing can be flushed without - * stucking into devdrn oblivion. - */ - if (d->clones != NULL) { - snd_clone_destroy(d->clones); - d->clones = NULL; - } - if (d->play_sysctl_tree != NULL) { sysctl_ctx_free(&d->play_sysctl_ctx); d->play_sysctl_tree = NULL; diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c --- a/sys/dev/sound/usb/uaudio.c +++ b/sys/dev/sound/usb/uaudio.c @@ -1250,20 +1250,12 @@ unsigned i = uaudio_get_child_index_by_dev(sc, dev); int error = 0; -repeat: - if (sc->sc_child[i].pcm_registered) { + if (sc->sc_child[i].pcm_registered) error = pcm_unregister(dev); - } else { - if (sc->sc_child[i].mixer_init) - error = mixer_uninit(dev); - } + else if (sc->sc_child[i].mixer_init) + error = mixer_uninit(dev); - if (error) { - device_printf(dev, "Waiting for sound application to exit!\n"); - usb_pause_mtx(NULL, 2 * hz); - goto repeat; /* try again */ - } - return (0); /* success */ + return (error); } static int