mirror of
https://github.com/opnsense/src.git
synced 2026-02-18 18:20:26 -05: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 02d4eeabfd73e6a827f5d42601e99aad92060b04)
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
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd March 24, 2024
|
||||
.Dd December 4, 2024
|
||||
.Dt SOUND 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
|
@ -358,14 +358,12 @@ A value of 0 will use a low and aggressive latency profile which can result
|
|||
in possible underruns if the application cannot keep up with a rapid irq
|
||||
rate, especially during high workload.
|
||||
The default value is 1, which is considered a moderate/safe latency profile.
|
||||
.It Va hw.snd.maxautovchans
|
||||
Global VCHAN setting that only affects devices with at least one playback or
|
||||
recording channel available.
|
||||
The sound system will dynamically create up to this many VCHANs.
|
||||
Set to
|
||||
.Dq 0
|
||||
if no VCHANs are desired.
|
||||
Maximum value is 256.
|
||||
.It Va hw.snd.vchans_enable
|
||||
Global VCHAN setting to enable (1) or disable (0) VCHANs.
|
||||
This setting can be overridden for an individual device by using the
|
||||
.Va dev.pcm.%d.[play|rec].vchans
|
||||
tunables.
|
||||
Default is enabled.
|
||||
.It Va hw.snd.report_soft_formats
|
||||
Controls the internal format conversion if it is
|
||||
available transparently to the application software.
|
||||
|
|
@ -432,11 +430,8 @@ The recommended way to use bitperfect mode is to disable VCHANs and enable this
|
|||
sysctl.
|
||||
Default is disabled.
|
||||
.It Va dev.pcm.%d.[play|rec].vchans
|
||||
The current number of VCHANs allocated per device.
|
||||
This can be set to preallocate a certain number of VCHANs.
|
||||
Setting this value to
|
||||
.Dq 0
|
||||
will disable VCHANs for this device.
|
||||
Enable (1) or disable (0) VCHANs.
|
||||
Default is enabled.
|
||||
.It Va dev.pcm.%d.[play|rec].vchanformat
|
||||
Format for VCHAN mixing.
|
||||
All playback paths will be converted to this format before the mixing
|
||||
|
|
|
|||
|
|
@ -1172,7 +1172,7 @@ chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls,
|
|||
struct feeder_class *fc;
|
||||
struct snd_dbuf *b, *bs;
|
||||
char buf[CHN_NAMELEN];
|
||||
int i, direction;
|
||||
int err, i, direction;
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
PCM_LOCKASSERT(d);
|
||||
|
|
@ -1279,8 +1279,18 @@ chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls,
|
|||
bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_WAITOK);
|
||||
}
|
||||
|
||||
if ((c->flags & CHN_F_VIRTUAL) == 0) {
|
||||
CHN_LOCK(c);
|
||||
err = chn_reset(c, c->format, c->speed);
|
||||
CHN_UNLOCK(c);
|
||||
if (err != 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
PCM_LOCK(d);
|
||||
CHN_INSERT_SORT_ASCEND(d, c, channels.pcm);
|
||||
if ((c->flags & CHN_F_VIRTUAL) == 0)
|
||||
CHN_INSERT_SORT_ASCEND(d, c, channels.pcm.primary);
|
||||
|
||||
switch (c->type) {
|
||||
case PCMDIR_PLAY:
|
||||
|
|
@ -1332,6 +1342,8 @@ chn_kill(struct pcm_channel *c)
|
|||
|
||||
PCM_LOCK(d);
|
||||
CHN_REMOVE(d, c, channels.pcm);
|
||||
if ((c->flags & CHN_F_VIRTUAL) == 0)
|
||||
CHN_REMOVE(d, c, channels.pcm.primary);
|
||||
|
||||
switch (c->type) {
|
||||
case PCMDIR_PLAY:
|
||||
|
|
|
|||
|
|
@ -160,6 +160,9 @@ struct pcm_channel {
|
|||
struct {
|
||||
SLIST_ENTRY(pcm_channel) link;
|
||||
} opened;
|
||||
struct {
|
||||
SLIST_ENTRY(pcm_channel) link;
|
||||
} primary;
|
||||
} pcm;
|
||||
} channels;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/vchan.h>
|
||||
#include <sys/ctype.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/rwlock.h>
|
||||
|
|
@ -158,6 +159,81 @@ dsp_unlock_chans(struct dsp_cdevpriv *priv, uint32_t prio)
|
|||
CHN_UNLOCK(priv->wrch);
|
||||
}
|
||||
|
||||
static int
|
||||
dsp_chn_alloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
|
||||
int flags, struct thread *td)
|
||||
{
|
||||
struct pcm_channel *c;
|
||||
char *comm;
|
||||
pid_t pid;
|
||||
int err;
|
||||
bool vdir_enabled;
|
||||
|
||||
KASSERT(d != NULL && ch != NULL &&
|
||||
(direction == PCMDIR_PLAY || direction == PCMDIR_REC),
|
||||
("%s(): invalid d=%p ch=%p direction=%d",
|
||||
__func__, d, ch, direction));
|
||||
PCM_BUSYASSERT(d);
|
||||
|
||||
pid = td->td_proc->p_pid;
|
||||
comm = td->td_proc->p_comm;
|
||||
|
||||
vdir_enabled = (direction == PCMDIR_PLAY && d->flags & SD_F_PVCHANS) ||
|
||||
(direction == PCMDIR_REC && d->flags & SD_F_RVCHANS);
|
||||
|
||||
*ch = NULL;
|
||||
CHN_FOREACH(c, d, channels.pcm.primary) {
|
||||
CHN_LOCK(c);
|
||||
if (c->direction != direction) {
|
||||
CHN_UNLOCK(c);
|
||||
continue;
|
||||
}
|
||||
/* Find an available primary channel to use. */
|
||||
if ((c->flags & CHN_F_BUSY) == 0 ||
|
||||
(vdir_enabled && (c->flags & CHN_F_HAS_VCHAN)))
|
||||
break;
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
if (c == NULL)
|
||||
return (EBUSY);
|
||||
|
||||
/*
|
||||
* We can have the following cases:
|
||||
* - vchans are enabled, add a new vchan to the primary channel.
|
||||
* - vchans are disabled, use the primary channel directly.
|
||||
*/
|
||||
if (vdir_enabled && ((c->flags & CHN_F_BUSY) == 0 ||
|
||||
c->flags & CHN_F_HAS_VCHAN)) {
|
||||
err = vchan_create(c, ch);
|
||||
CHN_UNLOCK(c);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
CHN_LOCK(*ch);
|
||||
} else if ((c->flags & CHN_F_BUSY) == 0) {
|
||||
*ch = c;
|
||||
} else {
|
||||
CHN_UNLOCK(c);
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
(*ch)->flags |= CHN_F_BUSY;
|
||||
if (flags & O_NONBLOCK)
|
||||
(*ch)->flags |= CHN_F_NBIO;
|
||||
if (flags & O_EXCL)
|
||||
(*ch)->flags |= CHN_F_EXCLUSIVE;
|
||||
(*ch)->pid = pid;
|
||||
strlcpy((*ch)->comm, (comm != NULL) ? comm : CHN_COMM_UNKNOWN,
|
||||
sizeof((*ch)->comm));
|
||||
|
||||
if ((err = chn_reset(*ch, (*ch)->format, (*ch)->speed)) != 0)
|
||||
return (err);
|
||||
chn_vpc_reset(*ch, SND_VOL_C_PCM, 0);
|
||||
|
||||
CHN_UNLOCK(*ch);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#define DSP_F_VALID(x) ((x) & (FREAD | FWRITE))
|
||||
#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE))
|
||||
#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x))
|
||||
|
|
@ -168,7 +244,7 @@ static void
|
|||
dsp_close(void *data)
|
||||
{
|
||||
struct dsp_cdevpriv *priv = data;
|
||||
struct pcm_channel *rdch, *wrch;
|
||||
struct pcm_channel *rdch, *wrch, *parent;
|
||||
struct snddev_info *d;
|
||||
int sg_ids;
|
||||
|
||||
|
|
@ -214,12 +290,20 @@ dsp_close(void *data)
|
|||
if (sg_ids != 0)
|
||||
free_unr(pcmsg_unrhdr, sg_ids);
|
||||
|
||||
CHN_LOCK(rdch);
|
||||
chn_abort(rdch); /* won't sleep */
|
||||
rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
|
||||
CHN_F_DEAD | CHN_F_EXCLUSIVE);
|
||||
chn_reset(rdch, 0, 0);
|
||||
chn_release(rdch);
|
||||
if (rdch->flags & CHN_F_VIRTUAL) {
|
||||
parent = rdch->parentchannel;
|
||||
CHN_LOCK(parent);
|
||||
CHN_LOCK(rdch);
|
||||
vchan_destroy(rdch);
|
||||
CHN_UNLOCK(parent);
|
||||
} else {
|
||||
CHN_LOCK(rdch);
|
||||
chn_abort(rdch); /* won't sleep */
|
||||
rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
|
||||
CHN_F_DEAD | CHN_F_EXCLUSIVE);
|
||||
chn_reset(rdch, 0, 0);
|
||||
chn_release(rdch);
|
||||
}
|
||||
}
|
||||
if (wrch != NULL) {
|
||||
/*
|
||||
|
|
@ -231,12 +315,20 @@ dsp_close(void *data)
|
|||
if (sg_ids != 0)
|
||||
free_unr(pcmsg_unrhdr, sg_ids);
|
||||
|
||||
CHN_LOCK(wrch);
|
||||
chn_flush(wrch); /* may sleep */
|
||||
wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
|
||||
CHN_F_DEAD | CHN_F_EXCLUSIVE);
|
||||
chn_reset(wrch, 0, 0);
|
||||
chn_release(wrch);
|
||||
if (wrch->flags & CHN_F_VIRTUAL) {
|
||||
parent = wrch->parentchannel;
|
||||
CHN_LOCK(parent);
|
||||
CHN_LOCK(wrch);
|
||||
vchan_destroy(wrch);
|
||||
CHN_UNLOCK(parent);
|
||||
} else {
|
||||
CHN_LOCK(wrch);
|
||||
chn_flush(wrch); /* may sleep */
|
||||
wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP |
|
||||
CHN_F_DEAD | CHN_F_EXCLUSIVE);
|
||||
chn_reset(wrch, 0, 0);
|
||||
chn_release(wrch);
|
||||
}
|
||||
}
|
||||
PCM_LOCK(d);
|
||||
}
|
||||
|
|
@ -254,10 +346,9 @@ static int
|
|||
dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
||||
{
|
||||
struct dsp_cdevpriv *priv;
|
||||
struct pcm_channel *rdch, *wrch, *ch;
|
||||
struct pcm_channel *ch;
|
||||
struct snddev_info *d;
|
||||
uint32_t fmt, spd;
|
||||
int error, rderror, wrerror, dir;
|
||||
int error, dir;
|
||||
|
||||
/* Kind of impossible.. */
|
||||
if (i_dev == NULL || td == NULL)
|
||||
|
|
@ -267,11 +358,11 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
|||
if (!DSP_REGISTERED(d))
|
||||
return (EBADF);
|
||||
|
||||
if (PCM_CHANCOUNT(d) >= PCM_MAXCHANS)
|
||||
return (ENOMEM);
|
||||
|
||||
priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
priv->sc = d;
|
||||
priv->rdch = NULL;
|
||||
priv->wrch = NULL;
|
||||
priv->volch = NULL;
|
||||
|
||||
error = devfs_set_cdevpriv(priv, dsp_close);
|
||||
if (error != 0)
|
||||
|
|
@ -334,98 +425,30 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
|||
PCM_ACQUIRE(d);
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
spd = DSP_DEFAULT_SPEED;
|
||||
|
||||
rdch = NULL;
|
||||
wrch = NULL;
|
||||
rderror = 0;
|
||||
wrerror = 0;
|
||||
|
||||
if (DSP_F_READ(flags)) {
|
||||
/* open for read */
|
||||
rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC,
|
||||
td->td_proc->p_pid, td->td_proc->p_comm);
|
||||
|
||||
if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0)
|
||||
rderror = ENXIO;
|
||||
|
||||
if (rderror != 0) {
|
||||
if (rdch != NULL)
|
||||
chn_release(rdch);
|
||||
if (!DSP_F_DUPLEX(flags)) {
|
||||
PCM_RELEASE_QUICK(d);
|
||||
PCM_GIANT_EXIT(d);
|
||||
return (rderror);
|
||||
}
|
||||
rdch = NULL;
|
||||
} else {
|
||||
if (flags & O_NONBLOCK)
|
||||
rdch->flags |= CHN_F_NBIO;
|
||||
if (flags & O_EXCL)
|
||||
rdch->flags |= CHN_F_EXCLUSIVE;
|
||||
chn_vpc_reset(rdch, SND_VOL_C_PCM, 0);
|
||||
CHN_UNLOCK(rdch);
|
||||
}
|
||||
}
|
||||
|
||||
if (DSP_F_WRITE(flags)) {
|
||||
/* open for write */
|
||||
wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY,
|
||||
td->td_proc->p_pid, td->td_proc->p_comm);
|
||||
|
||||
if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0)
|
||||
wrerror = ENXIO;
|
||||
|
||||
if (wrerror != 0) {
|
||||
if (wrch != NULL)
|
||||
chn_release(wrch);
|
||||
if (!DSP_F_DUPLEX(flags)) {
|
||||
if (rdch != NULL) {
|
||||
/*
|
||||
* Lock, and release previously created
|
||||
* record channel
|
||||
*/
|
||||
CHN_LOCK(rdch);
|
||||
chn_release(rdch);
|
||||
}
|
||||
PCM_RELEASE_QUICK(d);
|
||||
PCM_GIANT_EXIT(d);
|
||||
return (wrerror);
|
||||
}
|
||||
wrch = NULL;
|
||||
} else {
|
||||
if (flags & O_NONBLOCK)
|
||||
wrch->flags |= CHN_F_NBIO;
|
||||
if (flags & O_EXCL)
|
||||
wrch->flags |= CHN_F_EXCLUSIVE;
|
||||
chn_vpc_reset(wrch, SND_VOL_C_PCM, 0);
|
||||
CHN_UNLOCK(wrch);
|
||||
error = dsp_chn_alloc(d, &priv->wrch, PCMDIR_PLAY, flags, td);
|
||||
if (error != 0) {
|
||||
PCM_RELEASE_QUICK(d);
|
||||
PCM_GIANT_EXIT(d);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
PCM_LOCK(d);
|
||||
|
||||
if (wrch == NULL && rdch == NULL) {
|
||||
PCM_RELEASE(d);
|
||||
PCM_LOCK(d);
|
||||
CHN_INSERT_HEAD(d, priv->wrch, channels.pcm.opened);
|
||||
PCM_UNLOCK(d);
|
||||
}
|
||||
if (DSP_F_READ(flags)) {
|
||||
error = dsp_chn_alloc(d, &priv->rdch, PCMDIR_REC, flags, td);
|
||||
if (error != 0) {
|
||||
PCM_RELEASE_QUICK(d);
|
||||
PCM_GIANT_EXIT(d);
|
||||
return (error);
|
||||
}
|
||||
PCM_LOCK(d);
|
||||
CHN_INSERT_HEAD(d, priv->rdch, channels.pcm.opened);
|
||||
PCM_UNLOCK(d);
|
||||
PCM_GIANT_EXIT(d);
|
||||
if (wrerror != 0)
|
||||
return (wrerror);
|
||||
if (rderror != 0)
|
||||
return (rderror);
|
||||
return (EINVAL);
|
||||
}
|
||||
if (rdch != NULL)
|
||||
CHN_INSERT_HEAD(d, rdch, channels.pcm.opened);
|
||||
if (wrch != NULL)
|
||||
CHN_INSERT_HEAD(d, wrch, channels.pcm.opened);
|
||||
priv->rdch = rdch;
|
||||
priv->wrch = wrch;
|
||||
|
||||
PCM_RELEASE(d);
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
PCM_RELEASE_QUICK(d);
|
||||
PCM_GIANT_LEAVE(d);
|
||||
|
||||
return (0);
|
||||
|
|
|
|||
|
|
@ -107,62 +107,6 @@ snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand
|
|||
return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep);
|
||||
}
|
||||
|
||||
int
|
||||
pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
|
||||
pid_t pid, char *comm)
|
||||
{
|
||||
struct pcm_channel *c;
|
||||
int err, vchancount;
|
||||
bool retry;
|
||||
|
||||
KASSERT(d != NULL && ch != NULL &&
|
||||
(direction == PCMDIR_PLAY || direction == PCMDIR_REC),
|
||||
("%s(): invalid d=%p ch=%p direction=%d pid=%d",
|
||||
__func__, d, ch, direction, pid));
|
||||
PCM_BUSYASSERT(d);
|
||||
|
||||
*ch = NULL;
|
||||
vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
|
||||
d->rvchancount;
|
||||
retry = false;
|
||||
|
||||
retry_chnalloc:
|
||||
/* Scan for a free channel. */
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
CHN_LOCK(c);
|
||||
if (c->direction != direction) {
|
||||
CHN_UNLOCK(c);
|
||||
continue;
|
||||
}
|
||||
if (!(c->flags & CHN_F_BUSY)) {
|
||||
c->flags |= CHN_F_BUSY;
|
||||
c->pid = pid;
|
||||
strlcpy(c->comm, (comm != NULL) ? comm :
|
||||
CHN_COMM_UNKNOWN, sizeof(c->comm));
|
||||
*ch = c;
|
||||
|
||||
return (0);
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
/* Maybe next time... */
|
||||
if (retry)
|
||||
return (EBUSY);
|
||||
|
||||
/* No channel available. We also cannot create more VCHANs. */
|
||||
if (!(vchancount > 0 && vchancount < snd_maxautovchans))
|
||||
return (ENOTSUP);
|
||||
|
||||
/* Increase the VCHAN count and try to get the new channel. */
|
||||
err = vchan_setnew(d, direction, vchancount + 1);
|
||||
if (err == 0) {
|
||||
retry = true;
|
||||
goto retry_chnalloc;
|
||||
}
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
|
|
@ -303,7 +247,10 @@ pcm_setstatus(device_t dev, char *str)
|
|||
if (d->playcount > 0 || d->reccount > 0)
|
||||
d->flags |= SD_F_AUTOVCHAN;
|
||||
|
||||
vchan_setmaxauto(d, snd_maxautovchans);
|
||||
if (d->playcount > 0)
|
||||
d->flags |= SD_F_PVCHANS;
|
||||
if (d->reccount > 0)
|
||||
d->flags |= SD_F_RVCHANS;
|
||||
|
||||
strlcpy(d->status, str, SND_STATUSLEN);
|
||||
sndstat_register(dev, d->status);
|
||||
|
|
@ -516,6 +463,7 @@ pcm_register(device_t dev, void *devinfo, int numplay __unused,
|
|||
CHN_INIT(d, channels.pcm);
|
||||
CHN_INIT(d, channels.pcm.busy);
|
||||
CHN_INIT(d, channels.pcm.opened);
|
||||
CHN_INIT(d, channels.pcm.primary);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
|
@ -732,9 +680,7 @@ sound_global_init(void)
|
|||
if (snd_unit < 0)
|
||||
snd_unit = -1;
|
||||
|
||||
if (snd_maxautovchans < 0 ||
|
||||
snd_maxautovchans > SND_MAXVCHANS)
|
||||
snd_maxautovchans = 0;
|
||||
snd_vchans_enable = true;
|
||||
|
||||
if (chn_latency < CHN_LATENCY_MIN ||
|
||||
chn_latency > CHN_LATENCY_MAX)
|
||||
|
|
@ -758,11 +704,11 @@ sound_global_init(void)
|
|||
feeder_rate_round = FEEDRATE_ROUNDHZ;
|
||||
|
||||
if (bootverbose)
|
||||
printf("%s: snd_unit=%d snd_maxautovchans=%d "
|
||||
printf("%s: snd_unit=%d snd_vchans_enable=%d "
|
||||
"latency=%d "
|
||||
"feeder_rate_min=%d feeder_rate_max=%d "
|
||||
"feeder_rate_round=%d\n",
|
||||
__func__, snd_unit, snd_maxautovchans,
|
||||
__func__, snd_unit, snd_vchans_enable,
|
||||
chn_latency,
|
||||
feeder_rate_min, feeder_rate_max,
|
||||
feeder_rate_round);
|
||||
|
|
|
|||
|
|
@ -99,8 +99,6 @@ struct snd_mixer;
|
|||
#define SOUND_PREFVER SOUND_MODVER
|
||||
#define SOUND_MAXVER SOUND_MODVER
|
||||
|
||||
#define SND_MAXVCHANS 256
|
||||
|
||||
#define SD_F_SIMPLEX 0x00000001
|
||||
#define SD_F_AUTOVCHAN 0x00000002
|
||||
#define SD_F_SOFTPCMVOL 0x00000004
|
||||
|
|
@ -113,6 +111,8 @@ struct snd_mixer;
|
|||
#define SD_F_EQ_ENABLED 0x00000200 /* EQ enabled */
|
||||
#define SD_F_EQ_BYPASSED 0x00000400 /* EQ bypassed */
|
||||
#define SD_F_EQ_PC 0x00000800 /* EQ per-channel */
|
||||
#define SD_F_PVCHANS 0x00001000 /* Playback vchans enabled */
|
||||
#define SD_F_RVCHANS 0x00002000 /* Recording vchans enabled */
|
||||
|
||||
#define SD_F_EQ_DEFAULT (SD_F_EQ | SD_F_EQ_ENABLED)
|
||||
#define SD_F_EQ_MASK (SD_F_EQ | SD_F_EQ_ENABLED | \
|
||||
|
|
@ -134,12 +134,15 @@ struct snd_mixer;
|
|||
"\012EQ_ENABLED" \
|
||||
"\013EQ_BYPASSED" \
|
||||
"\014EQ_PC" \
|
||||
"\015PVCHANS" \
|
||||
"\016RVCHANS" \
|
||||
"\035PRIO_RD" \
|
||||
"\036PRIO_WR"
|
||||
|
||||
#define PCM_ALIVE(x) ((x) != NULL && (x)->lock != NULL)
|
||||
#define PCM_REGISTERED(x) (PCM_ALIVE(x) && ((x)->flags & SD_F_REGISTERED))
|
||||
|
||||
#define PCM_MAXCHANS 10000
|
||||
#define PCM_CHANCOUNT(d) \
|
||||
(d->playcount + d->pvchancount + d->reccount + d->rvchancount)
|
||||
|
||||
|
|
@ -168,9 +171,6 @@ extern struct unrhdr *pcmsg_unrhdr;
|
|||
|
||||
SYSCTL_DECL(_hw_snd);
|
||||
|
||||
int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
|
||||
pid_t pid, char *comm);
|
||||
|
||||
int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo);
|
||||
unsigned int pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz);
|
||||
int pcm_register(device_t dev, void *devinfo, int numplay, int numrec);
|
||||
|
|
@ -224,6 +224,9 @@ struct snddev_info {
|
|||
struct {
|
||||
SLIST_HEAD(, pcm_channel) head;
|
||||
} opened;
|
||||
struct {
|
||||
SLIST_HEAD(, pcm_channel) head;
|
||||
} primary;
|
||||
} pcm;
|
||||
} channels;
|
||||
unsigned playcount, reccount, pvchancount, rvchancount;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ struct vchan_info {
|
|||
int trigger;
|
||||
};
|
||||
|
||||
int snd_maxautovchans = 16;
|
||||
bool snd_vchans_enable = true;
|
||||
|
||||
static void *
|
||||
vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
|
||||
|
|
@ -277,7 +277,7 @@ vchan_getparentchannel(struct snddev_info *d,
|
|||
*ch = NULL;
|
||||
break;
|
||||
}
|
||||
} else if (c->flags & CHN_F_HAS_VCHAN) {
|
||||
} else {
|
||||
/* No way!! */
|
||||
if (*ch != NULL) {
|
||||
CHN_UNLOCK(c);
|
||||
|
|
@ -299,8 +299,7 @@ static int
|
|||
sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
int direction, vchancount;
|
||||
int err, cnt;
|
||||
int err, enabled, flag;
|
||||
|
||||
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
|
||||
if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
|
||||
|
|
@ -311,43 +310,44 @@ sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)
|
|||
|
||||
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||
case VCHAN_PLAY:
|
||||
direction = PCMDIR_PLAY;
|
||||
vchancount = d->pvchancount;
|
||||
cnt = d->playcount;
|
||||
/* Exit if we do not support this direction. */
|
||||
if (d->playcount < 1) {
|
||||
PCM_UNLOCK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
flag = SD_F_PVCHANS;
|
||||
break;
|
||||
case VCHAN_REC:
|
||||
direction = PCMDIR_REC;
|
||||
vchancount = d->rvchancount;
|
||||
cnt = d->reccount;
|
||||
if (d->reccount < 1) {
|
||||
PCM_UNLOCK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
flag = SD_F_RVCHANS;
|
||||
break;
|
||||
default:
|
||||
PCM_UNLOCK(d);
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cnt < 1) {
|
||||
PCM_UNLOCK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
enabled = (d->flags & flag) != 0;
|
||||
|
||||
PCM_ACQUIRE(d);
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
cnt = vchancount;
|
||||
err = sysctl_handle_int(oidp, &cnt, 0, req);
|
||||
|
||||
if (err == 0 && req->newptr != NULL && vchancount != cnt) {
|
||||
if (cnt < 0)
|
||||
cnt = 0;
|
||||
if (cnt > SND_MAXVCHANS)
|
||||
cnt = SND_MAXVCHANS;
|
||||
err = vchan_setnew(d, direction, cnt);
|
||||
err = sysctl_handle_int(oidp, &enabled, 0, req);
|
||||
if (err != 0 || req->newptr == NULL) {
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (err);
|
||||
}
|
||||
|
||||
if (enabled <= 0)
|
||||
d->flags &= ~flag;
|
||||
else
|
||||
d->flags |= flag;
|
||||
|
||||
PCM_RELEASE_QUICK(d);
|
||||
|
||||
return err;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -368,15 +368,22 @@ sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)
|
|||
|
||||
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||
case VCHAN_PLAY:
|
||||
if ((d->flags & SD_F_PVCHANS) == 0) {
|
||||
PCM_UNLOCK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
direction = PCMDIR_PLAY;
|
||||
break;
|
||||
case VCHAN_REC:
|
||||
if ((d->flags & SD_F_RVCHANS) == 0) {
|
||||
PCM_UNLOCK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
direction = PCMDIR_REC;
|
||||
break;
|
||||
default:
|
||||
PCM_UNLOCK(d);
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
PCM_ACQUIRE(d);
|
||||
|
|
@ -450,7 +457,7 @@ sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
|
|||
struct snddev_info *d;
|
||||
struct pcm_channel *c, *ch;
|
||||
struct pcmchan_caps *caps;
|
||||
int *vchanrate, vchancount, direction, ret, newspd, restart;
|
||||
int *vchanrate, direction, ret, newspd, restart;
|
||||
|
||||
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
|
||||
if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
|
||||
|
|
@ -461,24 +468,24 @@ sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)
|
|||
|
||||
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||
case VCHAN_PLAY:
|
||||
if ((d->flags & SD_F_PVCHANS) == 0) {
|
||||
PCM_UNLOCK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
direction = PCMDIR_PLAY;
|
||||
vchancount = d->pvchancount;
|
||||
vchanrate = &d->pvchanrate;
|
||||
break;
|
||||
case VCHAN_REC:
|
||||
if ((d->flags & SD_F_RVCHANS) == 0) {
|
||||
PCM_UNLOCK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
direction = PCMDIR_REC;
|
||||
vchancount = d->rvchancount;
|
||||
vchanrate = &d->rvchanrate;
|
||||
break;
|
||||
default:
|
||||
PCM_UNLOCK(d);
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (vchancount < 1) {
|
||||
PCM_UNLOCK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
PCM_ACQUIRE(d);
|
||||
|
|
@ -555,7 +562,7 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
|
|||
struct snddev_info *d;
|
||||
struct pcm_channel *c, *ch;
|
||||
uint32_t newfmt;
|
||||
int *vchanformat, vchancount, direction, ret, restart;
|
||||
int *vchanformat, direction, ret, restart;
|
||||
char fmtstr[AFMTSTR_LEN];
|
||||
|
||||
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
|
||||
|
|
@ -567,24 +574,24 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
|
|||
|
||||
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||
case VCHAN_PLAY:
|
||||
if ((d->flags & SD_F_PVCHANS) == 0) {
|
||||
PCM_UNLOCK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
direction = PCMDIR_PLAY;
|
||||
vchancount = d->pvchancount;
|
||||
vchanformat = &d->pvchanformat;
|
||||
break;
|
||||
case VCHAN_REC:
|
||||
if ((d->flags & SD_F_RVCHANS) == 0) {
|
||||
PCM_UNLOCK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
direction = PCMDIR_REC;
|
||||
vchancount = d->rvchancount;
|
||||
vchanformat = &d->rvchanformat;
|
||||
break;
|
||||
default:
|
||||
PCM_UNLOCK(d);
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (vchancount < 1) {
|
||||
PCM_UNLOCK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
PCM_ACQUIRE(d);
|
||||
|
|
@ -660,7 +667,7 @@ sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)
|
|||
"play.vchanrate" : "rec.vchanrate"
|
||||
|
||||
int
|
||||
vchan_create(struct pcm_channel *parent)
|
||||
vchan_create(struct pcm_channel *parent, struct pcm_channel **child)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct pcm_channel *ch;
|
||||
|
|
@ -676,9 +683,6 @@ vchan_create(struct pcm_channel *parent)
|
|||
PCM_BUSYASSERT(d);
|
||||
CHN_LOCKASSERT(parent);
|
||||
|
||||
if (!(parent->flags & CHN_F_BUSY))
|
||||
return (EBUSY);
|
||||
|
||||
if (!(parent->direction == PCMDIR_PLAY ||
|
||||
parent->direction == PCMDIR_REC))
|
||||
return (EINVAL);
|
||||
|
|
@ -713,10 +717,12 @@ vchan_create(struct pcm_channel *parent)
|
|||
*/
|
||||
CHN_INSERT_SORT_DESCEND(parent, ch, children);
|
||||
|
||||
*child = ch;
|
||||
|
||||
if (parent->flags & CHN_F_HAS_VCHAN)
|
||||
return (0);
|
||||
|
||||
parent->flags |= CHN_F_HAS_VCHAN;
|
||||
parent->flags |= CHN_F_HAS_VCHAN | CHN_F_BUSY;
|
||||
|
||||
parent_caps = chn_getcaps(parent);
|
||||
if (parent_caps == NULL) {
|
||||
|
|
@ -807,6 +813,7 @@ vchan_create(struct pcm_channel *parent)
|
|||
fail:
|
||||
CHN_LOCK(ch);
|
||||
vchan_destroy(ch);
|
||||
*child = NULL;
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
|
@ -878,166 +885,40 @@ vchan_sync(struct pcm_channel *c)
|
|||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
vchan_setnew(struct snddev_info *d, int direction, int newcnt)
|
||||
{
|
||||
struct pcm_channel *c, *ch, *nch;
|
||||
struct pcmchan_caps *caps;
|
||||
int i, err, vcnt;
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
|
||||
if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
|
||||
(direction == PCMDIR_REC && d->reccount < 1))
|
||||
return (ENODEV);
|
||||
|
||||
if (!(d->flags & SD_F_AUTOVCHAN))
|
||||
return (EINVAL);
|
||||
|
||||
if (newcnt < 0 || newcnt > SND_MAXVCHANS)
|
||||
return (E2BIG);
|
||||
|
||||
if (direction == PCMDIR_PLAY)
|
||||
vcnt = d->pvchancount;
|
||||
else if (direction == PCMDIR_REC)
|
||||
vcnt = d->rvchancount;
|
||||
else
|
||||
return (EINVAL);
|
||||
|
||||
if (newcnt > vcnt) {
|
||||
/* add new vchans - find a parent channel first */
|
||||
ch = NULL;
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
CHN_LOCK(c);
|
||||
if (c->direction == direction &&
|
||||
((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
|
||||
!(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
|
||||
/*
|
||||
* Reuse hw channel with vchans already
|
||||
* created.
|
||||
*/
|
||||
if (c->flags & CHN_F_HAS_VCHAN) {
|
||||
ch = c;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* No vchans ever created, look for
|
||||
* channels with supported formats.
|
||||
*/
|
||||
caps = chn_getcaps(c);
|
||||
if (caps == NULL) {
|
||||
CHN_UNLOCK(c);
|
||||
continue;
|
||||
}
|
||||
for (i = 0; caps->fmtlist[i] != 0; i++) {
|
||||
if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
|
||||
break;
|
||||
}
|
||||
if (caps->fmtlist[i] != 0) {
|
||||
ch = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
if (ch == NULL)
|
||||
return (EBUSY);
|
||||
ch->flags |= CHN_F_BUSY;
|
||||
err = 0;
|
||||
while (err == 0 && newcnt > vcnt) {
|
||||
err = vchan_create(ch);
|
||||
if (err == 0)
|
||||
vcnt++;
|
||||
else if (err == E2BIG && newcnt > vcnt)
|
||||
device_printf(d->dev,
|
||||
"%s: err=%d Maximum channel reached.\n",
|
||||
__func__, err);
|
||||
}
|
||||
if (vcnt == 0)
|
||||
ch->flags &= ~CHN_F_BUSY;
|
||||
CHN_UNLOCK(ch);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
} else if (newcnt < vcnt) {
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
CHN_LOCK(c);
|
||||
if (c->direction != direction ||
|
||||
CHN_EMPTY(c, children) ||
|
||||
!(c->flags & CHN_F_HAS_VCHAN)) {
|
||||
CHN_UNLOCK(c);
|
||||
continue;
|
||||
}
|
||||
CHN_FOREACH_SAFE(ch, c, nch, children) {
|
||||
CHN_LOCK(ch);
|
||||
if (vcnt == 1 && ch->flags & CHN_F_BUSY) {
|
||||
CHN_UNLOCK(ch);
|
||||
break;
|
||||
}
|
||||
if (!(ch->flags & CHN_F_BUSY)) {
|
||||
err = vchan_destroy(ch);
|
||||
if (err == 0)
|
||||
vcnt--;
|
||||
} else
|
||||
CHN_UNLOCK(ch);
|
||||
if (vcnt == newcnt)
|
||||
break;
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -1053,7 +934,7 @@ vchan_initsys(device_t dev)
|
|||
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",
|
||||
|
|
@ -1079,7 +960,7 @@ vchan_initsys(device_t dev)
|
|||
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",
|
||||
|
|
|
|||
|
|
@ -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 @@ int vchan_sync(struct pcm_channel *);
|
|||
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);
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue