Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/sound/pcm/sound.c
Show First 20 Lines • Show All 205 Lines • ▼ Show 20 Lines | pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
pcm_killchans(struct snddev_info *d) | pcm_killchans(struct snddev_info *d) | ||||
{ | { | ||||
struct pcm_channel *ch; | struct pcm_channel *ch; | ||||
bool found; | bool again; | ||||
PCM_BUSYASSERT(d); | PCM_BUSYASSERT(d); | ||||
do { | KASSERT(!PCM_REGISTERED(d), ("%s(): still registered\n", __func__)); | ||||
found = false; | |||||
for (;;) { | |||||
again = false; | |||||
/* Make sure all channels are stopped. */ | |||||
CHN_FOREACH(ch, d, channels.pcm) { | CHN_FOREACH(ch, d, channels.pcm) { | ||||
CHN_LOCK(ch); | CHN_LOCK(ch); | ||||
/* | if ((ch->flags & CHN_F_SLEEPING) == 0 && | ||||
* Make sure no channel has went to sleep in the | CHN_STOPPED(ch) && ch->inprog == 0) { | ||||
* 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); | CHN_UNLOCK(ch); | ||||
break; | continue; | ||||
} | } | ||||
chn_shutdown(ch); | |||||
if (ch->direction == PCMDIR_PLAY) | |||||
chn_flush(ch); | |||||
else | |||||
chn_abort(ch); | |||||
CHN_UNLOCK(ch); | CHN_UNLOCK(ch); | ||||
again = true; | |||||
} | } | ||||
dev_submerge.ch: Do we need a `break` here? This makes the shutdown of the channels fully sequential. Is this… | |||||
Done Inline ActionsIt can probably go away. I will test it now. christos: It can probably go away. I will test it now. | |||||
/* | /* | ||||
* All channels are still sleeping. Sleep for a bit and try | * Some channels are still active. Sleep for a bit and try | ||||
* again to see if any of them is awake now. | * again. | ||||
*/ | */ | ||||
if (!found) { | if (again) | ||||
pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0); | pause_sbt("pcmkillchans", mstosbt(5), 0, 0); | ||||
continue; | else | ||||
break; | |||||
} | } | ||||
/* All channels are finally dead. */ | |||||
while (!CHN_EMPTY(d, channels.pcm)) { | |||||
ch = CHN_FIRST(d, channels.pcm); | |||||
chn_kill(ch); | chn_kill(ch); | ||||
} while (!CHN_EMPTY(d, channels.pcm)); | |||||
} | } | ||||
if (d->p_unr != NULL) | |||||
delete_unrhdr(d->p_unr); | |||||
if (d->vp_unr != NULL) | |||||
delete_unrhdr(d->vp_unr); | |||||
if (d->r_unr != NULL) | |||||
delete_unrhdr(d->r_unr); | |||||
if (d->vr_unr != NULL) | |||||
delete_unrhdr(d->vr_unr); | |||||
} | |||||
static int | static int | ||||
pcm_best_unit(int old) | pcm_best_unit(int old) | ||||
{ | { | ||||
struct snddev_info *d; | struct snddev_info *d; | ||||
int i, best, bestprio, prio; | int i, best, bestprio, prio; | ||||
best = -1; | best = -1; | ||||
bestprio = -100; | bestprio = -100; | ||||
▲ Show 20 Lines • Show All 249 Lines • ▼ Show 20 Lines | pcm_register(device_t dev, char *str) | ||||
return (dsp_make_dev(dev)); | return (dsp_make_dev(dev)); | ||||
} | } | ||||
int | int | ||||
pcm_unregister(device_t dev) | pcm_unregister(device_t dev) | ||||
{ | { | ||||
struct snddev_info *d; | struct snddev_info *d; | ||||
struct pcm_channel *ch; | |||||
d = device_get_softc(dev); | d = device_get_softc(dev); | ||||
if (!PCM_ALIVE(d)) { | if (!PCM_ALIVE(d)) { | ||||
device_printf(dev, "unregister: device not configured\n"); | device_printf(dev, "unregister: device not configured\n"); | ||||
return (0); | return (0); | ||||
} | } | ||||
PCM_LOCK(d); | PCM_LOCK(d); | ||||
PCM_WAIT(d); | PCM_WAIT(d); | ||||
d->flags |= SD_F_DETACHING; | d->flags |= SD_F_DETACHING; | ||||
d->flags |= SD_F_DYING; | |||||
d->flags &= ~SD_F_REGISTERED; | |||||
PCM_ACQUIRE(d); | PCM_ACQUIRE(d); | ||||
PCM_UNLOCK(d); | PCM_UNLOCK(d); | ||||
CHN_FOREACH(ch, d, channels.pcm) { | pcm_killchans(d); | ||||
CHN_LOCK(ch); | |||||
/* | |||||
* 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); | |||||
} | |||||
/* remove /dev/sndstat entry first */ | PCM_RELEASE_QUICK(d); | ||||
sndstat_unregister(dev); | |||||
PCM_LOCK(d); | |||||
d->flags |= SD_F_DYING; | |||||
d->flags &= ~SD_F_REGISTERED; | |||||
PCM_UNLOCK(d); | |||||
if (d->play_sysctl_tree != NULL) { | if (d->play_sysctl_tree != NULL) { | ||||
sysctl_ctx_free(&d->play_sysctl_ctx); | sysctl_ctx_free(&d->play_sysctl_ctx); | ||||
d->play_sysctl_tree = NULL; | d->play_sysctl_tree = NULL; | ||||
} | } | ||||
if (d->rec_sysctl_tree != NULL) { | if (d->rec_sysctl_tree != NULL) { | ||||
sysctl_ctx_free(&d->rec_sysctl_ctx); | sysctl_ctx_free(&d->rec_sysctl_ctx); | ||||
d->rec_sysctl_tree = NULL; | d->rec_sysctl_tree = NULL; | ||||
} | } | ||||
sndstat_unregister(dev); | |||||
mixer_uninit(dev); | |||||
dsp_destroy_dev(dev); | dsp_destroy_dev(dev); | ||||
(void)mixer_uninit(dev); | |||||
pcm_killchans(d); | |||||
PCM_LOCK(d); | |||||
PCM_RELEASE(d); | |||||
cv_destroy(&d->cv); | cv_destroy(&d->cv); | ||||
PCM_UNLOCK(d); | |||||
snd_mtxfree(d->lock); | snd_mtxfree(d->lock); | ||||
if (d->p_unr != NULL) | |||||
delete_unrhdr(d->p_unr); | |||||
if (d->vp_unr != NULL) | |||||
delete_unrhdr(d->vp_unr); | |||||
if (d->r_unr != NULL) | |||||
delete_unrhdr(d->r_unr); | |||||
if (d->vr_unr != NULL) | |||||
delete_unrhdr(d->vr_unr); | |||||
if (snd_unit == device_get_unit(dev)) { | if (snd_unit == device_get_unit(dev)) { | ||||
snd_unit = pcm_best_unit(-1); | snd_unit = pcm_best_unit(-1); | ||||
if (snd_unit_auto == 0) | if (snd_unit_auto == 0) | ||||
snd_unit_auto = 1; | snd_unit_auto = 1; | ||||
} | } | ||||
return (0); | return (0); | ||||
▲ Show 20 Lines • Show All 230 Lines • Show Last 20 Lines |
Do we need a break here? This makes the shutdown of the channels fully sequential. Is this required or would it make sense to shutdown all the channels in parallel?