various locking fixes, rework open logic and channel registration
PR: kern/28084
This commit is contained in:
parent
4d153eaeb6
commit
b8f0d9e0b2
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user