Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 25 Feb 2025 11:48:14 GMT
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: 02d4eeabfd73 - main - sound: Allocate vchans on-demand
Message-ID:  <202502251148.51PBmEaZ050898@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by christos:

URL: https://cgit.FreeBSD.org/src/commit/?id=02d4eeabfd73e6a827f5d42601e99aad92060b04

commit 02d4eeabfd73e6a827f5d42601e99aad92060b04
Author:     Christos Margiolis <christos@FreeBSD.org>
AuthorDate: 2025-02-25 11:43:39 +0000
Commit:     Christos Margiolis <christos@FreeBSD.org>
CommitDate: 2025-02-25 11:43:39 +0000

    sound: Allocate vchans on-demand
    
    Refactor pcm_chnalloc() and merge with parts of vchan_setnew() (now
    removed) and dsp_open()’s channel creation into a new dsp_chn_alloc()
    function. The function is responsible for either using a free HW channel
    (if vchans are disabled), or allocating a new vchan.
    
    Clean up allocated vchans associated with a given dsp_cdevpriv on
    dsp_close() instead of leaving them unused.
    
    hw.snd.vchans_enable (previously hw.snd.maxautovchans) and
    dev.pcm.X.{play|rec}.vchans now work as tunables to only enable/disable
    vchans, as opposed to setting their number and/or (de-)allocating
    vchans. Since these sysctls do not trigger any (de-)allocations anymore,
    their effect is instantaneous, whereas before we could have frozen the
    machine (when trying to allocate new vchans) when setting
    dev.pcm.X.{play|rec}.vchans to a very large value.
    
    Create a new "primary" channel sublist so that we do not waste time
    looping through all channels in dsp_chn_alloc(), since we are only
    looking for a parent channel to either use, or add a new vchan to. This
    guarantees a steady traversal speed, as the parent channels are most
    likely going to be just a handful (2). What was currently in place was a
    loop through the whole channel list, which meant that the traversal
    would take longer the more channels were added to that list.
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Reviewed by:    dev_submerge.ch
    Differential Revision:  https://reviews.freebsd.org/D47917
---
 share/man/man4/pcm.4        |  23 ++--
 sys/dev/sound/pcm/channel.c |  14 ++-
 sys/dev/sound/pcm/channel.h |   3 +
 sys/dev/sound/pcm/dsp.c     | 233 ++++++++++++++++++++----------------
 sys/dev/sound/pcm/sound.c   |  70 ++---------
 sys/dev/sound/pcm/sound.h   |  13 +-
 sys/dev/sound/pcm/vchan.c   | 285 +++++++++++++-------------------------------
 sys/dev/sound/pcm/vchan.h   |   7 +-
 8 files changed, 254 insertions(+), 394 deletions(-)

diff --git a/share/man/man4/pcm.4 b/share/man/man4/pcm.4
index 769f562fe91e..e25ffe264ae8 100644
--- a/share/man/man4/pcm.4
+++ b/share/man/man4/pcm.4
@@ -23,7 +23,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 24, 2024
+.Dd December 4, 2024
 .Dt SOUND 4
 .Os
 .Sh NAME
@@ -360,14 +360,12 @@ A value of 0 will use a low and aggressive latency profile which can result
 in possible underruns if the application cannot keep up with a rapid irq
 rate, especially during high workload.
 The default value is 1, which is considered a moderate/safe latency profile.
-.It Va hw.snd.maxautovchans
-Global VCHAN setting that only affects devices with at least one playback or
-recording channel available.
-The sound system will dynamically create up to this many VCHANs.
-Set to
-.Dq 0
-if no VCHANs are desired.
-Maximum value is 256.
+.It Va hw.snd.vchans_enable
+Global VCHAN setting to enable (1) or disable (0) VCHANs.
+This setting can be overridden for an individual device by using the
+.Va dev.pcm.%d.[play|rec].vchans
+tunables.
+Default is enabled.
 .It Va hw.snd.report_soft_formats
 Controls the internal format conversion if it is
 available transparently to the application software.
@@ -434,11 +432,8 @@ The recommended way to use bitperfect mode is to disable VCHANs and enable this
 sysctl.
 Default is disabled.
 .It Va dev.pcm.%d.[play|rec].vchans
-The current number of VCHANs allocated per device.
-This can be set to preallocate a certain number of VCHANs.
-Setting this value to
-.Dq 0
-will disable VCHANs for this device.
+Enable (1) or disable (0) VCHANs.
+Default is enabled.
 .It Va dev.pcm.%d.[play|rec].vchanformat
 Format for VCHAN mixing.
 All playback paths will be converted to this format before the mixing
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 58315610312e..17c11dc33b7a 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -1172,7 +1172,7 @@ chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls,
 	struct feeder_class *fc;
 	struct snd_dbuf *b, *bs;
 	char buf[CHN_NAMELEN];
-	int i, direction;
+	int err, i, direction;
 
 	PCM_BUSYASSERT(d);
 	PCM_LOCKASSERT(d);
@@ -1279,8 +1279,18 @@ chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls,
 		bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_WAITOK);
 	}
 
+	if ((c->flags & CHN_F_VIRTUAL) == 0) {
+		CHN_LOCK(c);
+		err = chn_reset(c, c->format, c->speed);
+		CHN_UNLOCK(c);
+		if (err != 0)
+			goto fail;
+	}
+
 	PCM_LOCK(d);
 	CHN_INSERT_SORT_ASCEND(d, c, channels.pcm);
+	if ((c->flags & CHN_F_VIRTUAL) == 0)
+		CHN_INSERT_SORT_ASCEND(d, c, channels.pcm.primary);
 
 	switch (c->type) {
 	case PCMDIR_PLAY:
@@ -1332,6 +1342,8 @@ chn_kill(struct pcm_channel *c)
 
 	PCM_LOCK(d);
 	CHN_REMOVE(d, c, channels.pcm);
+	if ((c->flags & CHN_F_VIRTUAL) == 0)
+		CHN_REMOVE(d, c, channels.pcm.primary);
 
 	switch (c->type) {
 	case PCMDIR_PLAY:
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
index 6eaad8cc2c0b..31c617a6df78 100644
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -160,6 +160,9 @@ struct pcm_channel {
 			struct {
 				SLIST_ENTRY(pcm_channel) link;
 			} opened;
+			struct {
+				SLIST_ENTRY(pcm_channel) link;
+			} primary;
 		} pcm;
 	} channels;
 
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index dcbdd581c82a..88e0580c5c45 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -37,6 +37,7 @@
 #endif
 
 #include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/vchan.h>
 #include <sys/ctype.h>
 #include <sys/lock.h>
 #include <sys/rwlock.h>
@@ -158,6 +159,81 @@ dsp_unlock_chans(struct dsp_cdevpriv *priv, uint32_t prio)
 		CHN_UNLOCK(priv->wrch);
 }
 
+static int
+dsp_chn_alloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
+    int flags, struct thread *td)
+{
+	struct pcm_channel *c;
+	char *comm;
+	pid_t pid;
+	int err;
+	bool vdir_enabled;
+
+	KASSERT(d != NULL && ch != NULL &&
+	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
+	    ("%s(): invalid d=%p ch=%p direction=%d",
+	    __func__, d, ch, direction));
+	PCM_BUSYASSERT(d);
+
+	pid = td->td_proc->p_pid;
+	comm = td->td_proc->p_comm;
+
+	vdir_enabled = (direction == PCMDIR_PLAY && d->flags & SD_F_PVCHANS) ||
+	    (direction == PCMDIR_REC && d->flags & SD_F_RVCHANS);
+
+	*ch = NULL;
+	CHN_FOREACH(c, d, channels.pcm.primary) {
+		CHN_LOCK(c);
+		if (c->direction != direction) {
+			CHN_UNLOCK(c);
+			continue;
+		}
+		/* Find an available primary channel to use. */
+		if ((c->flags & CHN_F_BUSY) == 0 ||
+		    (vdir_enabled && (c->flags & CHN_F_HAS_VCHAN)))
+			break;
+		CHN_UNLOCK(c);
+	}
+	if (c == NULL)
+		return (EBUSY);
+
+	/*
+	 * We can have the following cases:
+	 * - vchans are enabled, add a new vchan to the primary channel.
+	 * - vchans are disabled, use the primary channel directly.
+	 */
+	if (vdir_enabled && ((c->flags & CHN_F_BUSY) == 0 ||
+	    c->flags & CHN_F_HAS_VCHAN)) {
+		err = vchan_create(c, ch);
+		CHN_UNLOCK(c);
+		if (err != 0)
+			return (err);
+		CHN_LOCK(*ch);
+	} else if ((c->flags & CHN_F_BUSY) == 0) {
+		*ch = c;
+	} else {
+		CHN_UNLOCK(c);
+		return (ENODEV);
+	}
+
+	(*ch)->flags |= CHN_F_BUSY;
+	if (flags & O_NONBLOCK)
+		(*ch)->flags |= CHN_F_NBIO;
+	if (flags & O_EXCL)
+		(*ch)->flags |= CHN_F_EXCLUSIVE;
+	(*ch)->pid = pid;
+	strlcpy((*ch)->comm, (comm != NULL) ? comm : CHN_COMM_UNKNOWN,
+	    sizeof((*ch)->comm));
+
+	if ((err = chn_reset(*ch, (*ch)->format, (*ch)->speed)) != 0)
+		return (err);
+	chn_vpc_reset(*ch, SND_VOL_C_PCM, 0);
+
+	CHN_UNLOCK(*ch);
+
+	return (0);
+}
+
 #define DSP_F_VALID(x)		((x) & (FREAD | FWRITE))
 #define DSP_F_DUPLEX(x)		(((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
 #define DSP_F_SIMPLEX(x)	(!DSP_F_DUPLEX(x))
@@ -168,7 +244,7 @@ static void
 dsp_close(void *data)
 {
 	struct dsp_cdevpriv *priv = data;
-	struct pcm_channel *rdch, *wrch;
+	struct pcm_channel *rdch, *wrch, *parent;
 	struct snddev_info *d;
 	int sg_ids;
 
@@ -214,12 +290,20 @@ dsp_close(void *data)
 			if (sg_ids != 0)
 				free_unr(pcmsg_unrhdr, sg_ids);
 
-			CHN_LOCK(rdch);
-			chn_abort(rdch); /* won't sleep */
-			rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
-			    CHN_F_DEAD | CHN_F_EXCLUSIVE);
-			chn_reset(rdch, 0, 0);
-			chn_release(rdch);
+			if (rdch->flags & CHN_F_VIRTUAL) {
+				parent = rdch->parentchannel;
+				CHN_LOCK(parent);
+				CHN_LOCK(rdch);
+				vchan_destroy(rdch);
+				CHN_UNLOCK(parent);
+			} else {
+				CHN_LOCK(rdch);
+				chn_abort(rdch); /* won't sleep */
+				rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
+				    CHN_F_DEAD | CHN_F_EXCLUSIVE);
+				chn_reset(rdch, 0, 0);
+				chn_release(rdch);
+			}
 		}
 		if (wrch != NULL) {
 			/*
@@ -231,12 +315,20 @@ dsp_close(void *data)
 			if (sg_ids != 0)
 				free_unr(pcmsg_unrhdr, sg_ids);
 
-			CHN_LOCK(wrch);
-			chn_flush(wrch); /* may sleep */
-			wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
-			    CHN_F_DEAD | CHN_F_EXCLUSIVE);
-			chn_reset(wrch, 0, 0);
-			chn_release(wrch);
+			if (wrch->flags & CHN_F_VIRTUAL) {
+				parent = wrch->parentchannel;
+				CHN_LOCK(parent);
+				CHN_LOCK(wrch);
+				vchan_destroy(wrch);
+				CHN_UNLOCK(parent);
+			} else {
+				CHN_LOCK(wrch);
+				chn_flush(wrch); /* may sleep */
+				wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
+				    CHN_F_DEAD | CHN_F_EXCLUSIVE);
+				chn_reset(wrch, 0, 0);
+				chn_release(wrch);
+			}
 		}
 		PCM_LOCK(d);
 	}
@@ -254,10 +346,9 @@ static int
 dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
 {
 	struct dsp_cdevpriv *priv;
-	struct pcm_channel *rdch, *wrch, *ch;
+	struct pcm_channel *ch;
 	struct snddev_info *d;
-	uint32_t fmt, spd;
-	int error, rderror, wrerror, dir;
+	int error, dir;
 
 	/* Kind of impossible.. */
 	if (i_dev == NULL || td == NULL)
@@ -267,11 +358,11 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
 	if (!DSP_REGISTERED(d))
 		return (EBADF);
 
+	if (PCM_CHANCOUNT(d) >= PCM_MAXCHANS)
+		return (ENOMEM);
+
 	priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK | M_ZERO);
 	priv->sc = d;
-	priv->rdch = NULL;
-	priv->wrch = NULL;
-	priv->volch = NULL;
 
 	error = devfs_set_cdevpriv(priv, dsp_close);
 	if (error != 0)
@@ -334,98 +425,30 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
 	PCM_ACQUIRE(d);
 	PCM_UNLOCK(d);
 
-	fmt = SND_FORMAT(AFMT_U8, 1, 0);
-	spd = DSP_DEFAULT_SPEED;
-
-	rdch = NULL;
-	wrch = NULL;
-	rderror = 0;
-	wrerror = 0;
-
-	if (DSP_F_READ(flags)) {
-		/* open for read */
-		rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC,
-		    td->td_proc->p_pid, td->td_proc->p_comm);
-
-		if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0)
-			rderror = ENXIO;
-
-		if (rderror != 0) {
-			if (rdch != NULL)
-				chn_release(rdch);
-			if (!DSP_F_DUPLEX(flags)) {
-				PCM_RELEASE_QUICK(d);
-				PCM_GIANT_EXIT(d);
-				return (rderror);
-			}
-			rdch = NULL;
-		} else {
-			if (flags & O_NONBLOCK)
-				rdch->flags |= CHN_F_NBIO;
-			if (flags & O_EXCL)
-				rdch->flags |= CHN_F_EXCLUSIVE;
-			chn_vpc_reset(rdch, SND_VOL_C_PCM, 0);
-		 	CHN_UNLOCK(rdch);
-		}
-	}
-
 	if (DSP_F_WRITE(flags)) {
-		/* open for write */
-		wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY,
-		    td->td_proc->p_pid, td->td_proc->p_comm);
-
-		if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0)
-			wrerror = ENXIO;
-
-		if (wrerror != 0) {
-			if (wrch != NULL)
-				chn_release(wrch);
-			if (!DSP_F_DUPLEX(flags)) {
-				if (rdch != NULL) {
-					/*
-					 * Lock, and release previously created
-					 * record channel
-					 */
-					CHN_LOCK(rdch);
-					chn_release(rdch);
-				}
-				PCM_RELEASE_QUICK(d);
-				PCM_GIANT_EXIT(d);
-				return (wrerror);
-			}
-			wrch = NULL;
-		} else {
-			if (flags & O_NONBLOCK)
-				wrch->flags |= CHN_F_NBIO;
-			if (flags & O_EXCL)
-				wrch->flags |= CHN_F_EXCLUSIVE;
-			chn_vpc_reset(wrch, SND_VOL_C_PCM, 0);
-			CHN_UNLOCK(wrch);
+		error = dsp_chn_alloc(d, &priv->wrch, PCMDIR_PLAY, flags, td);
+		if (error != 0) {
+			PCM_RELEASE_QUICK(d);
+			PCM_GIANT_EXIT(d);
+			return (error);
 		}
+		PCM_LOCK(d);
+		CHN_INSERT_HEAD(d, priv->wrch, channels.pcm.opened);
+		PCM_UNLOCK(d);
 	}
-
-	PCM_LOCK(d);
-
-	if (wrch == NULL && rdch == NULL) {
-		PCM_RELEASE(d);
+	if (DSP_F_READ(flags)) {
+		error = dsp_chn_alloc(d, &priv->rdch, PCMDIR_REC, flags, td);
+		if (error != 0) {
+			PCM_RELEASE_QUICK(d);
+			PCM_GIANT_EXIT(d);
+			return (error);
+		}
+		PCM_LOCK(d);
+		CHN_INSERT_HEAD(d, priv->rdch, channels.pcm.opened);
 		PCM_UNLOCK(d);
-		PCM_GIANT_EXIT(d);
-		if (wrerror != 0)
-			return (wrerror);
-		if (rderror != 0)
-			return (rderror);
-		return (EINVAL);
 	}
-	if (rdch != NULL)
-		CHN_INSERT_HEAD(d, rdch, channels.pcm.opened);
-	if (wrch != NULL)
-		CHN_INSERT_HEAD(d, wrch, channels.pcm.opened);
-	priv->rdch = rdch;
-	priv->wrch = wrch;
-
-	PCM_RELEASE(d);
-	PCM_UNLOCK(d);
 
+	PCM_RELEASE_QUICK(d);
 	PCM_GIANT_LEAVE(d);
 
 	return (0);
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index 2d57852e036d..c0934dde7718 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -107,62 +107,6 @@ 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);
 }
 
-int
-pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
-    pid_t pid, char *comm)
-{
-	struct pcm_channel *c;
-	int err, vchancount;
-	bool retry;
-
-	KASSERT(d != NULL && ch != NULL &&
-	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
-	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d",
-	    __func__, d, ch, direction, pid));
-	PCM_BUSYASSERT(d);
-
-	*ch = NULL;
-	vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
-	    d->rvchancount;
-	retry = false;
-
-retry_chnalloc:
-	/* Scan for a free channel. */
-	CHN_FOREACH(c, d, channels.pcm) {
-		CHN_LOCK(c);
-		if (c->direction != direction) {
-			CHN_UNLOCK(c);
-			continue;
-		}
-		if (!(c->flags & CHN_F_BUSY)) {
-			c->flags |= CHN_F_BUSY;
-			c->pid = pid;
-			strlcpy(c->comm, (comm != NULL) ? comm :
-			    CHN_COMM_UNKNOWN, sizeof(c->comm));
-			*ch = c;
-
-			return (0);
-		}
-		CHN_UNLOCK(c);
-	}
-	/* Maybe next time... */
-	if (retry)
-		return (EBUSY);
-
-	/* No channel available. We also cannot create more VCHANs. */
-	if (!(vchancount > 0 && vchancount < snd_maxautovchans))
-		return (ENOTSUP);
-
-	/* Increase the VCHAN count and try to get the new channel. */
-	err = vchan_setnew(d, direction, vchancount + 1);
-	if (err == 0) {
-		retry = true;
-		goto retry_chnalloc;
-	}
-
-	return (err);
-}
-
 static int
 sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
 {
@@ -472,6 +416,7 @@ pcm_init(device_t dev, void *devinfo)
 	CHN_INIT(d, channels.pcm);
 	CHN_INIT(d, channels.pcm.busy);
 	CHN_INIT(d, channels.pcm.opened);
+	CHN_INIT(d, channels.pcm.primary);
 }
 
 int
@@ -491,7 +436,10 @@ pcm_register(device_t dev, char *str)
 	if (d->playcount > 0 || d->reccount > 0)
 		d->flags |= SD_F_AUTOVCHAN;
 
-	vchan_setmaxauto(d, snd_maxautovchans);
+	if (d->playcount > 0)
+		d->flags |= SD_F_PVCHANS;
+	if (d->reccount > 0)
+		d->flags |= SD_F_RVCHANS;
 
 	strlcpy(d->status, str, SND_STATUSLEN);
 	sndstat_register(dev, d->status);
@@ -733,9 +681,7 @@ sound_global_init(void)
 	if (snd_unit < 0)
 		snd_unit = -1;
 
-	if (snd_maxautovchans < 0 ||
-	    snd_maxautovchans > SND_MAXVCHANS)
-		snd_maxautovchans = 0;
+	snd_vchans_enable = true;
 
 	if (chn_latency < CHN_LATENCY_MIN ||
 	    chn_latency > CHN_LATENCY_MAX)
@@ -759,11 +705,11 @@ sound_global_init(void)
 		feeder_rate_round = FEEDRATE_ROUNDHZ;
 
 	if (bootverbose)
-		printf("%s: snd_unit=%d snd_maxautovchans=%d "
+		printf("%s: snd_unit=%d snd_vchans_enable=%d "
 		    "latency=%d "
 		    "feeder_rate_min=%d feeder_rate_max=%d "
 		    "feeder_rate_round=%d\n",
-		    __func__, snd_unit, snd_maxautovchans,
+		    __func__, snd_unit, snd_vchans_enable,
 		    chn_latency,
 		    feeder_rate_min, feeder_rate_max,
 		    feeder_rate_round);
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index 019fe67ac892..0452a58dfcbf 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -99,8 +99,6 @@ struct snd_mixer;
 #define SOUND_PREFVER	SOUND_MODVER
 #define SOUND_MAXVER	SOUND_MODVER
 
-#define SND_MAXVCHANS		256
-
 #define SD_F_SIMPLEX		0x00000001
 #define SD_F_AUTOVCHAN		0x00000002
 #define SD_F_SOFTPCMVOL		0x00000004
@@ -113,6 +111,8 @@ struct snd_mixer;
 #define SD_F_EQ_ENABLED		0x00000200	/* EQ enabled */
 #define SD_F_EQ_BYPASSED	0x00000400	/* EQ bypassed */
 #define SD_F_EQ_PC		0x00000800	/* EQ per-channel */
+#define SD_F_PVCHANS		0x00001000	/* Playback vchans enabled */
+#define SD_F_RVCHANS		0x00002000	/* Recording vchans enabled */
 
 #define SD_F_EQ_DEFAULT		(SD_F_EQ | SD_F_EQ_ENABLED)
 #define SD_F_EQ_MASK		(SD_F_EQ | SD_F_EQ_ENABLED |		\
@@ -134,12 +134,15 @@ struct snd_mixer;
 				"\012EQ_ENABLED"			\
 				"\013EQ_BYPASSED"			\
 				"\014EQ_PC"				\
+				"\015PVCHANS"				\
+				"\016RVCHANS"				\
 				"\035PRIO_RD"				\
 				"\036PRIO_WR"
 
 #define PCM_ALIVE(x)		((x) != NULL && (x)->lock != NULL)
 #define PCM_REGISTERED(x)	(PCM_ALIVE(x) && ((x)->flags & SD_F_REGISTERED))
 
+#define	PCM_MAXCHANS		10000
 #define	PCM_CHANCOUNT(d)	\
 	(d->playcount + d->pvchancount + d->reccount + d->rvchancount)
 
@@ -168,9 +171,6 @@ extern struct unrhdr *pcmsg_unrhdr;
 
 SYSCTL_DECL(_hw_snd);
 
-int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
-    pid_t pid, char *comm);
-
 int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo);
 unsigned int pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz);
 void pcm_init(device_t dev, void *devinfo);
@@ -224,6 +224,9 @@ struct snddev_info {
 			struct {
 				SLIST_HEAD(, pcm_channel) head;
 			} opened;
+			struct {
+				SLIST_HEAD(, pcm_channel) head;
+			} primary;
 		} pcm;
 	} channels;
 	unsigned playcount, reccount, pvchancount, rvchancount;
diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c
index b0caec3acfec..297120199fe7 100644
--- a/sys/dev/sound/pcm/vchan.c
+++ b/sys/dev/sound/pcm/vchan.c
@@ -61,7 +61,7 @@ struct vchan_info {
 	int trigger;
 };
 
-int snd_maxautovchans = 16;
+bool snd_vchans_enable = true;
 
 static void *
 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
@@ -277,7 +277,7 @@ vchan_getparentchannel(struct snddev_info *d,
 				*ch = NULL;
 				break;
 			}
-		} else if (c->flags & CHN_F_HAS_VCHAN) {
+		} else {
 			/* No way!! */
 			if (*ch != NULL) {
 				CHN_UNLOCK(c);
@@ -299,8 +299,7 @@ static int
 sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
 {
 	struct snddev_info *d;
-	int direction, vchancount;
-	int err, cnt;
+	int err, enabled, flag;
 
 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
@@ -311,43 +310,44 @@ sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
 
 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
 	case VCHAN_PLAY:
-		direction = PCMDIR_PLAY;
-		vchancount = d->pvchancount;
-		cnt = d->playcount;
+		/* Exit if we do not support this direction. */
+		if (d->playcount < 1) {
+			PCM_UNLOCK(d);
+			return (ENODEV);
+		}
+		flag = SD_F_PVCHANS;
 		break;
 	case VCHAN_REC:
-		direction = PCMDIR_REC;
-		vchancount = d->rvchancount;
-		cnt = d->reccount;
+		if (d->reccount < 1) {
+			PCM_UNLOCK(d);
+			return (ENODEV);
+		}
+		flag = SD_F_RVCHANS;
 		break;
 	default:
 		PCM_UNLOCK(d);
 		return (EINVAL);
-		break;
 	}
 
-	if (cnt < 1) {
-		PCM_UNLOCK(d);
-		return (ENODEV);
-	}
+	enabled = (d->flags & flag) != 0;
 
 	PCM_ACQUIRE(d);
 	PCM_UNLOCK(d);
 
-	cnt = vchancount;
-	err = sysctl_handle_int(oidp, &cnt, 0, req);
-
-	if (err == 0 && req->newptr != NULL && vchancount != cnt) {
-		if (cnt < 0)
-			cnt = 0;
-		if (cnt > SND_MAXVCHANS)
-			cnt = SND_MAXVCHANS;
-		err = vchan_setnew(d, direction, cnt);
+	err = sysctl_handle_int(oidp, &enabled, 0, req);
+	if (err != 0 || req->newptr == NULL) {
+		PCM_RELEASE_QUICK(d);
+		return (err);
 	}
 
+	if (enabled <= 0)
+		d->flags &= ~flag;
+	else
+		d->flags |= flag;
+
 	PCM_RELEASE_QUICK(d);
 
-	return err;
+	return (0);
 }
 
 static int
@@ -368,15 +368,22 @@ sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
 
 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
 	case VCHAN_PLAY:
+		if ((d->flags & SD_F_PVCHANS) == 0) {
+			PCM_UNLOCK(d);
+			return (ENODEV);
+		}
 		direction = PCMDIR_PLAY;
 		break;
 	case VCHAN_REC:
+		if ((d->flags & SD_F_RVCHANS) == 0) {
+			PCM_UNLOCK(d);
+			return (ENODEV);
+		}
 		direction = PCMDIR_REC;
 		break;
 	default:
 		PCM_UNLOCK(d);
 		return (EINVAL);
-		break;
 	}
 
 	PCM_ACQUIRE(d);
@@ -450,7 +457,7 @@ sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
 	struct snddev_info *d;
 	struct pcm_channel *c, *ch;
 	struct pcmchan_caps *caps;
-	int *vchanrate, vchancount, direction, ret, newspd, restart;
+	int *vchanrate, direction, ret, newspd, restart;
 
 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
@@ -461,24 +468,24 @@ sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
 
 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
 	case VCHAN_PLAY:
+		if ((d->flags & SD_F_PVCHANS) == 0) {
+			PCM_UNLOCK(d);
+			return (ENODEV);
+		}
 		direction = PCMDIR_PLAY;
-		vchancount = d->pvchancount;
 		vchanrate = &d->pvchanrate;
 		break;
 	case VCHAN_REC:
+		if ((d->flags & SD_F_RVCHANS) == 0) {
+			PCM_UNLOCK(d);
+			return (ENODEV);
+		}
 		direction = PCMDIR_REC;
-		vchancount = d->rvchancount;
 		vchanrate = &d->rvchanrate;
 		break;
 	default:
 		PCM_UNLOCK(d);
 		return (EINVAL);
-		break;
-	}
-
-	if (vchancount < 1) {
-		PCM_UNLOCK(d);
-		return (EINVAL);
 	}
 
 	PCM_ACQUIRE(d);
@@ -555,7 +562,7 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
 	struct snddev_info *d;
 	struct pcm_channel *c, *ch;
 	uint32_t newfmt;
-	int *vchanformat, vchancount, direction, ret, restart;
+	int *vchanformat, direction, ret, restart;
 	char fmtstr[AFMTSTR_LEN];
 
 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
@@ -567,24 +574,24 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
 
 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
 	case VCHAN_PLAY:
+		if ((d->flags & SD_F_PVCHANS) == 0) {
+			PCM_UNLOCK(d);
+			return (ENODEV);
+		}
 		direction = PCMDIR_PLAY;
-		vchancount = d->pvchancount;
 		vchanformat = &d->pvchanformat;
 		break;
 	case VCHAN_REC:
+		if ((d->flags & SD_F_RVCHANS) == 0) {
+			PCM_UNLOCK(d);
+			return (ENODEV);
+		}
 		direction = PCMDIR_REC;
-		vchancount = d->rvchancount;
 		vchanformat = &d->rvchanformat;
 		break;
 	default:
 		PCM_UNLOCK(d);
 		return (EINVAL);
-		break;
-	}
-
-	if (vchancount < 1) {
-		PCM_UNLOCK(d);
-		return (EINVAL);
 	}
 
 	PCM_ACQUIRE(d);
@@ -660,7 +667,7 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
 				"play.vchanrate" : "rec.vchanrate"
 
 int
-vchan_create(struct pcm_channel *parent)
+vchan_create(struct pcm_channel *parent, struct pcm_channel **child)
 {
 	struct snddev_info *d;
 	struct pcm_channel *ch;
@@ -676,9 +683,6 @@ vchan_create(struct pcm_channel *parent)
 	PCM_BUSYASSERT(d);
 	CHN_LOCKASSERT(parent);
 
-	if (!(parent->flags & CHN_F_BUSY))
-		return (EBUSY);
-
 	if (!(parent->direction == PCMDIR_PLAY ||
 	    parent->direction == PCMDIR_REC))
 		return (EINVAL);
@@ -713,10 +717,12 @@ vchan_create(struct pcm_channel *parent)
 	 */
 	CHN_INSERT_SORT_DESCEND(parent, ch, children);
 
+	*child = ch;
+
 	if (parent->flags & CHN_F_HAS_VCHAN)
 		return (0);
 
-	parent->flags |= CHN_F_HAS_VCHAN;
+	parent->flags |= CHN_F_HAS_VCHAN | CHN_F_BUSY;
 
 	parent_caps = chn_getcaps(parent);
 	if (parent_caps == NULL) {
@@ -807,6 +813,7 @@ vchan_create(struct pcm_channel *parent)
 fail:
 	CHN_LOCK(ch);
 	vchan_destroy(ch);
+	*child = NULL;
 
 	return (ret);
 }
@@ -878,166 +885,40 @@ vchan_sync(struct pcm_channel *c)
 	return (ret);
 }
 
-int
-vchan_setnew(struct snddev_info *d, int direction, int newcnt)
-{
-	struct pcm_channel *c, *ch, *nch;
-	struct pcmchan_caps *caps;
-	int i, err, vcnt;
-
-	PCM_BUSYASSERT(d);
-
-	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
-	    (direction == PCMDIR_REC && d->reccount < 1))
-		return (ENODEV);
-
-	if (!(d->flags & SD_F_AUTOVCHAN))
-		return (EINVAL);
-
-	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
-		return (E2BIG);
-
-	if (direction == PCMDIR_PLAY)
-		vcnt = d->pvchancount;
-	else if (direction == PCMDIR_REC)
-		vcnt = d->rvchancount;
-	else
-		return (EINVAL);
-
-	if (newcnt > vcnt) {
-		/* add new vchans - find a parent channel first */
-		ch = NULL;
-		CHN_FOREACH(c, d, channels.pcm) {
-			CHN_LOCK(c);
-			if (c->direction == direction &&
-			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
-			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
-				/*
-				 * Reuse hw channel with vchans already
-				 * created.
-				 */
-				if (c->flags & CHN_F_HAS_VCHAN) {
-					ch = c;
-					break;
-				}
-				/*
-				 * No vchans ever created, look for
-				 * channels with supported formats.
-				 */
-				caps = chn_getcaps(c);
-				if (caps == NULL) {
-					CHN_UNLOCK(c);
-					continue;
-				}
-				for (i = 0; caps->fmtlist[i] != 0; i++) {
-					if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
-						break;
-				}
-				if (caps->fmtlist[i] != 0) {
-					ch = c;
-					break;
-				}
-			}
-			CHN_UNLOCK(c);
-		}
-		if (ch == NULL)
-			return (EBUSY);
-		ch->flags |= CHN_F_BUSY;
-		err = 0;
-		while (err == 0 && newcnt > vcnt) {
-			err = vchan_create(ch);
-			if (err == 0)
-				vcnt++;
-			else if (err == E2BIG && newcnt > vcnt)
-				device_printf(d->dev,
-				    "%s: err=%d Maximum channel reached.\n",
-				    __func__, err);
-		}
-		if (vcnt == 0)
-			ch->flags &= ~CHN_F_BUSY;
-		CHN_UNLOCK(ch);
-		if (err != 0)
-			return (err);
-	} else if (newcnt < vcnt) {
-		CHN_FOREACH(c, d, channels.pcm) {
-			CHN_LOCK(c);
-			if (c->direction != direction ||
-			    CHN_EMPTY(c, children) ||
-			    !(c->flags & CHN_F_HAS_VCHAN)) {
-				CHN_UNLOCK(c);
-				continue;
-			}
-			CHN_FOREACH_SAFE(ch, c, nch, children) {
-				CHN_LOCK(ch);
-				if (vcnt == 1 && ch->flags & CHN_F_BUSY) {
-					CHN_UNLOCK(ch);
-					break;
-				}
-				if (!(ch->flags & CHN_F_BUSY)) {
-					err = vchan_destroy(ch);
-					if (err == 0)
-						vcnt--;
-				} else
-					CHN_UNLOCK(ch);
-				if (vcnt == newcnt)
-					break;
-			}
-			CHN_UNLOCK(c);
-			break;
-		}
-	}
-
-	return (0);
*** 122 LINES SKIPPED ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202502251148.51PBmEaZ050898>