various locking fixes, rework open logic and channel registration

PR:             kern/28084
This commit is contained in:
Cameron Grant 2001-06-14 13:31:30 +00:00
parent 4d153eaeb6
commit b8f0d9e0b2
5 changed files with 320 additions and 147 deletions

View File

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

View File

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

View File

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

View File

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

View File

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