Page MenuHomeFreeBSD

D47917.id147538.diff
No OneTemporary

D47917.id147538.diff

diff --git a/share/man/man4/pcm.4 b/share/man/man4/pcm.4
--- 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 @@
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 overriden 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 @@
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.h b/sys/dev/sound/pcm/channel.h
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -162,6 +162,9 @@
struct {
SLIST_ENTRY(pcm_channel) link;
} opened;
+ struct {
+ SLIST_ENTRY(pcm_channel) link;
+ } primary;
} pcm;
} channels;
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -1169,7 +1169,7 @@
struct feeder_class *fc;
struct snd_dbuf *b, *bs;
char buf[CHN_NAMELEN];
- int i, direction;
+ int err, i, direction, fmt, spd;
PCM_BUSYASSERT(d);
PCM_LOCKASSERT(d);
@@ -1178,10 +1178,14 @@
case PCMDIR_PLAY:
case PCMDIR_PLAY_VIRTUAL:
direction = PCMDIR_PLAY;
+ fmt = d->pvchanformat;
+ spd = d->pvchanrate;
break;
case PCMDIR_REC:
case PCMDIR_REC_VIRTUAL:
direction = PCMDIR_REC;
+ fmt = d->rvchanformat;
+ spd = d->rvchanrate;
break;
default:
device_printf(d->dev,
@@ -1202,8 +1206,8 @@
c->direction = direction;
c->type = dir;
c->unit = alloc_unr(chn_getunr(d, c->type));
- c->format = SND_FORMAT(AFMT_U8, 1, 0);
- c->speed = DSP_DEFAULT_SPEED;
+ c->format = fmt;
+ c->speed = spd;
c->pid = -1;
c->latency = -1;
c->timeout = 1;
@@ -1276,8 +1280,19 @@
bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_WAITOK);
}
+ /* vchans get a chn_reset() from vchan_create(). */
+ 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:
@@ -1329,6 +1344,8 @@
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/dsp.c b/sys/dev/sound/pcm/dsp.c
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -145,6 +145,78 @@
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, fmt, spd;
+
+ 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);
+
+ fmt = SND_FORMAT(AFMT_U8, 1, 0);
+ spd = DSP_DEFAULT_SPEED;
+ pid = td->td_proc->p_pid;
+ comm = td->td_proc->p_comm;
+
+ *ch = NULL;
+ CHN_FOREACH(c, d, channels.pcm.primary) {
+ CHN_LOCK(c);
+ if (c->direction != direction) {
+ CHN_UNLOCK(c);
+ continue;
+ }
+ /*
+ * Find a primary channel to use. We can have the following cases:
+ * - vchans are enabled, find a primary channel and use it to
+ * add a new vchan to.
+ * - vchans are disabled, use the first free primary channel
+ * itself.
+ */
+ if ((direction == PCMDIR_PLAY && d->flags & SD_F_PVCHANS) ||
+ (direction == PCMDIR_REC && d->flags & SD_F_RVCHANS) ||
+ (c->flags & CHN_F_BUSY) == 0)
+ break;
+ CHN_UNLOCK(c);
+ }
+ if (c == NULL)
+ return (EBUSY);
+
+ if ((direction == PCMDIR_PLAY && d->flags & SD_F_PVCHANS) ||
+ (direction == PCMDIR_REC && d->flags & SD_F_RVCHANS)) {
+ err = vchan_create(c, ch);
+ CHN_UNLOCK(c);
+ if (err != 0)
+ return (err);
+ CHN_LOCK(*ch);
+ } else {
+ *ch = c;
+ }
+
+ (*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, fmt, spd)) != 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))
@@ -155,7 +227,7 @@
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;
@@ -201,12 +273,20 @@
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) {
/*
@@ -218,12 +298,20 @@
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);
}
@@ -241,10 +329,9 @@
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)
@@ -254,6 +341,9 @@
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;
@@ -321,94 +411,21 @@
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);
- }
+ if ((DSP_F_WRITE(flags) && (error = dsp_chn_alloc(d, &priv->wrch,
+ PCMDIR_PLAY, flags, td)) != 0) ||
+ (DSP_F_READ(flags) && (error = dsp_chn_alloc(d, &priv->rdch,
+ PCMDIR_REC, flags, td)) != 0)) {
+ PCM_RELEASE_QUICK(d);
+ PCM_GIANT_EXIT(d);
+ return (error);
}
PCM_LOCK(d);
- if (wrch == NULL && rdch == NULL) {
- PCM_RELEASE(d);
- 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;
+ if (priv->rdch != NULL)
+ CHN_INSERT_HEAD(d, priv->rdch, channels.pcm.opened);
+ if (priv->wrch != NULL)
+ CHN_INSERT_HEAD(d, priv->wrch, channels.pcm.opened);
PCM_RELEASE(d);
PCM_UNLOCK(d);
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -112,8 +112,6 @@
#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
@@ -126,6 +124,8 @@
#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 | \
@@ -147,12 +147,15 @@
"\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)
@@ -247,9 +250,6 @@
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);
@@ -303,6 +303,9 @@
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/sound.c b/sys/dev/sound/pcm/sound.c
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -96,62 +96,6 @@
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)
{
@@ -449,10 +393,10 @@
d->playcount = 0;
d->pvchancount = 0;
d->rvchancount = 0;
- d->pvchanrate = 0;
- d->pvchanformat = 0;
- d->rvchanrate = 0;
- d->rvchanformat = 0;
+ d->pvchanrate = VCHAN_DEFAULT_RATE;
+ d->pvchanformat = VCHAN_DEFAULT_FORMAT;;
+ d->rvchanrate = VCHAN_DEFAULT_RATE;
+ d->rvchanformat = VCHAN_DEFAULT_FORMAT;
d->p_unr = new_unrhdr(0, INT_MAX, NULL);
d->vp_unr = new_unrhdr(0, INT_MAX, NULL);
d->r_unr = new_unrhdr(0, INT_MAX, NULL);
@@ -461,6 +405,7 @@
CHN_INIT(d, channels.pcm);
CHN_INIT(d, channels.pcm.busy);
CHN_INIT(d, channels.pcm.opened);
+ CHN_INIT(d, channels.pcm.primary);
}
int
@@ -480,7 +425,10 @@
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);
@@ -722,9 +670,7 @@
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)
@@ -748,11 +694,11 @@
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/vchan.h b/sys/dev/sound/pcm/vchan.h
--- a/sys/dev/sound/pcm/vchan.h
+++ b/sys/dev/sound/pcm/vchan.h
@@ -30,9 +30,9 @@
#ifndef _SND_VCHAN_H_
#define _SND_VCHAN_H_
-extern int snd_maxautovchans;
+extern bool snd_vchans_enable;
-int vchan_create(struct pcm_channel *);
+int vchan_create(struct pcm_channel *, struct pcm_channel **);
int vchan_destroy(struct pcm_channel *);
#ifdef SND_DEBUG
@@ -47,9 +47,6 @@
sndbuf_getfmt((c)->bufhard) != (c)->parentchannel->format || \
sndbuf_getspd((c)->bufhard) != (c)->parentchannel->speed))
-int vchan_setnew(struct snddev_info *, int, int);
-void vchan_setmaxauto(struct snddev_info *, int);
-
void vchan_initsys(device_t);
/*
diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c
--- a/sys/dev/sound/pcm/vchan.c
+++ b/sys/dev/sound/pcm/vchan.c
@@ -56,7 +56,7 @@
int trigger;
};
-int snd_maxautovchans = 16;
+bool snd_vchans_enable = true;
static void *
vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
@@ -272,7 +272,7 @@
*ch = NULL;
break;
}
- } else if (c->flags & CHN_F_HAS_VCHAN) {
+ } else {
/* No way!! */
if (*ch != NULL) {
CHN_UNLOCK(c);
@@ -294,8 +294,7 @@
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))
@@ -306,43 +305,44 @@
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
@@ -363,15 +363,22 @@
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);
@@ -445,7 +452,7 @@
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))
@@ -456,24 +463,24 @@
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);
@@ -492,9 +499,7 @@
KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
__func__, direction, c->direction));
- CHN_LOCK(c);
- newspd = c->speed;
- CHN_UNLOCK(c);
+ newspd = *vchanrate;
ret = sysctl_handle_int(oidp, &newspd, 0, req);
if (ret != 0 || req->newptr == NULL) {
@@ -552,7 +557,7 @@
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));
@@ -564,24 +569,24 @@
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);
@@ -600,15 +605,11 @@
KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d",
__func__, direction, c->direction));
- CHN_LOCK(c);
-
bzero(fmtstr, sizeof(fmtstr));
- if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format)
+ if (snd_afmt2str(*vchanformat, fmtstr, sizeof(fmtstr)) != *vchanformat)
strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr));
- CHN_UNLOCK(c);
-
ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
if (ret != 0 || req->newptr == NULL) {
PCM_RELEASE_QUICK(d);
@@ -661,7 +662,7 @@
"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;
@@ -677,9 +678,6 @@
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);
@@ -714,10 +712,12 @@
*/
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,7 +807,7 @@
fail:
CHN_REMOVE(parent, ch, children);
- parent->flags &= ~CHN_F_HAS_VCHAN;
+ parent->flags &= ~(CHN_F_HAS_VCHAN | CHN_F_BUSY);
CHN_UNLOCK(parent);
chn_kill(ch);
CHN_LOCK(parent);
@@ -889,166 +889,40 @@
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);
-}
-
-void
-vchan_setmaxauto(struct snddev_info *d, int num)
-{
- PCM_BUSYASSERT(d);
-
- if (num < 0)
- return;
-
- if (num >= 0 && d->pvchancount > num)
- (void)vchan_setnew(d, PCMDIR_PLAY, num);
- else if (num > 0 && d->pvchancount == 0)
- (void)vchan_setnew(d, PCMDIR_PLAY, 1);
-
- if (num >= 0 && d->rvchancount > num)
- (void)vchan_setnew(d, PCMDIR_REC, num);
- else if (num > 0 && d->rvchancount == 0)
- (void)vchan_setnew(d, PCMDIR_REC, 1);
-}
-
static int
-sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
+sysctl_hw_snd_vchans_enable(SYSCTL_HANDLER_ARGS)
{
struct snddev_info *d;
int i, v, error;
- v = snd_maxautovchans;
+ v = snd_vchans_enable;
error = sysctl_handle_int(oidp, &v, 0, req);
- if (error == 0 && req->newptr != NULL) {
- if (v < 0)
- v = 0;
- if (v > SND_MAXVCHANS)
- v = SND_MAXVCHANS;
- snd_maxautovchans = v;
- for (i = 0; pcm_devclass != NULL &&
- i < devclass_get_maxunit(pcm_devclass); i++) {
- d = devclass_get_softc(pcm_devclass, i);
- if (!PCM_REGISTERED(d))
- continue;
- PCM_ACQUIRE_QUICK(d);
- vchan_setmaxauto(d, v);
- PCM_RELEASE_QUICK(d);
- }
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ snd_vchans_enable = v >= 1;
+
+ for (i = 0; pcm_devclass != NULL &&
+ i < devclass_get_maxunit(pcm_devclass); i++) {
+ d = devclass_get_softc(pcm_devclass, i);
+ if (!PCM_REGISTERED(d))
+ continue;
+ PCM_ACQUIRE_QUICK(d);
+ if (snd_vchans_enable) {
+ if (d->playcount > 0)
+ d->flags |= SD_F_PVCHANS;
+ if (d->reccount > 0)
+ d->flags |= SD_F_RVCHANS;
+ } else
+ d->flags &= ~(SD_F_PVCHANS | SD_F_RVCHANS);
+ PCM_RELEASE_QUICK(d);
}
- return (error);
+
+ return (0);
}
-SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans,
+SYSCTL_PROC(_hw_snd, OID_AUTO, vchans_enable,
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
- sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
+ sysctl_hw_snd_vchans_enable, "I", "global virtual channel switch");
void
vchan_initsys(device_t dev)
@@ -1064,7 +938,7 @@
SYSCTL_CHILDREN(d->play_sysctl_tree),
OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
- sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
+ sysctl_dev_pcm_vchans, "I", "virtual channels enabled");
SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
SYSCTL_CHILDREN(d->play_sysctl_tree),
OID_AUTO, "vchanmode",
@@ -1090,7 +964,7 @@
OID_AUTO, "vchans",
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
- sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
+ sysctl_dev_pcm_vchans, "I", "virtual channels enabled");
SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
SYSCTL_CHILDREN(d->rec_sysctl_tree),
OID_AUTO, "vchanmode",

File Metadata

Mime Type
text/plain
Expires
Thu, Jan 9, 3:21 AM (7 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15728920
Default Alt Text
D47917.id147538.diff (27 KB)

Event Timeline