Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 28 May 2026 14:08:39 +0000
From:      Christos Margiolis <christos@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 9fb56421425f - main - sound: Centralize and improve hot-swapping
Message-ID:  <6a184c67.2041a.8cbcb0c@gitrepo.freebsd.org>

index | next in thread | raw e-mail

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);
 }


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a184c67.2041a.8cbcb0c>