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.h b/sys/dev/sound/clone.h --- a/sys/dev/sound/clone.h +++ b/sys/dev/sound/clone.h @@ -102,6 +102,7 @@ int snd_clone_enable(struct snd_clone *); int snd_clone_disable(struct snd_clone *); int snd_clone_getsize(struct snd_clone *); +int snd_clone_getrefcount(struct snd_clone *); int snd_clone_getmaxunit(struct snd_clone *); int snd_clone_setmaxunit(struct snd_clone *, int); int snd_clone_getdeadline(struct snd_clone *); @@ -112,6 +113,10 @@ uint32_t snd_clone_setdevflags(struct cdev *, uint32_t); int snd_clone_gc(struct snd_clone *); void snd_clone_destroy(struct snd_clone *); +void snd_clone_lock(struct snd_clone *); +void snd_clone_unlock(struct snd_clone *); +void snd_clone_sleep(struct snd_clone *); +void snd_clone_wakeup(struct snd_clone *); int snd_clone_acquire(struct cdev *); int snd_clone_release(struct cdev *); int snd_clone_ref(struct cdev *); 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 /* @@ -80,6 +77,8 @@ struct snd_clone { TAILQ_HEAD(link_head, snd_clone_entry) head; struct timespec tsp; + struct cv cv; + struct mtx *lock; int refcount; int size; int typemask; @@ -115,6 +114,8 @@ ("invalid clone flags=0x%08x", flags)); c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO); + c->lock = snd_mtxcreate("clone lock", "clone manager"); + cv_init(&c->cv, "clone"); c->refcount = 0; c->size = 0; c->typemask = typemask; @@ -188,6 +189,14 @@ return (c->size); } +int +snd_clone_getrefcount(struct snd_clone *c) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + return (c->refcount); +} + int snd_clone_getmaxunit(struct snd_clone *c) { @@ -370,9 +379,41 @@ ce = tmp; } + cv_destroy(&c->cv); + snd_mtxfree(c->lock); free(c, M_DEVBUF); } +void +snd_clone_lock(struct snd_clone *c) +{ + mtx_lock(c->lock); +} + +void +snd_clone_unlock(struct snd_clone *c) +{ + mtx_unlock(c->lock); +} + +void +snd_clone_sleep(struct snd_clone *c) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + mtx_assert(c->lock, MA_OWNED); + cv_wait(&c->cv, c->lock); +} + +void +snd_clone_wakeup(struct snd_clone *c) +{ + SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone")); + + mtx_assert(c->lock, MA_OWNED); + cv_broadcastpri(&c->cv, PRIBIO); +} + /* * snd_clone_acquire() : The vital part of concurrency management. Must be * called somewhere at the beginning of open() handler. ENODEV is not really @@ -489,6 +530,9 @@ (c->refcount == 0 && (c->flags & SND_CLONE_GC_LASTREF)))) (void)snd_clone_gc(c); + if (c->refcount == 0) + snd_clone_wakeup(ce->parent); + return (c->refcount); } diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c --- a/sys/dev/sound/pcm/dsp.c +++ b/sys/dev/sound/pcm/dsp.c @@ -802,7 +802,15 @@ * here and rely on pcm cv serialization. */ PCM_UNLOCK(d); + + /* + * pcm_unregister() has already locked until all clones are unref'd. + */ + if (!PCM_DETACHING(d)) + snd_clone_lock(d->clones); (void)snd_clone_unref(i_dev); + if (!PCM_DETACHING(d)) + snd_clone_unlock(d->clones); PCM_LOCK(d); PCM_RELEASE(d); diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h --- a/sys/dev/sound/pcm/sound.h +++ b/sys/dev/sound/pcm/sound.h @@ -320,7 +320,6 @@ pid_t pid, char *comm, int devunit); 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); struct pcm_channel *pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo); int pcm_chn_destroy(struct pcm_channel *ch); @@ -379,7 +378,6 @@ struct snd_clone *clones; unsigned devcount, playcount, reccount, pvchancount, rvchancount ; unsigned flags; - int inprog; unsigned int bufsz; void *devinfo; device_t dev; 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 @@ -392,16 +392,6 @@ return (c->refcount); } -int -pcm_inprog(struct snddev_info *d, int delta) -{ - PCM_LOCKASSERT(d); - - d->inprog += delta; - - return (d->inprog); -} - static void pcm_setmaxautovchans(struct snddev_info *d, int num) { @@ -1127,7 +1117,6 @@ d->pvchanformat = 0; d->rvchanrate = 0; d->rvchanformat = 0; - d->inprog = 0; /* * Create clone manager, disabled by default. Cloning will be @@ -1182,50 +1171,42 @@ d->flags |= SD_F_DETACHING; - if (d->inprog != 0) { - device_printf(dev, "unregister: operation in progress\n"); - PCM_UNLOCK(d); - return (EBUSY); - } - PCM_ACQUIRE(d); PCM_UNLOCK(d); 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 sleeping thread + * and kill the channel immediately. + */ + CHN_BROADCAST(&ch->intr_cv); + ch->flags |= CHN_F_DEAD; } CHN_UNLOCK(ch); } if (d->clones != NULL) { - if (snd_clone_busy(d->clones) != 0) { - device_printf(dev, "unregister: clone busy\n"); + while (snd_clone_getrefcount(d->clones) > 0) { PCM_RELEASE_QUICK(d); - return (EBUSY); - } else { - PCM_LOCK(d); - (void)snd_clone_disable(d->clones); - PCM_UNLOCK(d); + /* + * Wait for all sleeping threads to drain so that we + * can destroy the clones safely. + */ + snd_clone_lock(d->clones); + snd_clone_sleep(d->clones); + snd_clone_unlock(d->clones); + PCM_ACQUIRE_QUICK(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); } + (void)mixer_uninit(dev); + /* remove /dev/sndstat entry first */ sndstat_unregister(dev); 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