diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c index 72f2852e4661..b3cfbe4c1e6d 100644 --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -490,7 +490,7 @@ chn_poll(struct pcm_channel *c, int ev, struct proc *p) struct snd_dbuf *bs = c->bufsoft; int ret; - CHN_LOCK(c); + CHN_LOCKASSERT(c); if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED)) chn_start(c, 1); ret = 0; @@ -498,7 +498,6 @@ chn_poll(struct pcm_channel *c, int ev, struct proc *p) ret = ev; else selrecord(p, sndbuf_getsel(bs)); - CHN_UNLOCK(c); return ret; } diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c index c31d955bfa32..52e4f4e981cb 100644 --- a/sys/dev/sound/pcm/dsp.c +++ b/sys/dev/sound/pcm/dsp.c @@ -64,49 +64,7 @@ dsp_open(struct snddev_info *d, int chan, int oflags, int devtype, pid_t pid) struct pcm_channel *rdch, *wrch; u_int32_t fmt; - if (chan >= d->chancount) - return ENODEV; - if ((d->flags & SD_F_SIMPLEX) && (d->arec[chan] || d->aplay[chan])) - return EBUSY; - - rdch = d->arec[chan]; - wrch = d->aplay[chan]; - - if (oflags & FREAD) { - if (rdch == NULL) { - rdch = pcm_chnalloc(d, PCMDIR_REC); - if (!rdch) - return EBUSY; - rdch->pid = pid; - } else - return EBUSY; - } - - if (oflags & FWRITE) { - if (wrch == NULL) { - wrch = pcm_chnalloc(d, PCMDIR_PLAY); - if (!wrch) { - if (rdch && (oflags & FREAD)) - pcm_chnfree(rdch); - return EBUSY; - } - wrch->pid = pid; - } else - return EBUSY; - } - - if (rdch) { - CHN_LOCK(rdch); - pcm_chnref(rdch, 1); - } - if (wrch) { - CHN_LOCK(wrch); - pcm_chnref(wrch, 1); - } - - d->aplay[chan] = wrch; - d->arec[chan] = rdch; - + /* decide default format */ switch (devtype) { case SND_DEV_DSP16: fmt = AFMT_S16_LE; @@ -125,21 +83,100 @@ dsp_open(struct snddev_info *d, int chan, int oflags, int devtype, pid_t pid) break; default: - return ENXIO; + panic("impossible devtype %d", devtype); } - if (rdch && (oflags & FREAD)) { - chn_reset(rdch, fmt); - if (oflags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; + /* lock snddev so nobody else can monkey with it */ + snd_mtxlock(d->lock); + if (chan >= d->chancount) { + /* not a valid channel, exit */ + snd_mtxunlock(d->lock); + return ENODEV; } - if (wrch && (oflags & FWRITE)) { - chn_reset(wrch, fmt); - if (oflags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO; + if ((d->flags & SD_F_SIMPLEX) && (d->arec[chan] || d->aplay[chan])) { + /* simplex device, already open, exit */ + snd_mtxunlock(d->lock); + return EBUSY; + } + + /* if we get here, the open request is valid */ + rdch = d->arec[chan]; + wrch = d->aplay[chan]; + + if (oflags & FREAD) { + /* open for read */ + if (rdch == NULL) { + /* not already open, try to get a channel */ + rdch = pcm_chnalloc(d, PCMDIR_REC, pid); + if (!rdch) { + /* no channel available, exit */ + snd_mtxunlock(d->lock); + return EBUSY; + } + /* got a channel, already locked for us */ + } else { + /* already open for read, exit */ + snd_mtxunlock(d->lock); + return EBUSY; + } + } + + if (oflags & FWRITE) { + /* open for write */ + if (wrch == NULL) { + /* not already open, try to get a channel */ + wrch = pcm_chnalloc(d, PCMDIR_PLAY, pid); + if (!wrch) { + /* no channel available */ + if (rdch && (oflags & FREAD)) { + /* just opened a read channel, release it */ + pcm_chnrelease(rdch); + } + /* exit */ + snd_mtxunlock(d->lock); + return EBUSY; + } + /* got a channel, already locked for us */ + } else { + /* already open for write */ + if (rdch && (oflags & FREAD)) { + /* just opened a read channel, release it */ + pcm_chnrelease(rdch); + } + /* exit */ + snd_mtxunlock(d->lock); + return EBUSY; + } + } + + d->aplay[chan] = wrch; + d->arec[chan] = rdch; + snd_mtxunlock(d->lock); + /* finished with snddev, new channels still locked */ + + /* bump refcounts, reset and unlock any channels that we just opened */ + if (rdch) { + if (oflags & FREAD) { + chn_reset(rdch, fmt); + if (oflags & O_NONBLOCK) + rdch->flags |= CHN_F_NBIO; + } else { + CHN_LOCK(rdch); + pcm_chnref(rdch, 1); + } + CHN_UNLOCK(rdch); + } + if (wrch) { + if (oflags & FWRITE) { + chn_reset(wrch, fmt); + if (oflags & O_NONBLOCK) + wrch->flags |= CHN_F_NBIO; + } else { + CHN_LOCK(wrch); + pcm_chnref(wrch, 1); + } + CHN_UNLOCK(wrch); } - if (wrch) - CHN_UNLOCK(wrch); - if (rdch) - CHN_UNLOCK(rdch); return 0; } @@ -147,37 +184,56 @@ int dsp_close(struct snddev_info *d, int chan, int devtype) { struct pcm_channel *rdch, *wrch; + int exit; + snd_mtxlock(d->lock); rdch = d->arec[chan]; wrch = d->aplay[chan]; - if (rdch && pcm_chnref(rdch, -1)) - return 0; - if (wrch && pcm_chnref(wrch, -1)) + exit = 0; + + /* decrement refcount for each channel, exit if nonzero */ + if (rdch) { + CHN_LOCK(rdch); + if (pcm_chnref(rdch, -1) > 0) { + CHN_UNLOCK(rdch); + exit = 1; + } + } + if (wrch) { + CHN_LOCK(wrch); + if (pcm_chnref(wrch, -1) > 0) { + CHN_UNLOCK(wrch); + exit = 1; + } + } + if (exit) { + snd_mtxunlock(d->lock); return 0; + } + + /* both refcounts are zero, abort and release */ + + if (d->fakechan) + d->fakechan->flags = 0; d->aplay[chan] = NULL; d->arec[chan] = NULL; d->flags &= ~SD_F_TRANSIENT; + snd_mtxunlock(d->lock); if (rdch) { - CHN_LOCK(rdch); chn_abort(rdch); rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); chn_reset(rdch, 0); - rdch->pid = -1; - pcm_chnfree(rdch); - CHN_UNLOCK(rdch); + pcm_chnrelease(rdch); } if (wrch) { - CHN_LOCK(wrch); chn_flush(wrch); wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); chn_reset(wrch, 0); - wrch->pid = -1; - pcm_chnfree(wrch); - CHN_UNLOCK(wrch); + pcm_chnrelease(wrch); } return 0; @@ -189,11 +245,14 @@ dsp_read(struct snddev_info *d, int chan, struct uio *buf, int flag) struct pcm_channel *rdch, *wrch; int ret; + snd_mtxlock(d->lock); if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD; if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan); getchns(d, chan, &rdch, &wrch); CHN_LOCK(rdch); + snd_mtxunlock(d->lock); + KASSERT(rdch, ("dsp_read: nonexistant channel")); KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel")); @@ -215,11 +274,14 @@ dsp_write(struct snddev_info *d, int chan, struct uio *buf, int flag) struct pcm_channel *rdch, *wrch; int ret; + snd_mtxlock(d->lock); if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR; if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan); getchns(d, chan, &rdch, &wrch); CHN_LOCK(wrch); + snd_mtxunlock(d->lock); + KASSERT(wrch, ("dsp_write: nonexistant channel")); KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel")); @@ -240,10 +302,16 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) { int ret = 0, *arg_i = (int *)arg; u_long s; - struct pcm_channel *wrch = NULL, *rdch = NULL; + struct pcm_channel *wrch, *rdch; + snd_mtxlock(d->lock); rdch = d->arec[chan]; wrch = d->aplay[chan]; + if (rdch) + CHN_LOCK(rdch); + if (wrch) + CHN_LOCK(wrch); + snd_mtxunlock(d->lock); if (rdch && (rdch->flags & CHN_F_DEAD)) rdch = NULL; @@ -252,10 +320,6 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) if (!(rdch || wrch)) return EINVAL; - if (wrch) - CHN_LOCK(wrch); - if (rdch) - CHN_LOCK(rdch); /* * all routines are called with int. blocked. Make sure that * ints are re-enabled when calling slow or blocking functions! @@ -277,6 +341,7 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) case AIOSSIZE: /* set the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; + if (wrch) chn_setblocksize(wrch, 2, p->play_size); if (rdch) @@ -286,6 +351,7 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) case AIOGSIZE: /* get the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; + if (wrch) p->play_size = sndbuf_getblksz(wrch->bufsoft); if (rdch) @@ -296,6 +362,7 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) case AIOSFMT: { snd_chan_param *p = (snd_chan_param *)arg; + if (wrch) { chn_setformat(wrch, p->play_format); chn_setspeed(wrch, p->play_rate); @@ -310,6 +377,7 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) case AIOGFMT: { snd_chan_param *p = (snd_chan_param *)arg; + p->play_rate = wrch? wrch->speed : 0; p->rec_rate = rdch? rdch->speed : 0; p->play_format = wrch? wrch->format : 0; @@ -321,8 +389,11 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) { snd_capabilities *p = (snd_capabilities *)arg; struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; - if (rdch) rcaps = chn_getcaps(rdch); - if (wrch) pcaps = chn_getcaps(wrch); + + if (rdch) + rcaps = chn_getcaps(rdch); + if (wrch) + pcaps = chn_getcaps(wrch); p->rate_min = max(rcaps? rcaps->minspeed : 0, pcaps? pcaps->minspeed : 0); p->rate_max = min(rcaps? rcaps->maxspeed : 1000000, @@ -371,11 +442,15 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) case SNDCTL_DSP_NONBLOCK: case FIONBIO: /* set/clear non-blocking i/o */ - if (rdch) rdch->flags &= ~CHN_F_NBIO; - if (wrch) wrch->flags &= ~CHN_F_NBIO; + if (rdch) + rdch->flags &= ~CHN_F_NBIO; + if (wrch) + wrch->flags &= ~CHN_F_NBIO; if (*arg_i) { - if (rdch) rdch->flags |= CHN_F_NBIO; - if (wrch) wrch->flags |= CHN_F_NBIO; + if (rdch) + rdch->flags |= CHN_F_NBIO; + if (wrch) + wrch->flags |= CHN_F_NBIO; } break; @@ -395,8 +470,10 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) case SNDCTL_DSP_SETBLKSIZE: RANGE(*arg_i, 16, 65536); - if (wrch) chn_setblocksize(wrch, 2, *arg_i); - if (rdch) chn_setblocksize(rdch, 2, *arg_i); + if (wrch) + chn_setblocksize(wrch, 2, *arg_i); + if (rdch) + chn_setblocksize(rdch, 2, *arg_i); break; case SNDCTL_DSP_RESET: @@ -456,7 +533,6 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) break ; case SNDCTL_DSP_SETFMT: /* sets _one_ format */ - splx(s); if ((*arg_i != AFMT_QUERY)) { if (wrch) ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO)); @@ -545,7 +621,8 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) a->blocks = sndbuf_getblocks(bs) - rdch->blocks; a->ptr = sndbuf_getreadyptr(bs); rdch->blocks = sndbuf_getblocks(bs); - } else ret = EINVAL; + } else + ret = EINVAL; } break; @@ -560,7 +637,8 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) a->blocks = sndbuf_getblocks(bs) - wrch->blocks; a->ptr = sndbuf_getreadyptr(bs); wrch->blocks = sndbuf_getblocks(bs); - } else ret = EINVAL; + } else + ret = EINVAL; } break; @@ -633,26 +711,41 @@ dsp_ioctl(struct snddev_info *d, int chan, u_long cmd, caddr_t arg) ret = EINVAL; break; } - if (rdch) - CHN_UNLOCK(rdch); if (wrch) CHN_UNLOCK(wrch); + if (rdch) + CHN_UNLOCK(rdch); + splx(s); return ret; } int dsp_poll(struct snddev_info *d, int chan, int events, struct proc *p) { - int ret = 0, e; + int ret, e; struct pcm_channel *wrch = NULL, *rdch = NULL; + ret = 0; + snd_mtxlock(d->lock); getchns(d, chan, &rdch, &wrch); + if (rdch) + CHN_LOCK(rdch); + if (wrch) + CHN_LOCK(wrch); - e = events & (POLLOUT | POLLWRNORM); - if (wrch && e) ret |= chn_poll(wrch, e, p); - - e = events & (POLLIN | POLLRDNORM); - if (rdch && e) ret |= chn_poll(rdch, e, p); + if (wrch) { + e = (events & (POLLOUT | POLLWRNORM)); + if (e) + ret |= chn_poll(wrch, e, p); + CHN_UNLOCK(wrch); + } + if (rdch) { + e = (events & (POLLIN | POLLRDNORM)); + if (e) + ret |= chn_poll(rdch, e, p); + CHN_UNLOCK(rdch); + } + snd_mtxunlock(d->lock); return ret; } diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c index 7e693e3b0c6b..3f14d89973d0 100644 --- a/sys/dev/sound/pcm/sound.c +++ b/sys/dev/sound/pcm/sound.c @@ -35,6 +35,7 @@ #include "feeder_if.h" #undef SNDSTAT_VERBOSE +#define PCM_MAXCHANS 256 static dev_t status_dev = 0; static int do_status(int action, struct uio *buf); @@ -168,33 +169,34 @@ snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand return bus_setup_intr(dev, res, flags, hand, param, cookiep); } +/* return a locked channel */ struct pcm_channel * -pcm_chnalloc(struct snddev_info *d, int direction) +pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid) { struct pcm_channel *c; struct snddev_channel *sce; - snd_mtxlock(d->lock); + snd_mtxassert(d->lock); SLIST_FOREACH(sce, &d->channels, link) { c = sce->channel; CHN_LOCK(c); if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { c->flags |= CHN_F_BUSY; - CHN_UNLOCK(c); - snd_mtxunlock(d->lock); + c->pid = pid; return c; } CHN_UNLOCK(c); } - snd_mtxunlock(d->lock); return NULL; } +/* release a locked channel and unlock it */ int -pcm_chnfree(struct pcm_channel *c) +pcm_chnrelease(struct pcm_channel *c) { - CHN_LOCK(c); + CHN_LOCKASSERT(c); c->flags &= ~CHN_F_BUSY; + c->pid = -1; CHN_UNLOCK(c); return 0; } @@ -204,24 +206,18 @@ pcm_chnref(struct pcm_channel *c, int ref) { int r; - CHN_LOCK(c); + CHN_LOCKASSERT(c); c->refcount += ref; r = c->refcount; - CHN_UNLOCK(c); return r; } #ifdef USING_DEVFS static void -pcm_makelinks(void *dummy) +snd_setdefaultunit(struct snddev_info *d) { - int unit; - dev_t pdev; static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0; - struct snddev_info *d; - if (pcm_devclass == NULL || devfs_present == 0) - return; if (dsp) { destroy_dev(dsp); dsp = 0; @@ -239,33 +235,73 @@ pcm_makelinks(void *dummy) mixer = 0; } - unit = snd_unit; - if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) - return; - d = devclass_get_softc(pcm_devclass, unit); - if (d == NULL || d->chancount == 0) + if (d == NULL) return; - pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0)); - dsp = make_dev_alias(pdev, "dsp"); - pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0)); - dspW = make_dev_alias(pdev, "dspW"); - pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); - audio = make_dev_alias(pdev, "audio"); - pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); - mixer = make_dev_alias(pdev, "mixer"); + if (d->dspdev) + dsp = make_dev_alias(d->dspdev, "dsp"); + if (d->dspWdev) + dspW = make_dev_alias(d->dspWdev, "dspW"); + if (d->audiodev) + audio = make_dev_alias(d->audiodev, "audio"); + if (d->mixerdev) + mixer = make_dev_alias(d->mixerdev, "mixer"); +} +#endif + +static void +pcm_relinkdspunit(struct snddev_info *d) +{ +#ifdef USING_DEVFS + int unit = device_get_unit(d->dev); + dev_t pdev; + + if (d->dspdev) { + destroy_dev(d->dspdev); + d->dspdev = 0; + } + if (d->dspWdev) { + destroy_dev(d->dspWdev); + d->dspWdev = 0; + } + if (d->audiodev) { + destroy_dev(d->audiodev); + d->audiodev = 0; + } + + if (d->defaultchan < d->chancount) { + pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->defaultchan)); + d->dspdev = make_dev_alias(pdev, "dsp%d", unit); + + pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->defaultchan)); + d->dspWdev = make_dev_alias(pdev, "dspW%d", unit); + + pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->defaultchan)); + d->audiodev = make_dev_alias(pdev, "audio%d", unit); + } + + if (unit == snd_unit) + snd_setdefaultunit(d); +#endif } +#ifdef USING_DEVFS static int sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) { + struct snddev_info *d; int error, unit; unit = snd_unit; error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); if (error == 0 && req->newptr != NULL) { + if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) + return EINVAL; + d = devclass_get_softc(pcm_devclass, unit); + if (d == NULL || d->chancount == 0) + return EINVAL; snd_unit = unit; - pcm_makelinks(NULL); + snd_setdefaultunit(d); } return (error); } @@ -342,15 +378,43 @@ int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) { struct snddev_channel *sce; + struct pcm_channel **aplay, **arec; int unit = device_get_unit(d->dev); + int cc, sz; + + snd_mtxlock(d->lock); + if (d->chancount == d->maxchans) { + cc = d->maxchans? d->maxchans * 2 : 2; + sz = cc * sizeof(struct pcm_channel *); + aplay = malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); + arec = malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); + if (aplay == NULL || arec == NULL) { + if (aplay) + free(aplay, M_DEVBUF); + if (arec) + free(arec, M_DEVBUF); + snd_mtxunlock(d->lock); + return EINVAL; + } + if (d->aplay) { + bcopy(d->aplay, aplay, d->maxchans * sizeof(struct pcm_channel *)); + free(d->aplay, M_DEVBUF); + } + d->aplay = aplay; + if (d->arec) { + bcopy(d->arec, arec, d->maxchans * sizeof(struct pcm_channel *)); + free(d->arec, M_DEVBUF); + } + d->arec = arec; + d->maxchans = cc; + } sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); if (!sce) { - free(ch, M_DEVBUF); + snd_mtxunlock(d->lock); return ENOMEM; } - snd_mtxlock(d->lock); sce->channel = ch; SLIST_INSERT_HEAD(&d->channels, sce, link); @@ -362,10 +426,9 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); /* XXX SND_DEV_NORESET? */ -#ifdef USING_DEVFS if (d->chancount++ == 0) - pcm_makelinks(NULL); -#endif + pcm_relinkdspunit(d); + snd_mtxunlock(d->lock); return 0; @@ -390,10 +453,8 @@ pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) SLIST_REMOVE(&d->channels, sce, snddev_channel, link); free(sce, M_DEVBUF); -#ifdef USING_DEVFS if (d->chancount == 0) - pcm_makelinks(NULL); -#endif + pcm_relinkdspunit(d); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); destroy_dev(pdev); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); @@ -462,7 +523,13 @@ void pcm_setflags(device_t dev, u_int32_t val) { struct snddev_info *d = device_get_softc(dev); - +/* + if ((val & SD_F_SIMPLEX) && (d->fakechan == NULL)) { + device_printf(dev, "set simplex mode\n"); + d->fakechan = fkchan_setup(dev); + chn_init(d->fakechan, NULL, 0); + } +*/ d->flags = val; } @@ -478,7 +545,7 @@ pcm_getdevinfo(device_t dev) int pcm_register(device_t dev, void *devinfo, int numplay, int numrec) { - int sz, unit = device_get_unit(dev); + int unit = device_get_unit(dev); struct snddev_info *d = device_get_softc(dev); d->lock = snd_mtxcreate(device_get_nameunit(dev)); @@ -489,26 +556,33 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) UID_ROOT, GID_WHEEL, 0444, "sndstat"); } - make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), + d->mixerdev = make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); + d->dspdev = 0; + d->dspWdev = 0; + d->audiodev = 0; d->dev = dev; d->devinfo = devinfo; d->chancount = 0; - d->maxchans = numplay + numrec; + d->defaultchan = 0; + d->maxchans = 0; + d->aplay = NULL; + d->arec = NULL; +/* sz = d->maxchans * sizeof(struct pcm_channel *); if (sz > 0) { d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); if (!d->arec || !d->aplay) goto no; - - if (numplay == 0 || numrec == 0) - d->flags |= SD_F_SIMPLEX; - - d->fakechan = fkchan_setup(dev); - chn_init(d->fakechan, NULL, 0); } +*/ + if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) { + d->flags |= SD_F_SIMPLEX; + } + d->fakechan = fkchan_setup(dev); + chn_init(d->fakechan, NULL, 0); #ifdef SND_DYNSYSCTL sysctl_ctx_init(&d->sysctl_tree); @@ -521,13 +595,17 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) } #endif #if 1 - vchan_initsys(d); + if (numplay > 0) + vchan_initsys(d); #endif + snd_setdefaultunit(d); snd_mtxunlock(d->lock); return 0; no: +/* if (d->aplay) free(d->aplay, M_DEVBUF); if (d->arec) free(d->arec, M_DEVBUF); +*/ /* snd_mtxunlock(d->lock); */ snd_mtxfree(d->lock); return ENXIO; @@ -539,7 +617,6 @@ pcm_unregister(device_t dev) int unit = device_get_unit(dev); struct snddev_info *d = device_get_softc(dev); struct snddev_channel *sce; - dev_t pdev; snd_mtxlock(d->lock); SLIST_FOREACH(sce, &d->channels, link) { @@ -559,9 +636,9 @@ pcm_unregister(device_t dev) d->sysctl_tree_top = NULL; sysctl_ctx_free(&d->sysctl_tree); #endif - - pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); - destroy_dev(pdev); + if (unit == snd_unit) + snd_setdefaultunit(NULL); + destroy_dev(d->mixerdev); mixer_uninit(dev); while (d->chancount > 0) diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h index 9f321839f854..0ead94f19b97 100644 --- a/sys/dev/sound/pcm/sound.h +++ b/sys/dev/sound/pcm/sound.h @@ -108,7 +108,7 @@ struct snddev_channel { struct snddev_info { SLIST_HEAD(, snddev_channel) channels; struct pcm_channel **aplay, **arec, *fakechan; - unsigned chancount, maxchans; + unsigned chancount, maxchans, defaultchan; struct snd_mixer *mixer; unsigned flags; void *devinfo; @@ -116,6 +116,7 @@ struct snddev_info { char status[SND_STATUSLEN]; struct sysctl_ctx_list sysctl_tree; struct sysctl_oid *sysctl_tree_top; + dev_t dspdev, dspWdev, audiodev, mixerdev; void *lock; }; @@ -198,8 +199,8 @@ int fkchan_kill(struct pcm_channel *c); SYSCTL_DECL(_hw_snd); -struct pcm_channel *pcm_chnalloc(struct snddev_info *d, int direction); -int pcm_chnfree(struct pcm_channel *c); +struct pcm_channel *pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid); +int pcm_chnrelease(struct pcm_channel *c); int pcm_chnref(struct pcm_channel *c, int ref); struct pcm_channel *pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo); diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c index dc5358d37bb6..3866ece06b81 100644 --- a/sys/dev/sound/pcm/vchan.c +++ b/sys/dev/sound/pcm/vchan.c @@ -332,7 +332,7 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) struct snddev_info *d; struct snddev_channel *sce; struct pcm_channel *c; - int err, newcnt, cnt; + int err, oldcnt, newcnt, cnt; d = oidp->oid_arg1; @@ -343,6 +343,7 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) cnt++; } + oldcnt = cnt; newcnt = cnt; err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); @@ -379,6 +380,8 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) if (err == 0) cnt++; } + if (SLIST_EMPTY(&c->children)) + c->flags &= ~CHN_F_BUSY; } else if (newcnt < cnt) { while (err == 0 && newcnt < cnt) { SLIST_FOREACH(sce, &d->channels, link) {