diff options
author | ariff <ariff@FreeBSD.org> | 2007-06-16 03:37:28 +0000 |
---|---|---|
committer | ariff <ariff@FreeBSD.org> | 2007-06-16 03:37:28 +0000 |
commit | 98cd3abe8b02f0ae3d389eb9e6ecbf4bf39032f6 (patch) | |
tree | ee24d563e820c63a513c58c01e33792788d64cfd /sys/dev | |
parent | a2d7081fdfd21f76333ba013ab76221e98217db8 (diff) | |
download | FreeBSD-src-98cd3abe8b02f0ae3d389eb9e6ecbf4bf39032f6.zip FreeBSD-src-98cd3abe8b02f0ae3d389eb9e6ecbf4bf39032f6.tar.gz |
Last (again ?!?) major commit for RELENG_7, featuring total Giant
eradication in/from userland path, countless locking fixes, etc.
- General sleep call through msleep(9) has been converted to condvar(9)
with better consistencies.
- Heavily guard every possible "slow path" entries (open(), close(),
few ioctl()s, sysctls), but once it entering "fast path" (io, interrupt
started), they are free to fly on their own.
- Rearrange locking sequences, resulting better concurrency and
serialization. Large part doesn't even need locking at all, and will be
removed in future. Less clutter, except in few places due to lock
ordering.
- Anonymous mixer object creation/deletion to simplify mixer handling
beyond typical mixer ioctls.
Submitted by: chibis (with modifications)
- Add few mix_[get|set|..] functions to avoid calling mixer_ioctl()
directly using cryptic arguments.
- Locking fixes to avoid possible deadlock with (still under Giant) USB.
- Better simplex/duplex device handling.
- Recover mmap() functionality for recording, which has been lost
since 2.2.x - 3.x (the introduction of newpcm). Full-duplex mmap still
doesn't work (due to VM/page design), but people still can mmap
both by opening each direction separately. mmaped playback is guarantee
to work either way.
- New sysctl: "hw.snd.compat_linux_mmap" to allow PROT_EXEC page
mapping, due to recent changes in linux compatibility layer which
require it. All linux applications that using sound + mmap() (mostly games)
require this to be enabled. Disabled by default.
- Other goodies.. too many, that will increase releng7 shareholder value
and make users of releng6 (and below) cry ;)
* This commit should be atomic. If anything goes wrong (not counting problem
originated from elsewhere), I will not hesitate to revert everything back
within 12 hours. This substantial changes itself not a rocket science
and the process has begun for almost 2 years, and lots of incremental
changes are already in place during that period of time.
* Some issues does occur in snd_emu10kx (note the 'x') due to various
internal locking issues and it is currently being worked on by chibis.
Tested by: chibis (Yuriy Tsibizov), joel, Alexandre Vieira,
many innocent souls...
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/sound/pci/es137x.c | 58 | ||||
-rw-r--r-- | sys/dev/sound/pcm/buffer.c | 2 | ||||
-rw-r--r-- | sys/dev/sound/pcm/channel.c | 390 | ||||
-rw-r--r-- | sys/dev/sound/pcm/channel.h | 30 | ||||
-rw-r--r-- | sys/dev/sound/pcm/dsp.c | 1124 | ||||
-rw-r--r-- | sys/dev/sound/pcm/dsp.h | 5 | ||||
-rw-r--r-- | sys/dev/sound/pcm/feeder.c | 21 | ||||
-rw-r--r-- | sys/dev/sound/pcm/feeder_rate.c | 10 | ||||
-rw-r--r-- | sys/dev/sound/pcm/mixer.c | 548 | ||||
-rw-r--r-- | sys/dev/sound/pcm/mixer.h | 23 | ||||
-rw-r--r-- | sys/dev/sound/pcm/sndstat.c | 40 | ||||
-rw-r--r-- | sys/dev/sound/pcm/sound.c | 618 | ||||
-rw-r--r-- | sys/dev/sound/pcm/sound.h | 208 | ||||
-rw-r--r-- | sys/dev/sound/pcm/vchan.c | 131 | ||||
-rw-r--r-- | sys/dev/sound/usb/uaudio.c | 20 | ||||
-rw-r--r-- | sys/dev/sound/version.h | 2 |
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_ */ |