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:
Ariff Abdullah 2007-06-16 03:37:28 +00:00
parent 48dabb921d
commit e4e61333ff
16 changed files with 2041 additions and 1251 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -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);
}
/**

View file

@ -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

View file

@ -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_ */

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;
}

View file

@ -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_ */