summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorcg <cg@FreeBSD.org>2001-06-14 13:31:30 +0000
committercg <cg@FreeBSD.org>2001-06-14 13:31:30 +0000
commit2ec7f0d19f7d1d4e02ec98b605a460f015c9f6b5 (patch)
tree9f09ca16ead4ae8551bd8e8a2b22dfd4bc4becc8 /sys/dev
parent089e07ab6ac0a4433ae32cbdb9e4c1b4936995a6 (diff)
downloadFreeBSD-src-2ec7f0d19f7d1d4e02ec98b605a460f015c9f6b5.zip
FreeBSD-src-2ec7f0d19f7d1d4e02ec98b605a460f015c9f6b5.tar.gz
various locking fixes, rework open logic and channel registration
PR: kern/28084
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/sound/pcm/channel.c3
-rw-r--r--sys/dev/sound/pcm/dsp.c265
-rw-r--r--sys/dev/sound/pcm/sound.c181
-rw-r--r--sys/dev/sound/pcm/sound.h7
-rw-r--r--sys/dev/sound/pcm/vchan.c5
5 files changed, 317 insertions, 144 deletions
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 72f2852..b3cfbe4 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 c31d955..52e4f4e 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -64,82 +64,119 @@ 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)
+ /* decide default format */
+ switch (devtype) {
+ case SND_DEV_DSP16:
+ fmt = AFMT_S16_LE;
+ break;
+
+ case SND_DEV_DSP:
+ fmt = AFMT_U8;
+ break;
+
+ case SND_DEV_AUDIO:
+ fmt = AFMT_MU_LAW;
+ break;
+
+ case SND_DEV_NORESET:
+ fmt = 0;
+ break;
+
+ default:
+ panic("impossible devtype %d", devtype);
+ }
+
+ /* 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 ((d->flags & SD_F_SIMPLEX) && (d->arec[chan] || d->aplay[chan]))
+ }
+ 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) {
- rdch = pcm_chnalloc(d, PCMDIR_REC);
- if (!rdch)
+ /* 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;
- rdch->pid = pid;
- } else
+ }
+ /* 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) {
- wrch = pcm_chnalloc(d, PCMDIR_PLAY);
+ /* not already open, try to get a channel */
+ wrch = pcm_chnalloc(d, PCMDIR_PLAY, pid);
if (!wrch) {
- if (rdch && (oflags & FREAD))
- pcm_chnfree(rdch);
+ /* no channel available */
+ if (rdch && (oflags & FREAD)) {
+ /* just opened a read channel, release it */
+ pcm_chnrelease(rdch);
+ }
+ /* exit */
+ snd_mtxunlock(d->lock);
return EBUSY;
}
- wrch->pid = pid;
- } else
+ /* 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;
- }
-
- 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;
+ snd_mtxunlock(d->lock);
+ /* finished with snddev, new channels still locked */
- switch (devtype) {
- case SND_DEV_DSP16:
- fmt = AFMT_S16_LE;
- break;
-
- case SND_DEV_DSP:
- fmt = AFMT_U8;
- break;
-
- case SND_DEV_AUDIO:
- fmt = AFMT_MU_LAW;
- break;
-
- case SND_DEV_NORESET:
- fmt = 0;
- break;
-
- default:
- return ENXIO;
- }
-
- if (rdch && (oflags & FREAD)) {
- chn_reset(rdch, fmt);
- if (oflags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO;
+ /* 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 && (oflags & FWRITE)) {
- chn_reset(wrch, fmt);
- if (oflags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO;
+ 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 7e693e3..3f14d89 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 @@ gotit:
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 9f32183..0ead94f 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 dc5358d..3866ece 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 @@ addok:
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) {
OpenPOWER on IntegriCloud