summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/sound/pci/es137x.c58
-rw-r--r--sys/dev/sound/pcm/buffer.c2
-rw-r--r--sys/dev/sound/pcm/channel.c390
-rw-r--r--sys/dev/sound/pcm/channel.h30
-rw-r--r--sys/dev/sound/pcm/dsp.c1124
-rw-r--r--sys/dev/sound/pcm/dsp.h5
-rw-r--r--sys/dev/sound/pcm/feeder.c21
-rw-r--r--sys/dev/sound/pcm/feeder_rate.c10
-rw-r--r--sys/dev/sound/pcm/mixer.c548
-rw-r--r--sys/dev/sound/pcm/mixer.h23
-rw-r--r--sys/dev/sound/pcm/sndstat.c40
-rw-r--r--sys/dev/sound/pcm/sound.c618
-rw-r--r--sys/dev/sound/pcm/sound.h208
-rw-r--r--sys/dev/sound/pcm/vchan.c131
-rw-r--r--sys/dev/sound/usb/uaudio.c20
-rw-r--r--sys/dev/sound/version.h2
16 files changed, 2010 insertions, 1220 deletions
diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c
index e332db3..2a2e5a7 100644
--- a/sys/dev/sound/pci/es137x.c
+++ b/sys/dev/sound/pci/es137x.c
@@ -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);
}
diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c
index ad252d0..98fb60e 100644
--- a/sys/dev/sound/pcm/buffer.c
+++ b/sys/dev/sound/pcm/buffer.c
@@ -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);
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index c1c472d..2ecb444 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -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,25 +438,8 @@ chn_write(struct pcm_channel *c, struct uio *buf)
}
}
- 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);
+ return (ret);
}
-#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);
}
/**
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
index 42e3921..65175a3 100644
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -93,6 +93,10 @@ struct pcm_channel {
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.
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 93ba6a4..cb185f6 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -29,22 +29,24 @@
SND_DECLARE_FILE("$FreeBSD$");
+static int dsp_mmap_allow_prot_exec = 0;
+SYSCTL_INT(_hw_snd, OID_AUTO, compat_linux_mmap, CTLFLAG_RW,
+ &dsp_mmap_allow_prot_exec, 0, "linux mmap compatibility");
+
struct dsp_cdevinfo {
struct pcm_channel *rdch, *wrch;
+ int busy, simplex;
+ TAILQ_ENTRY(dsp_cdevinfo) link;
};
-#define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch)
-#define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch)
+#define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch)
+#define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch)
+#define PCM_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex)
-#define PCMDEV_ACQUIRE(x) do { \
- if ((x)->si_drv1 == NULL) \
- (x)->si_drv1 = x; \
-} while(0)
+#define DSP_CDEVINFO_CACHESIZE 8
-#define PCMDEV_RELEASE(x) do { \
- if ((x)->si_drv1 == x) \
- (x)->si_drv1 = NULL; \
-} while(0)
+#define DSP_REGISTERED(x, y) (PCM_REGISTERED(x) && \
+ (y) != NULL && (y)->si_drv1 != NULL)
#define OLDPCM_IOCTL
@@ -58,7 +60,6 @@ static d_mmap_t dsp_mmap;
struct cdevsw dsp_cdevsw = {
.d_version = D_VERSION,
- .d_flags = D_NEEDGIANT,
.d_open = dsp_open,
.d_close = dsp_close,
.d_read = dsp_read,
@@ -95,7 +96,7 @@ dsp_get_info(struct cdev *dev)
return (devclass_get_softc(pcm_devclass, PCMUNIT(dev)));
}
-static u_int32_t
+static uint32_t
dsp_get_flags(struct cdev *dev)
{
device_t bdev;
@@ -106,7 +107,7 @@ dsp_get_flags(struct cdev *dev)
}
static void
-dsp_set_flags(struct cdev *dev, u_int32_t flags)
+dsp_set_flags(struct cdev *dev, uint32_t flags)
{
device_t bdev;
@@ -118,93 +119,218 @@ dsp_set_flags(struct cdev *dev, u_int32_t flags)
/*
* return the channels associated with an open device instance.
- * set the priority if the device is simplex and one direction (only) is
- * specified.
* lock channels specified.
*/
static int
-getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
+getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch,
+ uint32_t prio)
{
struct snddev_info *d;
- u_int32_t flags;
-
- d = dsp_get_info(dev);
- if (d == NULL)
- return -1;
- pcm_inprog(d, 1);
- pcm_lock(d);
- flags = dsp_get_flags(dev);
- KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
- ("getchns: read and write both prioritised"));
+ struct pcm_channel *ch;
+ uint32_t flags;
- if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
- flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
+ if (PCM_SIMPLEX(dev) != 0) {
+ d = dsp_get_info(dev);
+ if (!PCM_REGISTERED(d))
+ return (ENXIO);
+ pcm_lock(d);
+ PCM_WAIT(d);
+ PCM_ACQUIRE(d);
+ /*
+ * Note: order is important -
+ * pcm flags -> prio query flags -> wild guess
+ */
+ ch = NULL;
+ flags = dsp_get_flags(dev);
+ if (flags & SD_F_PRIO_WR) {
+ ch = PCM_RDCH(dev);
+ PCM_RDCH(dev) = NULL;
+ } else if (flags & SD_F_PRIO_RD) {
+ ch = PCM_WRCH(dev);
+ PCM_WRCH(dev) = NULL;
+ } else if (prio & SD_F_PRIO_WR) {
+ ch = PCM_RDCH(dev);
+ PCM_RDCH(dev) = NULL;
+ flags |= SD_F_PRIO_WR;
+ } else if (prio & SD_F_PRIO_RD) {
+ ch = PCM_WRCH(dev);
+ PCM_WRCH(dev) = NULL;
+ flags |= SD_F_PRIO_RD;
+ } else if (PCM_WRCH(dev) != NULL) {
+ ch = PCM_RDCH(dev);
+ PCM_RDCH(dev) = NULL;
+ flags |= SD_F_PRIO_WR;
+ } else if (PCM_RDCH(dev) != NULL) {
+ ch = PCM_WRCH(dev);
+ PCM_WRCH(dev) = NULL;
+ flags |= SD_F_PRIO_RD;
+ }
+ PCM_SIMPLEX(dev) = 0;
dsp_set_flags(dev, flags);
+ if (ch != NULL) {
+ CHN_LOCK(ch);
+ pcm_chnref(ch, -1);
+ pcm_chnrelease(ch);
+ }
+ PCM_RELEASE(d);
+ pcm_unlock(d);
}
*rdch = PCM_RDCH(dev);
*wrch = PCM_WRCH(dev);
- if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
- if (prio) {
- if (*rdch && flags & SD_F_PRIO_WR) {
- PCM_RDCH(dev) = NULL;
- *rdch = pcm_getfakechan(d);
- } else if (*wrch && flags & SD_F_PRIO_RD) {
- PCM_WRCH(dev) = NULL;
- *wrch = pcm_getfakechan(d);
- }
- }
- pcm_getfakechan(d)->flags |= CHN_F_BUSY;
- }
- pcm_unlock(d);
-
- if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
+ if (*rdch != NULL && (prio & SD_F_PRIO_RD))
CHN_LOCK(*rdch);
- if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
+ if (*wrch != NULL && (prio & SD_F_PRIO_WR))
CHN_LOCK(*wrch);
- return 0;
+ return (0);
}
/* unlock specified channels */
static void
-relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
+relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch,
+ uint32_t prio)
{
- struct snddev_info *d;
-
- d = dsp_get_info(dev);
- if (d == NULL)
- return;
- if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
+ if (wrch != NULL && (prio & SD_F_PRIO_WR))
CHN_UNLOCK(wrch);
- if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
+ if (rdch != NULL && (prio & SD_F_PRIO_RD))
CHN_UNLOCK(rdch);
- pcm_inprog(d, -1);
}
static void
dsp_cdevinfo_alloc(struct cdev *dev,
struct pcm_channel *rdch, struct pcm_channel *wrch)
{
- KASSERT(dev != NULL && dev->si_drv1 == dev && rdch != wrch,
+ struct snddev_info *d;
+ struct dsp_cdevinfo *cdi;
+ int simplex;
+
+ d = dsp_get_info(dev);
+
+ KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL &&
+ rdch != wrch,
("bogus %s(), what are you trying to accomplish here?", __func__));
-
- dev->si_drv1 = malloc(sizeof(struct dsp_cdevinfo), M_DEVBUF,
- M_WAITOK | M_ZERO);
- PCM_RDCH(dev) = rdch;
- PCM_WRCH(dev) = wrch;
+ PCM_BUSYASSERT(d);
+ mtx_assert(d->lock, MA_OWNED);
+
+ simplex = (dsp_get_flags(dev) & SD_F_SIMPLEX) ? 1 : 0;
+
+ /*
+ * Scan for free instance entry and put it into the end of list.
+ * Create new one if necessary.
+ */
+ TAILQ_FOREACH(cdi, &d->dsp_cdevinfo_pool, link) {
+ if (cdi->busy != 0)
+ break;
+ cdi->rdch = rdch;
+ cdi->wrch = wrch;
+ cdi->simplex = simplex;
+ cdi->busy = 1;
+ TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
+ TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link);
+ dev->si_drv1 = cdi;
+ return;
+ }
+ pcm_unlock(d);
+ cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcm_lock(d);
+ cdi->rdch = rdch;
+ cdi->wrch = wrch;
+ cdi->simplex = simplex;
+ cdi->busy = 1;
+ TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link);
+ dev->si_drv1 = cdi;
}
static void
dsp_cdevinfo_free(struct cdev *dev)
{
- KASSERT(dev != NULL && dev->si_drv1 != NULL &&
+ struct snddev_info *d;
+ struct dsp_cdevinfo *cdi, *tmp;
+ uint32_t flags;
+ int i;
+
+ d = dsp_get_info(dev);
+
+ KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL &&
PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL,
("bogus %s(), what are you trying to accomplish here?", __func__));
+ PCM_BUSYASSERT(d);
+ mtx_assert(d->lock, MA_OWNED);
- free(dev->si_drv1, M_DEVBUF);
+ cdi = dev->si_drv1;
dev->si_drv1 = NULL;
+ cdi->rdch = NULL;
+ cdi->wrch = NULL;
+ cdi->simplex = 0;
+ cdi->busy = 0;
+
+ /*
+ * Once it is free, move it back to the beginning of list for
+ * faster new entry allocation.
+ */
+ TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
+ TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link);
+
+ /*
+ * Scan the list, cache free entries up to DSP_CDEVINFO_CACHESIZE.
+ * Reset simplex flags.
+ */
+ flags = dsp_get_flags(dev) & ~SD_F_PRIO_SET;
+ i = DSP_CDEVINFO_CACHESIZE;
+ TAILQ_FOREACH_SAFE(cdi, &d->dsp_cdevinfo_pool, link, tmp) {
+ if (cdi->busy != 0) {
+ if (cdi->simplex == 0) {
+ if (cdi->rdch != NULL)
+ flags |= SD_F_PRIO_RD;
+ if (cdi->wrch != NULL)
+ flags |= SD_F_PRIO_WR;
+ }
+ } else {
+ if (i == 0) {
+ TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
+ free(cdi, M_DEVBUF);
+ } else
+ i--;
+ }
+ }
+ dsp_set_flags(dev, flags);
+}
+
+void
+dsp_cdevinfo_init(struct snddev_info *d)
+{
+ struct dsp_cdevinfo *cdi;
+ int i;
+
+ KASSERT(d != NULL, ("NULL snddev_info"));
+ PCM_BUSYASSERT(d);
+ mtx_assert(d->lock, MA_NOTOWNED);
+
+ TAILQ_INIT(&d->dsp_cdevinfo_pool);
+ for (i = 0; i < DSP_CDEVINFO_CACHESIZE; i++) {
+ cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO);
+ TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link);
+ }
+}
+
+void
+dsp_cdevinfo_flush(struct snddev_info *d)
+{
+ struct dsp_cdevinfo *cdi, *tmp;
+
+ KASSERT(d != NULL, ("NULL snddev_info"));
+ PCM_BUSYASSERT(d);
+ mtx_assert(d->lock, MA_NOTOWNED);
+
+ cdi = TAILQ_FIRST(&d->dsp_cdevinfo_pool);
+ while (cdi != NULL) {
+ tmp = TAILQ_NEXT(cdi, link);
+ free(cdi, M_DEVBUF);
+ cdi = tmp;
+ }
+ TAILQ_INIT(&d->dsp_cdevinfo_pool);
}
/* duplex / simplex cdev type */
@@ -244,28 +370,49 @@ static const struct {
AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY },
{ SND_DEV_DSPHW_VREC, "dsp", ".vr", 1, 1, SND_MAXVCHANS,
AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY },
+ { SND_DEV_DSPHW_CD, "dspcd", ".", 0, 0, 0,
+ AFMT_S16_LE | AFMT_STEREO, 44100, DSP_CDEV_TYPE_RDWR },
+ { SND_DEV_DSP_MMAP, "dsp_mmap", ".", 0, 0, 0,
+ AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDWR },
};
+#define DSP_FIXUP_ERROR() do { \
+ prio = dsp_get_flags(i_dev); \
+ if (!DSP_F_VALID(flags)) \
+ error = EINVAL; \
+ if (!DSP_F_DUPLEX(flags) && \
+ ((DSP_F_READ(flags) && d->reccount == 0) || \
+ (DSP_F_WRITE(flags) && d->playcount == 0))) \
+ error = ENOTSUP; \
+ else if (!DSP_F_DUPLEX(flags) && (prio & SD_F_SIMPLEX) && \
+ ((DSP_F_READ(flags) && (prio & SD_F_PRIO_WR)) || \
+ (DSP_F_WRITE(flags) && (prio & SD_F_PRIO_RD)))) \
+ error = EBUSY; \
+ else if (DSP_REGISTERED(d, i_dev)) \
+ error = EBUSY; \
+} while(0)
+
static int
dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
struct pcm_channel *rdch, *wrch;
struct snddev_info *d;
- uint32_t fmt, spd;
- int i, error, devtype;
- int wdevunit, rdevunit;
+ uint32_t fmt, spd, prio;
+ int i, error, rderror, wrerror, devtype, wdevunit, rdevunit;
/* Kind of impossible.. */
if (i_dev == NULL || td == NULL)
- return ENODEV;
+ return (ENODEV);
- /* This too.. */
d = dsp_get_info(i_dev);
- if (d == NULL)
- return EBADF;
+ if (!PCM_REGISTERED(d))
+ return (EBADF);
+
+ PCM_GIANT_ENTER(d);
/* Lock snddev so nobody else can monkey with it. */
pcm_lock(d);
+ PCM_WAIT(d);
/*
* Try to acquire cloned device before someone else pick it.
@@ -273,31 +420,29 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
*/
error = snd_clone_acquire(i_dev);
if (!(error == 0 || error == ENODEV)) {
+ DSP_FIXUP_ERROR();
pcm_unlock(d);
- return error;
+ PCM_GIANT_EXIT(d);
+ return (error);
}
- if (!DSP_F_VALID(flags))
- error = EINVAL;
- else if (i_dev->si_drv1 != NULL)
- error = EBUSY;
- else if (DSP_F_DUPLEX(flags) &&
- (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
- error = ENOTSUP;
- else
- error = 0;
+ error = 0;
+ DSP_FIXUP_ERROR();
if (error != 0) {
(void)snd_clone_release(i_dev);
pcm_unlock(d);
- return error;
+ PCM_GIANT_EXIT(d);
+ return (error);
}
/*
- * Fake busy state by pointing si_drv1 to something else since
- * we have to give up locking somewhere during setup process.
+ * That is just enough. Acquire and unlock pcm lock so
+ * the other will just have to wait until we finish doing
+ * everything.
*/
- PCMDEV_ACQUIRE(i_dev);
+ PCM_ACQUIRE(d);
+ pcm_unlock(d);
devtype = PCMDEV(i_dev);
wdevunit = -1;
@@ -317,9 +462,9 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
* simplex, opposite direction? Please be gone..
*/
(void)snd_clone_release(i_dev);
- PCMDEV_RELEASE(i_dev);
- pcm_unlock(d);
- return ENOTSUP;
+ PCM_RELEASE_QUICK(d);
+ PCM_GIANT_EXIT(d);
+ return (ENOTSUP);
}
if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY)
wdevunit = dev2unit(i_dev);
@@ -336,6 +481,8 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
rdch = NULL;
wrch = NULL;
+ rderror = 0;
+ wrerror = 0;
/*
* if we get here, the open request is valid- either:
@@ -345,81 +492,92 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
*/
if (DSP_F_READ(flags)) {
/* open for read */
- pcm_unlock(d);
- error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid,
- rdevunit);
+ rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC,
+ td->td_proc->p_pid, rdevunit);
- if (error == 0 && (chn_reset(rdch, fmt) != 0 ||
+ if (rderror == 0 && (chn_reset(rdch, fmt) != 0 ||
(chn_setspeed(rdch, spd) != 0)))
- error = ENODEV;
+ rderror = ENXIO;
- if (error != 0) {
+ if (rderror != 0) {
if (rdch != NULL)
pcm_chnrelease(rdch);
- pcm_lock(d);
- (void)snd_clone_release(i_dev);
- PCMDEV_RELEASE(i_dev);
- pcm_unlock(d);
- return error;
+ if (!DSP_F_DUPLEX(flags)) {
+ (void)snd_clone_release(i_dev);
+ PCM_RELEASE_QUICK(d);
+ PCM_GIANT_EXIT(d);
+ return (rderror);
+ }
+ rdch = NULL;
+ } else {
+ if (flags & O_NONBLOCK)
+ rdch->flags |= CHN_F_NBIO;
+ pcm_chnref(rdch, 1);
+ CHN_UNLOCK(rdch);
}
-
- if (flags & O_NONBLOCK)
- rdch->flags |= CHN_F_NBIO;
- pcm_chnref(rdch, 1);
- CHN_UNLOCK(rdch);
- pcm_lock(d);
}
if (DSP_F_WRITE(flags)) {
/* open for write */
- pcm_unlock(d);
- error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid,
- wdevunit);
+ wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY,
+ td->td_proc->p_pid, wdevunit);
- if (error == 0 && (chn_reset(wrch, fmt) != 0 ||
+ if (wrerror == 0 && (chn_reset(wrch, fmt) != 0 ||
(chn_setspeed(wrch, spd) != 0)))
- error = ENODEV;
+ wrerror = ENXIO;
- if (error != 0) {
+ if (wrerror != 0) {
if (wrch != NULL)
pcm_chnrelease(wrch);
- if (rdch != NULL) {
- /*
- * Lock, deref and release previously
- * created record channel
- */
- CHN_LOCK(rdch);
- pcm_chnref(rdch, -1);
- pcm_chnrelease(rdch);
+ if (!DSP_F_DUPLEX(flags)) {
+ if (rdch != NULL) {
+ /*
+ * Lock, deref and release previously
+ * created record channel
+ */
+ CHN_LOCK(rdch);
+ pcm_chnref(rdch, -1);
+ pcm_chnrelease(rdch);
+ }
+ (void)snd_clone_release(i_dev);
+ PCM_RELEASE_QUICK(d);
+ PCM_GIANT_EXIT(d);
+ return (wrerror);
}
- pcm_lock(d);
- (void)snd_clone_release(i_dev);
- PCMDEV_RELEASE(i_dev);
- pcm_unlock(d);
- return error;
+ wrch = NULL;
+ } else {
+ if (flags & O_NONBLOCK)
+ wrch->flags |= CHN_F_NBIO;
+ pcm_chnref(wrch, 1);
+ CHN_UNLOCK(wrch);
}
+ }
- if (flags & O_NONBLOCK)
- wrch->flags |= CHN_F_NBIO;
- pcm_chnref(wrch, 1);
- CHN_UNLOCK(wrch);
- pcm_lock(d);
+ if (rdch == NULL && wrch == NULL) {
+ (void)snd_clone_release(i_dev);
+ PCM_RELEASE_QUICK(d);
+ PCM_GIANT_EXIT(d);
+ return ((wrerror != 0) ? wrerror : rderror);
}
+ pcm_lock(d);
+
+ /*
+ * We're done. Allocate channels information for this cdev.
+ */
+ dsp_cdevinfo_alloc(i_dev, rdch, wrch);
+
/*
* Increase clone refcount for its automatic garbage collector.
*/
(void)snd_clone_ref(i_dev);
+ PCM_RELEASE(d);
pcm_unlock(d);
- /*
- * We're done. Allocate and point si_drv1 to a real
- * allocated structure.
- */
- dsp_cdevinfo_alloc(i_dev, rdch, wrch);
+ PCM_GIANT_LEAVE(d);
- return 0;
+ return (0);
}
static int
@@ -427,24 +585,25 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
{
struct pcm_channel *rdch, *wrch;
struct snddev_info *d;
- int refs, sg_ids[2];
+ int sg_ids, refs;
d = dsp_get_info(i_dev);
- if (d == NULL)
- return EBADF;
+ if (!DSP_REGISTERED(d, i_dev))
+ return (EBADF);
+
+ PCM_GIANT_ENTER(d);
+
pcm_lock(d);
+ PCM_WAIT(d);
+
rdch = PCM_RDCH(i_dev);
wrch = PCM_WRCH(i_dev);
- /*
- * Free_unr() may sleep, so store released syncgroup IDs until after
- * all locks are released.
- */
- sg_ids[0] = sg_ids[1] = 0;
-
if (rdch || wrch) {
- refs = 0;
+ PCM_ACQUIRE(d);
pcm_unlock(d);
+
+ refs = 0;
if (rdch) {
/*
* The channel itself need not be locked because:
@@ -456,8 +615,10 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
* unless it's closed/dereferenced first.
*/
PCM_SG_LOCK();
- sg_ids[0] = chn_syncdestroy(rdch);
+ sg_ids = chn_syncdestroy(rdch);
PCM_SG_UNLOCK();
+ if (sg_ids != 0)
+ free_unr(pcmsg_unrhdr, sg_ids);
CHN_LOCK(rdch);
refs += pcm_chnref(rdch, -1);
@@ -465,14 +626,17 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
chn_reset(rdch, 0);
pcm_chnrelease(rdch);
+ PCM_RDCH(i_dev) = NULL;
}
if (wrch) {
/*
* Please see block above.
*/
PCM_SG_LOCK();
- sg_ids[1] = chn_syncdestroy(wrch);
+ sg_ids = chn_syncdestroy(wrch);
PCM_SG_UNLOCK();
+ if (sg_ids != 0)
+ free_unr(pcmsg_unrhdr, sg_ids);
CHN_LOCK(wrch);
refs += pcm_chnref(wrch, -1);
@@ -480,22 +644,15 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
chn_reset(wrch, 0);
pcm_chnrelease(wrch);
+ PCM_WRCH(i_dev) = NULL;
}
pcm_lock(d);
- if (rdch)
- PCM_RDCH(i_dev) = NULL;
- if (wrch)
- PCM_WRCH(i_dev) = NULL;
/*
* If there are no more references, release the channels.
*/
if (refs == 0 && PCM_RDCH(i_dev) == NULL &&
PCM_WRCH(i_dev) == NULL) {
- if (pcm_getfakechan(d))
- pcm_getfakechan(d)->flags = 0;
- /* What is this?!? */
- dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
dsp_cdevinfo_free(i_dev);
/*
* Release clone busy state and unref it
@@ -506,72 +663,100 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
(void)snd_clone_release(i_dev);
(void)snd_clone_unref(i_dev);
}
+ PCM_RELEASE(d);
}
pcm_unlock(d);
- if (sg_ids[0])
- free_unr(pcmsg_unrhdr, sg_ids[0]);
- if (sg_ids[1])
- free_unr(pcmsg_unrhdr, sg_ids[1]);
+ PCM_GIANT_LEAVE(d);
- return 0;
+ return (0);
}
-static int
-dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
+static __inline int
+dsp_io_ops(struct cdev *i_dev, struct uio *buf)
{
- struct pcm_channel *rdch, *wrch;
- int ret;
+ struct snddev_info *d;
+ struct pcm_channel **ch, *rdch, *wrch;
+ int (*chn_io)(struct pcm_channel *, struct uio *);
+ int prio, ret;
+ pid_t runpid;
- getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
+ KASSERT(i_dev != NULL && buf != NULL &&
+ (buf->uio_rw == UIO_READ || buf->uio_rw == UIO_WRITE),
+ ("%s(): io train wreck!", __func__));
- if (rdch == NULL || !(rdch->flags & CHN_F_BUSY))
- return EBADF;
+ d = dsp_get_info(i_dev);
+ if (!DSP_REGISTERED(d, i_dev))
+ return (EBADF);
- if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
- relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
- return EINVAL;
- }
- if (!(rdch->flags & CHN_F_RUNNING))
- rdch->flags |= CHN_F_RUNNING;
- ret = chn_read(rdch, buf);
- relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
+ PCM_GIANT_ENTER(d);
- return ret;
-}
+ switch (buf->uio_rw) {
+ case UIO_READ:
+ prio = SD_F_PRIO_RD;
+ ch = &rdch;
+ chn_io = chn_read;
+ break;
+ case UIO_WRITE:
+ prio = SD_F_PRIO_WR;
+ ch = &wrch;
+ chn_io = chn_write;
+ break;
+ default:
+ panic("invalid/corrupted uio direction: %d", buf->uio_rw);
+ break;
+ }
-static int
-dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
-{
- struct pcm_channel *rdch, *wrch;
- int ret;
+ rdch = NULL;
+ wrch = NULL;
+ runpid = buf->uio_td->td_proc->p_pid;
- getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
+ getchns(i_dev, &rdch, &wrch, prio);
- if (wrch == NULL || !(wrch->flags & CHN_F_BUSY))
- return EBADF;
+ if (*ch == NULL || !((*ch)->flags & CHN_F_BUSY)) {
+ PCM_GIANT_EXIT(d);
+ return (EBADF);
+ }
- if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
- relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
- return EINVAL;
+ if (((*ch)->flags & (CHN_F_MAPPED | CHN_F_DEAD)) ||
+ (((*ch)->flags & CHN_F_RUNNING) && (*ch)->pid != runpid)) {
+ relchns(i_dev, rdch, wrch, prio);
+ PCM_GIANT_EXIT(d);
+ return (EINVAL);
+ } else if (!((*ch)->flags & CHN_F_RUNNING)) {
+ (*ch)->flags |= CHN_F_RUNNING;
+ (*ch)->pid = runpid;
}
- if (!(wrch->flags & CHN_F_RUNNING))
- wrch->flags |= CHN_F_RUNNING;
/*
- * Chn_write() must give up channel lock in order to copy bytes from
- * userland, so up the "in progress" counter to make sure someone
- * else doesn't come along and muss up the buffer.
+ * chn_read/write must give up channel lock in order to copy bytes
+ * from/to userland, so up the "in progress" counter to make sure
+ * someone else doesn't come along and muss up the buffer.
*/
- ++wrch->inprog;
- ret = chn_write(wrch, buf);
- --wrch->inprog;
- cv_signal(&wrch->cv);
+ ++(*ch)->inprog;
+ ret = chn_io(*ch, buf);
+ --(*ch)->inprog;
+
+ CHN_BROADCAST(&(*ch)->cv);
+
+ relchns(i_dev, rdch, wrch, prio);
- relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
+ PCM_GIANT_LEAVE(d);
- return ret;
+ return (ret);
+}
+
+static int
+dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
+{
+ return (dsp_io_ops(i_dev, buf));
+}
+
+static int
+dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
+{
+ return (dsp_io_ops(i_dev, buf));
}
static int
@@ -579,28 +764,37 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
{
struct pcm_channel *chn, *rdch, *wrch;
struct snddev_info *d;
- int kill;
- int ret = 0, *arg_i = (int *)arg, tmp;
- int xcmd;
+ int *arg_i, ret, kill, tmp, xcmd;
+
+ d = dsp_get_info(i_dev);
+ if (!DSP_REGISTERED(d, i_dev))
+ return (EBADF);
+
+ PCM_GIANT_ENTER(d);
+ arg_i = (int *)arg;
+ ret = 0;
xcmd = 0;
/*
* this is an evil hack to allow broken apps to perform mixer ioctls
* on dsp devices.
*/
-
- d = dsp_get_info(i_dev);
- if (d == NULL)
- return EBADF;
if (IOCGROUP(cmd) == 'M') {
/*
* This is at least, a bug to bug compatible with OSS.
*/
- if (d->mixer_dev != NULL)
- return mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
- else
- return EBADF;
+ if (d->mixer_dev != NULL) {
+ PCM_ACQUIRE_QUICK(d);
+ ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td,
+ MIXER_CMD_DIRECT);
+ PCM_RELEASE_QUICK(d);
+ } else
+ ret = EBADF;
+
+ PCM_GIANT_EXIT(d);
+
+ return (ret);
}
/*
@@ -608,6 +802,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
* and MIDI). Handle those special cases here.
*/
if (IOCGROUP(cmd) == 'X') {
+ PCM_ACQUIRE_QUICK(d);
switch(cmd) {
case SNDCTL_SYSINFO:
sound_oss_sysinfo((oss_sysinfo *)arg);
@@ -621,8 +816,9 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
default:
ret = EINVAL;
}
-
- return ret;
+ PCM_RELEASE_QUICK(d);
+ PCM_GIANT_EXIT(d);
+ return (ret);
}
getchns(i_dev, &rdch, &wrch, 0);
@@ -634,13 +830,20 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
kill |= 2;
if (kill == 3) {
relchns(i_dev, rdch, wrch, 0);
- return EINVAL;
+ PCM_GIANT_EXIT(d);
+ return (EINVAL);
}
if (kill & 1)
wrch = NULL;
if (kill & 2)
rdch = NULL;
+ if (wrch == NULL && rdch == NULL) {
+ relchns(i_dev, rdch, wrch, 0);
+ PCM_GIANT_EXIT(d);
+ return (EINVAL);
+ }
+
switch(cmd) {
#ifdef OLDPCM_IOCTL
/*
@@ -667,6 +870,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
p->play_size = 0;
p->rec_size = 0;
+ PCM_ACQUIRE_QUICK(d);
if (wrch) {
CHN_LOCK(wrch);
chn_setblocksize(wrch, 2, p->play_size);
@@ -679,6 +883,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
p->rec_size = sndbuf_getblksz(rdch->bufsoft);
CHN_UNLOCK(rdch);
}
+ PCM_RELEASE_QUICK(d);
}
break;
case AIOGSIZE: /* get the current blocksize */
@@ -709,6 +914,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
ret = EINVAL;
break;
}
+ PCM_ACQUIRE_QUICK(d);
if (wrch) {
CHN_LOCK(wrch);
if (cmd == AIOSFMT && p->play_format != 0) {
@@ -735,6 +941,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
p->rec_rate = 0;
p->rec_format = 0;
}
+ PCM_RELEASE_QUICK(d);
}
break;
@@ -744,6 +951,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
struct cdev *pdev;
+ pcm_lock(d);
if (rdch) {
CHN_LOCK(rdch);
rcaps = chn_getcaps(rdch);
@@ -767,10 +975,11 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
p->mixers = 1; /* default: one mixer */
p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
p->left = p->right = 100;
- if (rdch)
- CHN_UNLOCK(rdch);
if (wrch)
CHN_UNLOCK(wrch);
+ if (rdch)
+ CHN_UNLOCK(rdch);
+ pcm_unlock(d);
}
break;
@@ -850,10 +1059,11 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
*arg_i = 0;
ret = EINVAL;
}
- break ;
+ break;
case SNDCTL_DSP_SETBLKSIZE:
RANGE(*arg_i, 16, 65536);
+ PCM_ACQUIRE_QUICK(d);
if (wrch) {
CHN_LOCK(wrch);
chn_setblocksize(wrch, 2, *arg_i);
@@ -864,6 +1074,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
chn_setblocksize(rdch, 2, *arg_i);
CHN_UNLOCK(rdch);
}
+ PCM_RELEASE_QUICK(d);
break;
case SNDCTL_DSP_RESET:
@@ -889,12 +1100,14 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
CHN_LOCK(wrch);
chn_sync(wrch, 0);
CHN_UNLOCK(wrch);
- }
+ } else
+ ret = EINVAL;
break;
case SNDCTL_DSP_SPEED:
/* chn_setspeed may sleep */
tmp = 0;
+ PCM_ACQUIRE_QUICK(d);
if (wrch) {
CHN_LOCK(wrch);
ret = chn_setspeed(wrch, *arg_i);
@@ -908,6 +1121,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
tmp = rdch->speed;
CHN_UNLOCK(rdch);
}
+ PCM_RELEASE_QUICK(d);
*arg_i = tmp;
break;
@@ -926,6 +1140,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
case SNDCTL_DSP_STEREO:
tmp = -1;
*arg_i = (*arg_i)? AFMT_STEREO : 0;
+ PCM_ACQUIRE_QUICK(d);
if (wrch) {
CHN_LOCK(wrch);
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
@@ -939,6 +1154,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
CHN_UNLOCK(rdch);
}
+ PCM_RELEASE_QUICK(d);
*arg_i = tmp;
break;
@@ -947,6 +1163,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
if (*arg_i != 0) {
tmp = 0;
*arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
+ PCM_ACQUIRE_QUICK(d);
if (wrch) {
CHN_LOCK(wrch);
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
@@ -960,6 +1177,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
CHN_UNLOCK(rdch);
}
+ PCM_RELEASE_QUICK(d);
*arg_i = tmp;
} else {
chn = wrch ? wrch : rdch;
@@ -991,11 +1209,12 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
*arg_i = 0;
ret = EINVAL;
}
- break ;
+ break;
case SNDCTL_DSP_SETFMT: /* sets _one_ format */
if ((*arg_i != AFMT_QUERY)) {
tmp = 0;
+ PCM_ACQUIRE_QUICK(d);
if (wrch) {
CHN_LOCK(wrch);
ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
@@ -1009,6 +1228,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
tmp = rdch->format & ~AFMT_STEREO;
CHN_UNLOCK(rdch);
}
+ PCM_RELEASE_QUICK(d);
*arg_i = tmp;
} else {
chn = wrch ? wrch : rdch;
@@ -1021,10 +1241,10 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
case SNDCTL_DSP_SETFRAGMENT:
DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
{
- u_int32_t fragln = (*arg_i) & 0x0000ffff;
- u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
- u_int32_t fragsz;
- u_int32_t r_maxfrags, r_fragsz;
+ uint32_t fragln = (*arg_i) & 0x0000ffff;
+ uint32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
+ uint32_t fragsz;
+ uint32_t r_maxfrags, r_fragsz;
RANGE(fragln, 4, 16);
fragsz = 1 << fragln;
@@ -1037,6 +1257,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
+ PCM_ACQUIRE_QUICK(d);
if (rdch) {
CHN_LOCK(rdch);
ret = chn_setblocksize(rdch, maxfrags, fragsz);
@@ -1057,6 +1278,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
maxfrags = r_maxfrags;
fragsz = r_fragsz;
}
+ PCM_RELEASE_QUICK(d);
fragln = 0;
while (fragsz > 1) {
@@ -1080,7 +1302,8 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
a->fragstotal = sndbuf_getblkcnt(bs);
a->fragsize = sndbuf_getblksz(bs);
CHN_UNLOCK(rdch);
- }
+ } else
+ ret = EINVAL;
}
break;
@@ -1098,7 +1321,8 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
a->fragstotal = sndbuf_getblkcnt(bs);
a->fragsize = sndbuf_getblksz(bs);
CHN_UNLOCK(wrch);
- }
+ } else
+ ret = EINVAL;
}
break;
@@ -1139,9 +1363,11 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
break;
case SNDCTL_DSP_GETCAPS:
+ pcm_lock(d);
*arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
*arg_i |= DSP_CAP_DUPLEX;
+ pcm_unlock(d);
break;
case SOUND_PCM_READ_BITS:
@@ -1220,7 +1446,8 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
wrch->flags &= ~CHN_F_NOTRIGGER;
chn_start(wrch, 1);
CHN_UNLOCK(wrch);
- }
+ } else
+ ret = EINVAL;
break;
case SNDCTL_DSP_SETDUPLEX:
@@ -1228,8 +1455,10 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
* switch to full-duplex mode if card is in half-duplex
* mode and is able to work in full-duplex mode
*/
+ pcm_lock(d);
if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
+ pcm_unlock(d);
break;
/*
@@ -1253,18 +1482,24 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
if (xcmd == 0)
xcmd = SOUND_MIXER_WRITE_PCM;
- if (d->mixer_dev != NULL)
- ret = mixer_ioctl(d->mixer_dev, xcmd, arg, -1, td);
- else
+ if (d->mixer_dev != NULL) {
+ PCM_ACQUIRE_QUICK(d);
+ ret = mixer_ioctl_cmd(d->mixer_dev, xcmd, arg, -1, td,
+ MIXER_CMD_DIRECT);
+ PCM_RELEASE_QUICK(d);
+ } else
ret = ENOTSUP;
break;
case SNDCTL_DSP_GET_RECSRC_NAMES:
case SNDCTL_DSP_GET_RECSRC:
case SNDCTL_DSP_SET_RECSRC:
- if (d->mixer_dev != NULL)
- ret = mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
- else
+ if (d->mixer_dev != NULL) {
+ PCM_ACQUIRE_QUICK(d);
+ ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td,
+ MIXER_CMD_DIRECT);
+ PCM_RELEASE_QUICK(d);
+ } else
ret = ENOTSUP;
break;
@@ -1451,15 +1686,21 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
break;
case SNDCTL_DSP_SYNCGROUP:
+ PCM_ACQUIRE_QUICK(d);
ret = dsp_oss_syncgroup(wrch, rdch, (oss_syncgroup *)arg);
+ PCM_RELEASE_QUICK(d);
break;
case SNDCTL_DSP_SYNCSTART:
+ PCM_ACQUIRE_QUICK(d);
ret = dsp_oss_syncstart(*arg_i);
+ PCM_RELEASE_QUICK(d);
break;
case SNDCTL_DSP_POLICY:
+ PCM_ACQUIRE_QUICK(d);
ret = dsp_oss_policy(wrch, rdch, *arg_i);
+ PCM_RELEASE_QUICK(d);
break;
#ifdef OSSV4_EXPERIMENT
@@ -1488,6 +1729,10 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
}
break;
+ /*
+ * XXX Once implemented, revisit this for proper cv protection
+ * (if necessary).
+ */
case SNDCTL_DSP_COOKEDMODE:
ret = dsp_oss_cookedmode(wrch, rdch, *arg_i);
break;
@@ -1552,77 +1797,113 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *
ret = EINVAL;
break;
}
+
relchns(i_dev, rdch, wrch, 0);
- return ret;
+
+ PCM_GIANT_LEAVE(d);
+
+ return (ret);
}
static int
dsp_poll(struct cdev *i_dev, int events, struct thread *td)
{
- struct pcm_channel *wrch = NULL, *rdch = NULL;
+ struct snddev_info *d;
+ struct pcm_channel *wrch, *rdch;
int ret, e;
+ d = dsp_get_info(i_dev);
+ if (!DSP_REGISTERED(d, i_dev))
+ return (EBADF);
+
+ PCM_GIANT_ENTER(d);
+
+ wrch = NULL;
+ rdch = NULL;
ret = 0;
+
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
- if (wrch) {
+ if (wrch != NULL && !(wrch->flags & CHN_F_DEAD)) {
e = (events & (POLLOUT | POLLWRNORM));
if (e)
ret |= chn_poll(wrch, e, td);
}
- if (rdch) {
+
+ if (rdch != NULL && !(rdch->flags & CHN_F_DEAD)) {
e = (events & (POLLIN | POLLRDNORM));
if (e)
ret |= chn_poll(rdch, e, td);
}
+
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
- return ret;
+ PCM_GIANT_LEAVE(d);
+
+ return (ret);
}
static int
dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
{
- struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
-
- if (nprot & PROT_EXEC)
- return -1;
+ struct snddev_info *d;
+ struct pcm_channel *wrch, *rdch, *c;
- getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
-#if 0
/*
- * XXX the linux api uses the nprot to select read/write buffer
- * our vm system doesn't allow this, so force write buffer
+ * Reject PROT_EXEC by default. It just doesn't makes sense.
+ * Unfortunately, we have to give up this one due to linux_mmap
+ * changes.
+ *
+ * http://lists.freebsd.org/pipermail/freebsd-emulation/2007-June/003698.html
+ *
*/
+ if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec == 0)
+ return (-1);
- if (wrch && (nprot & PROT_WRITE)) {
- c = wrch;
- } else if (rdch && (nprot & PROT_READ)) {
- c = rdch;
- } else {
- return -1;
- }
-#else
- c = wrch;
-#endif
+ d = dsp_get_info(i_dev);
+ if (!DSP_REGISTERED(d, i_dev))
+ return (-1);
- if (c == NULL) {
- relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
- return -1;
- }
+ PCM_GIANT_ENTER(d);
+
+ getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
- if (offset >= sndbuf_getsize(c->bufsoft)) {
+ /*
+ * XXX The linux api uses the nprot to select read/write buffer
+ * our vm system doesn't allow this, so force write buffer.
+ *
+ * This is just a quack to fool full-duplex mmap, so that at
+ * least playback _or_ recording works. If you really got the
+ * urge to make _both_ work at the same time, avoid O_RDWR.
+ * Just open each direction separately and mmap() it.
+ *
+ * Failure is not an option due to INVARIANTS check within
+ * device_pager.c, which means, we have to give up one over
+ * another.
+ */
+ c = (wrch != NULL) ? wrch : rdch;
+
+ if (c == NULL || (c->flags & CHN_F_MMAP_INVALID) ||
+ offset >= sndbuf_getsize(c->bufsoft) ||
+ (wrch != NULL && (wrch->flags & CHN_F_MMAP_INVALID)) ||
+ (rdch != NULL && (rdch->flags & CHN_F_MMAP_INVALID))) {
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
- return -1;
+ PCM_GIANT_EXIT(d);
+ return (-1);
}
- if (!(c->flags & CHN_F_MAPPED))
- c->flags |= CHN_F_MAPPED;
+ /* XXX full-duplex quack. */
+ if (wrch != NULL)
+ wrch->flags |= CHN_F_MAPPED;
+ if (rdch != NULL)
+ rdch->flags |= CHN_F_MAPPED;
*paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset));
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
- return 0;
+ PCM_GIANT_LEAVE(d);
+
+ return (0);
}
#ifdef USING_DEVFS
@@ -1706,8 +1987,8 @@ dsp_clone(void *arg,
devname = NULL;
devsep = NULL;
- for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])) &&
- unit == -1; i++) {
+ for (i = 0; unit == -1 &&
+ i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) {
devtype = dsp_cdevs[i].type;
devname = dsp_cdevs[i].name;
devsep = dsp_cdevs[i].sep;
@@ -1723,22 +2004,28 @@ dsp_clone(void *arg,
}
d = devclass_get_softc(pcm_devclass, unit);
- if (d == NULL || d->clones == NULL)
+ if (!PCM_REGISTERED(d) || d->clones == NULL)
return;
+ /* XXX Need Giant magic entry ??? */
+
pcm_lock(d);
if (snd_clone_disabled(d->clones)) {
pcm_unlock(d);
return;
}
+ PCM_WAIT(d);
+ PCM_ACQUIRE(d);
+ pcm_unlock(d);
+
udcmask = snd_u2unit(unit) | snd_d2unit(devtype);
if (devhw != 0) {
KASSERT(devcmax <= dsp_cmax,
("overflow: devcmax=%d, dsp_cmax=%d", devcmax, dsp_cmax));
if (cunit > devcmax) {
- pcm_unlock(d);
+ PCM_RELEASE_QUICK(d);
return;
}
udcmask |= snd_c2unit(cunit);
@@ -1788,7 +2075,7 @@ dsp_clone(void *arg,
tumax = -1;
goto dsp_clone_alloc;
}
- pcm_unlock(d);
+ PCM_RELEASE_QUICK(d);
return;
}
@@ -1798,14 +2085,13 @@ dsp_clone_alloc:
snd_clone_setmaxunit(d->clones, tumax);
if (ce != NULL) {
udcmask |= snd_c2unit(cunit);
- pcm_unlock(d);
*dev = make_dev(&dsp_cdevsw, unit2minor(udcmask),
UID_ROOT, GID_WHEEL, 0666, "%s%d%s%d",
devname, unit, devsep, cunit);
- pcm_lock(d);
snd_clone_register(ce, *dev);
}
- pcm_unlock(d);
+
+ PCM_RELEASE_QUICK(d);
if (*dev != NULL)
dev_ref(*dev);
@@ -1896,7 +2182,7 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
struct pcm_channel *ch;
struct snddev_info *d;
uint32_t fmts;
- int i, nchan, ret, *rates, minch, maxch;
+ int i, nchan, *rates, minch, maxch;
char *devname, buf[CHN_NAMELEN];
/*
@@ -1904,13 +2190,12 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
* DSP device. (Users may use this ioctl with /dev/mixer and
* /dev/midi.)
*/
- if ((ai->dev == -1) && (i_dev->si_devsw != &dsp_cdevsw))
- return EINVAL;
+ if (ai->dev == -1 && i_dev->si_devsw != &dsp_cdevsw)
+ return (EINVAL);
ch = NULL;
devname = NULL;
nchan = 0;
- ret = 0;
bzero(buf, sizeof(buf));
/*
@@ -1920,175 +2205,171 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
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);
CHN_FOREACH(ch, d, channels.pcm) {
mtx_assert(ch->lock, MA_NOTOWNED);
CHN_LOCK(ch);
if (ai->dev == -1) {
- if ((ch == PCM_RDCH(i_dev)) || /* record ch */
- (ch == PCM_WRCH(i_dev))) { /* playback ch */
- devname = i_dev->si_name;
- goto dspfound;
+ if (DSP_REGISTERED(d, i_dev) &&
+ (ch == PCM_RDCH(i_dev) || /* record ch */
+ ch == PCM_WRCH(i_dev))) { /* playback ch */
+ devname = dsp_unit2name(buf,
+ sizeof(buf), ch->unit);
}
} else if (ai->dev == nchan) {
devname = dsp_unit2name(buf, sizeof(buf),
ch->unit);
- goto dspfound;
}
+ if (devname != NULL)
+ break;
CHN_UNLOCK(ch);
- /*
- * XXX I really doubt if this is correct.
- */
++nchan;
}
- pcm_unlock(d);
- pcm_inprog(d, -1);
- }
-
- /* Exhausted the search -- nothing is locked, so return. */
- return EINVAL;
-
-dspfound:
- /* Should've found the device, but something isn't right */
- if (devname == NULL) {
- ret = EINVAL;
- goto out;
- }
+ if (devname != NULL) {
+ /*
+ * At this point, the following synchronization stuff
+ * has happened:
+ * - a specific PCM device is locked.
+ * - a specific audio channel has been locked, so be
+ * sure to unlock when exiting;
+ */
- /*
- * 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 audio channel has been locked, so be sure to unlock
- * when exiting;
- */
+ caps = chn_getcaps(ch);
- caps = chn_getcaps(ch);
+ /*
+ * With all handles collected, zero out the user's
+ * container and begin filling in its fields.
+ */
+ bzero((void *)ai, sizeof(oss_audioinfo));
- /*
- * With all handles collected, zero out the user's container and
- * begin filling in its fields.
- */
- bzero((void *)ai, sizeof(oss_audioinfo));
+ ai->dev = nchan;
+ strlcpy(ai->name, ch->name, sizeof(ai->name));
- ai->dev = nchan;
- strlcpy(ai->name, ch->name, sizeof(ai->name));
+ if ((ch->flags & CHN_F_BUSY) == 0)
+ ai->busy = 0;
+ else
+ ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ;
+
+ /**
+ * @note
+ * @c cmd - OSSv4 docs: "Only supported under Linux at
+ * this moment." Cop-out, I know, but I'll save
+ * running around in the process table for later.
+ * Is there a risk of leaking information?
+ */
+ ai->pid = ch->pid;
- if ((ch->flags & CHN_F_BUSY) == 0)
- ai->busy = 0;
- else
- ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ;
+ /*
+ * These flags stolen from SNDCTL_DSP_GETCAPS handler.
+ * Note, however, that a single channel operates in
+ * only one direction, so DSP_CAP_DUPLEX is out.
+ */
+ /**
+ * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep
+ * these in pcmchan::caps?
+ */
+ ai->caps = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
- /**
- * @note
- * @c cmd - OSSv4 docs: "Only supported under Linux at this moment."
- * Cop-out, I know, but I'll save running around in the process
- * table for later. Is there a risk of leaking information?
- */
- ai->pid = ch->pid;
+ /*
+ * Collect formats supported @b natively by the
+ * device. Also determine min/max channels. (I.e.,
+ * mono, stereo, or both?)
+ *
+ * If any channel is stereo, maxch = 2;
+ * if all channels are stereo, minch = 2, too;
+ * if any channel is mono, minch = 1;
+ * and if all channels are mono, maxch = 1.
+ */
+ minch = 0;
+ maxch = 0;
+ fmts = 0;
+ for (i = 0; caps->fmtlist[i]; i++) {
+ fmts |= caps->fmtlist[i];
+ if (caps->fmtlist[i] & AFMT_STEREO) {
+ minch = (minch == 0) ? 2 : minch;
+ maxch = 2;
+ } else {
+ minch = 1;
+ maxch = (maxch == 0) ? 1 : maxch;
+ }
+ }
- /*
- * These flags stolen from SNDCTL_DSP_GETCAPS handler. Note, however,
- * that a single channel operates in only one direction, so
- * DSP_CAP_DUPLEX is out.
- */
- /**
- * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep these in
- * pcmchan::caps?
- */
- ai->caps = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
+ if (ch->direction == PCMDIR_PLAY)
+ ai->oformats = fmts;
+ else
+ ai->iformats = fmts;
- /*
- * Collect formats supported @b natively by the device. Also
- * determine min/max channels. (I.e., mono, stereo, or both?)
- *
- * If any channel is stereo, maxch = 2;
- * if all channels are stereo, minch = 2, too;
- * if any channel is mono, minch = 1;
- * and if all channels are mono, maxch = 1.
- */
- minch = 0;
- maxch = 0;
- fmts = 0;
- for (i = 0; caps->fmtlist[i]; i++) {
- fmts |= caps->fmtlist[i];
- if (caps->fmtlist[i] & AFMT_STEREO) {
- minch = (minch == 0) ? 2 : minch;
- maxch = 2;
- } else {
- minch = 1;
- maxch = (maxch == 0) ? 1 : maxch;
- }
- }
+ /**
+ * @note
+ * @c magic - OSSv4 docs: "Reserved for internal use
+ * by OSS."
+ *
+ * @par
+ * @c card_number - OSSv4 docs: "Number of the sound
+ * card where this device belongs or -1 if this
+ * information is not available. Applications
+ * should normally not use this field for any
+ * purpose."
+ */
+ ai->card_number = -1;
+ /**
+ * @todo @c song_name - depends first on
+ * SNDCTL_[GS]ETSONG @todo @c label - depends
+ * on SNDCTL_[GS]ETLABEL
+ * @todo @c port_number - routing information?
+ */
+ ai->port_number = -1;
+ ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1;
+ /**
+ * @note
+ * @c real_device - OSSv4 docs: "Obsolete."
+ */
+ ai->real_device = -1;
+ strlcpy(ai->devnode, devname, sizeof(ai->devnode));
+ ai->enabled = device_is_attached(d->dev) ? 1 : 0;
+ /**
+ * @note
+ * @c flags - OSSv4 docs: "Reserved for future use."
+ *
+ * @note
+ * @c binding - OSSv4 docs: "Reserved for future use."
+ *
+ * @todo @c handle - haven't decided how to generate
+ * this yet; bus, vendor, device IDs?
+ */
+ ai->min_rate = caps->minspeed;
+ ai->max_rate = caps->maxspeed;
- if (ch->direction == PCMDIR_PLAY)
- ai->oformats = fmts;
- else
- ai->iformats = fmts;
+ ai->min_channels = minch;
+ ai->max_channels = maxch;
- /**
- * @note
- * @c magic - OSSv4 docs: "Reserved for internal use by OSS."
- *
- * @par
- * @c card_number - OSSv4 docs: "Number of the sound card where this
- * device belongs or -1 if this information is not available.
- * Applications should normally not use this field for any
- * purpose."
- */
- ai->card_number = -1;
- /**
- * @todo @c song_name - depends first on SNDCTL_[GS]ETSONG
- * @todo @c label - depends on SNDCTL_[GS]ETLABEL
- * @todo @c port_number - routing information?
- */
- ai->port_number = -1;
- ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1;
- /**
- * @note
- * @c real_device - OSSv4 docs: "Obsolete."
- */
- ai->real_device = -1;
- strlcpy(ai->devnode, devname, sizeof(ai->devnode));
- ai->enabled = device_is_attached(d->dev) ? 1 : 0;
- /**
- * @note
- * @c flags - OSSv4 docs: "Reserved for future use."
- *
- * @note
- * @c binding - OSSv4 docs: "Reserved for future use."
- *
- * @todo @c handle - haven't decided how to generate this yet; bus,
- * vendor, device IDs?
- */
- ai->min_rate = caps->minspeed;
- ai->max_rate = caps->maxspeed;
+ ai->nrates = chn_getrates(ch, &rates);
+ if (ai->nrates > OSS_MAX_SAMPLE_RATES)
+ ai->nrates = OSS_MAX_SAMPLE_RATES;
- ai->min_channels = minch;
- ai->max_channels = maxch;
+ for (i = 0; i < ai->nrates; i++)
+ ai->rates[i] = rates[i];
- ai->nrates = chn_getrates(ch, &rates);
- if (ai->nrates > OSS_MAX_SAMPLE_RATES)
- ai->nrates = OSS_MAX_SAMPLE_RATES;
+ CHN_UNLOCK(ch);
+ }
- for (i = 0; i < ai->nrates; i++)
- ai->rates[i] = rates[i];
+ pcm_unlock(d);
-out:
- CHN_UNLOCK(ch);
- pcm_unlock(d);
- pcm_inprog(d, -1);
+ if (devname != NULL)
+ return (0);
+ }
- return ret;
+ /* Exhausted the search -- nothing is locked, so return. */
+ return (EINVAL);
}
/**
@@ -2259,7 +2540,7 @@ out:
if (sg_ids[2])
free_unr(pcmsg_unrhdr, sg_ids[2]);
- return ret;
+ return (ret);
}
/**
@@ -2325,7 +2606,8 @@ dsp_oss_syncstart(int sg_id)
}
/** @todo Is PRIBIO correct/ */
- ret = msleep(sm, &snd_pcm_syncgroups_mtx, PRIBIO | PCATCH, "pcmsgrp", timo);
+ ret = msleep(sm, &snd_pcm_syncgroups_mtx,
+ PRIBIO | PCATCH, "pcmsg", timo);
if (ret == EINTR || ret == ERESTART)
break;
@@ -2363,7 +2645,7 @@ dsp_oss_syncstart(int sg_id)
if (ret == 0)
free_unr(pcmsg_unrhdr, sg_id);
- return ret;
+ return (ret);
}
/**
@@ -2402,7 +2684,7 @@ dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy)
int ret;
if (policy < CHN_POLICY_MIN || policy > CHN_POLICY_MAX)
- return EIO;
+ return (EIO);
/* Default: success */
ret = 0;
@@ -2422,7 +2704,7 @@ dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy)
if (ret)
ret = EIO;
- return ret;
+ return (ret);
}
#ifdef OSSV4_EXPERIMENT
@@ -2455,7 +2737,7 @@ dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy)
static int
dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled)
{
- return EINVAL;
+ return (EINVAL);
}
/**
@@ -2478,7 +2760,7 @@ dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabl
static int
dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map)
{
- return EINVAL;
+ return (EINVAL);
}
/**
@@ -2498,7 +2780,7 @@ dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned
static int
dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map)
{
- return EINVAL;
+ return (EINVAL);
}
/**
@@ -2526,7 +2808,7 @@ dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned
static int
dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label)
{
- return EINVAL;
+ return (EINVAL);
}
/**
@@ -2550,7 +2832,7 @@ dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t
static int
dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label)
{
- return EINVAL;
+ return (EINVAL);
}
/**
@@ -2575,7 +2857,7 @@ dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t
static int
dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song)
{
- return EINVAL;
+ return (EINVAL);
}
/**
@@ -2600,7 +2882,7 @@ dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname
static int
dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song)
{
- return EINVAL;
+ return (EINVAL);
}
/**
@@ -2629,6 +2911,6 @@ dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname
static int
dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name)
{
- return EINVAL;
+ return (EINVAL);
}
#endif /* !OSSV4_EXPERIMENT */
diff --git a/sys/dev/sound/pcm/dsp.h b/sys/dev/sound/pcm/dsp.h
index 015e4ac..b02085c 100644
--- a/sys/dev/sound/pcm/dsp.h
+++ b/sys/dev/sound/pcm/dsp.h
@@ -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_ */
diff --git a/sys/dev/sound/pcm/feeder.c b/sys/dev/sound/pcm/feeder.c
index fefbc92..5d3b7af 100644
--- a/sys/dev/sound/pcm/feeder.c
+++ b/sys/dev/sound/pcm/feeder.c
@@ -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);
diff --git a/sys/dev/sound/pcm/feeder_rate.c b/sys/dev/sound/pcm/feeder_rate.c
index 79f06f1..cf9d7c1 100644
--- a/sys/dev/sound/pcm/feeder_rate.c
+++ b/sys/dev/sound/pcm/feeder_rate.c
@@ -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;
}
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
index b7b5032..6196817 100644
--- a/sys/dev/sound/pcm/mixer.c
+++ b/sys/dev/sound/pcm/mixer.c
@@ -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,30 +540,92 @@ mix_getdevinfo(struct snd_mixer *m)
return m->devinfo;
}
-int
-mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
+static struct snd_mixer *
+mixer_obj_create(device_t dev, kobj_class_t cls, void *devinfo,
+ int type, const char *desc)
{
- struct snddev_info *snddev;
struct snd_mixer *m;
- u_int16_t v;
- struct cdev *pdev;
- int i, unit, devunit, val;
+ 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, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev));
- m->lock = snd_mtxcreate(m->name, "pcm mixer");
- m->type = cls->name;
+ 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 < 32; i++) {
+ 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))
- goto bad;
+ 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)
+{
+ struct snddev_info *snddev;
+ struct snd_mixer *m;
+ u_int16_t v;
+ struct cdev *pdev;
+ int i, unit, devunit, val;
+
+ 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;
- snd_mtxlock(m->lock);
+ d = device_get_softc(m->dev);
+ if (!PCM_REGISTERED(d))
+ return (EBADF);
- m->busy = 1;
+ /* 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;
- snd_mtxlock(m->lock);
+ d = device_get_softc(m->dev);
+ if (!PCM_REGISTERED(d))
+ return (EBADF);
- if (!m->busy) {
- snd_mtxunlock(m->lock);
- return EBADF;
- }
- m->busy = 0;
+ /* XXX Need Giant magic entry ??? */
+ snd_mtxlock(m->lock);
+ 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;
- }
- ++nmix;
- }
-
- pcm_unlock(d);
- pcm_inprog(d, -1);
- }
-
- /*
- * 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;
+ 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);
- 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));
+ /*
+ * 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."
+ */
- 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])."
+ */
- /**
- * @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;
- 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;
- */
+ pcm_unlock(d);
- mtx_unlock(m->lock);
- pcm_unlock(d);
- pcm_inprog(d, -1);
+ if (m != NULL)
+ return (0);
+ }
- return ret;
+ return (EINVAL);
}
diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h
index 09f4b60..bcded4b 100644
--- a/sys/dev/sound/pcm/mixer.h
+++ b/sys/dev/sound/pcm/mixer.h
@@ -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)
diff --git a/sys/dev/sound/pcm/sndstat.c b/sys/dev/sound/pcm/sndstat.c
index db65989..9d9f9db 100644
--- a/sys/dev/sound/pcm/sndstat.c
+++ b/sys/dev/sound/pcm/sndstat.c
@@ -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);
}
}
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index 4f6d078..5e8924e 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -131,12 +131,17 @@ snd_mtxunlock(void *m)
int
snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
{
+ struct snddev_info *d;
#ifdef USING_MUTEX
flags &= INTR_MPSAFE;
flags |= INTR_TYPE_AV;
#else
flags = INTR_TYPE_AV;
#endif
+ d = device_get_softc(dev);
+ if (d != NULL && (flags & INTR_MPSAFE))
+ d->flags |= SD_F_MPSAFE;
+
return bus_setup_intr(dev, res, flags,
#if __FreeBSD_version >= 700031
NULL,
@@ -169,7 +174,7 @@ pcm_clonereset(struct snddev_info *d)
{
int cmax;
- snd_mtxassert(d->lock);
+ PCM_BUSYASSERT(d);
cmax = d->playcount + d->reccount - 1;
if (d->pvchancount > 0)
@@ -188,34 +193,24 @@ pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
struct pcm_channel *c, *ch, *nch;
int err, vcnt;
- err = 0;
+ PCM_BUSYASSERT(d);
- pcm_inprog(d, 1);
-
if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
- (direction == PCMDIR_REC && d->reccount < 1)) {
- err = ENODEV;
- goto pcm_setvchans_out;
- }
+ (direction == PCMDIR_REC && d->reccount < 1))
+ return (ENODEV);
- if (!(d->flags & SD_F_AUTOVCHAN)) {
- err = EINVAL;
- goto pcm_setvchans_out;
- }
+ if (!(d->flags & SD_F_AUTOVCHAN))
+ return (EINVAL);
- if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
- err = E2BIG;
- goto pcm_setvchans_out;
- }
+ if (newcnt < 0 || newcnt > SND_MAXVCHANS)
+ return (E2BIG);
if (direction == PCMDIR_PLAY)
vcnt = d->pvchancount;
else if (direction == PCMDIR_REC)
vcnt = d->rvchancount;
- else {
- err = EINVAL;
- goto pcm_setvchans_out;
- }
+ else
+ return (EINVAL);
if (newcnt > vcnt) {
KASSERT(num == -1 ||
@@ -223,20 +218,23 @@ pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
num, newcnt, vcnt));
/* add new vchans - find a parent channel first */
+ ch = NULL;
CHN_FOREACH(c, d, channels.pcm) {
CHN_LOCK(c);
if (c->direction == direction &&
((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
- !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)))))
- goto pcm_setvchans_addok;
+ !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
+ ch = c;
+ break;
+ }
CHN_UNLOCK(c);
}
- err = EBUSY;
- goto pcm_setvchans_out;
-pcm_setvchans_addok:
- c->flags |= CHN_F_BUSY;
+ if (ch == NULL)
+ return (EBUSY);
+ ch->flags |= CHN_F_BUSY;
+ err = 0;
while (err == 0 && newcnt > vcnt) {
- err = vchan_create(c, num);
+ err = vchan_create(ch, num);
if (err == 0)
vcnt++;
else if (err == E2BIG && newcnt > vcnt)
@@ -245,11 +243,12 @@ pcm_setvchans_addok:
__func__, err);
}
if (vcnt == 0)
- c->flags &= ~CHN_F_BUSY;
- CHN_UNLOCK(c);
- pcm_lock(d);
- pcm_clonereset(d);
- pcm_unlock(d);
+ ch->flags &= ~CHN_F_BUSY;
+ CHN_UNLOCK(ch);
+ if (err != 0)
+ return (err);
+ else
+ pcm_clonereset(d);
} else if (newcnt < vcnt) {
KASSERT(num == -1,
("bogus vchan_destroy() request num=%d", num));
@@ -272,22 +271,16 @@ pcm_setvchans_addok:
vcnt--;
} else
CHN_UNLOCK(ch);
- if (vcnt == newcnt) {
- err = 0;
+ if (vcnt == newcnt)
break;
- }
}
CHN_UNLOCK(c);
break;
}
- pcm_lock(d);
pcm_clonereset(d);
- pcm_unlock(d);
}
-pcm_setvchans_out:
- pcm_inprog(d, -1);
- return err;
+ return (0);
}
/* return error status and a locked channel */
@@ -301,8 +294,9 @@ pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
!(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
(direction == PCMDIR_PLAY || direction == PCMDIR_REC),
- ("%s() invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
+ ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
__func__, d, ch, direction, pid, devunit));
+ PCM_BUSYASSERT(d);
/* Double check again. */
if (devunit != -1) {
@@ -326,7 +320,7 @@ pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
}
retry_chnalloc:
- err = ENODEV;
+ err = EOPNOTSUPP;
/* scan for a free channel */
CHN_FOREACH(c, d, channels.pcm) {
CHN_LOCK(c);
@@ -351,10 +345,12 @@ retry_chnalloc:
CHN_UNLOCK(c);
}
+ if (devunit == -2)
+ return (err);
+
/* no channel available */
- if (devunit == -1 || (devunit != -2 &&
- (snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
- snd_unit2d(devunit) == SND_DEV_DSPHW_VREC))) {
+ if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
+ snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
if (direction == PCMDIR_PLAY)
vchancount = d->pvchancount;
else
@@ -378,43 +374,42 @@ retry_chnalloc:
int
pcm_chnrelease(struct pcm_channel *c)
{
+ PCM_BUSYASSERT(c->parentsnddev);
CHN_LOCKASSERT(c);
+
c->flags &= ~CHN_F_BUSY;
c->pid = -1;
CHN_UNLOCK(c);
- return 0;
+
+ return (0);
}
int
pcm_chnref(struct pcm_channel *c, int ref)
{
- int r;
-
+ PCM_BUSYASSERT(c->parentsnddev);
CHN_LOCKASSERT(c);
+
c->refcount += ref;
- r = c->refcount;
- return r;
+
+ return (c->refcount);
}
int
pcm_inprog(struct snddev_info *d, int delta)
{
- int r;
-
- if (delta == 0)
- return d->inprog;
+ snd_mtxassert(d->lock);
- /* backtrace(); */
- pcm_lock(d);
d->inprog += delta;
- r = d->inprog;
- pcm_unlock(d);
- return r;
+
+ return (d->inprog);
}
static void
pcm_setmaxautovchans(struct snddev_info *d, int num)
{
+ PCM_BUSYASSERT(d);
+
if (num < 0)
return;
@@ -428,9 +423,7 @@ pcm_setmaxautovchans(struct snddev_info *d, int num)
else if (num > 0 && d->rvchancount == 0)
(void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
- pcm_lock(d);
pcm_clonereset(d);
- pcm_unlock(d);
}
#ifdef USING_DEVFS
@@ -444,7 +437,7 @@ sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
error = sysctl_handle_int(oidp, &unit, 0, req);
if (error == 0 && req->newptr != NULL) {
d = devclass_get_softc(pcm_devclass, unit);
- if (d == NULL || CHN_EMPTY(d, channels.pcm))
+ if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
return EINVAL;
snd_unit = unit;
}
@@ -472,9 +465,11 @@ sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
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;
+ PCM_ACQUIRE_QUICK(d);
pcm_setmaxautovchans(d, v);
+ PCM_RELEASE_QUICK(d);
}
}
return (error);
@@ -490,11 +485,12 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
int udc, device, chan;
char *dirs, *devname, buf[CHN_NAMELEN];
+ PCM_BUSYASSERT(d);
+ snd_mtxassert(d->lock);
KASSERT(num >= -1, ("invalid num=%d", num));
- pcm_lock(d);
- switch(dir) {
+ switch (dir) {
case PCMDIR_PLAY:
dirs = "play";
direction = PCMDIR_PLAY;
@@ -524,16 +520,13 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
max = SND_MAXVCHANS;
break;
default:
- pcm_unlock(d);
- return NULL;
+ return (NULL);
}
chan = (num == -1) ? 0 : num;
- if (*pnum >= max || chan >= max) {
- pcm_unlock(d);
- return NULL;
- }
+ if (*pnum >= max || chan >= max)
+ return (NULL);
rpnum = 0;
@@ -544,15 +537,13 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
if (num != -1) {
device_printf(d->dev,
"channel num=%d allocated!\n", chan);
- pcm_unlock(d);
- return NULL;
+ return (NULL);
}
chan++;
if (chan >= max) {
device_printf(d->dev,
"chan=%d > %d\n", chan, max);
- pcm_unlock(d);
- return NULL;
+ return (NULL);
}
}
rpnum++;
@@ -562,8 +553,7 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
device_printf(d->dev,
"%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
__func__, dirs, *pnum, rpnum);
- pcm_unlock(d);
- return NULL;
+ return (NULL);
}
udc = snd_mkunit(device_get_unit(d->dev), device, chan);
@@ -572,13 +562,10 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
if (devname == NULL) {
device_printf(d->dev,
"Failed to query device name udc=0x%08x\n", udc);
- pcm_unlock(d);
- return NULL;
+ return (NULL);
}
- (*pnum)++;
pcm_unlock(d);
-
ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
ch->unit = udc;
@@ -591,19 +578,16 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
device_get_nameunit(ch->dev), dirs, devname);
err = chn_init(ch, devinfo, dir, direction);
+ pcm_lock(d);
if (err) {
device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
ch->name, err);
kobj_delete(ch->methods, M_DEVBUF);
free(ch, M_DEVBUF);
- pcm_lock(d);
- (*pnum)--;
- pcm_unlock(d);
-
- return NULL;
+ return (NULL);
}
- return ch;
+ return (ch);
}
int
@@ -613,16 +597,19 @@ pcm_chn_destroy(struct pcm_channel *ch)
int err;
d = ch->parentsnddev;
+ PCM_BUSYASSERT(d);
+
err = chn_kill(ch);
if (err) {
- device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
- return err;
+ device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n",
+ ch->name, err);
+ return (err);
}
kobj_delete(ch->methods, M_DEVBUF);
free(ch, M_DEVBUF);
- return 0;
+ return (0);
}
int
@@ -631,8 +618,8 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
struct pcm_channel *tmp, *after;
int num;
- pcm_lock(d);
-
+ PCM_BUSYASSERT(d);
+ snd_mtxassert(d->lock);
KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
@@ -648,8 +635,7 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
device_printf(d->dev, "%s(): Device collision "
"old=%p new=%p devunit=0x%08x\n",
__func__, tmp, ch, ch->unit);
- pcm_unlock(d);
- return ENODEV;
+ return (ENODEV);
}
if (CHN_DEV(tmp) < CHN_DEV(ch)) {
if (num == 0)
@@ -670,10 +656,26 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
CHN_INSERT_HEAD(d, ch, channels.pcm);
}
+ switch (CHN_DEV(ch)) {
+ case SND_DEV_DSPHW_PLAY:
+ d->playcount++;
+ break;
+ case SND_DEV_DSPHW_VPLAY:
+ d->pvchancount++;
+ break;
+ case SND_DEV_DSPHW_REC:
+ d->reccount++;
+ break;
+ case SND_DEV_DSPHW_VREC:
+ d->rvchancount++;
+ break;
+ default:
+ break;
+ }
+
d->devcount++;
- pcm_unlock(d);
- return 0;
+ return (0);
}
int
@@ -681,6 +683,9 @@ pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
{
struct pcm_channel *tmp;
+ PCM_BUSYASSERT(d);
+ snd_mtxassert(d->lock);
+
tmp = NULL;
CHN_FOREACH(tmp, d, channels.pcm) {
@@ -689,9 +694,10 @@ pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
}
if (tmp != ch)
- return EINVAL;
+ return (EINVAL);
CHN_REMOVE(d, ch, channels.pcm);
+
switch (CHN_DEV(ch)) {
case SND_DEV_DSPHW_PLAY:
d->playcount--;
@@ -709,7 +715,9 @@ pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
break;
}
- return 0;
+ d->devcount--;
+
+ return (0);
}
int
@@ -719,20 +727,26 @@ pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
struct pcm_channel *ch;
int err;
+ PCM_BUSYASSERT(d);
+
+ pcm_lock(d);
ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
if (!ch) {
- device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
- return ENODEV;
+ device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
+ cls->name, dir, devinfo);
+ pcm_unlock(d);
+ return (ENODEV);
}
err = pcm_chn_add(d, ch);
+ pcm_unlock(d);
if (err) {
- device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
+ device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
+ ch->name, err);
pcm_chn_destroy(ch);
- return err;
}
- return err;
+ return (err);
}
static int
@@ -740,11 +754,15 @@ pcm_killchan(device_t dev)
{
struct snddev_info *d = device_get_softc(dev);
struct pcm_channel *ch;
- int error = 0;
+ int error;
+
+ PCM_BUSYASSERT(d);
ch = CHN_FIRST(d, channels.pcm);
+ pcm_lock(d);
error = pcm_chn_remove(d, ch);
+ pcm_unlock(d);
if (error)
return (error);
return (pcm_chn_destroy(ch));
@@ -755,20 +773,35 @@ pcm_setstatus(device_t dev, char *str)
{
struct snddev_info *d = device_get_softc(dev);
- pcm_setmaxautovchans(d, snd_maxautovchans);
+ PCM_BUSYASSERT(d);
- pcm_lock(d);
+ if (d->playcount == 0 || d->reccount == 0)
+ d->flags |= SD_F_SIMPLEX;
+
+ if ((d->playcount > 0 || d->reccount > 0) &&
+ !(d->flags & SD_F_AUTOVCHAN)) {
+ d->flags |= SD_F_AUTOVCHAN;
+ vchan_initsys(dev);
+ }
+
+ pcm_setmaxautovchans(d, snd_maxautovchans);
strlcpy(d->status, str, SND_STATUSLEN);
+ pcm_lock(d);
+
/* Last stage, enable cloning. */
- if (d->clones != NULL) {
+ if (d->clones != NULL)
(void)snd_clone_enable(d->clones);
- }
+
+ /* Done, we're ready.. */
+ d->flags |= SD_F_REGISTERED;
+
+ PCM_RELEASE(d);
pcm_unlock(d);
- return 0;
+ return (0);
}
uint32_t
@@ -834,24 +867,23 @@ sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
int err;
d = oidp->oid_arg1;
- if (d == NULL || d->clones == NULL)
+ if (!PCM_REGISTERED(d) || d->clones == NULL)
return (ENODEV);
- pcm_lock(d);
+ PCM_ACQUIRE_QUICK(d);
+
flags = snd_clone_getflags(d->clones);
- pcm_unlock(d);
err = sysctl_handle_int(oidp, &flags, 0, req);
if (err == 0 && req->newptr != NULL) {
- if ((flags & ~SND_CLONE_MASK))
+ if (flags & ~SND_CLONE_MASK)
err = EINVAL;
- else {
- pcm_lock(d);
+ else
(void)snd_clone_setflags(d->clones, flags);
- pcm_unlock(d);
- }
}
+ PCM_RELEASE_QUICK(d);
+
return (err);
}
@@ -862,24 +894,23 @@ sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS)
int err, deadline;
d = oidp->oid_arg1;
- if (d == NULL || d->clones == NULL)
+ if (!PCM_REGISTERED(d) || d->clones == NULL)
return (ENODEV);
- pcm_lock(d);
+ PCM_ACQUIRE_QUICK(d);
+
deadline = snd_clone_getdeadline(d->clones);
- pcm_unlock(d);
err = sysctl_handle_int(oidp, &deadline, 0, req);
if (err == 0 && req->newptr != NULL) {
if (deadline < 0)
err = EINVAL;
- else {
- pcm_lock(d);
+ else
(void)snd_clone_setdeadline(d->clones, deadline);
- pcm_unlock(d);
- }
}
+ PCM_RELEASE_QUICK(d);
+
return (err);
}
@@ -890,16 +921,18 @@ sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS)
int err, val;
d = oidp->oid_arg1;
- if (d == NULL || d->clones == NULL)
+ if (!PCM_REGISTERED(d) || d->clones == NULL)
return (ENODEV);
val = 0;
err = sysctl_handle_int(oidp, &val, 0, req);
if (err == 0 && req->newptr != NULL && val != 0) {
- pcm_lock(d);
- (void)snd_clone_gc(d->clones);
- pcm_unlock(d);
+ PCM_ACQUIRE_QUICK(d);
+ val = snd_clone_gc(d->clones);
+ PCM_RELEASE_QUICK(d);
+ if (bootverbose != 0 || snd_verbose > 3)
+ device_printf(d->dev, "clone gc: pruned=%d\n", val);
}
return (err);
@@ -918,11 +951,14 @@ sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS)
for (i = 0; pcm_devclass != NULL &&
i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
- if (d == NULL || d->clones == NULL)
+ if (!PCM_REGISTERED(d) || d->clones == NULL)
continue;
- pcm_lock(d);
- (void)snd_clone_gc(d->clones);
- pcm_unlock(d);
+ PCM_ACQUIRE_QUICK(d);
+ val = snd_clone_gc(d->clones);
+ PCM_RELEASE_QUICK(d);
+ if (bootverbose != 0 || snd_verbose > 3)
+ device_printf(d->dev, "clone gc: pruned=%d\n",
+ val);
}
}
@@ -953,8 +989,11 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
}
d = device_get_softc(dev);
+ d->dev = dev;
d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
-
+ cv_init(&d->cv, device_get_nameunit(dev));
+ PCM_ACQUIRE_QUICK(d);
+ dsp_cdevinfo_init(d);
#if 0
/*
* d->flags should be cleared by the allocator of the softc.
@@ -963,7 +1002,6 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
*/
d->flags = 0;
#endif
- d->dev = dev;
d->devinfo = devinfo;
d->devcount = 0;
d->reccount = 0;
@@ -981,26 +1019,22 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
* enabled during final stage of driver iniialization through
* pcm_setstatus().
*/
- d->clones = snd_clone_create(
-#ifdef SND_DIAGNOSTIC
- d->lock,
-#endif
- SND_U_MASK | SND_D_MASK, PCMMAXCLONE, SND_CLONE_DEADLINE_DEFAULT,
+ d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE,
+ SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK |
SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF |
SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED);
if (bootverbose != 0 || snd_verbose > 3) {
- pcm_lock(d);
device_printf(dev,
"clone manager: deadline=%dms flags=0x%08x\n",
snd_clone_getdeadline(d->clones),
snd_clone_getflags(d->clones));
- pcm_unlock(d);
}
CHN_INIT(d, channels.pcm);
CHN_INIT(d, channels.pcm.busy);
+ /* XXX This is incorrect, but lets play along for now. */
if ((numplay == 0 || numrec == 0) && numplay != numrec)
d->flags |= SD_F_SIMPLEX;
@@ -1039,73 +1073,103 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
"clone garbage collector");
#endif
#endif
+
if (numplay > 0 || numrec > 0) {
d->flags |= SD_F_AUTOVCHAN;
vchan_initsys(dev);
}
sndstat_register(dev, d->status, sndstat_prepare_pcm);
+
return 0;
}
int
pcm_unregister(device_t dev)
{
- struct snddev_info *d = device_get_softc(dev);
+ struct snddev_info *d;
struct pcm_channel *ch;
struct thread *td;
int i;
td = curthread;
+ d = device_get_softc(dev);
+
+ if (!PCM_ALIVE(d)) {
+ device_printf(dev, "unregister: device not configured\n");
+ return (0);
+ }
if (sndstat_acquire(td) != 0) {
device_printf(dev, "unregister: sndstat busy\n");
- return EBUSY;
+ return (EBUSY);
}
pcm_lock(d);
- if (d->inprog) {
+ PCM_WAIT(d);
+
+ if (d->inprog != 0) {
device_printf(dev, "unregister: operation in progress\n");
pcm_unlock(d);
sndstat_release(td);
- return EBUSY;
+ return (EBUSY);
}
+ PCM_ACQUIRE(d);
+ pcm_unlock(d);
+
CHN_FOREACH(ch, d, channels.pcm) {
+ CHN_LOCK(ch);
if (ch->refcount > 0) {
- device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
- pcm_unlock(d);
+ device_printf(dev,
+ "unregister: channel %s busy (pid %d)\n",
+ ch->name, ch->pid);
+ CHN_UNLOCK(ch);
+ PCM_RELEASE_QUICK(d);
sndstat_release(td);
- return EBUSY;
+ return (EBUSY);
}
+ CHN_UNLOCK(ch);
}
if (d->clones != NULL) {
if (snd_clone_busy(d->clones) != 0) {
device_printf(dev, "unregister: clone busy\n");
- pcm_unlock(d);
+ PCM_RELEASE_QUICK(d);
sndstat_release(td);
- return EBUSY;
- } else
+ return (EBUSY);
+ } else {
+ pcm_lock(d);
(void)snd_clone_disable(d->clones);
+ pcm_unlock(d);
+ }
}
if (mixer_uninit(dev) == EBUSY) {
device_printf(dev, "unregister: mixer busy\n");
+ pcm_lock(d);
if (d->clones != NULL)
(void)snd_clone_enable(d->clones);
+ PCM_RELEASE(d);
pcm_unlock(d);
sndstat_release(td);
- return EBUSY;
+ return (EBUSY);
}
+ pcm_lock(d);
+ d->flags |= SD_F_DYING;
+ d->flags &= ~SD_F_REGISTERED;
+ pcm_unlock(d);
+
+ /*
+ * No lock being held, so this thing can be flushed without
+ * stucking into devdrn oblivion.
+ */
if (d->clones != NULL) {
snd_clone_destroy(d->clones);
d->clones = NULL;
}
- d->devcount = 0;
-
#ifdef SND_DYNSYSCTL
if (d->play_sysctl_tree != NULL) {
sysctl_ctx_free(&d->play_sysctl_ctx);
@@ -1123,6 +1187,11 @@ pcm_unregister(device_t dev)
chn_kill(d->fakechan);
fkchan_kill(d->fakechan);
+ dsp_cdevinfo_flush(d);
+
+ pcm_lock(d);
+ PCM_RELEASE(d);
+ cv_destroy(&d->cv);
pcm_unlock(d);
snd_mtxfree(d->lock);
sndstat_unregister(dev);
@@ -1134,15 +1203,17 @@ pcm_unregister(device_t dev)
*/
for (i = 0; pcm_devclass != NULL &&
i < devclass_get_maxunit(pcm_devclass); i++) {
- if (device_get_unit(dev) == i ||
- devclass_get_softc(pcm_devclass, i) == NULL)
+ if (device_get_unit(dev) == i)
continue;
- snd_unit = i;
- break;
+ d = devclass_get_softc(pcm_devclass, i);
+ if (PCM_REGISTERED(d)) {
+ snd_unit = i;
+ break;
+ }
}
}
- return 0;
+ return (0);
}
/************************************************************************/
@@ -1161,83 +1232,83 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
if (!d)
return ENXIO;
- pcm_lock(d);
- if (!CHN_EMPTY(d, channels.pcm)) {
- sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
- d->playcount, d->pvchancount,
- d->reccount, d->rvchancount,
- (d->flags & SD_F_SIMPLEX)? "" : " duplex",
+ PCM_BUSYASSERT(d);
+
+ if (CHN_EMPTY(d, channels.pcm)) {
+ sbuf_printf(s, " (mixer only)");
+ return 0;
+ }
+
+ sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
+ d->playcount, d->pvchancount,
+ d->reccount, d->rvchancount,
+ (d->flags & SD_F_SIMPLEX)? "" : " duplex",
#ifdef USING_DEVFS
- (device_get_unit(dev) == snd_unit)? " default" : ""
+ (device_get_unit(dev) == snd_unit)? " default" : ""
#else
- ""
+ ""
#endif
- );
+ );
- if (verbose <= 1) {
- pcm_unlock(d);
- return 0;
- }
+ if (verbose <= 1)
+ return 0;
- CHN_FOREACH(c, d, channels.pcm) {
+ CHN_FOREACH(c, d, channels.pcm) {
- 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);
- if (c->speed != sndbuf_getspd(c->bufhard))
- sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
- sbuf_printf(s, ", fmt 0x%08x", c->format);
- if (c->format != sndbuf_getfmt(c->bufhard))
- sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
- sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
- if (c->pid != -1)
- sbuf_printf(s, ", pid %d", c->pid);
- sbuf_printf(s, "\n\t");
-
- sbuf_printf(s, "interrupts %d, ", c->interrupts);
- if (c->direction == PCMDIR_REC)
- sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
- c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
- sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
- sndbuf_getblkcnt(c->bufhard),
- sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
- sndbuf_getblkcnt(c->bufsoft));
- else
- sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
- c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
- sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
- sndbuf_getblkcnt(c->bufhard),
- sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
- sndbuf_getblkcnt(c->bufsoft));
- sbuf_printf(s, "\n\t");
-
- sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
+ 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);
+ if (c->speed != sndbuf_getspd(c->bufhard))
+ sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
+ sbuf_printf(s, ", fmt 0x%08x", c->format);
+ if (c->format != sndbuf_getfmt(c->bufhard))
+ sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
+ sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
+ if (c->pid != -1)
+ sbuf_printf(s, ", pid %d", c->pid);
+ sbuf_printf(s, "\n\t");
+
+ sbuf_printf(s, "interrupts %d, ", c->interrupts);
+ if (c->direction == PCMDIR_REC)
+ sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
+ sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
+ sndbuf_getblkcnt(c->bufhard),
+ sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
+ sndbuf_getblkcnt(c->bufsoft));
+ else
+ sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
+ sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
+ sndbuf_getblkcnt(c->bufhard),
+ sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
+ sndbuf_getblkcnt(c->bufsoft));
+ sbuf_printf(s, "\n\t");
+
+ sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
+ sbuf_printf(s, " -> ");
+ f = c->feeder;
+ while (f->source != NULL)
+ f = f->source;
+ while (f != NULL) {
+ sbuf_printf(s, "%s", f->class->name);
+ if (f->desc->type == FEEDER_FMT)
+ 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 ||
+ f->desc->type == FEEDER_VOLUME)
+ sbuf_printf(s, "(0x%08x)", f->desc->out);
sbuf_printf(s, " -> ");
- f = c->feeder;
- while (f->source != NULL)
- f = f->source;
- while (f != NULL) {
- sbuf_printf(s, "%s", f->class->name);
- if (f->desc->type == FEEDER_FMT)
- 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 ||
- 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");
+ f = f->parent;
}
- } else
- sbuf_printf(s, " (mixer only)");
- pcm_unlock(d);
+ sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
+ }
return 0;
}
@@ -1250,41 +1321,53 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
{
struct snddev_info *d;
int direction, vchancount;
- int err, newcnt;
+ int err, cnt;
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
- if (d == NULL)
- return EINVAL;
+ 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:
- if (d->playcount < 1)
- return ENODEV;
direction = PCMDIR_PLAY;
vchancount = d->pvchancount;
+ cnt = d->playcount;
break;
case VCHAN_REC:
- if (d->reccount < 1)
- return ENODEV;
direction = PCMDIR_REC;
vchancount = d->rvchancount;
+ cnt = d->reccount;
break;
default:
- return EINVAL;
+ pcm_unlock(d);
+ return (EINVAL);
break;
}
- newcnt = vchancount;
- err = sysctl_handle_int(oidp, &newcnt, 0, req);
+ if (cnt < 1) {
+ pcm_unlock(d);
+ return (ENODEV);
+ }
- if (err == 0 && req->newptr != NULL && vchancount != newcnt) {
- if (newcnt < 0)
- newcnt = 0;
- if (newcnt > SND_MAXVCHANS)
- newcnt = SND_MAXVCHANS;
- err = pcm_setvchans(d, direction, newcnt, -1);
+ PCM_ACQUIRE(d);
+ pcm_unlock(d);
+
+ cnt = vchancount;
+ err = sysctl_handle_int(oidp, &cnt, 0, req);
+
+ if (err == 0 && req->newptr != NULL && vchancount != cnt) {
+ if (cnt < 0)
+ cnt = 0;
+ if (cnt > SND_MAXVCHANS)
+ cnt = SND_MAXVCHANS;
+ err = pcm_setvchans(d, direction, cnt, -1);
}
+ PCM_RELEASE_QUICK(d);
+
return err;
}
#endif
@@ -1332,37 +1415,34 @@ sound_oss_sysinfo(oss_sysinfo *si)
si->numaudios = 0;
bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
- if (pcm_devclass != NULL) {
- j = 0;
+ j = 0;
- for (i = 0; pcm_devclass != NULL &&
- i < devclass_get_maxunit(pcm_devclass); i++) {
- d = devclass_get_softc(pcm_devclass, i);
- if (!d)
- continue;
+ for (i = 0; pcm_devclass != NULL &&
+ i < devclass_get_maxunit(pcm_devclass); i++) {
+ d = devclass_get_softc(pcm_devclass, i);
+ if (!PCM_REGISTERED(d))
+ continue;
- /* See note in function's docblock */
- mtx_assert(d->lock, MA_NOTOWNED);
- /* Increment device's "operations in progress" */
- pcm_inprog(d, 1);
- pcm_lock(d);
+ /* XXX Need Giant magic entry ??? */
- si->numaudios += d->devcount;
- ++ncards;
+ /* See note in function's docblock */
+ mtx_assert(d->lock, MA_NOTOWNED);
+ pcm_lock(d);
- CHN_FOREACH(c, d, channels.pcm) {
- mtx_assert(c->lock, MA_NOTOWNED);
- CHN_LOCK(c);
- if (c->flags & CHN_F_BUSY)
- si->openedaudio[j / intnbits] |=
- (1 << (j % intnbits));
- CHN_UNLOCK(c);
- j++;
- }
+ si->numaudios += d->devcount;
+ ++ncards;
- pcm_unlock(d);
- pcm_inprog(d, -1);
+ CHN_FOREACH(c, d, channels.pcm) {
+ mtx_assert(c->lock, MA_NOTOWNED);
+ CHN_LOCK(c);
+ if (c->flags & CHN_F_BUSY)
+ si->openedaudio[j / intnbits] |=
+ (1 << (j % intnbits));
+ CHN_UNLOCK(c);
+ j++;
}
+
+ pcm_unlock(d);
}
si->numsynths = 0; /* OSSv4 docs: this field is obsolete */
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index 1f1aa0d..1ce2c77 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -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
diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c
index 81684d7..d881654 100644
--- a/sys/dev/sound/pcm/vchan.c
+++ b/sys/dev/sound/pcm/vchan.c
@@ -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);
diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c
index 504cc17..c860db9 100644
--- a/sys/dev/sound/usb/uaudio.c
+++ b/sys/dev/sound/usb/uaudio.c
@@ -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;
}
diff --git a/sys/dev/sound/version.h b/sys/dev/sound/version.h
index d7f6dd5..a0961af 100644
--- a/sys/dev/sound/version.h
+++ b/sys/dev/sound/version.h
@@ -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_ */
OpenPOWER on IntegriCloud