diff options
-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_ */ |