mirror of
https://github.com/opnsense/src.git
synced 2026-02-13 07:44:48 -05:00
Last (again ?!?) major commit for RELENG_7, featuring total Giant
eradication in/from userland path, countless locking fixes, etc.
- General sleep call through msleep(9) has been converted to condvar(9)
with better consistencies.
- Heavily guard every possible "slow path" entries (open(), close(),
few ioctl()s, sysctls), but once it entering "fast path" (io, interrupt
started), they are free to fly on their own.
- Rearrange locking sequences, resulting better concurrency and
serialization. Large part doesn't even need locking at all, and will be
removed in future. Less clutter, except in few places due to lock
ordering.
- Anonymous mixer object creation/deletion to simplify mixer handling
beyond typical mixer ioctls.
Submitted by: chibis (with modifications)
- Add few mix_[get|set|..] functions to avoid calling mixer_ioctl()
directly using cryptic arguments.
- Locking fixes to avoid possible deadlock with (still under Giant) USB.
- Better simplex/duplex device handling.
- Recover mmap() functionality for recording, which has been lost
since 2.2.x - 3.x (the introduction of newpcm). Full-duplex mmap still
doesn't work (due to VM/page design), but people still can mmap
both by opening each direction separately. mmaped playback is guarantee
to work either way.
- New sysctl: "hw.snd.compat_linux_mmap" to allow PROT_EXEC page
mapping, due to recent changes in linux compatibility layer which
require it. All linux applications that using sound + mmap() (mostly games)
require this to be enabled. Disabled by default.
- Other goodies.. too many, that will increase releng7 shareholder value
and make users of releng6 (and below) cry ;)
* This commit should be atomic. If anything goes wrong (not counting problem
originated from elsewhere), I will not hesitate to revert everything back
within 12 hours. This substantial changes itself not a rocket science
and the process has begun for almost 2 years, and lots of incremental
changes are already in place during that period of time.
* Some issues does occur in snd_emu10kx (note the 'x') due to various
internal locking issues and it is currently being worked on by chibis.
Tested by: chibis (Yuriy Tsibizov), joel, Alexandre Vieira,
many innocent souls...
This commit is contained in:
parent
48dabb921d
commit
e4e61333ff
16 changed files with 2041 additions and 1251 deletions
|
|
@ -1480,14 +1480,14 @@ sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS)
|
|||
struct es_info *es;
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
struct cdev *i_dev;
|
||||
device_t dev;
|
||||
uint32_t val, set;
|
||||
int recsrc, level, err;
|
||||
|
||||
dev = oidp->oid_arg1;
|
||||
d = device_get_softc(dev);
|
||||
if (d == NULL || d->mixer_dev == NULL || d->mixer_dev->si_drv1 == NULL)
|
||||
if (!PCM_REGISTERED(d) || d->mixer_dev == NULL ||
|
||||
d->mixer_dev->si_drv1 == NULL)
|
||||
return (EINVAL);
|
||||
es = d->devinfo;
|
||||
if (es == NULL)
|
||||
|
|
@ -1504,22 +1504,27 @@ sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS)
|
|||
return (EINVAL);
|
||||
if (val == set)
|
||||
return (0);
|
||||
i_dev = d->mixer_dev;
|
||||
if (mixer_ioctl(i_dev, 0, (caddr_t)&recsrc, 0, NULL) != EBADF)
|
||||
PCM_ACQUIRE_QUICK(d);
|
||||
m = (d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 : NULL;
|
||||
if (m == NULL) {
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
if (mixer_busy(m) != 0) {
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EBUSY);
|
||||
err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&level,
|
||||
-1, NULL);
|
||||
if (!err)
|
||||
err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_RECSRC),
|
||||
(caddr_t)&recsrc, -1, NULL);
|
||||
if (err)
|
||||
return (err);
|
||||
if (level < 0)
|
||||
return (EINVAL);
|
||||
}
|
||||
level = mix_get(m, SOUND_MIXER_PCM);
|
||||
recsrc = mix_getrecsrc(m);
|
||||
if (level < 0 || recsrc < 0) {
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
ES_LOCK(es);
|
||||
if (es->ctrl & (CTRL_ADC_EN | CTRL_DAC1_EN | CTRL_DAC2_EN)) {
|
||||
ES_UNLOCK(es);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EBUSY);
|
||||
}
|
||||
if (val)
|
||||
|
|
@ -1527,20 +1532,16 @@ sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS)
|
|||
else
|
||||
es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 0);
|
||||
ES_UNLOCK(es);
|
||||
m = i_dev->si_drv1;
|
||||
if (!val) {
|
||||
mix_setdevs(m, mix_getdevs(d->mixer_dev->si_drv1) |
|
||||
(1 << SOUND_MIXER_SYNTH));
|
||||
mix_setrecdevs(m, mix_getrecdevs(d->mixer_dev->si_drv1) |
|
||||
(1 << SOUND_MIXER_SYNTH));
|
||||
err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_SYNTH),
|
||||
(caddr_t)&level, -1, NULL);
|
||||
mix_setdevs(m, mix_getdevs(m) | (1 << SOUND_MIXER_SYNTH));
|
||||
mix_setrecdevs(m, mix_getrecdevs(m) | (1 << SOUND_MIXER_SYNTH));
|
||||
err = mix_set(m, SOUND_MIXER_SYNTH, level & 0x7f,
|
||||
(level >> 8) & 0x7f);
|
||||
} else {
|
||||
err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_SYNTH),
|
||||
(caddr_t)&level, -1, NULL);
|
||||
mix_setdevs(m, mix_getdevs(d->mixer_dev->si_drv1) &
|
||||
~(1 << SOUND_MIXER_SYNTH));
|
||||
mix_setrecdevs(m, mix_getrecdevs(d->mixer_dev->si_drv1) &
|
||||
err = mix_set(m, SOUND_MIXER_SYNTH, level & 0x7f,
|
||||
(level >> 8) & 0x7f);
|
||||
mix_setdevs(m, mix_getdevs(m) & ~(1 << SOUND_MIXER_SYNTH));
|
||||
mix_setrecdevs(m, mix_getrecdevs(m) &
|
||||
~(1 << SOUND_MIXER_SYNTH));
|
||||
}
|
||||
if (!err) {
|
||||
|
|
@ -1550,10 +1551,11 @@ sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS)
|
|||
else if (recsrc & (1 << SOUND_MIXER_SYNTH))
|
||||
recsrc |= 1 << SOUND_MIXER_PCM;
|
||||
if (level != recsrc)
|
||||
err = mixer_ioctl(i_dev,
|
||||
MIXER_WRITE(SOUND_MIXER_RECSRC),
|
||||
(caddr_t)&recsrc, -1, NULL);
|
||||
err = mix_setrecsrc(m, recsrc);
|
||||
}
|
||||
|
||||
PCM_RELEASE_QUICK(d);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
|
|||
|
||||
bufsize = blkcnt * blksz;
|
||||
|
||||
if (b->tmpbuf == NULL || bufsize > b->allocsize ||
|
||||
if (bufsize > b->allocsize ||
|
||||
bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) {
|
||||
allocsize = round_page(bufsize);
|
||||
chn_unlock(b->channel);
|
||||
|
|
|
|||
|
|
@ -33,30 +33,6 @@
|
|||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
|
||||
#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
|
||||
#if 0
|
||||
#define DMA_ALIGN_THRESHOLD 4
|
||||
#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1))
|
||||
#endif
|
||||
|
||||
#define CHN_STARTED(c) ((c)->flags & CHN_F_TRIGGERED)
|
||||
#define CHN_STOPPED(c) (!CHN_STARTED(c))
|
||||
#define CHN_DIRSTR(c) (((c)->direction == PCMDIR_PLAY) ? \
|
||||
"PCMDIR_PLAY" : "PCMDIR_REC")
|
||||
|
||||
#define BUF_PARENT(c, b) \
|
||||
(((c) != NULL && (c)->parentchannel != NULL && \
|
||||
(c)->parentchannel->bufhard != NULL) ? \
|
||||
(c)->parentchannel->bufhard : (b))
|
||||
|
||||
#define CHN_TIMEOUT 5
|
||||
#define CHN_TIMEOUT_MIN 1
|
||||
#define CHN_TIMEOUT_MAX 10
|
||||
|
||||
/*
|
||||
#define DEB(x) x
|
||||
*/
|
||||
|
||||
int report_soft_formats = 1;
|
||||
SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
|
||||
&report_soft_formats, 1, "report software-emulated formats");
|
||||
|
|
@ -166,32 +142,44 @@ static int chn_buildfeeder(struct pcm_channel *c);
|
|||
static void
|
||||
chn_lockinit(struct pcm_channel *c, int dir)
|
||||
{
|
||||
switch(dir) {
|
||||
switch (dir) {
|
||||
case PCMDIR_PLAY:
|
||||
c->lock = snd_mtxcreate(c->name, "pcm play channel");
|
||||
cv_init(&c->intr_cv, "pcmwr");
|
||||
break;
|
||||
case PCMDIR_PLAY_VIRTUAL:
|
||||
c->lock = snd_mtxcreate(c->name, "pcm virtual play channel");
|
||||
cv_init(&c->intr_cv, "pcmwrv");
|
||||
break;
|
||||
case PCMDIR_REC:
|
||||
c->lock = snd_mtxcreate(c->name, "pcm record channel");
|
||||
cv_init(&c->intr_cv, "pcmrd");
|
||||
break;
|
||||
case PCMDIR_REC_VIRTUAL:
|
||||
c->lock = snd_mtxcreate(c->name, "pcm virtual record channel");
|
||||
cv_init(&c->intr_cv, "pcmrdv");
|
||||
break;
|
||||
case 0:
|
||||
c->lock = snd_mtxcreate(c->name, "pcm fake channel");
|
||||
cv_init(&c->intr_cv, "pcmfk");
|
||||
break;
|
||||
}
|
||||
|
||||
cv_init(&c->cv, c->name);
|
||||
cv_init(&c->cv, "pcmchn");
|
||||
}
|
||||
|
||||
static void
|
||||
chn_lockdestroy(struct pcm_channel *c)
|
||||
{
|
||||
snd_mtxfree(c->lock);
|
||||
CHN_LOCKASSERT(c);
|
||||
|
||||
CHN_BROADCAST(&c->cv);
|
||||
CHN_BROADCAST(&c->intr_cv);
|
||||
|
||||
cv_destroy(&c->cv);
|
||||
cv_destroy(&c->intr_cv);
|
||||
|
||||
snd_mtxfree(c->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -236,18 +224,23 @@ chn_pollreset(struct pcm_channel *c)
|
|||
static void
|
||||
chn_wakeup(struct pcm_channel *c)
|
||||
{
|
||||
struct snd_dbuf *bs = c->bufsoft;
|
||||
struct snd_dbuf *bs;
|
||||
struct pcm_channel *ch;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
if (CHN_EMPTY(c, children)) {
|
||||
|
||||
bs = c->bufsoft;
|
||||
|
||||
if (CHN_EMPTY(c, children.busy)) {
|
||||
if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
|
||||
selwakeuppri(sndbuf_getsel(bs), PRIBIO);
|
||||
} else if (CHN_EMPTY(c, children.busy)) {
|
||||
CHN_FOREACH(ch, c, children) {
|
||||
CHN_LOCK(ch);
|
||||
chn_wakeup(ch);
|
||||
CHN_UNLOCK(ch);
|
||||
if (c->flags & CHN_F_SLEEPING) {
|
||||
/*
|
||||
* Ok, I can just panic it right here since it is
|
||||
* quite obvious that we never allow multiple waiters
|
||||
* from userland. I'm too generous...
|
||||
*/
|
||||
CHN_BROADCAST(&c->intr_cv);
|
||||
}
|
||||
} else {
|
||||
CHN_FOREACH(ch, c, children.busy) {
|
||||
|
|
@ -256,27 +249,23 @@ chn_wakeup(struct pcm_channel *c)
|
|||
CHN_UNLOCK(ch);
|
||||
}
|
||||
}
|
||||
if (c->flags & CHN_F_SLEEPING)
|
||||
wakeup_one(bs);
|
||||
}
|
||||
|
||||
static int
|
||||
chn_sleep(struct pcm_channel *c, char *str, int timeout)
|
||||
chn_sleep(struct pcm_channel *c, int timeout)
|
||||
{
|
||||
struct snd_dbuf *bs = c->bufsoft;
|
||||
int ret;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
|
||||
if (c->flags & CHN_F_DEAD)
|
||||
return (EINVAL);
|
||||
|
||||
c->flags |= CHN_F_SLEEPING;
|
||||
#ifdef USING_MUTEX
|
||||
ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout);
|
||||
#else
|
||||
ret = tsleep(bs, PRIBIO | PCATCH, str, timeout);
|
||||
#endif
|
||||
ret = cv_timedwait_sig(&c->intr_cv, c->lock, timeout);
|
||||
c->flags &= ~CHN_F_SLEEPING;
|
||||
|
||||
return ret;
|
||||
return ((c->flags & CHN_F_DEAD) ? EINVAL : ret);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -298,13 +287,6 @@ chn_dmaupdate(struct pcm_channel *c)
|
|||
delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
|
||||
sndbuf_sethwptr(b, hwptr);
|
||||
|
||||
DEB(
|
||||
if (delta >= ((sndbuf_getsize(b) * 15) / 16)) {
|
||||
if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING)))
|
||||
device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr);
|
||||
}
|
||||
);
|
||||
|
||||
if (c->direction == PCMDIR_PLAY) {
|
||||
amt = min(delta, sndbuf_getready(b));
|
||||
amt -= amt % sndbuf_getbps(b);
|
||||
|
|
@ -354,23 +336,11 @@ chn_wrfeed(struct pcm_channel *c)
|
|||
unsigned int ret, amt;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
#if 0
|
||||
DEB(
|
||||
if (c->flags & CHN_F_CLOSING) {
|
||||
sndbuf_dump(b, "b", 0x02);
|
||||
sndbuf_dump(bs, "bs", 0x02);
|
||||
})
|
||||
#endif
|
||||
|
||||
if ((c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_CLOSING))
|
||||
sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
|
||||
|
||||
amt = sndbuf_getfree(b);
|
||||
DEB(if (amt > sndbuf_getsize(bs) &&
|
||||
sndbuf_getbps(bs) >= sndbuf_getbps(b)) {
|
||||
printf("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
|
||||
amt, sndbuf_getsize(bs), c->flags);
|
||||
});
|
||||
|
||||
ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
|
||||
/*
|
||||
|
|
@ -439,8 +409,11 @@ chn_write(struct pcm_channel *c, struct uio *buf)
|
|||
sndbuf_acquire(bs, NULL, t);
|
||||
}
|
||||
ret = 0;
|
||||
if (CHN_STOPPED(c))
|
||||
chn_start(c, 0);
|
||||
if (CHN_STOPPED(c)) {
|
||||
ret = chn_start(c, 0);
|
||||
if (ret != 0)
|
||||
c->flags |= CHN_F_DEAD;
|
||||
}
|
||||
} else if (c->flags & (CHN_F_NBIO | CHN_F_NOTRIGGER)) {
|
||||
/**
|
||||
* @todo Evaluate whether EAGAIN is truly desirable.
|
||||
|
|
@ -454,7 +427,7 @@ chn_write(struct pcm_channel *c, struct uio *buf)
|
|||
*/
|
||||
ret = EAGAIN;
|
||||
} else {
|
||||
ret = chn_sleep(c, "pcmwr", timeout);
|
||||
ret = chn_sleep(c, timeout);
|
||||
if (ret == EAGAIN) {
|
||||
ret = EINVAL;
|
||||
c->flags |= CHN_F_DEAD;
|
||||
|
|
@ -465,26 +438,9 @@ chn_write(struct pcm_channel *c, struct uio *buf)
|
|||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int
|
||||
chn_rddump(struct pcm_channel *c, unsigned int cnt)
|
||||
{
|
||||
struct snd_dbuf *b = c->bufhard;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
#if 0
|
||||
static u_int32_t kk = 0;
|
||||
printf("%u: dumping %d bytes\n", ++kk, cnt);
|
||||
#endif
|
||||
c->xruns++;
|
||||
sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
|
||||
return sndbuf_dispose(b, NULL, cnt);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Feed new data from the read buffer. Can be called in the bottom half.
|
||||
*/
|
||||
|
|
@ -496,19 +452,10 @@ chn_rdfeed(struct pcm_channel *c)
|
|||
unsigned int ret, amt;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
DEB(
|
||||
if (c->flags & CHN_F_CLOSING) {
|
||||
sndbuf_dump(b, "b", 0x02);
|
||||
sndbuf_dump(bs, "bs", 0x02);
|
||||
})
|
||||
|
||||
#if 0
|
||||
amt = sndbuf_getready(b);
|
||||
if (sndbuf_getfree(bs) < amt) {
|
||||
c->xruns++;
|
||||
amt = sndbuf_getfree(bs);
|
||||
}
|
||||
#endif
|
||||
if (c->flags & CHN_F_MAPPED)
|
||||
sndbuf_dispose(bs, NULL, sndbuf_getready(bs));
|
||||
|
||||
amt = sndbuf_getfree(bs);
|
||||
ret = (amt > 0) ? sndbuf_feed(b, bs, c, c->feeder, amt) : ENOSPC;
|
||||
|
||||
|
|
@ -573,8 +520,13 @@ chn_read(struct pcm_channel *c, struct uio *buf)
|
|||
|
||||
CHN_LOCKASSERT(c);
|
||||
|
||||
if (CHN_STOPPED(c))
|
||||
chn_start(c, 0);
|
||||
if (CHN_STOPPED(c)) {
|
||||
ret = chn_start(c, 0);
|
||||
if (ret != 0) {
|
||||
c->flags |= CHN_F_DEAD;
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
timeout = chn_timeout * hz;
|
||||
|
|
@ -601,7 +553,7 @@ chn_read(struct pcm_channel *c, struct uio *buf)
|
|||
} else if (c->flags & (CHN_F_NBIO | CHN_F_NOTRIGGER))
|
||||
ret = EAGAIN;
|
||||
else {
|
||||
ret = chn_sleep(c, "pcmrd", timeout);
|
||||
ret = chn_sleep(c, timeout);
|
||||
if (ret == EAGAIN) {
|
||||
ret = EINVAL;
|
||||
c->flags |= CHN_F_DEAD;
|
||||
|
|
@ -612,7 +564,7 @@ chn_read(struct pcm_channel *c, struct uio *buf)
|
|||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -633,11 +585,14 @@ chn_start(struct pcm_channel *c, int force)
|
|||
u_int32_t i, j;
|
||||
struct snd_dbuf *b = c->bufhard;
|
||||
struct snd_dbuf *bs = c->bufsoft;
|
||||
int err;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
/* if we're running, or if we're prevented from triggering, bail */
|
||||
if (CHN_STARTED(c) || ((c->flags & CHN_F_NOTRIGGER) && !force))
|
||||
return EINVAL;
|
||||
return (EINVAL);
|
||||
|
||||
err = 0;
|
||||
|
||||
if (force) {
|
||||
i = 1;
|
||||
|
|
@ -653,7 +608,7 @@ chn_start(struct pcm_channel *c, int force)
|
|||
} else {
|
||||
struct snd_dbuf *pb;
|
||||
|
||||
pb = BUF_PARENT(c, b);
|
||||
pb = CHN_BUF_PARENT(c, b);
|
||||
i = sndbuf_xbytes(sndbuf_getready(bs), bs, pb);
|
||||
j = sndbuf_getbps(pb);
|
||||
}
|
||||
|
|
@ -687,10 +642,10 @@ chn_start(struct pcm_channel *c, int force)
|
|||
(sndbuf_getsize(b) * 1000) /
|
||||
(sndbuf_getbps(b) * sndbuf_getspd(b)));
|
||||
}
|
||||
chn_trigger(c, PCMTRIG_START);
|
||||
err = chn_trigger(c, PCMTRIG_START);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (err);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -718,26 +673,26 @@ chn_sync(struct pcm_channel *c, int threshold)
|
|||
|
||||
CHN_LOCKASSERT(c);
|
||||
|
||||
if (c->direction != PCMDIR_PLAY)
|
||||
return (EINVAL);
|
||||
|
||||
bs = c->bufsoft;
|
||||
|
||||
if ((c->flags & (CHN_F_DEAD | CHN_F_ABORTING)) ||
|
||||
(threshold < 1 && sndbuf_getready(bs) < 1))
|
||||
return 0;
|
||||
|
||||
if (c->direction != PCMDIR_PLAY)
|
||||
return EINVAL;
|
||||
return (0);
|
||||
|
||||
/* if we haven't yet started and nothing is buffered, else start*/
|
||||
if (CHN_STOPPED(c)) {
|
||||
if (threshold > 0 || sndbuf_getready(bs) > 0) {
|
||||
ret = chn_start(c, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
} else
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
b = BUF_PARENT(c, c->bufhard);
|
||||
b = CHN_BUF_PARENT(c, c->bufhard);
|
||||
|
||||
minflush = threshold + sndbuf_xbytes(sndbuf_getready(b), b, bs);
|
||||
|
||||
|
|
@ -788,12 +743,11 @@ chn_sync(struct pcm_channel *c, int threshold)
|
|||
cflag = c->flags & CHN_F_CLOSING;
|
||||
c->flags |= CHN_F_CLOSING;
|
||||
while (count > 0 && (resid > 0 || minflush > 0)) {
|
||||
ret = chn_sleep(c, "pcmsyn", c->timeout);
|
||||
ret = chn_sleep(c, c->timeout);
|
||||
if (ret == ERESTART || ret == EINTR) {
|
||||
c->flags |= CHN_F_ABORTING;
|
||||
break;
|
||||
}
|
||||
if (ret == 0 || ret == EAGAIN) {
|
||||
} else if (ret == 0 || ret == EAGAIN) {
|
||||
resid = sndbuf_getready(bs);
|
||||
if (resid == residp) {
|
||||
--count;
|
||||
|
|
@ -821,7 +775,8 @@ chn_sync(struct pcm_channel *c, int threshold)
|
|||
minflush -= threshold;
|
||||
}
|
||||
residp = resid;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
}
|
||||
c->flags &= ~CHN_F_CLOSING;
|
||||
c->flags |= cflag;
|
||||
|
|
@ -832,7 +787,7 @@ chn_sync(struct pcm_channel *c, int threshold)
|
|||
__func__, c->timeout, count, hcount, resid, residp,
|
||||
minflush, ret);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* called externally, handle locking */
|
||||
|
|
@ -843,14 +798,17 @@ chn_poll(struct pcm_channel *c, int ev, struct thread *td)
|
|||
int ret;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
if (!(c->flags & (CHN_F_MAPPED | CHN_F_TRIGGERED)))
|
||||
chn_start(c, 1);
|
||||
if (!(c->flags & (CHN_F_MAPPED | CHN_F_TRIGGERED))) {
|
||||
ret = chn_start(c, 1);
|
||||
if (ret != 0)
|
||||
return (0);
|
||||
}
|
||||
ret = 0;
|
||||
if (chn_polltrigger(c) && chn_pollreset(c))
|
||||
ret = ev;
|
||||
else
|
||||
selrecord(td, sndbuf_getsel(bs));
|
||||
return ret;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1192,6 +1150,7 @@ out:
|
|||
sndbuf_destroy(bs);
|
||||
if (b)
|
||||
sndbuf_destroy(b);
|
||||
CHN_LOCK(c);
|
||||
c->flags |= CHN_F_DEAD;
|
||||
chn_lockdestroy(c);
|
||||
|
||||
|
|
@ -1216,11 +1175,13 @@ chn_kill(struct pcm_channel *c)
|
|||
;
|
||||
if (CHANNEL_FREE(c->methods, c->devinfo))
|
||||
sndbuf_free(b);
|
||||
c->flags |= CHN_F_DEAD;
|
||||
sndbuf_destroy(bs);
|
||||
sndbuf_destroy(b);
|
||||
CHN_LOCK(c);
|
||||
c->flags |= CHN_F_DEAD;
|
||||
chn_lockdestroy(c);
|
||||
return 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -1500,9 +1461,11 @@ chn_resizebuf(struct pcm_channel *c, int latency,
|
|||
}
|
||||
|
||||
if (c->parentchannel != NULL) {
|
||||
pb = BUF_PARENT(c, NULL);
|
||||
pb = CHN_BUF_PARENT(c, NULL);
|
||||
CHN_UNLOCK(c);
|
||||
CHN_LOCK(c->parentchannel);
|
||||
chn_notify(c->parentchannel, CHN_N_BLOCKSIZE);
|
||||
CHN_UNLOCK(c->parentchannel);
|
||||
CHN_LOCK(c);
|
||||
limit = (limit != 0 && pb != NULL) ?
|
||||
sndbuf_xbytes(sndbuf_getsize(pb), pb, bs) : 0;
|
||||
|
|
@ -1776,50 +1739,53 @@ chn_trigger(struct pcm_channel *c, int go)
|
|||
if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
|
||||
sndbuf_dmabounce(b);
|
||||
#endif
|
||||
if (PCMTRIG_COMMON(go) && go == c->trigger)
|
||||
if (!PCMTRIG_COMMON(go))
|
||||
return (CHANNEL_TRIGGER(c->methods, c->devinfo, go));
|
||||
|
||||
if (go == c->trigger)
|
||||
return (0);
|
||||
|
||||
ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
|
||||
if (ret == 0) {
|
||||
switch (go) {
|
||||
case PCMTRIG_START:
|
||||
if (snd_verbose > 3)
|
||||
device_printf(c->dev,
|
||||
"%s() %s: calling go=0x%08x , "
|
||||
"prev=0x%08x\n", __func__, c->name, go,
|
||||
c->trigger);
|
||||
if (c->trigger != PCMTRIG_START) {
|
||||
c->trigger = go;
|
||||
CHN_UNLOCK(c);
|
||||
pcm_lock(d);
|
||||
CHN_INSERT_HEAD(d, c, channels.pcm.busy);
|
||||
pcm_unlock(d);
|
||||
CHN_LOCK(c);
|
||||
}
|
||||
break;
|
||||
case PCMTRIG_STOP:
|
||||
case PCMTRIG_ABORT:
|
||||
if (snd_verbose > 3)
|
||||
device_printf(c->dev,
|
||||
"%s() %s: calling go=0x%08x , "
|
||||
"prev=0x%08x\n", __func__, c->name, go,
|
||||
c->trigger);
|
||||
if (c->trigger == PCMTRIG_START) {
|
||||
c->trigger = go;
|
||||
CHN_UNLOCK(c);
|
||||
pcm_lock(d);
|
||||
CHN_REMOVE(d, c, channels.pcm.busy);
|
||||
pcm_unlock(d);
|
||||
CHN_LOCK(c);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
switch (go) {
|
||||
case PCMTRIG_START:
|
||||
if (snd_verbose > 3)
|
||||
device_printf(c->dev,
|
||||
"%s() %s: calling go=0x%08x , "
|
||||
"prev=0x%08x\n", __func__, c->name, go,
|
||||
c->trigger);
|
||||
if (c->trigger != PCMTRIG_START) {
|
||||
c->trigger = go;
|
||||
CHN_UNLOCK(c);
|
||||
pcm_lock(d);
|
||||
CHN_INSERT_HEAD(d, c, channels.pcm.busy);
|
||||
pcm_unlock(d);
|
||||
CHN_LOCK(c);
|
||||
}
|
||||
break;
|
||||
case PCMTRIG_STOP:
|
||||
case PCMTRIG_ABORT:
|
||||
if (snd_verbose > 3)
|
||||
device_printf(c->dev,
|
||||
"%s() %s: calling go=0x%08x , "
|
||||
"prev=0x%08x\n", __func__, c->name, go,
|
||||
c->trigger);
|
||||
if (c->trigger == PCMTRIG_START) {
|
||||
c->trigger = go;
|
||||
CHN_UNLOCK(c);
|
||||
pcm_lock(d);
|
||||
CHN_REMOVE(d, c, channels.pcm.busy);
|
||||
pcm_unlock(d);
|
||||
CHN_LOCK(c);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1836,19 +1802,6 @@ chn_trigger(struct pcm_channel *c, int go)
|
|||
int
|
||||
chn_getptr(struct pcm_channel *c)
|
||||
{
|
||||
#if 0
|
||||
int hwptr;
|
||||
int a = (1 << c->align) - 1;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
|
||||
/* don't allow unaligned values in the hwa ptr */
|
||||
#if 1
|
||||
hwptr &= ~a ; /* Apply channel align mask */
|
||||
#endif
|
||||
hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
|
||||
return hwptr;
|
||||
#endif
|
||||
int hwptr;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
|
|
@ -1889,6 +1842,7 @@ chn_buildfeeder(struct pcm_channel *c)
|
|||
{
|
||||
struct feeder_class *fc;
|
||||
struct pcm_feederdesc desc;
|
||||
struct snd_mixer *m;
|
||||
u_int32_t tmp[2], type, flags, hwfmt, *fmtlist;
|
||||
int err;
|
||||
char fmtstr[AFMTSTR_MAXSZ];
|
||||
|
|
@ -1940,18 +1894,25 @@ chn_buildfeeder(struct pcm_channel *c)
|
|||
} else
|
||||
return EOPNOTSUPP;
|
||||
|
||||
/* XXX These are too much.. */
|
||||
if (c->parentsnddev != NULL && c->parentsnddev->mixer_dev != NULL &&
|
||||
c->parentsnddev->mixer_dev->si_drv1 != NULL)
|
||||
m = c->parentsnddev->mixer_dev->si_drv1;
|
||||
else
|
||||
m = NULL;
|
||||
|
||||
c->feederflags &= ~(1 << FEEDER_VOLUME);
|
||||
if (c->direction == PCMDIR_PLAY &&
|
||||
!(c->flags & CHN_F_VIRTUAL) && c->parentsnddev &&
|
||||
(c->parentsnddev->flags & SD_F_SOFTPCMVOL) &&
|
||||
c->parentsnddev->mixer_dev)
|
||||
if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL) && m &&
|
||||
(c->parentsnddev->flags & SD_F_SOFTPCMVOL))
|
||||
c->feederflags |= 1 << FEEDER_VOLUME;
|
||||
|
||||
if (!(c->flags & CHN_F_VIRTUAL) && c->parentsnddev &&
|
||||
((c->direction == PCMDIR_PLAY &&
|
||||
(c->parentsnddev->flags & SD_F_PSWAPLR)) ||
|
||||
(c->direction == PCMDIR_REC &&
|
||||
(c->parentsnddev->flags & SD_F_RSWAPLR))))
|
||||
c->feederflags |= 1 << FEEDER_SWAPLR;
|
||||
|
||||
flags = c->feederflags;
|
||||
fmtlist = chn_getcaps(c)->fmtlist;
|
||||
|
||||
|
|
@ -2064,34 +2025,27 @@ chn_buildfeeder(struct pcm_channel *c)
|
|||
sndbuf_setfmt(c->bufhard, hwfmt);
|
||||
|
||||
if ((flags & (1 << FEEDER_VOLUME))) {
|
||||
u_int32_t parent = SOUND_MIXER_NONE;
|
||||
u_int32_t parent;
|
||||
int vol, left, right;
|
||||
|
||||
vol = 100 | (100 << 8);
|
||||
|
||||
CHN_UNLOCK(c);
|
||||
/*
|
||||
* XXX This is ugly! The way mixer subs being so secretive
|
||||
* about its own internals force us to use this silly
|
||||
* monkey trick.
|
||||
*/
|
||||
if (mixer_ioctl(c->parentsnddev->mixer_dev,
|
||||
MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&vol, -1, NULL) != 0)
|
||||
device_printf(c->dev, "Soft PCM Volume: Failed to read default value\n");
|
||||
vol = mix_get(m, SOUND_MIXER_PCM);
|
||||
if (vol == -1) {
|
||||
device_printf(c->dev,
|
||||
"Soft PCM Volume: Failed to read default value\n");
|
||||
vol = 100 | (100 << 8);
|
||||
}
|
||||
left = vol & 0x7f;
|
||||
right = (vol >> 8) & 0x7f;
|
||||
if (c->parentsnddev != NULL &&
|
||||
c->parentsnddev->mixer_dev != NULL &&
|
||||
c->parentsnddev->mixer_dev->si_drv1 != NULL)
|
||||
parent = mix_getparent(
|
||||
c->parentsnddev->mixer_dev->si_drv1,
|
||||
SOUND_MIXER_PCM);
|
||||
parent = mix_getparent(m, SOUND_MIXER_PCM);
|
||||
if (parent != SOUND_MIXER_NONE) {
|
||||
vol = 100 | (100 << 8);
|
||||
if (mixer_ioctl(c->parentsnddev->mixer_dev,
|
||||
MIXER_READ(parent),
|
||||
(caddr_t)&vol, -1, NULL) != 0)
|
||||
device_printf(c->dev, "Soft Volume: Failed to read parent default value\n");
|
||||
vol = mix_get(m, parent);
|
||||
if (vol == -1) {
|
||||
device_printf(c->dev,
|
||||
"Soft Volume: Failed to read parent "
|
||||
"default value\n");
|
||||
vol = 100 | (100 << 8);
|
||||
}
|
||||
left = (left * (vol & 0x7f)) / 100;
|
||||
right = (right * ((vol >> 8) & 0x7f)) / 100;
|
||||
}
|
||||
|
|
@ -2105,39 +2059,31 @@ chn_buildfeeder(struct pcm_channel *c)
|
|||
int
|
||||
chn_notify(struct pcm_channel *c, u_int32_t flags)
|
||||
{
|
||||
int run;
|
||||
int err, run, nrun;
|
||||
|
||||
CHN_LOCK(c);
|
||||
CHN_LOCKASSERT(c);
|
||||
|
||||
if (CHN_EMPTY(c, children)) {
|
||||
CHN_UNLOCK(c);
|
||||
return ENODEV;
|
||||
}
|
||||
if (CHN_EMPTY(c, children))
|
||||
return (ENODEV);
|
||||
|
||||
err = 0;
|
||||
|
||||
run = (CHN_STARTED(c)) ? 1 : 0;
|
||||
/*
|
||||
* if the hwchan is running, we can't change its rate, format or
|
||||
* If the hwchan is running, we can't change its rate, format or
|
||||
* blocksize
|
||||
*/
|
||||
run = (CHN_STARTED(c)) ? 1 : 0;
|
||||
if (run)
|
||||
flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
|
||||
|
||||
if (flags & CHN_N_RATE) {
|
||||
/*
|
||||
* we could do something here, like scan children and decide on
|
||||
* the most appropriate rate to mix at, but we don't for now
|
||||
*/
|
||||
/* XXX I'll make good use of this someday. */
|
||||
}
|
||||
if (flags & CHN_N_FORMAT) {
|
||||
/*
|
||||
* we could do something here, like scan children and decide on
|
||||
* the most appropriate mixer feeder to use, but we don't for now
|
||||
*/
|
||||
/* XXX I'll make good use of this someday. */
|
||||
}
|
||||
if (flags & CHN_N_VOLUME) {
|
||||
/*
|
||||
* we could do something here but we don't for now
|
||||
*/
|
||||
/* XXX I'll make good use of this someday. */
|
||||
}
|
||||
if (flags & CHN_N_BLOCKSIZE) {
|
||||
/*
|
||||
|
|
@ -2146,16 +2092,14 @@ chn_notify(struct pcm_channel *c, u_int32_t flags)
|
|||
chn_setlatency(c, chn_latency);
|
||||
}
|
||||
if (flags & CHN_N_TRIGGER) {
|
||||
int nrun;
|
||||
|
||||
nrun = CHN_EMPTY(c, children.busy) ? 0 : 1;
|
||||
if (nrun && !run)
|
||||
chn_start(c, 1);
|
||||
err = chn_start(c, 1);
|
||||
if (!nrun && run)
|
||||
chn_abort(c);
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
return 0;
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -92,6 +92,10 @@ struct pcm_channel {
|
|||
char name[CHN_NAMELEN];
|
||||
struct mtx *lock;
|
||||
int trigger;
|
||||
/**
|
||||
* For interrupt manipulations.
|
||||
*/
|
||||
struct cv intr_cv;
|
||||
/**
|
||||
* Increment,decrement this around operations that temporarily yield
|
||||
* lock.
|
||||
|
|
@ -199,6 +203,16 @@ struct pcm_channel {
|
|||
#define CHN_DEV(x) (snd_unit2d((x)->unit))
|
||||
#define CHN_CHAN(x) (snd_unit2c((x)->unit))
|
||||
|
||||
#define CHN_BUF_PARENT(x, y) \
|
||||
(((x) != NULL && (x)->parentchannel != NULL && \
|
||||
(x)->parentchannel->bufhard != NULL) ? \
|
||||
(x)->parentchannel->bufhard : (y))
|
||||
|
||||
#define CHN_BROADCAST(x) do { \
|
||||
if ((x)->cv_waiters != 0) \
|
||||
cv_broadcastpri(x, PRIBIO); \
|
||||
} while(0)
|
||||
|
||||
#include "channel_if.h"
|
||||
|
||||
int chn_reinit(struct pcm_channel *c);
|
||||
|
|
@ -315,8 +329,11 @@ extern int report_soft_formats;
|
|||
|
||||
#define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */
|
||||
|
||||
#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | \
|
||||
CHN_F_HAS_VCHAN | CHN_F_VIRTUAL)
|
||||
#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | \
|
||||
CHN_F_HAS_VCHAN | CHN_F_VIRTUAL)
|
||||
|
||||
#define CHN_F_MMAP_INVALID (CHN_F_DEAD | CHN_F_RUNNING)
|
||||
|
||||
|
||||
|
||||
#define CHN_N_RATE 0x00000001
|
||||
|
|
@ -336,6 +353,15 @@ extern int report_soft_formats;
|
|||
#define CHN_LATENCY_PROFILE_MAX 1
|
||||
#define CHN_LATENCY_PROFILE_DEFAULT CHN_LATENCY_PROFILE_MAX
|
||||
|
||||
#define CHN_STARTED(c) ((c)->flags & CHN_F_TRIGGERED)
|
||||
#define CHN_STOPPED(c) (!CHN_STARTED(c))
|
||||
#define CHN_DIRSTR(c) (((c)->direction == PCMDIR_PLAY) ? \
|
||||
"PCMDIR_PLAY" : "PCMDIR_REC")
|
||||
|
||||
#define CHN_TIMEOUT 5
|
||||
#define CHN_TIMEOUT_MIN 1
|
||||
#define CHN_TIMEOUT_MAX 10
|
||||
|
||||
/*
|
||||
* This should be large enough to hold all pcm data between
|
||||
* tsleeps in chn_{read,write} at the highest sample rate.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -31,7 +31,12 @@
|
|||
|
||||
extern struct cdevsw dsp_cdevsw;
|
||||
|
||||
struct dsp_cdevinfo;
|
||||
|
||||
char *dsp_unit2name(char *, size_t, int);
|
||||
int dsp_oss_audioinfo(struct cdev *, oss_audioinfo *);
|
||||
|
||||
void dsp_cdevinfo_init(struct snddev_info *);
|
||||
void dsp_cdevinfo_flush(struct snddev_info *);
|
||||
|
||||
#endif /* !_PCMDSP_H_ */
|
||||
|
|
|
|||
|
|
@ -868,32 +868,11 @@ feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u
|
|||
} else {
|
||||
if (l > 0)
|
||||
sndbuf_dispose(src, buffer, l);
|
||||
#if 1
|
||||
memset(buffer + l,
|
||||
sndbuf_zerodata(sndbuf_getfmt(src)),
|
||||
offset);
|
||||
if (!(ch->flags & CHN_F_CLOSING))
|
||||
ch->xruns++;
|
||||
#else
|
||||
if (l < 1 || (ch->flags & CHN_F_CLOSING)) {
|
||||
memset(buffer + l,
|
||||
sndbuf_zerodata(sndbuf_getfmt(src)),
|
||||
offset);
|
||||
if (!(ch->flags & CHN_F_CLOSING))
|
||||
ch->xruns++;
|
||||
} else {
|
||||
int cp, tgt;
|
||||
|
||||
tgt = l;
|
||||
while (offset > 0) {
|
||||
cp = min(l, offset);
|
||||
memcpy(buffer + tgt, buffer, cp);
|
||||
offset -= cp;
|
||||
tgt += cp;
|
||||
}
|
||||
ch->xruns++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else if (l > 0)
|
||||
sndbuf_dispose(src, buffer, l);
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ feed_rate_setup(struct pcm_feeder *f)
|
|||
if (!(RATE_FACTOR_SAFE(info->gx) && RATE_FACTOR_SAFE(info->gy)))
|
||||
return (-1);
|
||||
|
||||
for (i = 0; i < sizeof(convtbl) / sizeof(*convtbl); i++) {
|
||||
for (i = 0; i < sizeof(convtbl) / sizeof(convtbl[0]); i++) {
|
||||
if (convtbl[i].format == 0)
|
||||
return (-1);
|
||||
if ((f->desc->out & ~AFMT_STEREO) == convtbl[i].format) {
|
||||
|
|
@ -404,8 +404,8 @@ feed_rate_init(struct pcm_feeder *f)
|
|||
* bufsz = sample from last cycle + conversion space
|
||||
*/
|
||||
info->bufsz_init = 8 + feeder_buffersize;
|
||||
info->buffer = malloc(sizeof(*info->buffer) * info->bufsz_init,
|
||||
M_RATEFEEDER, M_NOWAIT | M_ZERO);
|
||||
info->buffer = malloc(info->bufsz_init, M_RATEFEEDER,
|
||||
M_NOWAIT | M_ZERO);
|
||||
if (info->buffer == NULL) {
|
||||
free(info, M_RATEFEEDER);
|
||||
return (ENOMEM);
|
||||
|
|
@ -480,7 +480,7 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
|
|||
* beginning of buffer.
|
||||
*/
|
||||
bcopy(info->buffer + info->pos - smpsz, info->buffer,
|
||||
sizeof(*info->buffer) * (smpsz << 1));
|
||||
smpsz << 1);
|
||||
info->pos = smpsz;
|
||||
info->bpos = smpsz << 1;
|
||||
}
|
||||
|
|
@ -574,7 +574,7 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
|
|||
info->stray));
|
||||
#endif
|
||||
bcopy(info->buffer + info->pos - smpsz, info->buffer,
|
||||
sizeof(*info->buffer) * smpsz);
|
||||
smpsz);
|
||||
info->bpos = smpsz;
|
||||
info->pos = smpsz;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,12 +35,12 @@ MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
|
|||
#define MIXER_NAMELEN 16
|
||||
struct snd_mixer {
|
||||
KOBJ_FIELDS;
|
||||
const char *type;
|
||||
void *devinfo;
|
||||
int busy;
|
||||
int hwvol_muted;
|
||||
int hwvol_mixer;
|
||||
int hwvol_step;
|
||||
int type;
|
||||
device_t dev;
|
||||
u_int32_t hwvol_mute_level;
|
||||
u_int32_t devs;
|
||||
|
|
@ -83,10 +83,10 @@ static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
|
|||
|
||||
static d_open_t mixer_open;
|
||||
static d_close_t mixer_close;
|
||||
static d_ioctl_t mixer_ioctl;
|
||||
|
||||
static struct cdevsw mixer_cdevsw = {
|
||||
.d_version = D_VERSION,
|
||||
.d_flags = D_NEEDGIANT,
|
||||
.d_open = mixer_open,
|
||||
.d_close = mixer_close,
|
||||
.d_ioctl = mixer_ioctl,
|
||||
|
|
@ -126,17 +126,45 @@ mixer_lookup(char *devname)
|
|||
}
|
||||
#endif
|
||||
|
||||
#define MIXER_SET_UNLOCK(x, y) do { \
|
||||
if ((y) != 0) \
|
||||
snd_mtxunlock((x)->lock); \
|
||||
} while(0)
|
||||
|
||||
#define MIXER_SET_LOCK(x, y) do { \
|
||||
if ((y) != 0) \
|
||||
snd_mtxlock((x)->lock); \
|
||||
} while(0)
|
||||
|
||||
static int
|
||||
mixer_set_softpcmvol(struct snd_mixer *mixer, struct snddev_info *d,
|
||||
unsigned left, unsigned right)
|
||||
mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d,
|
||||
unsigned left, unsigned right)
|
||||
{
|
||||
struct pcm_channel *c;
|
||||
int locked;
|
||||
int dropmtx, acquiremtx;
|
||||
|
||||
locked = (mixer->lock != NULL &&
|
||||
mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0;
|
||||
if (locked)
|
||||
snd_mtxunlock(mixer->lock);
|
||||
if (!PCM_REGISTERED(d))
|
||||
return (EINVAL);
|
||||
|
||||
if (mtx_owned(m->lock))
|
||||
dropmtx = 1;
|
||||
else
|
||||
dropmtx = 0;
|
||||
|
||||
if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0)
|
||||
acquiremtx = 0;
|
||||
else
|
||||
acquiremtx = 1;
|
||||
|
||||
/*
|
||||
* Be careful here. If we're coming from cdev ioctl, it is OK to
|
||||
* not doing locking AT ALL (except on individual channel) since
|
||||
* we've been heavily guarded by pcm cv, or if we're still
|
||||
* under Giant influence. Since we also have mix_* calls, we cannot
|
||||
* assume such protection and just do the lock as usuall.
|
||||
*/
|
||||
MIXER_SET_UNLOCK(m, dropmtx);
|
||||
MIXER_SET_LOCK(d, acquiremtx);
|
||||
|
||||
if (CHN_EMPTY(d, channels.pcm.busy)) {
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
|
|
@ -156,10 +184,10 @@ mixer_set_softpcmvol(struct snd_mixer *mixer, struct snddev_info *d,
|
|||
}
|
||||
}
|
||||
|
||||
if (locked)
|
||||
snd_mtxlock(mixer->lock);
|
||||
MIXER_SET_UNLOCK(d, acquiremtx);
|
||||
MIXER_SET_LOCK(m, dropmtx);
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -169,7 +197,7 @@ mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev)
|
|||
unsigned l, r, tl, tr;
|
||||
u_int32_t parent = SOUND_MIXER_NONE, child = 0;
|
||||
u_int32_t realdev;
|
||||
int i;
|
||||
int i, dropmtx;
|
||||
|
||||
if (m == NULL || dev >= SOUND_MIXER_NRDEVICES ||
|
||||
(0 == (m->devs & (1 << dev))))
|
||||
|
|
@ -183,6 +211,14 @@ mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev)
|
|||
if (d == NULL)
|
||||
return -1;
|
||||
|
||||
/* It is safe to drop this mutex due to Giant. */
|
||||
if (!(d->flags & SD_F_MPSAFE) && mtx_owned(m->lock) != 0)
|
||||
dropmtx = 1;
|
||||
else
|
||||
dropmtx = 0;
|
||||
|
||||
MIXER_SET_UNLOCK(m, dropmtx);
|
||||
|
||||
/* TODO: recursive handling */
|
||||
parent = m->parent[dev];
|
||||
if (parent >= SOUND_MIXER_NRDEVICES)
|
||||
|
|
@ -194,10 +230,12 @@ mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev)
|
|||
tl = (l * (m->level[parent] & 0x00ff)) / 100;
|
||||
tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100;
|
||||
if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
|
||||
mixer_set_softpcmvol(m, d, tl, tr);
|
||||
(void)mixer_set_softpcmvol(m, d, tl, tr);
|
||||
else if (realdev != SOUND_MIXER_NONE &&
|
||||
MIXER_SET(m, realdev, tl, tr) < 0)
|
||||
MIXER_SET(m, realdev, tl, tr) < 0) {
|
||||
MIXER_SET_LOCK(m, dropmtx);
|
||||
return -1;
|
||||
}
|
||||
} else if (child != 0) {
|
||||
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
|
||||
if (!(child & (1 << i)) || m->parent[i] != dev)
|
||||
|
|
@ -206,24 +244,30 @@ mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev)
|
|||
tl = (l * (m->level[i] & 0x00ff)) / 100;
|
||||
tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100;
|
||||
if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
|
||||
mixer_set_softpcmvol(m, d, tl, tr);
|
||||
(void)mixer_set_softpcmvol(m, d, tl, tr);
|
||||
else if (realdev != SOUND_MIXER_NONE)
|
||||
MIXER_SET(m, realdev, tl, tr);
|
||||
}
|
||||
realdev = m->realdev[dev];
|
||||
if (realdev != SOUND_MIXER_NONE &&
|
||||
MIXER_SET(m, realdev, l, r) < 0)
|
||||
MIXER_SET(m, realdev, l, r) < 0) {
|
||||
MIXER_SET_LOCK(m, dropmtx);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
|
||||
mixer_set_softpcmvol(m, d, l, r);
|
||||
(void)mixer_set_softpcmvol(m, d, l, r);
|
||||
else if (realdev != SOUND_MIXER_NONE &&
|
||||
MIXER_SET(m, realdev, l, r) < 0)
|
||||
MIXER_SET(m, realdev, l, r) < 0) {
|
||||
MIXER_SET_LOCK(m, dropmtx);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
m->level[dev] = l | (r << 8);
|
||||
|
||||
MIXER_SET_LOCK(m, dropmtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -232,16 +276,30 @@ mixer_get(struct snd_mixer *mixer, int dev)
|
|||
{
|
||||
if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
|
||||
return mixer->level[dev];
|
||||
else return -1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
int dropmtx;
|
||||
|
||||
d = device_get_softc(mixer->dev);
|
||||
if (d == NULL)
|
||||
return -1;
|
||||
if (!(d->flags & SD_F_MPSAFE) && mtx_owned(mixer->lock) != 0)
|
||||
dropmtx = 1;
|
||||
else
|
||||
dropmtx = 0;
|
||||
src &= mixer->recdevs;
|
||||
if (src == 0)
|
||||
src = SOUND_MASK_MIC;
|
||||
/* It is safe to drop this mutex due to Giant. */
|
||||
MIXER_SET_UNLOCK(mixer, dropmtx);
|
||||
mixer->recsrc = MIXER_SETRECSRC(mixer, src);
|
||||
MIXER_SET_LOCK(mixer, dropmtx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -482,6 +540,80 @@ mix_getdevinfo(struct snd_mixer *m)
|
|||
return m->devinfo;
|
||||
}
|
||||
|
||||
static struct snd_mixer *
|
||||
mixer_obj_create(device_t dev, kobj_class_t cls, void *devinfo,
|
||||
int type, const char *desc)
|
||||
{
|
||||
struct snd_mixer *m;
|
||||
int i;
|
||||
|
||||
KASSERT(dev != NULL && cls != NULL && devinfo != NULL,
|
||||
("%s(): NULL data dev=%p cls=%p devinfo=%p",
|
||||
__func__, dev, cls, devinfo));
|
||||
KASSERT(type == MIXER_TYPE_PRIMARY || type == MIXER_TYPE_SECONDARY,
|
||||
("invalid mixer type=%d", type));
|
||||
|
||||
m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
|
||||
snprintf(m->name, sizeof(m->name), "%s:mixer",
|
||||
device_get_nameunit(dev));
|
||||
if (desc != NULL) {
|
||||
strlcat(m->name, ":", sizeof(m->name));
|
||||
strlcat(m->name, desc, sizeof(m->name));
|
||||
}
|
||||
m->lock = snd_mtxcreate(m->name, (type == MIXER_TYPE_PRIMARY) ?
|
||||
"primary pcm mixer" : "secondary pcm mixer");
|
||||
m->type = type;
|
||||
m->devinfo = devinfo;
|
||||
m->busy = 0;
|
||||
m->dev = dev;
|
||||
for (i = 0; i < (sizeof(m->parent) / sizeof(m->parent[0])); i++) {
|
||||
m->parent[i] = SOUND_MIXER_NONE;
|
||||
m->child[i] = 0;
|
||||
m->realdev[i] = i;
|
||||
}
|
||||
|
||||
if (MIXER_INIT(m)) {
|
||||
snd_mtxlock(m->lock);
|
||||
snd_mtxfree(m->lock);
|
||||
kobj_delete((kobj_t)m, M_MIXER);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (m);
|
||||
}
|
||||
|
||||
int
|
||||
mixer_delete(struct snd_mixer *m)
|
||||
{
|
||||
KASSERT(m != NULL, ("NULL snd_mixer"));
|
||||
KASSERT(m->type == MIXER_TYPE_SECONDARY,
|
||||
("%s(): illegal mixer type=%d", __func__, m->type));
|
||||
|
||||
snd_mtxlock(m->lock);
|
||||
|
||||
MIXER_UNINIT(m);
|
||||
|
||||
snd_mtxfree(m->lock);
|
||||
kobj_delete((kobj_t)m, M_MIXER);
|
||||
|
||||
--mixer_count;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct snd_mixer *
|
||||
mixer_create(device_t dev, kobj_class_t cls, void *devinfo, const char *desc)
|
||||
{
|
||||
struct snd_mixer *m;
|
||||
|
||||
m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc);
|
||||
|
||||
if (m != NULL)
|
||||
++mixer_count;
|
||||
|
||||
return (m);
|
||||
}
|
||||
|
||||
int
|
||||
mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
|
||||
{
|
||||
|
|
@ -491,21 +623,9 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
|
|||
struct cdev *pdev;
|
||||
int i, unit, devunit, val;
|
||||
|
||||
m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
|
||||
snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev));
|
||||
m->lock = snd_mtxcreate(m->name, "pcm mixer");
|
||||
m->type = cls->name;
|
||||
m->devinfo = devinfo;
|
||||
m->busy = 0;
|
||||
m->dev = dev;
|
||||
for (i = 0; i < 32; i++) {
|
||||
m->parent[i] = SOUND_MIXER_NONE;
|
||||
m->child[i] = 0;
|
||||
m->realdev[i] = i;
|
||||
}
|
||||
|
||||
if (MIXER_INIT(m))
|
||||
goto bad;
|
||||
m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_PRIMARY, NULL);
|
||||
if (m == NULL)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
|
||||
v = snd_mixerdefaults[i];
|
||||
|
|
@ -556,13 +676,7 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
|
|||
device_printf(dev, "Soft PCM mixer ENABLED\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
snd_mtxlock(m->lock);
|
||||
snd_mtxfree(m->lock);
|
||||
kobj_delete((kobj_t)m, M_MIXER);
|
||||
return -1;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -577,7 +691,12 @@ mixer_uninit(device_t dev)
|
|||
pdev = mixer_get_devt(dev);
|
||||
if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL)
|
||||
return EBADF;
|
||||
|
||||
m = pdev->si_drv1;
|
||||
KASSERT(m != NULL, ("NULL snd_mixer"));
|
||||
KASSERT(m->type == MIXER_TYPE_PRIMARY,
|
||||
("%s(): illegal mixer type=%d", __func__, m->type));
|
||||
|
||||
snd_mtxlock(m->lock);
|
||||
|
||||
if (m->busy) {
|
||||
|
|
@ -733,42 +852,161 @@ mixer_hwvol_step(device_t dev, int left_step, int right_step)
|
|||
snd_mtxunlock(m->lock);
|
||||
}
|
||||
|
||||
int
|
||||
mixer_busy(struct snd_mixer *m)
|
||||
{
|
||||
KASSERT(m != NULL, ("NULL snd_mixer"));
|
||||
|
||||
return (m->busy);
|
||||
}
|
||||
|
||||
int
|
||||
mix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right)
|
||||
{
|
||||
int ret;
|
||||
|
||||
KASSERT(m != NULL, ("NULL snd_mixer"));
|
||||
|
||||
snd_mtxlock(m->lock);
|
||||
ret = mixer_set(m, dev, left | (right << 8));
|
||||
snd_mtxunlock(m->lock);
|
||||
|
||||
return ((ret != 0) ? ENXIO : 0);
|
||||
}
|
||||
|
||||
int
|
||||
mix_get(struct snd_mixer *m, u_int dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
KASSERT(m != NULL, ("NULL snd_mixer"));
|
||||
|
||||
snd_mtxlock(m->lock);
|
||||
ret = mixer_get(m, dev);
|
||||
snd_mtxunlock(m->lock);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
int ret;
|
||||
|
||||
KASSERT(m != NULL, ("NULL snd_mixer"));
|
||||
|
||||
snd_mtxlock(m->lock);
|
||||
ret = mixer_setrecsrc(m, src);
|
||||
snd_mtxunlock(m->lock);
|
||||
|
||||
return ((ret != 0) ? ENXIO : 0);
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
mix_getrecsrc(struct snd_mixer *m)
|
||||
{
|
||||
u_int32_t ret;
|
||||
|
||||
KASSERT(m != NULL, ("NULL snd_mixer"));
|
||||
|
||||
snd_mtxlock(m->lock);
|
||||
ret = mixer_getrecsrc(m);
|
||||
snd_mtxunlock(m->lock);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
mix_get_type(struct snd_mixer *m)
|
||||
{
|
||||
KASSERT(m != NULL, ("NULL snd_mixer"));
|
||||
|
||||
return (m->type);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
mixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
|
||||
|
||||
if (i_dev == NULL || i_dev->si_drv1 == NULL)
|
||||
return (EBADF);
|
||||
|
||||
m = i_dev->si_drv1;
|
||||
d = device_get_softc(m->dev);
|
||||
if (!PCM_REGISTERED(d))
|
||||
return (EBADF);
|
||||
|
||||
/* XXX Need Giant magic entry ??? */
|
||||
|
||||
snd_mtxlock(m->lock);
|
||||
|
||||
m->busy = 1;
|
||||
|
||||
snd_mtxunlock(m->lock);
|
||||
return 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
int ret;
|
||||
|
||||
if (i_dev == NULL || i_dev->si_drv1 == NULL)
|
||||
return (EBADF);
|
||||
|
||||
m = i_dev->si_drv1;
|
||||
d = device_get_softc(m->dev);
|
||||
if (!PCM_REGISTERED(d))
|
||||
return (EBADF);
|
||||
|
||||
/* XXX Need Giant magic entry ??? */
|
||||
|
||||
snd_mtxlock(m->lock);
|
||||
|
||||
if (!m->busy) {
|
||||
snd_mtxunlock(m->lock);
|
||||
return EBADF;
|
||||
}
|
||||
ret = (m->busy == 0) ? EBADF : 0;
|
||||
m->busy = 0;
|
||||
|
||||
snd_mtxunlock(m->lock);
|
||||
return 0;
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
|
||||
struct thread *td)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
int ret;
|
||||
|
||||
if (i_dev == NULL || i_dev->si_drv1 == NULL)
|
||||
return (EBADF);
|
||||
|
||||
d = device_get_softc(((struct snd_mixer *)i_dev->si_drv1)->dev);
|
||||
if (!PCM_REGISTERED(d))
|
||||
return (EBADF);
|
||||
|
||||
PCM_GIANT_ENTER(d);
|
||||
PCM_ACQUIRE_QUICK(d);
|
||||
|
||||
ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, MIXER_CMD_CDEV);
|
||||
|
||||
PCM_RELEASE_QUICK(d);
|
||||
PCM_GIANT_LEAVE(d);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX Make sure you can guarantee concurrency safety before calling this
|
||||
* function, be it through Giant, PCM_CV_*, etc !
|
||||
*/
|
||||
int
|
||||
mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
||||
mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
|
||||
struct thread *td, int from)
|
||||
{
|
||||
struct snd_mixer *m;
|
||||
int ret, *arg_i = (int *)arg;
|
||||
|
|
@ -777,17 +1015,17 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread
|
|||
m = i_dev->si_drv1;
|
||||
|
||||
if (m == NULL)
|
||||
return EBADF;
|
||||
return (EBADF);
|
||||
|
||||
snd_mtxlock(m->lock);
|
||||
if (mode != -1 && !m->busy) {
|
||||
if (from == MIXER_CMD_CDEV && !m->busy) {
|
||||
snd_mtxunlock(m->lock);
|
||||
return EBADF;
|
||||
return (EBADF);
|
||||
}
|
||||
|
||||
if (cmd == SNDCTL_MIXERINFO) {
|
||||
snd_mtxunlock(m->lock);
|
||||
return mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg);
|
||||
return (mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg));
|
||||
}
|
||||
|
||||
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
|
||||
|
|
@ -796,7 +1034,7 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread
|
|||
else
|
||||
ret = mixer_set(m, j, *arg_i);
|
||||
snd_mtxunlock(m->lock);
|
||||
return (ret == 0)? 0 : ENXIO;
|
||||
return ((ret == 0) ? 0 : ENXIO);
|
||||
}
|
||||
|
||||
if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
|
||||
|
|
@ -820,7 +1058,7 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread
|
|||
}
|
||||
*arg_i = v;
|
||||
snd_mtxunlock(m->lock);
|
||||
return (v != -1)? 0 : ENXIO;
|
||||
return ((v != -1) ? 0 : ENXIO);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
|
@ -828,10 +1066,13 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread
|
|||
switch (cmd) {
|
||||
/** @todo Double check return values, error codes. */
|
||||
case SNDCTL_SYSINFO:
|
||||
snd_mtxunlock(m->lock);
|
||||
sound_oss_sysinfo((oss_sysinfo *)arg);
|
||||
return (ret);
|
||||
break;
|
||||
case SNDCTL_AUDIOINFO:
|
||||
ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg);
|
||||
snd_mtxunlock(m->lock);
|
||||
return (dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg));
|
||||
break;
|
||||
case SNDCTL_DSP_GET_RECSRC_NAMES:
|
||||
bcopy((void *)&m->enuminfo, arg, sizeof(oss_mixer_enuminfo));
|
||||
|
|
@ -847,10 +1088,12 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread
|
|||
break;
|
||||
default:
|
||||
ret = ENXIO;
|
||||
break;
|
||||
}
|
||||
|
||||
snd_mtxunlock(m->lock);
|
||||
return ret;
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#ifdef USING_DEVFS
|
||||
|
|
@ -867,7 +1110,7 @@ mixer_clone(void *arg,
|
|||
return;
|
||||
if (strcmp(name, "mixer") == 0) {
|
||||
d = devclass_get_softc(pcm_devclass, snd_unit);
|
||||
if (d != NULL && d->mixer_dev != NULL) {
|
||||
if (PCM_REGISTERED(d) && d->mixer_dev != NULL) {
|
||||
*dev = d->mixer_dev;
|
||||
dev_ref(*dev);
|
||||
}
|
||||
|
|
@ -919,22 +1162,18 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
|
|||
{
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
struct cdev *t_cdev;
|
||||
int nmix, ret, pcmunit, i;
|
||||
int nmix, i;
|
||||
|
||||
/*
|
||||
* If probing the device handling the ioctl, make sure it's a mixer
|
||||
* device. (This ioctl is valid on audio, mixer, and midi devices.)
|
||||
*/
|
||||
if ((mi->dev == -1) && (i_dev->si_devsw != &mixer_cdevsw))
|
||||
return EINVAL;
|
||||
if (mi->dev == -1 && i_dev->si_devsw != &mixer_cdevsw)
|
||||
return (EINVAL);
|
||||
|
||||
d = NULL;
|
||||
m = NULL;
|
||||
t_cdev = NULL;
|
||||
nmix = 0;
|
||||
ret = 0;
|
||||
pcmunit = -1; /* pcmX */
|
||||
|
||||
/*
|
||||
* There's a 1:1 relationship between mixers and PCM devices, so
|
||||
|
|
@ -943,108 +1182,101 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
|
|||
for (i = 0; pcm_devclass != NULL &&
|
||||
i < devclass_get_maxunit(pcm_devclass); i++) {
|
||||
d = devclass_get_softc(pcm_devclass, i);
|
||||
if (d == NULL)
|
||||
if (!PCM_REGISTERED(d))
|
||||
continue;
|
||||
|
||||
/* XXX Need Giant magic entry */
|
||||
|
||||
/* See the note in function docblock. */
|
||||
mtx_assert(d->lock, MA_NOTOWNED);
|
||||
pcm_inprog(d, 1);
|
||||
pcm_lock(d);
|
||||
|
||||
if (d->mixer_dev != NULL) {
|
||||
if (((mi->dev == -1) && (d->mixer_dev == i_dev)) || (mi->dev == nmix)) {
|
||||
t_cdev = d->mixer_dev;
|
||||
pcmunit = i;
|
||||
break;
|
||||
}
|
||||
if (d->mixer_dev != NULL && d->mixer_dev->si_drv1 != NULL &&
|
||||
((mi->dev == -1 && d->mixer_dev == i_dev) ||
|
||||
mi->dev == nmix)) {
|
||||
m = d->mixer_dev->si_drv1;
|
||||
mtx_lock(m->lock);
|
||||
|
||||
/*
|
||||
* At this point, the following synchronization stuff
|
||||
* has happened:
|
||||
* - a specific PCM device is locked.
|
||||
* - a specific mixer device has been locked, so be
|
||||
* sure to unlock when existing.
|
||||
*/
|
||||
bzero((void *)mi, sizeof(*mi));
|
||||
mi->dev = nmix;
|
||||
snprintf(mi->id, sizeof(mi->id), "mixer%d", i);
|
||||
strlcpy(mi->name, m->name, sizeof(mi->name));
|
||||
mi->modify_counter = m->modify_counter;
|
||||
mi->card_number = i;
|
||||
/*
|
||||
* Currently, FreeBSD assumes 1:1 relationship between
|
||||
* a pcm and mixer devices, so this is hardcoded to 0.
|
||||
*/
|
||||
mi->port_number = 0;
|
||||
|
||||
/**
|
||||
* @todo Fill in @sa oss_mixerinfo::mixerhandle.
|
||||
* @note From 4Front: "mixerhandle is an arbitrary
|
||||
* string that identifies the mixer better than
|
||||
* the device number (mixerinfo.dev). Device
|
||||
* numbers may change depending on the order the
|
||||
* drivers are loaded. However the handle should
|
||||
* remain the same provided that the sound card
|
||||
* is not moved to another PCI slot."
|
||||
*/
|
||||
|
||||
/**
|
||||
* @note
|
||||
* @sa oss_mixerinfo::magic is a reserved field.
|
||||
*
|
||||
* @par
|
||||
* From 4Front: "magic is usually 0. However some
|
||||
* devices may have dedicated setup utilities and the
|
||||
* magic field may contain an unique driver specific
|
||||
* value (managed by [4Front])."
|
||||
*/
|
||||
|
||||
mi->enabled = device_is_attached(m->dev) ? 1 : 0;
|
||||
/**
|
||||
* The only flag for @sa oss_mixerinfo::caps is
|
||||
* currently MIXER_CAP_VIRTUAL, which I'm not sure we
|
||||
* really worry about.
|
||||
*/
|
||||
/**
|
||||
* Mixer extensions currently aren't supported, so
|
||||
* leave @sa oss_mixerinfo::nrext blank for now.
|
||||
*/
|
||||
/**
|
||||
* @todo Fill in @sa oss_mixerinfo::priority (requires
|
||||
* touching drivers?)
|
||||
* @note The priority field is for mixer applets to
|
||||
* determine which mixer should be the default, with 0
|
||||
* being least preferred and 10 being most preferred.
|
||||
* From 4Front: "OSS drivers like ICH use higher
|
||||
* values (10) because such chips are known to be used
|
||||
* only on motherboards. Drivers for high end pro
|
||||
* devices use 0 because they will never be the
|
||||
* default mixer. Other devices use values 1 to 9
|
||||
* depending on the estimated probability of being the
|
||||
* default device.
|
||||
*
|
||||
* XXX Described by Hannu@4Front, but not found in
|
||||
* soundcard.h.
|
||||
strlcpy(mi->devnode, d->mixer_dev->si_name,
|
||||
sizeof(mi->devnode));
|
||||
mi->legacy_device = i;
|
||||
*/
|
||||
mtx_unlock(m->lock);
|
||||
} else
|
||||
++nmix;
|
||||
}
|
||||
|
||||
pcm_unlock(d);
|
||||
pcm_inprog(d, -1);
|
||||
|
||||
if (m != NULL)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If t_cdev is NULL, then search was exhausted and device wasn't
|
||||
* found. No locks are held, so just return.
|
||||
*/
|
||||
if (t_cdev == NULL)
|
||||
return EINVAL;
|
||||
|
||||
m = t_cdev->si_drv1;
|
||||
mtx_lock(m->lock);
|
||||
|
||||
/*
|
||||
* At this point, the following synchronization stuff has happened:
|
||||
* - a specific PCM device is locked and its "in progress
|
||||
* operations" counter has been incremented, so be sure to unlock
|
||||
* and decrement when exiting;
|
||||
* - a specific mixer device has been locked, so be sure to unlock
|
||||
* when existing.
|
||||
*/
|
||||
|
||||
bzero((void *)mi, sizeof(*mi));
|
||||
|
||||
mi->dev = nmix;
|
||||
snprintf(mi->id, sizeof(mi->id), "mixer%d", dev2unit(t_cdev));
|
||||
strlcpy(mi->name, m->name, sizeof(mi->name));
|
||||
mi->modify_counter = m->modify_counter;
|
||||
mi->card_number = pcmunit;
|
||||
/*
|
||||
* Currently, FreeBSD assumes 1:1 relationship between a pcm and
|
||||
* mixer devices, so this is hardcoded to 0.
|
||||
*/
|
||||
mi->port_number = 0;
|
||||
|
||||
/**
|
||||
* @todo Fill in @sa oss_mixerinfo::mixerhandle.
|
||||
* @note From 4Front: "mixerhandle is an arbitrary string that
|
||||
* identifies the mixer better than the device number
|
||||
* (mixerinfo.dev). Device numbers may change depending on
|
||||
* the order the drivers are loaded. However the handle
|
||||
* should remain the same provided that the sound card is
|
||||
* not moved to another PCI slot."
|
||||
*/
|
||||
|
||||
/**
|
||||
* @note
|
||||
* @sa oss_mixerinfo::magic is a reserved field.
|
||||
*
|
||||
* @par
|
||||
* From 4Front: "magic is usually 0. However some devices may have
|
||||
* dedicated setup utilities and the magic field may contain an
|
||||
* unique driver specific value (managed by [4Front])."
|
||||
*/
|
||||
|
||||
mi->enabled = device_is_attached(m->dev) ? 1 : 0;
|
||||
/**
|
||||
* The only flag for @sa oss_mixerinfo::caps is currently
|
||||
* MIXER_CAP_VIRTUAL, which I'm not sure we really worry about.
|
||||
*/
|
||||
/**
|
||||
* Mixer extensions currently aren't supported, so leave
|
||||
* @sa oss_mixerinfo::nrext blank for now.
|
||||
*/
|
||||
/**
|
||||
* @todo Fill in @sa oss_mixerinfo::priority (requires touching
|
||||
* drivers?)
|
||||
* @note The priority field is for mixer applets to determine which
|
||||
* mixer should be the default, with 0 being least preferred and 10
|
||||
* being most preferred. From 4Front: "OSS drivers like ICH use
|
||||
* higher values (10) because such chips are known to be used only
|
||||
* on motherboards. Drivers for high end pro devices use 0 because
|
||||
* they will never be the default mixer. Other devices use values 1
|
||||
* to 9 depending on the estimated probability of being the default
|
||||
* device.
|
||||
*
|
||||
* XXX Described by Hannu@4Front, but not found in soundcard.h.
|
||||
strlcpy(mi->devnode, t_cdev->si_name, sizeof(mi->devnode));
|
||||
mi->legacy_device = pcmunit;
|
||||
*/
|
||||
|
||||
mtx_unlock(m->lock);
|
||||
pcm_unlock(d);
|
||||
pcm_inprog(d, -1);
|
||||
|
||||
return ret;
|
||||
return (EINVAL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,16 +26,27 @@
|
|||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
struct snd_mixer *mixer_create(device_t dev, kobj_class_t cls, void *devinfo,
|
||||
const char *desc);
|
||||
int mixer_delete(struct snd_mixer *m);
|
||||
int mixer_init(device_t dev, kobj_class_t cls, void *devinfo);
|
||||
int mixer_uninit(device_t dev);
|
||||
int mixer_reinit(device_t dev);
|
||||
int mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td);
|
||||
int mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td, int from);
|
||||
int mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi);
|
||||
|
||||
int mixer_hwvol_init(device_t dev);
|
||||
void mixer_hwvol_mute(device_t dev);
|
||||
void mixer_hwvol_step(device_t dev, int left_step, int right_step);
|
||||
|
||||
int mixer_busy(struct snd_mixer *m);
|
||||
|
||||
int mix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right);
|
||||
int mix_get(struct snd_mixer *m, u_int dev);
|
||||
int mix_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
u_int32_t mix_getrecsrc(struct snd_mixer *m);
|
||||
int mix_get_type(struct snd_mixer *m);
|
||||
|
||||
void mix_setdevs(struct snd_mixer *m, u_int32_t v);
|
||||
void mix_setrecdevs(struct snd_mixer *m, u_int32_t v);
|
||||
u_int32_t mix_getdevs(struct snd_mixer *m);
|
||||
|
|
@ -48,11 +59,17 @@ void *mix_getdevinfo(struct snd_mixer *m);
|
|||
|
||||
extern int mixer_count;
|
||||
|
||||
#define MIXER_CMD_DIRECT 0 /* send command within driver */
|
||||
#define MIXER_CMD_CDEV 1 /* send command from cdev/ioctl */
|
||||
|
||||
#define MIXER_TYPE_PRIMARY 0 /* mixer_init() */
|
||||
#define MIXER_TYPE_SECONDARY 1 /* mixer_create() */
|
||||
|
||||
/*
|
||||
* this is a kludge to allow hiding of the struct snd_mixer definition
|
||||
* 512 should be enough for all architectures
|
||||
*/
|
||||
# define MIXER_SIZE (512 + sizeof(struct kobj) + \
|
||||
sizeof(oss_mixer_enuminfo))
|
||||
#define MIXER_SIZE (512 + sizeof(struct kobj) + \
|
||||
sizeof(oss_mixer_enuminfo))
|
||||
|
||||
#define MIXER_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, MIXER_SIZE)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/vchan.h>
|
||||
#include <dev/sound/version.h>
|
||||
#ifdef USING_MUTEX
|
||||
#include <sys/sx.h>
|
||||
#endif
|
||||
|
|
@ -45,7 +46,6 @@ static d_read_t sndstat_read;
|
|||
|
||||
static struct cdevsw sndstat_cdevsw = {
|
||||
.d_version = D_VERSION,
|
||||
.d_flags = D_NEEDGIANT,
|
||||
.d_open = sndstat_open,
|
||||
.d_close = sndstat_close,
|
||||
.d_read = sndstat_read,
|
||||
|
|
@ -137,8 +137,6 @@ SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW,
|
|||
static int
|
||||
sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (sndstat_dev == NULL || i_dev != sndstat_dev)
|
||||
return EBADF;
|
||||
|
||||
|
|
@ -150,19 +148,13 @@ sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
|||
SNDSTAT_PID_SET(i_dev, td->td_proc->p_pid);
|
||||
mtx_unlock(&sndstat_lock);
|
||||
if (sbuf_new(&sndstat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
|
||||
error = ENXIO;
|
||||
goto out;
|
||||
}
|
||||
sndstat_bufptr = 0;
|
||||
error = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM;
|
||||
out:
|
||||
if (error) {
|
||||
mtx_lock(&sndstat_lock);
|
||||
SNDSTAT_FLUSH();
|
||||
SNDSTAT_PID_SET(i_dev, 0);
|
||||
mtx_unlock(&sndstat_lock);
|
||||
return ENXIO;
|
||||
}
|
||||
return (error);
|
||||
sndstat_bufptr = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -201,6 +193,16 @@ sndstat_read(struct cdev *i_dev, struct uio *buf, int flag)
|
|||
}
|
||||
mtx_unlock(&sndstat_lock);
|
||||
|
||||
if (sndstat_bufptr == 0) {
|
||||
err = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM;
|
||||
if (err) {
|
||||
mtx_lock(&sndstat_lock);
|
||||
SNDSTAT_FLUSH();
|
||||
mtx_unlock(&sndstat_lock);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr);
|
||||
err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0;
|
||||
sndstat_bufptr += l;
|
||||
|
|
@ -348,10 +350,11 @@ static int
|
|||
sndstat_prepare(struct sbuf *s)
|
||||
{
|
||||
struct sndstat_entry *ent;
|
||||
struct snddev_info *d;
|
||||
int i, j;
|
||||
|
||||
sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit)\n",
|
||||
(unsigned int)sizeof(intpcm_t) << 3);
|
||||
sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit %d/%s)\n",
|
||||
(u_int)sizeof(intpcm_t) << 3, SND_DRV_VERSION, MACHINE_ARCH);
|
||||
if (SLIST_EMPTY(&sndstat_devlist)) {
|
||||
sbuf_printf(s, "No devices installed.\n");
|
||||
sbuf_finish(s);
|
||||
|
|
@ -365,14 +368,21 @@ sndstat_prepare(struct sbuf *s)
|
|||
ent = sndstat_find(j, i);
|
||||
if (!ent)
|
||||
continue;
|
||||
d = device_get_softc(ent->dev);
|
||||
if (!PCM_REGISTERED(d))
|
||||
continue;
|
||||
/* XXX Need Giant magic entry ??? */
|
||||
PCM_ACQUIRE_QUICK(d);
|
||||
sbuf_printf(s, "%s:", device_get_nameunit(ent->dev));
|
||||
sbuf_printf(s, " <%s>", device_get_desc(ent->dev));
|
||||
sbuf_printf(s, " %s", ent->str);
|
||||
sbuf_printf(s, " %s [%s]", ent->str,
|
||||
(d->flags & SD_F_MPSAFE) ? "MPSAFE" : "GIANT");
|
||||
if (ent->handler)
|
||||
ent->handler(s, ent->dev, snd_verbose);
|
||||
else
|
||||
sbuf_printf(s, " [no handler]");
|
||||
sbuf_printf(s, "\n");
|
||||
PCM_RELEASE_QUICK(d);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -138,12 +138,21 @@ struct snd_mixer;
|
|||
#define SD_F_RSWAPLR 0x00000010
|
||||
#define SD_F_DYING 0x00000020
|
||||
#define SD_F_SUICIDE 0x00000040
|
||||
#define SD_F_BUSY 0x00000080
|
||||
#define SD_F_MPSAFE 0x00000100
|
||||
#define SD_F_REGISTERED 0x00000200
|
||||
|
||||
#define SD_F_PRIO_RD 0x10000000
|
||||
#define SD_F_PRIO_WR 0x20000000
|
||||
#define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR)
|
||||
#define SD_F_DIR_SET 0x40000000
|
||||
#define SD_F_TRANSIENT 0xf0000000
|
||||
|
||||
#define PCM_ALIVE(x) ((x) != NULL && (x)->lock != NULL && \
|
||||
!((x)->flags & SD_F_DYING))
|
||||
#define PCM_REGISTERED(x) (PCM_ALIVE(x) && \
|
||||
((x)->flags & SD_F_REGISTERED))
|
||||
|
||||
/* many variables should be reduced to a range. Here define a macro */
|
||||
#define RANGE(var, low, high) (var) = \
|
||||
(((var)<(low))? (low) : ((var)>(high))? (high) : (var))
|
||||
|
|
@ -464,6 +473,13 @@ int fkchan_kill(struct pcm_channel *c);
|
|||
#define SND_DEV_DSPHW_REC 13 /* specific record channel */
|
||||
#define SND_DEV_DSPHW_VREC 14 /* specific virtual record channel */
|
||||
|
||||
#define SND_DEV_DSPHW_CD 15 /* s16le/stereo 44100Hz CD */
|
||||
|
||||
#define SND_DEV_DSP_MMAP 16 /* OSSv4 compatible /dev/dsp_mmap */
|
||||
|
||||
#define SND_DEV_LAST SND_DEV_DSP_MMAP
|
||||
#define SND_DEV_MAX PCMMAXDEV
|
||||
|
||||
#define DSP_DEFAULT_SPEED 8000
|
||||
|
||||
#define ON 1
|
||||
|
|
@ -565,6 +581,7 @@ struct snddev_info {
|
|||
} busy;
|
||||
} pcm;
|
||||
} channels;
|
||||
TAILQ_HEAD(dsp_cdevinfo_linkhead, dsp_cdevinfo) dsp_cdevinfo_pool;
|
||||
struct snd_clone *clones;
|
||||
struct pcm_channel *fakechan;
|
||||
unsigned devcount, playcount, reccount, pvchancount, rvchancount ;
|
||||
|
|
@ -580,6 +597,7 @@ struct snddev_info {
|
|||
uint32_t rvchanrate, rvchanformat;
|
||||
struct sysctl_ctx_list play_sysctl_ctx, rec_sysctl_ctx;
|
||||
struct sysctl_oid *play_sysctl_tree, *rec_sysctl_tree;
|
||||
struct cv cv;
|
||||
};
|
||||
|
||||
void sound_oss_sysinfo(oss_sysinfo *);
|
||||
|
|
@ -592,6 +610,196 @@ void pcm_lock(struct snddev_info *d);
|
|||
void pcm_unlock(struct snddev_info *d);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For PCM_CV_[WAIT | ACQUIRE | RELEASE], be sure to surround these
|
||||
* with pcm_lock/unlock() sequence, or I'll come to gnaw upon you!
|
||||
*/
|
||||
#ifdef SND_DIAGNOSTIC
|
||||
#define PCM_WAIT(x) do { \
|
||||
if (mtx_owned((x)->lock) == 0) \
|
||||
panic("%s(%d): [PCM WAIT] Mutex not owned!", \
|
||||
__func__, __LINE__); \
|
||||
while ((x)->flags & SD_F_BUSY) { \
|
||||
if (snd_verbose > 3) \
|
||||
device_printf((x)->dev, \
|
||||
"%s(%d): [PCM WAIT] calling cv_wait().\n", \
|
||||
__func__, __LINE__); \
|
||||
cv_wait(&(x)->cv, (x)->lock); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define PCM_ACQUIRE(x) do { \
|
||||
if (mtx_owned((x)->lock) == 0) \
|
||||
panic("%s(%d): [PCM ACQUIRE] Mutex not owned!", \
|
||||
__func__, __LINE__); \
|
||||
if ((x)->flags & SD_F_BUSY) \
|
||||
panic("%s(%d): [PCM ACQUIRE] " \
|
||||
"Trying to acquire BUSY cv!", __func__, __LINE__); \
|
||||
(x)->flags |= SD_F_BUSY; \
|
||||
} while(0)
|
||||
|
||||
#define PCM_RELEASE(x) do { \
|
||||
if (mtx_owned((x)->lock) == 0) \
|
||||
panic("%s(%d): [PCM RELEASE] Mutex not owned!", \
|
||||
__func__, __LINE__); \
|
||||
if ((x)->flags & SD_F_BUSY) { \
|
||||
(x)->flags &= ~SD_F_BUSY; \
|
||||
if ((x)->cv.cv_waiters != 0) { \
|
||||
if ((x)->cv.cv_waiters > 1 && snd_verbose > 3) \
|
||||
device_printf((x)->dev, \
|
||||
"%s(%d): [PCM RELEASE] " \
|
||||
"cv_waiters=%d > 1!\n", \
|
||||
__func__, __LINE__, \
|
||||
(x)->cv.cv_waiters); \
|
||||
cv_broadcast(&(x)->cv); \
|
||||
} \
|
||||
} else \
|
||||
panic("%s(%d): [PCM RELEASE] Releasing non-BUSY cv!", \
|
||||
__func__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
/* Quick version, for shorter path. */
|
||||
#define PCM_ACQUIRE_QUICK(x) do { \
|
||||
if (mtx_owned((x)->lock) != 0) \
|
||||
panic("%s(%d): [PCM ACQUIRE QUICK] Mutex owned!", \
|
||||
__func__, __LINE__); \
|
||||
pcm_lock(x); \
|
||||
PCM_WAIT(x); \
|
||||
PCM_ACQUIRE(x); \
|
||||
pcm_unlock(x); \
|
||||
} while(0)
|
||||
|
||||
#define PCM_RELEASE_QUICK(x) do { \
|
||||
if (mtx_owned((x)->lock) != 0) \
|
||||
panic("%s(%d): [PCM RELEASE QUICK] Mutex owned!", \
|
||||
__func__, __LINE__); \
|
||||
pcm_lock(x); \
|
||||
PCM_RELEASE(x); \
|
||||
pcm_unlock(x); \
|
||||
} while(0)
|
||||
|
||||
#define PCM_BUSYASSERT(x) do { \
|
||||
if (!((x) != NULL && ((x)->flags & SD_F_BUSY))) \
|
||||
panic("%s(%d): [PCM BUSYASSERT] " \
|
||||
"Failed, snddev_info=%p", __func__, __LINE__, x); \
|
||||
} while(0)
|
||||
|
||||
#define PCM_GIANT_ENTER(x) do { \
|
||||
int _pcm_giant = 0; \
|
||||
if (mtx_owned((x)->lock) != 0) \
|
||||
panic("%s(%d): [GIANT ENTER] PCM lock owned!", \
|
||||
__func__, __LINE__); \
|
||||
if (mtx_owned(&Giant) != 0 && snd_verbose > 3) \
|
||||
device_printf((x)->dev, \
|
||||
"%s(%d): [GIANT ENTER] Giant owned!\n", \
|
||||
__func__, __LINE__); \
|
||||
if (!((x)->flags & SD_F_MPSAFE) && mtx_owned(&Giant) == 0) \
|
||||
do { \
|
||||
mtx_lock(&Giant); \
|
||||
_pcm_giant = 1; \
|
||||
} while(0)
|
||||
|
||||
#define PCM_GIANT_EXIT(x) do { \
|
||||
if (mtx_owned((x)->lock) != 0) \
|
||||
panic("%s(%d): [GIANT EXIT] PCM lock owned!", \
|
||||
__func__, __LINE__); \
|
||||
if (!(_pcm_giant == 0 || _pcm_giant == 1)) \
|
||||
panic("%s(%d): [GIANT EXIT] _pcm_giant screwed!", \
|
||||
__func__, __LINE__); \
|
||||
if ((x)->flags & SD_F_MPSAFE) { \
|
||||
if (_pcm_giant == 1) \
|
||||
panic("%s(%d): [GIANT EXIT] MPSAFE Giant?", \
|
||||
__func__, __LINE__); \
|
||||
if (mtx_owned(&Giant) != 0 && snd_verbose > 3) \
|
||||
device_printf((x)->dev, \
|
||||
"%s(%d): [GIANT EXIT] Giant owned!\n", \
|
||||
__func__, __LINE__); \
|
||||
} \
|
||||
if (_pcm_giant != 0) { \
|
||||
if (mtx_owned(&Giant) == 0) \
|
||||
panic("%s(%d): [GIANT EXIT] Giant not owned!", \
|
||||
__func__, __LINE__); \
|
||||
_pcm_giant = 0; \
|
||||
mtx_unlock(&Giant); \
|
||||
} \
|
||||
} while(0)
|
||||
#else /* SND_DIAGNOSTIC */
|
||||
#define PCM_WAIT(x) do { \
|
||||
mtx_assert((x)->lock, MA_OWNED); \
|
||||
while ((x)->flags & SD_F_BUSY) \
|
||||
cv_wait(&(x)->cv, (x)->lock); \
|
||||
} while(0)
|
||||
|
||||
#define PCM_ACQUIRE(x) do { \
|
||||
mtx_assert((x)->lock, MA_OWNED); \
|
||||
KASSERT(!((x)->flags & SD_F_BUSY), \
|
||||
("%s(%d): [PCM ACQUIRE] Trying to acquire BUSY cv!", \
|
||||
__func__, __LINE__)); \
|
||||
(x)->flags |= SD_F_BUSY; \
|
||||
} while(0)
|
||||
|
||||
#define PCM_RELEASE(x) do { \
|
||||
mtx_assert((x)->lock, MA_OWNED); \
|
||||
KASSERT((x)->flags & SD_F_BUSY, \
|
||||
("%s(%d): [PCM RELEASE] Releasing non-BUSY cv!", \
|
||||
__func__, __LINE__)); \
|
||||
(x)->flags &= ~SD_F_BUSY; \
|
||||
if ((x)->cv.cv_waiters != 0) \
|
||||
cv_broadcast(&(x)->cv); \
|
||||
} while(0)
|
||||
|
||||
/* Quick version, for shorter path. */
|
||||
#define PCM_ACQUIRE_QUICK(x) do { \
|
||||
mtx_assert((x)->lock, MA_NOTOWNED); \
|
||||
pcm_lock(x); \
|
||||
PCM_WAIT(x); \
|
||||
PCM_ACQUIRE(x); \
|
||||
pcm_unlock(x); \
|
||||
} while(0)
|
||||
|
||||
#define PCM_RELEASE_QUICK(x) do { \
|
||||
mtx_assert((x)->lock, MA_NOTOWNED); \
|
||||
pcm_lock(x); \
|
||||
PCM_RELEASE(x); \
|
||||
pcm_unlock(x); \
|
||||
} while(0)
|
||||
|
||||
#define PCM_BUSYASSERT(x) KASSERT(x != NULL && \
|
||||
((x)->flags & SD_F_BUSY), \
|
||||
("%s(%d): [PCM BUSYASSERT] " \
|
||||
"Failed, snddev_info=%p", \
|
||||
__func__, __LINE__, x))
|
||||
|
||||
#define PCM_GIANT_ENTER(x) do { \
|
||||
int _pcm_giant = 0; \
|
||||
mtx_assert((x)->lock, MA_NOTOWNED); \
|
||||
if (!((x)->flags & SD_F_MPSAFE) && mtx_owned(&Giant) == 0) \
|
||||
do { \
|
||||
mtx_lock(&Giant); \
|
||||
_pcm_giant = 1; \
|
||||
} while(0)
|
||||
|
||||
#define PCM_GIANT_EXIT(x) do { \
|
||||
mtx_assert((x)->lock, MA_NOTOWNED); \
|
||||
KASSERT(_pcm_giant == 0 || _pcm_giant == 1, \
|
||||
("%s(%d): [GIANT EXIT] _pcm_giant screwed!", \
|
||||
__func__, __LINE__)); \
|
||||
KASSERT(!((x)->flags & SD_F_MPSAFE) || \
|
||||
(((x)->flags & SD_F_MPSAFE) && _pcm_giant == 0), \
|
||||
("%s(%d): [GIANT EXIT] MPSAFE Giant?", \
|
||||
__func__, __LINE__)); \
|
||||
if (_pcm_giant != 0) { \
|
||||
mtx_assert(&Giant, MA_OWNED); \
|
||||
_pcm_giant = 0; \
|
||||
mtx_unlock(&Giant); \
|
||||
} \
|
||||
} while(0)
|
||||
#endif /* !SND_DIAGNOSTIC */
|
||||
|
||||
#define PCM_GIANT_LEAVE(x) \
|
||||
PCM_GIANT_EXIT(x); \
|
||||
} while(0)
|
||||
|
||||
#ifdef KLD_MODULE
|
||||
#define PCM_KLDSTRING(a) ("kld " # a)
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -222,10 +222,15 @@ feed_vchan_rec(struct pcm_channel *c)
|
|||
|
||||
CHN_FOREACH(ch, c, children.busy) {
|
||||
CHN_LOCK(ch);
|
||||
if (!(ch->flags & CHN_F_TRIGGERED)) {
|
||||
CHN_UNLOCK(ch);
|
||||
continue;
|
||||
}
|
||||
bs = ch->bufsoft;
|
||||
if (ch->flags & CHN_F_MAPPED)
|
||||
sndbuf_dispose(bs, NULL, sndbuf_getready(bs));
|
||||
cnt = sndbuf_getfree(bs);
|
||||
if (!(ch->flags & CHN_F_TRIGGERED) ||
|
||||
cnt < sndbuf_getbps(bs)) {
|
||||
if (cnt < sndbuf_getbps(bs)) {
|
||||
CHN_UNLOCK(ch);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -429,7 +434,7 @@ vchan_trigger(kobj_t obj, void *data, int go)
|
|||
{
|
||||
struct vchinfo *ch = data;
|
||||
struct pcm_channel *c, *p;
|
||||
int otrigger;
|
||||
int err, otrigger;
|
||||
|
||||
if (!PCMTRIG_COMMON(go) || go == ch->trigger)
|
||||
return (0);
|
||||
|
|
@ -458,11 +463,11 @@ vchan_trigger(kobj_t obj, void *data, int go)
|
|||
break;
|
||||
}
|
||||
|
||||
err = chn_notify(p, CHN_N_TRIGGER);
|
||||
CHN_UNLOCK(p);
|
||||
chn_notify(p, CHN_N_TRIGGER);
|
||||
CHN_LOCK(c);
|
||||
|
||||
return (0);
|
||||
return (err);
|
||||
}
|
||||
|
||||
static struct pcmchan_caps *
|
||||
|
|
@ -512,15 +517,15 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
|||
struct snddev_info *d;
|
||||
struct pcm_channel *c, *ch = NULL;
|
||||
struct pcmchan_caps *caps;
|
||||
int vchancount, *vchanrate;
|
||||
int direction;
|
||||
int err = 0;
|
||||
int newspd = 0;
|
||||
int *vchanrate, vchancount, direction, err, newspd;
|
||||
|
||||
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
|
||||
if (d == NULL || !(d->flags & SD_F_AUTOVCHAN))
|
||||
if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
|
||||
return (EINVAL);
|
||||
|
||||
pcm_lock(d);
|
||||
PCM_WAIT(d);
|
||||
|
||||
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||
case VCHAN_PLAY:
|
||||
direction = PCMDIR_PLAY;
|
||||
|
|
@ -533,16 +538,21 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
|||
vchanrate = &d->rvchanrate;
|
||||
break;
|
||||
default:
|
||||
pcm_unlock(d);
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (vchancount < 1)
|
||||
if (vchancount < 1) {
|
||||
pcm_unlock(d);
|
||||
return (EINVAL);
|
||||
if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
|
||||
pcm_inprog(d, -1);
|
||||
return (EINPROGRESS);
|
||||
}
|
||||
|
||||
PCM_ACQUIRE(d);
|
||||
pcm_unlock(d);
|
||||
|
||||
newspd = 0;
|
||||
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
CHN_LOCK(c);
|
||||
if (c->direction == direction) {
|
||||
|
|
@ -550,20 +560,14 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
|||
/* Sanity check */
|
||||
if (ch != NULL && ch != c->parentchannel) {
|
||||
CHN_UNLOCK(c);
|
||||
pcm_inprog(d, -1);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
if (req->newptr != NULL &&
|
||||
(c->flags & CHN_F_BUSY)) {
|
||||
CHN_UNLOCK(c);
|
||||
pcm_inprog(d, -1);
|
||||
return (EBUSY);
|
||||
}
|
||||
} else if (c->flags & CHN_F_HAS_VCHAN) {
|
||||
/* No way!! */
|
||||
if (ch != NULL) {
|
||||
CHN_UNLOCK(c);
|
||||
pcm_inprog(d, -1);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
ch = c;
|
||||
|
|
@ -573,14 +577,15 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
|||
CHN_UNLOCK(c);
|
||||
}
|
||||
if (ch == NULL) {
|
||||
pcm_inprog(d, -1);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
err = sysctl_handle_int(oidp, &newspd, 0, req);
|
||||
if (err == 0 && req->newptr != NULL) {
|
||||
if (newspd < 1 || newspd < feeder_rate_min ||
|
||||
newspd > feeder_rate_max) {
|
||||
pcm_inprog(d, -1);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
CHN_LOCK(ch);
|
||||
|
|
@ -589,11 +594,11 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
|||
if (caps == NULL || newspd < caps->minspeed ||
|
||||
newspd > caps->maxspeed) {
|
||||
CHN_UNLOCK(ch);
|
||||
pcm_inprog(d, -1);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
if (newspd != ch->speed) {
|
||||
if (CHN_STOPPED(ch) && newspd != ch->speed) {
|
||||
err = chn_setspeed(ch, newspd);
|
||||
/*
|
||||
* Try to avoid FEEDER_RATE on parent channel if the
|
||||
|
|
@ -604,16 +609,14 @@ sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
|
|||
newspd = sndbuf_getspd(ch->bufhard);
|
||||
err = chn_setspeed(ch, newspd);
|
||||
}
|
||||
CHN_UNLOCK(ch);
|
||||
if (err == 0) {
|
||||
pcm_lock(d);
|
||||
if (err == 0)
|
||||
*vchanrate = newspd;
|
||||
pcm_unlock(d);
|
||||
}
|
||||
} else
|
||||
CHN_UNLOCK(ch);
|
||||
}
|
||||
CHN_UNLOCK(ch);
|
||||
}
|
||||
pcm_inprog(d, -1);
|
||||
|
||||
PCM_RELEASE_QUICK(d);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
|
|
@ -623,15 +626,16 @@ sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
|
|||
struct snddev_info *d;
|
||||
struct pcm_channel *c, *ch = NULL;
|
||||
uint32_t newfmt, spd;
|
||||
int vchancount, *vchanformat;
|
||||
int direction;
|
||||
int err = 0, i;
|
||||
int *vchanformat, vchancount, direction, err, i;
|
||||
char fmtstr[AFMTSTR_MAXSZ];
|
||||
|
||||
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
|
||||
if (d == NULL || !(d->flags & SD_F_AUTOVCHAN))
|
||||
if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
|
||||
return (EINVAL);
|
||||
|
||||
pcm_lock(d);
|
||||
PCM_WAIT(d);
|
||||
|
||||
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||
case VCHAN_PLAY:
|
||||
direction = PCMDIR_PLAY;
|
||||
|
|
@ -644,16 +648,19 @@ sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
|
|||
vchanformat = &d->rvchanformat;
|
||||
break;
|
||||
default:
|
||||
pcm_unlock(d);
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (vchancount < 1)
|
||||
if (vchancount < 1) {
|
||||
pcm_unlock(d);
|
||||
return (EINVAL);
|
||||
if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
|
||||
pcm_inprog(d, -1);
|
||||
return (EINPROGRESS);
|
||||
}
|
||||
|
||||
PCM_ACQUIRE(d);
|
||||
pcm_unlock(d);
|
||||
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
CHN_LOCK(c);
|
||||
if (c->direction == direction) {
|
||||
|
|
@ -661,20 +668,14 @@ sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
|
|||
/* Sanity check */
|
||||
if (ch != NULL && ch != c->parentchannel) {
|
||||
CHN_UNLOCK(c);
|
||||
pcm_inprog(d, -1);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
if (req->newptr != NULL &&
|
||||
(c->flags & CHN_F_BUSY)) {
|
||||
CHN_UNLOCK(c);
|
||||
pcm_inprog(d, -1);
|
||||
return (EBUSY);
|
||||
}
|
||||
} else if (c->flags & CHN_F_HAS_VCHAN) {
|
||||
/* No way!! */
|
||||
if (ch != NULL) {
|
||||
CHN_UNLOCK(c);
|
||||
pcm_inprog(d, -1);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
ch = c;
|
||||
|
|
@ -690,9 +691,10 @@ sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
|
|||
CHN_UNLOCK(c);
|
||||
}
|
||||
if (ch == NULL) {
|
||||
pcm_inprog(d, -1);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
|
||||
if (err == 0 && req->newptr != NULL) {
|
||||
for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) {
|
||||
|
|
@ -704,26 +706,24 @@ sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
|
|||
}
|
||||
newfmt = vchan_valid_strformat(fmtstr);
|
||||
if (newfmt == 0) {
|
||||
pcm_inprog(d, -1);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
CHN_LOCK(ch);
|
||||
if (newfmt != ch->format) {
|
||||
if (CHN_STOPPED(ch) && newfmt != ch->format) {
|
||||
/* Get channel speed, before chn_reset() screw it. */
|
||||
spd = ch->speed;
|
||||
err = chn_reset(ch, newfmt);
|
||||
if (err == 0)
|
||||
err = chn_setspeed(ch, spd);
|
||||
CHN_UNLOCK(ch);
|
||||
if (err == 0) {
|
||||
pcm_lock(d);
|
||||
if (err == 0)
|
||||
*vchanformat = newfmt;
|
||||
pcm_unlock(d);
|
||||
}
|
||||
} else
|
||||
CHN_UNLOCK(ch);
|
||||
}
|
||||
CHN_UNLOCK(ch);
|
||||
}
|
||||
pcm_inprog(d, -1);
|
||||
|
||||
PCM_RELEASE_QUICK(d);
|
||||
|
||||
return (err);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -745,6 +745,8 @@ vchan_create(struct pcm_channel *parent, int num)
|
|||
int err, first, speed, r;
|
||||
int direction;
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
|
||||
if (!(parent->flags & CHN_F_BUSY))
|
||||
return (EBUSY);
|
||||
|
||||
|
|
@ -761,14 +763,17 @@ vchan_create(struct pcm_channel *parent, int num)
|
|||
CHN_UNLOCK(parent);
|
||||
|
||||
/* create a new playback channel */
|
||||
pcm_lock(d);
|
||||
ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent);
|
||||
if (ch == NULL) {
|
||||
pcm_unlock(d);
|
||||
CHN_LOCK(parent);
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
/* add us to our grandparent's channel list */
|
||||
err = pcm_chn_add(d, ch);
|
||||
pcm_unlock(d);
|
||||
if (err) {
|
||||
pcm_chn_destroy(ch);
|
||||
CHN_LOCK(parent);
|
||||
|
|
@ -904,7 +909,6 @@ vchan_create(struct pcm_channel *parent, int num)
|
|||
* Save new value.
|
||||
*/
|
||||
CHN_UNLOCK(parent);
|
||||
pcm_lock(d);
|
||||
if (direction == PCMDIR_PLAY_VIRTUAL) {
|
||||
d->pvchanformat = vchanfmt;
|
||||
d->pvchanrate = speed;
|
||||
|
|
@ -912,7 +916,6 @@ vchan_create(struct pcm_channel *parent, int num)
|
|||
d->rvchanformat = vchanfmt;
|
||||
d->rvchanrate = speed;
|
||||
}
|
||||
pcm_unlock(d);
|
||||
CHN_LOCK(parent);
|
||||
}
|
||||
}
|
||||
|
|
@ -943,6 +946,8 @@ vchan_destroy(struct pcm_channel *c)
|
|||
uint32_t spd;
|
||||
int err;
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
|
||||
CHN_LOCK(parent);
|
||||
if (!(parent->flags & CHN_F_BUSY)) {
|
||||
CHN_UNLOCK(parent);
|
||||
|
|
|
|||
|
|
@ -4498,7 +4498,7 @@ uaudio_mixer_setrecsrc(device_t dev, u_int32_t src)
|
|||
static int
|
||||
uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snddev_info *d;
|
||||
struct pcm_channel *c;
|
||||
struct pcm_feeder *f;
|
||||
device_t pa_dev = device_get_parent(dev);
|
||||
|
|
@ -4511,13 +4511,14 @@ uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
|
|||
if (!d)
|
||||
return ENXIO;
|
||||
|
||||
snd_mtxlock(d->lock);
|
||||
PCM_BUSYASSERT(d);
|
||||
|
||||
if (CHN_EMPTY(d, channels.pcm)) {
|
||||
sbuf_printf(s, " (mixer only)");
|
||||
snd_mtxunlock(d->lock);
|
||||
return 0;
|
||||
}
|
||||
sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
|
||||
|
||||
sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
|
||||
d->playcount, d->pvchancount,
|
||||
d->reccount, d->rvchancount,
|
||||
(d->flags & SD_F_SIMPLEX)? "" : " duplex",
|
||||
|
|
@ -4532,17 +4533,16 @@ uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
|
|||
sbuf_cat(s, sbuf_data(&(sc->uaudio_sndstat)));
|
||||
}
|
||||
|
||||
if (verbose <= 1) {
|
||||
snd_mtxunlock(d->lock);
|
||||
if (verbose <= 1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
sbuf_printf(s, "\n\t");
|
||||
|
||||
KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
|
||||
("hosed pcm channel setup"));
|
||||
|
||||
sbuf_printf(s, "\n\t");
|
||||
|
||||
/* it would be better to indent child channels */
|
||||
sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
|
||||
sbuf_printf(s, "spd %d", c->speed);
|
||||
|
|
@ -4584,14 +4584,14 @@ uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
|
|||
sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
|
||||
if (f->desc->type == FEEDER_RATE)
|
||||
sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
|
||||
if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
|
||||
if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
|
||||
f->desc->type == FEEDER_VOLUME)
|
||||
sbuf_printf(s, "(0x%08x)", f->desc->out);
|
||||
sbuf_printf(s, " -> ");
|
||||
f = f->parent;
|
||||
}
|
||||
sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
|
||||
}
|
||||
snd_mtxunlock(d->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,6 @@
|
|||
* Last 2 decimal places reserved for daily versioning, starting
|
||||
* with 0.
|
||||
*/
|
||||
#define SND_DRV_VERSION 2007060100
|
||||
#define SND_DRV_VERSION 2007061600
|
||||
|
||||
#endif /* !_SND_VERSION_H_ */
|
||||
|
|
|
|||
Loading…
Reference in a new issue