git: 02d4eeabfd73 - main - sound: Allocate vchans on-demand
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 25 Feb 2025 11:48:14 UTC
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 ***