diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h --- a/sys/dev/sound/pcm/channel.h +++ b/sys/dev/sound/pcm/channel.h @@ -264,6 +264,7 @@ int chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction); int chn_kill(struct pcm_channel *c); +void chn_shutdown(struct pcm_channel *c); int chn_reset(struct pcm_channel *c, u_int32_t fmt, u_int32_t spd); int chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right, int center); diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -1301,6 +1301,15 @@ return (0); } +void +chn_shutdown(struct pcm_channel *c) +{ + CHN_LOCKASSERT(c); + + chn_wakeup(c); + c->flags |= CHN_F_DEAD; +} + int chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right, int center) 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 @@ -674,23 +674,50 @@ return (err); } -static int -pcm_killchan(device_t dev) +static void +pcm_killchans(struct snddev_info *d) { - struct snddev_info *d = device_get_softc(dev); struct pcm_channel *ch; int error; + bool found; PCM_BUSYASSERT(d); + do { + found = false; + CHN_FOREACH(ch, d, channels.pcm) { + CHN_LOCK(ch); + /* + * Make sure no channel has went to sleep in the + * meantime. + */ + chn_shutdown(ch); + /* + * We have to give a thread sleeping in chn_sleep() a + * chance to observe that the channel is dead. + */ + if ((ch->flags & CHN_F_SLEEPING) == 0) { + found = true; + CHN_UNLOCK(ch); + break; + } + CHN_UNLOCK(ch); + } - ch = CHN_FIRST(d, channels.pcm); + /* + * All channels are still sleeping. Sleep for a bit and try + * again to see if any of them is awake now. + */ + if (!found) { + pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0); + continue; + } - PCM_LOCK(d); - error = pcm_chn_remove(d, ch); - PCM_UNLOCK(d); - if (error) - return (error); - return (pcm_chn_destroy(ch)); + PCM_LOCK(d); + error = pcm_chn_remove(d, ch); + PCM_UNLOCK(d); + if (error == 0) + pcm_chn_destroy(ch); + } while (!CHN_EMPTY(d, channels.pcm)); } static int @@ -1000,15 +1027,11 @@ CHN_FOREACH(ch, d, channels.pcm) { CHN_LOCK(ch); - 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; - } + /* + * Do not wait for the timeout in chn_read()/chn_write(). Wake + * up the sleeping thread and kill the channel. + */ + chn_shutdown(ch); chn_abort(ch); CHN_UNLOCK(ch); } @@ -1033,8 +1056,7 @@ dsp_destroy_dev(dev); (void)mixer_uninit(dev); - while (!CHN_EMPTY(d, channels.pcm)) - pcm_killchan(dev); + pcm_killchans(d); PCM_LOCK(d); PCM_RELEASE(d);