git: 221babdf9feb - main - sound: Simplify how snd_mixer is fetched and how the cdev is created

From: Christos Margiolis <christos_at_FreeBSD.org>
Date: Fri, 18 Sep 2026 18:18:37 UTC
The branch main has been updated by christos:

URL: https://cgit.FreeBSD.org/src/commit/?id=221babdf9feb7e1c1c001a4be735400ca6fb12cb

commit 221babdf9feb7e1c1c001a4be735400ca6fb12cb
Author:     Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2026-09-18 18:12:55 +0000
Commit:     Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2026-09-18 18:12:55 +0000

    sound: Simplify how snd_mixer is fetched and how the cdev is created
    
    The primary snd_mixer was reached by accessing the mixer cdev's si_drv1.
    This is tedious and ugly, so store the mixer in snddev_info->mixer and
    access it directly.
    
    Additionally, create the cdev in a new mixer_make_dev() function (in
    similar fashion to dsp_make_dev()) in pcm_register(), when everything is
    initialized, instead of risking potential races because mixer_init()
    (called before pcm_register()) used to create the cdev.
    
    Also add some NULL checks in pcm_register(), to avoid creating a mixer
    cdev when the driver (e.g., fdt/audio_soc.c) does not create a mixer in
    the first place, and similarly in pcm_unregister().
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 month
    Differential Revision:  https://reviews.freebsd.org/D59070
---
 sys/dev/sound/pci/es137x.c        |  5 +--
 sys/dev/sound/pcm/channel.c       |  3 +-
 sys/dev/sound/pcm/dsp.c           |  4 +-
 sys/dev/sound/pcm/feeder_volume.c |  2 +-
 sys/dev/sound/pcm/mixer.c         | 89 +++++++++++++++++++++++----------------
 sys/dev/sound/pcm/mixer.h         |  1 +
 sys/dev/sound/pcm/sound.c         | 12 +++++-
 sys/dev/sound/pcm/sound.h         |  1 +
 8 files changed, 70 insertions(+), 47 deletions(-)

diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c
index f67bb41e4542..bafc17f78c98 100644
--- a/sys/dev/sound/pci/es137x.c
+++ b/sys/dev/sound/pci/es137x.c
@@ -1516,8 +1516,7 @@ sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS)
 
 	dev = oidp->oid_arg1;
 	d = device_get_softc(dev);
-	if (!PCM_REGISTERED(d) || d->mixer_dev == NULL ||
-	    d->mixer_dev->si_drv1 == NULL)
+	if (!PCM_REGISTERED(d) || d->mixer_dev == NULL || d->mixer == NULL)
 		return (EINVAL);
 	es = d->devinfo;
 	if (es == NULL)
@@ -1535,7 +1534,7 @@ sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS)
 	if (val == set)
 		return (0);
 	PCM_ACQUIRE_QUICK(d);
-	m = (d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 : NULL;
+	m = d->mixer;
 	if (m == NULL) {
 		PCM_RELEASE_QUICK(d);
 		return (ENODEV);
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 029086265550..f97282ac80f1 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -2157,8 +2157,7 @@ chn_syncstate(struct pcm_channel *c)
 	struct snd_mixer *m;
 
 	d = (c != NULL) ? c->parentsnddev : NULL;
-	m = (d != NULL && d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 :
-	    NULL;
+	m = (d != NULL) ? d->mixer : NULL;
 
 	if (d == NULL || m == NULL)
 		return;
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index d32f83e3ae76..dfd5779d0dfb 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -925,7 +925,6 @@ dsp_ioctl(struct cdev *i_dev, unsigned long cmd, caddr_t arg, int mode,
 		{
 	    		snd_capabilities *p = (snd_capabilities *)arg;
 			struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
-			struct cdev *pdev;
 #ifdef COMPAT_FREEBSD32
 			snd_capabilities32 *p32 = (snd_capabilities32 *)arg;
 			snd_capabilities capabilities;
@@ -965,9 +964,8 @@ dsp_ioctl(struct cdev *i_dev, unsigned long cmd, caddr_t arg, int mode,
 				    (pcm_getflags(d->dev) & SD_F_SIMPLEX) ? 0 :
 				    AFMT_FULLDUPLEX;
 			}
-			pdev = d->mixer_dev;
 	    		p->mixers = 1; /* default: one mixer */
-	    		p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
+			p->inputs = d->mixer ? mix_getdevs(d->mixer) : 0;
 	    		p->left = p->right = 100;
 			if (wrch)
 				CHN_UNLOCK(wrch);
diff --git a/sys/dev/sound/pcm/feeder_volume.c b/sys/dev/sound/pcm/feeder_volume.c
index 5f40816b4065..a8e766c029d2 100644
--- a/sys/dev/sound/pcm/feeder_volume.c
+++ b/sys/dev/sound/pcm/feeder_volume.c
@@ -270,7 +270,7 @@ feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
 
 	/* Check if any controls are muted. */
 	d = (c != NULL) ? c->parentsnddev : NULL;
-	m = (d != NULL && d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 : NULL;
+	m = (d != NULL) ? d->mixer : NULL;
 
 	if (m != NULL)
 		master_muted = (mix_getmutedevs(m) & (1 << SND_VOL_C_MASTER));
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
index 74e8c2484c8a..59892b361b5f 100644
--- a/sys/dev/sound/pcm/mixer.c
+++ b/sys/dev/sound/pcm/mixer.c
@@ -75,14 +75,14 @@ static struct cdevsw mixer_cdevsw = {
 
 static eventhandler_tag mixer_ehtag = NULL;
 
-static struct cdev *
+static struct snd_mixer *
 mixer_get_devt(device_t dev)
 {
 	struct snddev_info *snddev;
 
 	snddev = device_get_softc(dev);
 
-	return snddev->mixer_dev;
+	return (snddev->mixer);
 }
 
 static int
@@ -616,7 +616,6 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
 	struct snddev_info *snddev;
 	struct snd_mixer *m;
 	uint16_t v;
-	struct cdev *pdev;
 	const char *name;
 	int i, unit, val;
 
@@ -646,10 +645,7 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
 
 	mixer_setrecsrc(m, 0); /* Set default input. */
 
-	pdev = make_dev(&mixer_cdevsw, 0, UID_ROOT, GID_AUDIO, 0660, "mixer%d",
-	    unit);
-	pdev->si_drv1 = m;
-	snddev->mixer_dev = pdev;
+	snddev->mixer = m;
 
 	if (bootverbose) {
 		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
@@ -685,20 +681,23 @@ mixer_uninit(device_t dev)
 	int i;
 	struct snddev_info *d;
 	struct snd_mixer *m;
-	struct cdev *pdev;
 
 	d = device_get_softc(dev);
-	pdev = mixer_get_devt(dev);
-	if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL)
+	if (d == NULL)
 		return EBADF;
+	m = d->mixer;
 
-	m = pdev->si_drv1;
 	KASSERT(m != NULL, ("NULL snd_mixer"));
 	KASSERT(m->type == MIXER_TYPE_PRIMARY,
 	    ("%s(): illegal mixer type=%d", __func__, m->type));
 
-	pdev->si_drv1 = NULL;
-	destroy_dev(pdev);
+	/*
+	 * snd_uaudio(4) in particular can call mixer_uninit() directly if
+	 * attach failed prior to pcm_register(), in which case the cdev will
+	 * not have been created. Do not call destroy_dev() unconditionally.
+	*/
+	if (d->mixer_dev != NULL)
+		destroy_dev(d->mixer_dev);
 
 	mtx_lock(&m->lock);
 
@@ -717,6 +716,7 @@ mixer_uninit(device_t dev)
 	kobj_delete((kobj_t)m, M_DEVBUF);
 
 	d->mixer_dev = NULL;
+	d->mixer = NULL;
 
 	return 0;
 }
@@ -725,11 +725,9 @@ int
 mixer_reinit(device_t dev)
 {
 	struct snd_mixer *m;
-	struct cdev *pdev;
 	int i;
 
-	pdev = mixer_get_devt(dev);
-	m = pdev->si_drv1;
+	m = mixer_get_devt(dev);
 	mtx_lock(&m->lock);
 
 	i = MIXER_REINIT(m);
@@ -751,6 +749,32 @@ mixer_reinit(device_t dev)
 	return 0;
 }
 
+int
+mixer_make_dev(device_t dev)
+{
+	struct make_dev_args devargs;
+	struct snddev_info *sc;
+	int err, unit;
+
+	sc = device_get_softc(dev);
+	unit = device_get_unit(dev);
+
+	make_dev_args_init(&devargs);
+	devargs.mda_devsw = &mixer_cdevsw;
+	devargs.mda_uid = UID_ROOT;
+	devargs.mda_gid = GID_AUDIO;
+	devargs.mda_mode = 0660;
+	devargs.mda_si_drv1 = sc->mixer;
+	err = make_dev_s(&devargs, &sc->mixer_dev, "mixer%d", unit);
+	if (err != 0) {
+		device_printf(dev, "failed to create mixer%d: error %d\n",
+		    unit, err);
+		return (err);
+	}
+
+	return (0);
+}
+
 static int
 sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
 {
@@ -781,10 +805,8 @@ int
 mixer_hwvol_init(device_t dev)
 {
 	struct snd_mixer *m;
-	struct cdev *pdev;
 
-	pdev = mixer_get_devt(dev);
-	m = pdev->si_drv1;
+	m = mixer_get_devt(dev);
 
 	m->hwvol_mixer = SOUND_MIXER_VOLUME;
 	m->hwvol_step = 5;
@@ -808,10 +830,8 @@ void
 mixer_hwvol_mute(device_t dev)
 {
 	struct snd_mixer *m;
-	struct cdev *pdev;
 
-	pdev = mixer_get_devt(dev);
-	m = pdev->si_drv1;
+	m = mixer_get_devt(dev);
 	mtx_lock(&m->lock);
 	mixer_hwvol_mute_locked(m);
 	mtx_unlock(&m->lock);
@@ -846,10 +866,8 @@ void
 mixer_hwvol_step(device_t dev, int left_step, int right_step)
 {
 	struct snd_mixer *m;
-	struct cdev *pdev;
 
-	pdev = mixer_get_devt(dev);
-	m = pdev->si_drv1;
+	m = mixer_get_devt(dev);
 	mtx_lock(&m->lock);
 	mixer_hwvol_step_locked(m, left_step, right_step);
 	mtx_unlock(&m->lock);
@@ -927,10 +945,9 @@ mixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
 	struct snddev_info *d;
 	struct snd_mixer *m;
 
-	if (i_dev == NULL || i_dev->si_drv1 == NULL)
-		return (EBADF);
-
 	m = i_dev->si_drv1;
+	if (m == NULL)
+		return (EBADF);
 	d = device_get_softc(m->dev);
 	if (!PCM_REGISTERED(d))
 		return (EBADF);
@@ -944,10 +961,9 @@ mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
 	struct snddev_info *d;
 	struct snd_mixer *m;
 
-	if (i_dev == NULL || i_dev->si_drv1 == NULL)
-		return (EBADF);
-
 	m = i_dev->si_drv1;
+	if (m == NULL)
+		return (EBADF);
 	d = device_get_softc(m->dev);
 	if (!PCM_REGISTERED(d))
 		return (EBADF);
@@ -960,12 +976,13 @@ mixer_ioctl(struct cdev *i_dev, unsigned long cmd, caddr_t arg, int mode,
     struct thread *td)
 {
 	struct snddev_info *d;
+	struct snd_mixer *m;
 	int ret;
 
-	if (i_dev == NULL || i_dev->si_drv1 == NULL)
+	m = i_dev->si_drv1;
+	if (m == NULL)
 		return (EBADF);
-
-	d = device_get_softc(((struct snd_mixer *)i_dev->si_drv1)->dev);
+	d = device_get_softc(m->dev);
 	if (!PCM_REGISTERED(d))
 		return (EBADF);
 
@@ -1235,14 +1252,14 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
 			continue;
 		}
 
-		if (d->mixer_dev->si_drv1 == NULL) {
+		if (d->mixer == NULL) {
 			mixer_oss_mixerinfo_unavail(mi, i);
 			PCM_UNLOCK(d);
 			bus_topo_unlock();
 			return (0);
 		}
 
-		m = d->mixer_dev->si_drv1;
+		m = d->mixer;
 		mtx_lock(&m->lock);
 
 		/*
diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h
index 5d6ae4d746af..fef086dec925 100644
--- a/sys/dev/sound/pcm/mixer.h
+++ b/sys/dev/sound/pcm/mixer.h
@@ -62,6 +62,7 @@ int mixer_delete(struct snd_mixer *m);
 int mixer_init(device_t dev, kobj_class_t cls, void *devinfo);
 int mixer_uninit(device_t dev);
 int mixer_reinit(device_t dev);
+int mixer_make_dev(device_t dev);
 int mixer_ioctl_cmd(struct cdev *i_dev, unsigned long cmd, caddr_t arg,
     int mode, struct thread *td);
 int mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi);
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index 101b38367873..aa4f1d97e243 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -332,7 +332,7 @@ sysctl_dev_pcm_mode(SYSCTL_HANDLER_ARGS)
 		mode |= PCM_MODE_PLAY;
 	if (d->reccount > 0)
 		mode |= PCM_MODE_REC;
-	if (d->mixer_dev != NULL)
+	if (d->mixer != NULL)
 		mode |= PCM_MODE_MIXER;
 	PCM_UNLOCK(d);
 
@@ -433,6 +433,13 @@ pcm_register(device_t dev, char *str)
 	err = dsp_make_dev(dev);
 	if (err)
 		return (err);
+	if (d->mixer != NULL) {
+		err = mixer_make_dev(dev);
+		if (err) {
+			dsp_destroy_dev(dev);
+			return (err);
+		}
+	}
 
 	bus_topo_lock();
 	if (snd_unit_auto < 0)
@@ -483,7 +490,8 @@ pcm_unregister(device_t dev)
 	}
 
 	sndstat_unregister(dev);
-	mixer_uninit(dev);
+	if (d->mixer != NULL)
+		mixer_uninit(dev);
 	dsp_destroy_dev(dev);
 
 	cv_destroy(&d->cv);
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index f156b557b251..5b9a12f948b8 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -191,6 +191,7 @@ struct snddev_info {
 	struct mtx lock;
 	struct cdev *mixer_dev;
 	struct cdev *dsp_dev;
+	struct snd_mixer *mixer;
 	uint32_t pvchanrate, pvchanformat, pvchanmode;
 	uint32_t rvchanrate, rvchanformat, rvchanmode;
 	int32_t eqpreamp;