The branch main has been updated by christos:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=9fb56421425fa35e56ce294284c08b09852052a5

commit 9fb56421425fa35e56ce294284c08b09852052a5
Author:     Christos Margiolis <[email protected]>
AuthorDate: 2026-05-21 12:22:38 +0000
Commit:     Christos Margiolis <[email protected]>
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);
 }

Reply via email to