git: 9fb56421425f - main - sound: Centralize and improve hot-swapping
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 28 May 2026 14:08:39 UTC
The branch main has been updated by christos:
URL: https://cgit.FreeBSD.org/src/commit/?id=9fb56421425fa35e56ce294284c08b09852052a5
commit 9fb56421425fa35e56ce294284c08b09852052a5
Author: Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2026-05-21 12:22:38 +0000
Commit: Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2026-05-28 14:08:20 +0000
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
---
sys/dev/sound/pcm/sound.c | 47 ++++++++++++++++++++++++++++++++++++-----------
1 file changed, 36 insertions(+), 11 deletions(-)
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index d98952d7a984..235142eb5209 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -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);
}