mirror of
https://github.com/opnsense/src.git
synced 2026-06-09 08:43:19 -04:00
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 02d4eeabfd)
This commit is contained in:
parent
a8fc617701
commit
960ee80949
8 changed files with 255 additions and 395 deletions
|
|
@ -23,7 +23,7 @@
|
||||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
.\" SUCH DAMAGE.
|
.\" SUCH DAMAGE.
|
||||||
.\"
|
.\"
|
||||||
.Dd March 24, 2024
|
.Dd December 4, 2024
|
||||||
.Dt SOUND 4
|
.Dt SOUND 4
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.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
|
in possible underruns if the application cannot keep up with a rapid irq
|
||||||
rate, especially during high workload.
|
rate, especially during high workload.
|
||||||
The default value is 1, which is considered a moderate/safe latency profile.
|
The default value is 1, which is considered a moderate/safe latency profile.
|
||||||
.It Va hw.snd.maxautovchans
|
.It Va hw.snd.vchans_enable
|
||||||
Global VCHAN setting that only affects devices with at least one playback or
|
Global VCHAN setting to enable (1) or disable (0) VCHANs.
|
||||||
recording channel available.
|
This setting can be overridden for an individual device by using the
|
||||||
The sound system will dynamically create up to this many VCHANs.
|
.Va dev.pcm.%d.[play|rec].vchans
|
||||||
Set to
|
tunables.
|
||||||
.Dq 0
|
Default is enabled.
|
||||||
if no VCHANs are desired.
|
|
||||||
Maximum value is 256.
|
|
||||||
.It Va hw.snd.report_soft_formats
|
.It Va hw.snd.report_soft_formats
|
||||||
Controls the internal format conversion if it is
|
Controls the internal format conversion if it is
|
||||||
available transparently to the application software.
|
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.
|
sysctl.
|
||||||
Default is disabled.
|
Default is disabled.
|
||||||
.It Va dev.pcm.%d.[play|rec].vchans
|
.It Va dev.pcm.%d.[play|rec].vchans
|
||||||
The current number of VCHANs allocated per device.
|
Enable (1) or disable (0) VCHANs.
|
||||||
This can be set to preallocate a certain number of VCHANs.
|
Default is enabled.
|
||||||
Setting this value to
|
|
||||||
.Dq 0
|
|
||||||
will disable VCHANs for this device.
|
|
||||||
.It Va dev.pcm.%d.[play|rec].vchanformat
|
.It Va dev.pcm.%d.[play|rec].vchanformat
|
||||||
Format for VCHAN mixing.
|
Format for VCHAN mixing.
|
||||||
All playback paths will be converted to this format before the mixing
|
All playback paths will be converted to this format before the mixing
|
||||||
|
|
|
||||||
|
|
@ -1172,7 +1172,7 @@ chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls,
|
||||||
struct feeder_class *fc;
|
struct feeder_class *fc;
|
||||||
struct snd_dbuf *b, *bs;
|
struct snd_dbuf *b, *bs;
|
||||||
char buf[CHN_NAMELEN];
|
char buf[CHN_NAMELEN];
|
||||||
int i, direction;
|
int err, i, direction;
|
||||||
|
|
||||||
PCM_BUSYASSERT(d);
|
PCM_BUSYASSERT(d);
|
||||||
PCM_LOCKASSERT(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);
|
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);
|
PCM_LOCK(d);
|
||||||
CHN_INSERT_SORT_ASCEND(d, c, channels.pcm);
|
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) {
|
switch (c->type) {
|
||||||
case PCMDIR_PLAY:
|
case PCMDIR_PLAY:
|
||||||
|
|
@ -1332,6 +1342,8 @@ chn_kill(struct pcm_channel *c)
|
||||||
|
|
||||||
PCM_LOCK(d);
|
PCM_LOCK(d);
|
||||||
CHN_REMOVE(d, c, channels.pcm);
|
CHN_REMOVE(d, c, channels.pcm);
|
||||||
|
if ((c->flags & CHN_F_VIRTUAL) == 0)
|
||||||
|
CHN_REMOVE(d, c, channels.pcm.primary);
|
||||||
|
|
||||||
switch (c->type) {
|
switch (c->type) {
|
||||||
case PCMDIR_PLAY:
|
case PCMDIR_PLAY:
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,9 @@ struct pcm_channel {
|
||||||
struct {
|
struct {
|
||||||
SLIST_ENTRY(pcm_channel) link;
|
SLIST_ENTRY(pcm_channel) link;
|
||||||
} opened;
|
} opened;
|
||||||
|
struct {
|
||||||
|
SLIST_ENTRY(pcm_channel) link;
|
||||||
|
} primary;
|
||||||
} pcm;
|
} pcm;
|
||||||
} channels;
|
} channels;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <dev/sound/pcm/sound.h>
|
#include <dev/sound/pcm/sound.h>
|
||||||
|
#include <dev/sound/pcm/vchan.h>
|
||||||
#include <sys/ctype.h>
|
#include <sys/ctype.h>
|
||||||
#include <sys/lock.h>
|
#include <sys/lock.h>
|
||||||
#include <sys/rwlock.h>
|
#include <sys/rwlock.h>
|
||||||
|
|
@ -158,6 +159,81 @@ dsp_unlock_chans(struct dsp_cdevpriv *priv, uint32_t prio)
|
||||||
CHN_UNLOCK(priv->wrch);
|
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_VALID(x) ((x) & (FREAD | FWRITE))
|
||||||
#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
|
#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
|
||||||
#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x))
|
#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x))
|
||||||
|
|
@ -168,7 +244,7 @@ static void
|
||||||
dsp_close(void *data)
|
dsp_close(void *data)
|
||||||
{
|
{
|
||||||
struct dsp_cdevpriv *priv = data;
|
struct dsp_cdevpriv *priv = data;
|
||||||
struct pcm_channel *rdch, *wrch;
|
struct pcm_channel *rdch, *wrch, *parent;
|
||||||
struct snddev_info *d;
|
struct snddev_info *d;
|
||||||
int sg_ids;
|
int sg_ids;
|
||||||
|
|
||||||
|
|
@ -214,12 +290,20 @@ dsp_close(void *data)
|
||||||
if (sg_ids != 0)
|
if (sg_ids != 0)
|
||||||
free_unr(pcmsg_unrhdr, sg_ids);
|
free_unr(pcmsg_unrhdr, sg_ids);
|
||||||
|
|
||||||
CHN_LOCK(rdch);
|
if (rdch->flags & CHN_F_VIRTUAL) {
|
||||||
chn_abort(rdch); /* won't sleep */
|
parent = rdch->parentchannel;
|
||||||
rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
|
CHN_LOCK(parent);
|
||||||
CHN_F_DEAD | CHN_F_EXCLUSIVE);
|
CHN_LOCK(rdch);
|
||||||
chn_reset(rdch, 0, 0);
|
vchan_destroy(rdch);
|
||||||
chn_release(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) {
|
if (wrch != NULL) {
|
||||||
/*
|
/*
|
||||||
|
|
@ -231,12 +315,20 @@ dsp_close(void *data)
|
||||||
if (sg_ids != 0)
|
if (sg_ids != 0)
|
||||||
free_unr(pcmsg_unrhdr, sg_ids);
|
free_unr(pcmsg_unrhdr, sg_ids);
|
||||||
|
|
||||||
CHN_LOCK(wrch);
|
if (wrch->flags & CHN_F_VIRTUAL) {
|
||||||
chn_flush(wrch); /* may sleep */
|
parent = wrch->parentchannel;
|
||||||
wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
|
CHN_LOCK(parent);
|
||||||
CHN_F_DEAD | CHN_F_EXCLUSIVE);
|
CHN_LOCK(wrch);
|
||||||
chn_reset(wrch, 0, 0);
|
vchan_destroy(wrch);
|
||||||
chn_release(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);
|
PCM_LOCK(d);
|
||||||
}
|
}
|
||||||
|
|
@ -254,10 +346,9 @@ static int
|
||||||
dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
||||||
{
|
{
|
||||||
struct dsp_cdevpriv *priv;
|
struct dsp_cdevpriv *priv;
|
||||||
struct pcm_channel *rdch, *wrch, *ch;
|
struct pcm_channel *ch;
|
||||||
struct snddev_info *d;
|
struct snddev_info *d;
|
||||||
uint32_t fmt, spd;
|
int error, dir;
|
||||||
int error, rderror, wrerror, dir;
|
|
||||||
|
|
||||||
/* Kind of impossible.. */
|
/* Kind of impossible.. */
|
||||||
if (i_dev == NULL || td == NULL)
|
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))
|
if (!DSP_REGISTERED(d))
|
||||||
return (EBADF);
|
return (EBADF);
|
||||||
|
|
||||||
|
if (PCM_CHANCOUNT(d) >= PCM_MAXCHANS)
|
||||||
|
return (ENOMEM);
|
||||||
|
|
||||||
priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK | M_ZERO);
|
priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||||
priv->sc = d;
|
priv->sc = d;
|
||||||
priv->rdch = NULL;
|
|
||||||
priv->wrch = NULL;
|
|
||||||
priv->volch = NULL;
|
|
||||||
|
|
||||||
error = devfs_set_cdevpriv(priv, dsp_close);
|
error = devfs_set_cdevpriv(priv, dsp_close);
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
|
|
@ -334,98 +425,30 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
||||||
PCM_ACQUIRE(d);
|
PCM_ACQUIRE(d);
|
||||||
PCM_UNLOCK(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)) {
|
if (DSP_F_WRITE(flags)) {
|
||||||
/* open for write */
|
error = dsp_chn_alloc(d, &priv->wrch, PCMDIR_PLAY, flags, td);
|
||||||
wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY,
|
if (error != 0) {
|
||||||
td->td_proc->p_pid, td->td_proc->p_comm);
|
PCM_RELEASE_QUICK(d);
|
||||||
|
PCM_GIANT_EXIT(d);
|
||||||
if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0)
|
return (error);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
PCM_LOCK(d);
|
||||||
|
CHN_INSERT_HEAD(d, priv->wrch, channels.pcm.opened);
|
||||||
PCM_LOCK(d);
|
PCM_UNLOCK(d);
|
||||||
|
}
|
||||||
if (wrch == NULL && rdch == NULL) {
|
if (DSP_F_READ(flags)) {
|
||||||
PCM_RELEASE(d);
|
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_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);
|
PCM_GIANT_LEAVE(d);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
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
|
static int
|
||||||
sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
|
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)
|
if (d->playcount > 0 || d->reccount > 0)
|
||||||
d->flags |= SD_F_AUTOVCHAN;
|
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);
|
strlcpy(d->status, str, SND_STATUSLEN);
|
||||||
sndstat_register(dev, d->status);
|
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);
|
||||||
CHN_INIT(d, channels.pcm.busy);
|
CHN_INIT(d, channels.pcm.busy);
|
||||||
CHN_INIT(d, channels.pcm.opened);
|
CHN_INIT(d, channels.pcm.opened);
|
||||||
|
CHN_INIT(d, channels.pcm.primary);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
@ -732,9 +680,7 @@ sound_global_init(void)
|
||||||
if (snd_unit < 0)
|
if (snd_unit < 0)
|
||||||
snd_unit = -1;
|
snd_unit = -1;
|
||||||
|
|
||||||
if (snd_maxautovchans < 0 ||
|
snd_vchans_enable = true;
|
||||||
snd_maxautovchans > SND_MAXVCHANS)
|
|
||||||
snd_maxautovchans = 0;
|
|
||||||
|
|
||||||
if (chn_latency < CHN_LATENCY_MIN ||
|
if (chn_latency < CHN_LATENCY_MIN ||
|
||||||
chn_latency > CHN_LATENCY_MAX)
|
chn_latency > CHN_LATENCY_MAX)
|
||||||
|
|
@ -758,11 +704,11 @@ sound_global_init(void)
|
||||||
feeder_rate_round = FEEDRATE_ROUNDHZ;
|
feeder_rate_round = FEEDRATE_ROUNDHZ;
|
||||||
|
|
||||||
if (bootverbose)
|
if (bootverbose)
|
||||||
printf("%s: snd_unit=%d snd_maxautovchans=%d "
|
printf("%s: snd_unit=%d snd_vchans_enable=%d "
|
||||||
"latency=%d "
|
"latency=%d "
|
||||||
"feeder_rate_min=%d feeder_rate_max=%d "
|
"feeder_rate_min=%d feeder_rate_max=%d "
|
||||||
"feeder_rate_round=%d\n",
|
"feeder_rate_round=%d\n",
|
||||||
__func__, snd_unit, snd_maxautovchans,
|
__func__, snd_unit, snd_vchans_enable,
|
||||||
chn_latency,
|
chn_latency,
|
||||||
feeder_rate_min, feeder_rate_max,
|
feeder_rate_min, feeder_rate_max,
|
||||||
feeder_rate_round);
|
feeder_rate_round);
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,6 @@ struct snd_mixer;
|
||||||
#define SOUND_PREFVER SOUND_MODVER
|
#define SOUND_PREFVER SOUND_MODVER
|
||||||
#define SOUND_MAXVER SOUND_MODVER
|
#define SOUND_MAXVER SOUND_MODVER
|
||||||
|
|
||||||
#define SND_MAXVCHANS 256
|
|
||||||
|
|
||||||
#define SD_F_SIMPLEX 0x00000001
|
#define SD_F_SIMPLEX 0x00000001
|
||||||
#define SD_F_AUTOVCHAN 0x00000002
|
#define SD_F_AUTOVCHAN 0x00000002
|
||||||
#define SD_F_SOFTPCMVOL 0x00000004
|
#define SD_F_SOFTPCMVOL 0x00000004
|
||||||
|
|
@ -113,6 +111,8 @@ struct snd_mixer;
|
||||||
#define SD_F_EQ_ENABLED 0x00000200 /* EQ enabled */
|
#define SD_F_EQ_ENABLED 0x00000200 /* EQ enabled */
|
||||||
#define SD_F_EQ_BYPASSED 0x00000400 /* EQ bypassed */
|
#define SD_F_EQ_BYPASSED 0x00000400 /* EQ bypassed */
|
||||||
#define SD_F_EQ_PC 0x00000800 /* EQ per-channel */
|
#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_DEFAULT (SD_F_EQ | SD_F_EQ_ENABLED)
|
||||||
#define SD_F_EQ_MASK (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" \
|
"\012EQ_ENABLED" \
|
||||||
"\013EQ_BYPASSED" \
|
"\013EQ_BYPASSED" \
|
||||||
"\014EQ_PC" \
|
"\014EQ_PC" \
|
||||||
|
"\015PVCHANS" \
|
||||||
|
"\016RVCHANS" \
|
||||||
"\035PRIO_RD" \
|
"\035PRIO_RD" \
|
||||||
"\036PRIO_WR"
|
"\036PRIO_WR"
|
||||||
|
|
||||||
#define PCM_ALIVE(x) ((x) != NULL && (x)->lock != NULL)
|
#define PCM_ALIVE(x) ((x) != NULL && (x)->lock != NULL)
|
||||||
#define PCM_REGISTERED(x) (PCM_ALIVE(x) && ((x)->flags & SD_F_REGISTERED))
|
#define PCM_REGISTERED(x) (PCM_ALIVE(x) && ((x)->flags & SD_F_REGISTERED))
|
||||||
|
|
||||||
|
#define PCM_MAXCHANS 10000
|
||||||
#define PCM_CHANCOUNT(d) \
|
#define PCM_CHANCOUNT(d) \
|
||||||
(d->playcount + d->pvchancount + d->reccount + d->rvchancount)
|
(d->playcount + d->pvchancount + d->reccount + d->rvchancount)
|
||||||
|
|
||||||
|
|
@ -168,9 +171,6 @@ extern struct unrhdr *pcmsg_unrhdr;
|
||||||
|
|
||||||
SYSCTL_DECL(_hw_snd);
|
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);
|
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);
|
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);
|
int pcm_register(device_t dev, void *devinfo, int numplay, int numrec);
|
||||||
|
|
@ -224,6 +224,9 @@ struct snddev_info {
|
||||||
struct {
|
struct {
|
||||||
SLIST_HEAD(, pcm_channel) head;
|
SLIST_HEAD(, pcm_channel) head;
|
||||||
} opened;
|
} opened;
|
||||||
|
struct {
|
||||||
|
SLIST_HEAD(, pcm_channel) head;
|
||||||
|
} primary;
|
||||||
} pcm;
|
} pcm;
|
||||||
} channels;
|
} channels;
|
||||||
unsigned playcount, reccount, pvchancount, rvchancount;
|
unsigned playcount, reccount, pvchancount, rvchancount;
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ struct vchan_info {
|
||||||
int trigger;
|
int trigger;
|
||||||
};
|
};
|
||||||
|
|
||||||
int snd_maxautovchans = 16;
|
bool snd_vchans_enable = true;
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
|
vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
|
||||||
|
|
@ -277,7 +277,7 @@ vchan_getparentchannel(struct snddev_info *d,
|
||||||
*ch = NULL;
|
*ch = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (c->flags & CHN_F_HAS_VCHAN) {
|
} else {
|
||||||
/* No way!! */
|
/* No way!! */
|
||||||
if (*ch != NULL) {
|
if (*ch != NULL) {
|
||||||
CHN_UNLOCK(c);
|
CHN_UNLOCK(c);
|
||||||
|
|
@ -299,8 +299,7 @@ static int
|
||||||
sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
|
sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
|
||||||
{
|
{
|
||||||
struct snddev_info *d;
|
struct snddev_info *d;
|
||||||
int direction, vchancount;
|
int err, enabled, flag;
|
||||||
int err, cnt;
|
|
||||||
|
|
||||||
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
|
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
|
||||||
if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
|
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)) {
|
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||||
case VCHAN_PLAY:
|
case VCHAN_PLAY:
|
||||||
direction = PCMDIR_PLAY;
|
/* Exit if we do not support this direction. */
|
||||||
vchancount = d->pvchancount;
|
if (d->playcount < 1) {
|
||||||
cnt = d->playcount;
|
PCM_UNLOCK(d);
|
||||||
|
return (ENODEV);
|
||||||
|
}
|
||||||
|
flag = SD_F_PVCHANS;
|
||||||
break;
|
break;
|
||||||
case VCHAN_REC:
|
case VCHAN_REC:
|
||||||
direction = PCMDIR_REC;
|
if (d->reccount < 1) {
|
||||||
vchancount = d->rvchancount;
|
PCM_UNLOCK(d);
|
||||||
cnt = d->reccount;
|
return (ENODEV);
|
||||||
|
}
|
||||||
|
flag = SD_F_RVCHANS;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PCM_UNLOCK(d);
|
PCM_UNLOCK(d);
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cnt < 1) {
|
enabled = (d->flags & flag) != 0;
|
||||||
PCM_UNLOCK(d);
|
|
||||||
return (ENODEV);
|
|
||||||
}
|
|
||||||
|
|
||||||
PCM_ACQUIRE(d);
|
PCM_ACQUIRE(d);
|
||||||
PCM_UNLOCK(d);
|
PCM_UNLOCK(d);
|
||||||
|
|
||||||
cnt = vchancount;
|
err = sysctl_handle_int(oidp, &enabled, 0, req);
|
||||||
err = sysctl_handle_int(oidp, &cnt, 0, req);
|
if (err != 0 || req->newptr == NULL) {
|
||||||
|
PCM_RELEASE_QUICK(d);
|
||||||
if (err == 0 && req->newptr != NULL && vchancount != cnt) {
|
return (err);
|
||||||
if (cnt < 0)
|
|
||||||
cnt = 0;
|
|
||||||
if (cnt > SND_MAXVCHANS)
|
|
||||||
cnt = SND_MAXVCHANS;
|
|
||||||
err = vchan_setnew(d, direction, cnt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enabled <= 0)
|
||||||
|
d->flags &= ~flag;
|
||||||
|
else
|
||||||
|
d->flags |= flag;
|
||||||
|
|
||||||
PCM_RELEASE_QUICK(d);
|
PCM_RELEASE_QUICK(d);
|
||||||
|
|
||||||
return err;
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
@ -368,15 +368,22 @@ sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
|
||||||
|
|
||||||
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||||
case VCHAN_PLAY:
|
case VCHAN_PLAY:
|
||||||
|
if ((d->flags & SD_F_PVCHANS) == 0) {
|
||||||
|
PCM_UNLOCK(d);
|
||||||
|
return (ENODEV);
|
||||||
|
}
|
||||||
direction = PCMDIR_PLAY;
|
direction = PCMDIR_PLAY;
|
||||||
break;
|
break;
|
||||||
case VCHAN_REC:
|
case VCHAN_REC:
|
||||||
|
if ((d->flags & SD_F_RVCHANS) == 0) {
|
||||||
|
PCM_UNLOCK(d);
|
||||||
|
return (ENODEV);
|
||||||
|
}
|
||||||
direction = PCMDIR_REC;
|
direction = PCMDIR_REC;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PCM_UNLOCK(d);
|
PCM_UNLOCK(d);
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PCM_ACQUIRE(d);
|
PCM_ACQUIRE(d);
|
||||||
|
|
@ -450,7 +457,7 @@ sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
|
||||||
struct snddev_info *d;
|
struct snddev_info *d;
|
||||||
struct pcm_channel *c, *ch;
|
struct pcm_channel *c, *ch;
|
||||||
struct pcmchan_caps *caps;
|
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));
|
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
|
||||||
if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
|
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)) {
|
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||||
case VCHAN_PLAY:
|
case VCHAN_PLAY:
|
||||||
|
if ((d->flags & SD_F_PVCHANS) == 0) {
|
||||||
|
PCM_UNLOCK(d);
|
||||||
|
return (ENODEV);
|
||||||
|
}
|
||||||
direction = PCMDIR_PLAY;
|
direction = PCMDIR_PLAY;
|
||||||
vchancount = d->pvchancount;
|
|
||||||
vchanrate = &d->pvchanrate;
|
vchanrate = &d->pvchanrate;
|
||||||
break;
|
break;
|
||||||
case VCHAN_REC:
|
case VCHAN_REC:
|
||||||
|
if ((d->flags & SD_F_RVCHANS) == 0) {
|
||||||
|
PCM_UNLOCK(d);
|
||||||
|
return (ENODEV);
|
||||||
|
}
|
||||||
direction = PCMDIR_REC;
|
direction = PCMDIR_REC;
|
||||||
vchancount = d->rvchancount;
|
|
||||||
vchanrate = &d->rvchanrate;
|
vchanrate = &d->rvchanrate;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PCM_UNLOCK(d);
|
PCM_UNLOCK(d);
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vchancount < 1) {
|
|
||||||
PCM_UNLOCK(d);
|
|
||||||
return (EINVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PCM_ACQUIRE(d);
|
PCM_ACQUIRE(d);
|
||||||
|
|
@ -555,7 +562,7 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
|
||||||
struct snddev_info *d;
|
struct snddev_info *d;
|
||||||
struct pcm_channel *c, *ch;
|
struct pcm_channel *c, *ch;
|
||||||
uint32_t newfmt;
|
uint32_t newfmt;
|
||||||
int *vchanformat, vchancount, direction, ret, restart;
|
int *vchanformat, direction, ret, restart;
|
||||||
char fmtstr[AFMTSTR_LEN];
|
char fmtstr[AFMTSTR_LEN];
|
||||||
|
|
||||||
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
|
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)) {
|
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||||
case VCHAN_PLAY:
|
case VCHAN_PLAY:
|
||||||
|
if ((d->flags & SD_F_PVCHANS) == 0) {
|
||||||
|
PCM_UNLOCK(d);
|
||||||
|
return (ENODEV);
|
||||||
|
}
|
||||||
direction = PCMDIR_PLAY;
|
direction = PCMDIR_PLAY;
|
||||||
vchancount = d->pvchancount;
|
|
||||||
vchanformat = &d->pvchanformat;
|
vchanformat = &d->pvchanformat;
|
||||||
break;
|
break;
|
||||||
case VCHAN_REC:
|
case VCHAN_REC:
|
||||||
|
if ((d->flags & SD_F_RVCHANS) == 0) {
|
||||||
|
PCM_UNLOCK(d);
|
||||||
|
return (ENODEV);
|
||||||
|
}
|
||||||
direction = PCMDIR_REC;
|
direction = PCMDIR_REC;
|
||||||
vchancount = d->rvchancount;
|
|
||||||
vchanformat = &d->rvchanformat;
|
vchanformat = &d->rvchanformat;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PCM_UNLOCK(d);
|
PCM_UNLOCK(d);
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vchancount < 1) {
|
|
||||||
PCM_UNLOCK(d);
|
|
||||||
return (EINVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PCM_ACQUIRE(d);
|
PCM_ACQUIRE(d);
|
||||||
|
|
@ -660,7 +667,7 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
|
||||||
"play.vchanrate" : "rec.vchanrate"
|
"play.vchanrate" : "rec.vchanrate"
|
||||||
|
|
||||||
int
|
int
|
||||||
vchan_create(struct pcm_channel *parent)
|
vchan_create(struct pcm_channel *parent, struct pcm_channel **child)
|
||||||
{
|
{
|
||||||
struct snddev_info *d;
|
struct snddev_info *d;
|
||||||
struct pcm_channel *ch;
|
struct pcm_channel *ch;
|
||||||
|
|
@ -676,9 +683,6 @@ vchan_create(struct pcm_channel *parent)
|
||||||
PCM_BUSYASSERT(d);
|
PCM_BUSYASSERT(d);
|
||||||
CHN_LOCKASSERT(parent);
|
CHN_LOCKASSERT(parent);
|
||||||
|
|
||||||
if (!(parent->flags & CHN_F_BUSY))
|
|
||||||
return (EBUSY);
|
|
||||||
|
|
||||||
if (!(parent->direction == PCMDIR_PLAY ||
|
if (!(parent->direction == PCMDIR_PLAY ||
|
||||||
parent->direction == PCMDIR_REC))
|
parent->direction == PCMDIR_REC))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
@ -713,10 +717,12 @@ vchan_create(struct pcm_channel *parent)
|
||||||
*/
|
*/
|
||||||
CHN_INSERT_SORT_DESCEND(parent, ch, children);
|
CHN_INSERT_SORT_DESCEND(parent, ch, children);
|
||||||
|
|
||||||
|
*child = ch;
|
||||||
|
|
||||||
if (parent->flags & CHN_F_HAS_VCHAN)
|
if (parent->flags & CHN_F_HAS_VCHAN)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
parent->flags |= CHN_F_HAS_VCHAN;
|
parent->flags |= CHN_F_HAS_VCHAN | CHN_F_BUSY;
|
||||||
|
|
||||||
parent_caps = chn_getcaps(parent);
|
parent_caps = chn_getcaps(parent);
|
||||||
if (parent_caps == NULL) {
|
if (parent_caps == NULL) {
|
||||||
|
|
@ -807,6 +813,7 @@ vchan_create(struct pcm_channel *parent)
|
||||||
fail:
|
fail:
|
||||||
CHN_LOCK(ch);
|
CHN_LOCK(ch);
|
||||||
vchan_destroy(ch);
|
vchan_destroy(ch);
|
||||||
|
*child = NULL;
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
@ -878,166 +885,40 @@ vchan_sync(struct pcm_channel *c)
|
||||||
return (ret);
|
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
|
static int
|
||||||
sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
|
sysctl_hw_snd_vchans_enable(SYSCTL_HANDLER_ARGS)
|
||||||
{
|
{
|
||||||
struct snddev_info *d;
|
struct snddev_info *d;
|
||||||
int i, v, error;
|
int i, v, error;
|
||||||
|
|
||||||
v = snd_maxautovchans;
|
v = snd_vchans_enable;
|
||||||
error = sysctl_handle_int(oidp, &v, 0, req);
|
error = sysctl_handle_int(oidp, &v, 0, req);
|
||||||
if (error == 0 && req->newptr != NULL) {
|
if (error != 0 || req->newptr == NULL)
|
||||||
if (v < 0)
|
return (error);
|
||||||
v = 0;
|
|
||||||
if (v > SND_MAXVCHANS)
|
snd_vchans_enable = v >= 1;
|
||||||
v = SND_MAXVCHANS;
|
|
||||||
snd_maxautovchans = v;
|
for (i = 0; pcm_devclass != NULL &&
|
||||||
for (i = 0; pcm_devclass != NULL &&
|
i < devclass_get_maxunit(pcm_devclass); i++) {
|
||||||
i < devclass_get_maxunit(pcm_devclass); i++) {
|
d = devclass_get_softc(pcm_devclass, i);
|
||||||
d = devclass_get_softc(pcm_devclass, i);
|
if (!PCM_REGISTERED(d))
|
||||||
if (!PCM_REGISTERED(d))
|
continue;
|
||||||
continue;
|
PCM_ACQUIRE_QUICK(d);
|
||||||
PCM_ACQUIRE_QUICK(d);
|
if (snd_vchans_enable) {
|
||||||
vchan_setmaxauto(d, v);
|
if (d->playcount > 0)
|
||||||
PCM_RELEASE_QUICK(d);
|
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),
|
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
|
void
|
||||||
vchan_initsys(device_t dev)
|
vchan_initsys(device_t dev)
|
||||||
|
|
@ -1053,7 +934,7 @@ vchan_initsys(device_t dev)
|
||||||
SYSCTL_CHILDREN(d->play_sysctl_tree),
|
SYSCTL_CHILDREN(d->play_sysctl_tree),
|
||||||
OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
|
OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
|
||||||
VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
|
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_ADD_PROC(&d->play_sysctl_ctx,
|
||||||
SYSCTL_CHILDREN(d->play_sysctl_tree),
|
SYSCTL_CHILDREN(d->play_sysctl_tree),
|
||||||
OID_AUTO, "vchanmode",
|
OID_AUTO, "vchanmode",
|
||||||
|
|
@ -1079,7 +960,7 @@ vchan_initsys(device_t dev)
|
||||||
OID_AUTO, "vchans",
|
OID_AUTO, "vchans",
|
||||||
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
|
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
|
||||||
VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
|
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_ADD_PROC(&d->rec_sysctl_ctx,
|
||||||
SYSCTL_CHILDREN(d->rec_sysctl_tree),
|
SYSCTL_CHILDREN(d->rec_sysctl_tree),
|
||||||
OID_AUTO, "vchanmode",
|
OID_AUTO, "vchanmode",
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@
|
||||||
#ifndef _SND_VCHAN_H_
|
#ifndef _SND_VCHAN_H_
|
||||||
#define _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 *);
|
int vchan_destroy(struct pcm_channel *);
|
||||||
|
|
||||||
#ifdef SND_DEBUG
|
#ifdef SND_DEBUG
|
||||||
|
|
@ -47,9 +47,6 @@ int vchan_sync(struct pcm_channel *);
|
||||||
sndbuf_getfmt((c)->bufhard) != (c)->parentchannel->format || \
|
sndbuf_getfmt((c)->bufhard) != (c)->parentchannel->format || \
|
||||||
sndbuf_getspd((c)->bufhard) != (c)->parentchannel->speed))
|
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);
|
void vchan_initsys(device_t);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue