sound: Centralize and improve hot-swapping
Introduce pcm_hotswap(), which is responsible for sending devctl SND/CONN notifications. There are two user-visible improvements with this patch: First, in pcm_unregister(), instead of just sending a SND/CONN/NODEV notification when all devices have detached, we also switch to the new default device if the previously default one has detached, but there are more left. Second, in pcm_register(), if the device happens to also be the new default device, we hot-swap to it. Additionally, if hw.snd.default_auto is set to 2, then we will essentially be hot-swapping to the newest attached device. The latter is especially useful for laptops like the Framework 16, which comes with a built-in snd_hda(4) speaker-microphone-only device, and headphones can work with the Framework Audio Expansion Card, which does not extend the snd_hda(4) device, but is in fact a separate snd_uaudio(4) device. To achieve automatic audio redirection between headphones and speakers in this case, there has to be a way to switch between different devices. The way the Audio Expansion Card works is by having snd_uaudio(4) attach to it when the headphones are plugged, and detach when unplugged, so this patch, along with hw.snd.default_auto=2, can pick up those attach events and switch automatically. Combined with the pcm_unregister() update, it becomes possible to switch back and forth between headphones and speakers. While here, be more robust and lock around snd_unit reads. In collaboration with: jrm Sponsored by: The FreeBSD Foundation MFC after: 1 week
This commit is contained in:
+36
-11
@@ -77,11 +77,30 @@ snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand
|
||||
return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep);
|
||||
}
|
||||
|
||||
static void
|
||||
pcm_hotswap(void)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
char buf[32];
|
||||
|
||||
bus_topo_assert();
|
||||
if (snd_unit >= 0) {
|
||||
d = devclass_get_softc(pcm_devclass, snd_unit);
|
||||
if (!PCM_REGISTERED(d))
|
||||
return;
|
||||
snprintf(buf, sizeof(buf), "cdev=dsp%d", snd_unit);
|
||||
if (d->reccount > 0)
|
||||
devctl_notify("SND", "CONN", "IN", buf);
|
||||
if (d->playcount > 0)
|
||||
devctl_notify("SND", "CONN", "OUT", buf);
|
||||
} else
|
||||
devctl_notify("SND", "CONN", "NODEV", NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
char buf[32];
|
||||
int error, unit;
|
||||
|
||||
unit = snd_unit;
|
||||
@@ -95,13 +114,8 @@ sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
|
||||
}
|
||||
snd_unit = unit;
|
||||
snd_unit_auto = 0;
|
||||
pcm_hotswap();
|
||||
bus_topo_unlock();
|
||||
|
||||
snprintf(buf, sizeof(buf), "cdev=dsp%d", snd_unit);
|
||||
if (d->reccount > 0)
|
||||
devctl_notify("SND", "CONN", "IN", buf);
|
||||
if (d->playcount > 0)
|
||||
devctl_notify("SND", "CONN", "OUT", buf);
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
@@ -373,6 +387,7 @@ int
|
||||
pcm_register(device_t dev, char *str)
|
||||
{
|
||||
struct snddev_info *d = device_get_softc(dev);
|
||||
int err;
|
||||
|
||||
/* should only be called once */
|
||||
if (d->flags & SD_F_REGISTERED)
|
||||
@@ -417,6 +432,13 @@ pcm_register(device_t dev, char *str)
|
||||
vchan_initsys(dev);
|
||||
feeder_eq_initsys(dev);
|
||||
|
||||
sndstat_register(dev, SNDST_TYPE_PCM, d->status);
|
||||
|
||||
err = dsp_make_dev(dev);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
bus_topo_lock();
|
||||
if (snd_unit_auto < 0)
|
||||
snd_unit_auto = (snd_unit < 0) ? 1 : 0;
|
||||
if (snd_unit < 0 || snd_unit_auto > 1)
|
||||
@@ -424,9 +446,11 @@ pcm_register(device_t dev, char *str)
|
||||
else if (snd_unit_auto == 1)
|
||||
snd_unit = pcm_best_unit(snd_unit);
|
||||
|
||||
sndstat_register(dev, SNDST_TYPE_PCM, d->status);
|
||||
if (snd_unit == device_get_unit(dev))
|
||||
pcm_hotswap();
|
||||
bus_topo_unlock();
|
||||
|
||||
return (dsp_make_dev(dev));
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -469,13 +493,14 @@ pcm_unregister(device_t dev)
|
||||
cv_destroy(&d->cv);
|
||||
mtx_destroy(&d->lock);
|
||||
|
||||
bus_topo_lock();
|
||||
if (snd_unit == device_get_unit(dev)) {
|
||||
snd_unit = pcm_best_unit(-1);
|
||||
if (snd_unit_auto == 0)
|
||||
snd_unit_auto = 1;
|
||||
if (snd_unit < 0)
|
||||
devctl_notify("SND", "CONN", "NODEV", NULL);
|
||||
pcm_hotswap();
|
||||
}
|
||||
bus_topo_unlock();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user