The branch stable/14 has been updated by christos:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=960ee8094913f1f96c6a432d84ae1f153b1a562c

commit 960ee8094913f1f96c6a432d84ae1f153b1a562c
Author:     Christos Margiolis <chris...@freebsd.org>
AuthorDate: 2025-02-25 11:43:39 +0000
Commit:     Christos Margiolis <chris...@freebsd.org>
CommitDate: 2025-03-04 15:46:05 +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
    
    (cherry picked from commit 02d4eeabfd73e6a827f5d42601e99aad92060b04)
---
 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 e406bd2c8343..ecaf732aad25 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
@@ -358,14 +358,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.
@@ -432,11 +430,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 b8f4efea8789..391c8e61dc19 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)
 {
@@ -303,7 +247,10 @@ pcm_setstatus(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);
@@ -516,6 +463,7 @@ pcm_register(device_t dev, void *devinfo, int numplay 
__unused,
        CHN_INIT(d, channels.pcm);
        CHN_INIT(d, channels.pcm.busy);
        CHN_INIT(d, channels.pcm.opened);
+       CHN_INIT(d, channels.pcm.primary);
 
        return (0);
 }
@@ -732,9 +680,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)
@@ -758,11 +704,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 f0dc454ac131..fddf5bfef1a8 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);
 int pcm_register(device_t dev, void *devinfo, int numplay, int numrec);
@@ -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;
-               }
*** 125 LINES SKIPPED ***

Reply via email to