Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F106961128
D47917.id147538.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
27 KB
Referenced Files
None
Subscribers
None
D47917.id147538.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D47917: sound: Allocate vchans on-demand
Attached
Detach File
Event Timeline
Log In to Comment