summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorcg <cg@FreeBSD.org>2000-06-20 23:27:12 +0000
committercg <cg@FreeBSD.org>2000-06-20 23:27:12 +0000
commitcc71ce279b47c761c710b6ca373564677dc63303 (patch)
treebf3ab11f1fea9b018349732564de44a049d8d6ef /sys
parent0790e5cf47ed3741eb769f7ab9ef76fdf5a55e02 (diff)
downloadFreeBSD-src-cc71ce279b47c761c710b6ca373564677dc63303.zip
FreeBSD-src-cc71ce279b47c761c710b6ca373564677dc63303.tar.gz
fix a bug where opening for write would not fail if channel allocation failed
when playing, if we stall for 1s with no data advancing, abort and mark the channel dead - fail all future operations
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/sound/pcm/channel.c17
-rw-r--r--sys/dev/sound/pcm/channel.h3
-rw-r--r--sys/dev/sound/pcm/datatypes.h2
-rw-r--r--sys/dev/sound/pcm/dsp.c17
-rw-r--r--sys/dev/sound/pcm/sound.c2
5 files changed, 30 insertions, 11 deletions
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 45568be..9f81751 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -428,7 +428,7 @@ chn_wrintr(pcm_channel *c)
int
chn_write(pcm_channel *c, struct uio *buf)
{
- int ret = 0, timeout, res, newsize;
+ int ret = 0, timeout, res, newsize, count;
long s;
snd_dbuf *b = &c->buffer;
snd_dbuf *bs = &c->buffer2nd;
@@ -460,9 +460,6 @@ chn_write(pcm_channel *c, struct uio *buf)
DEB(printf("pcm warning: frags reset to %d x %d\n", bs->blkcnt, bs->blksz));
}
- /* Store the initial size in the uio. */
- res = buf->uio_resid;
-
/*
* Fill up the secondary and DMA buffer.
* chn_wrfeed*() takes care of the alignment.
@@ -479,12 +476,18 @@ chn_write(pcm_channel *c, struct uio *buf)
chn_start(c);
if (ret == 0) {
+ count = hz;
/* Wait until all samples are played in blocking mode. */
- while (buf->uio_resid > 0) {
+ while ((buf->uio_resid > 0) && (count > 0)) {
/* Check for underflow before writing into the buffers. */
chn_checkunderflow(c);
/* Fill up the buffers with new pcm data. */
+ res = buf->uio_resid;
while (chn_wrfeed2nd(c, buf) > 0);
+ if (buf->uio_resid < res)
+ count = hz;
+ else
+ count--;
/* Have we finished to feed the secondary buffer? */
if (buf->uio_resid == 0)
@@ -499,6 +502,10 @@ chn_write(pcm_channel *c, struct uio *buf)
if (ret == EINTR || ret == ERESTART)
break;
}
+ if (count == 0) {
+ c->flags |= CHN_F_DEAD;
+ device_printf(c->parent->dev, "play interrupt timeout, channel dead\n");
+ }
} else
ret = 0;
c->flags &= ~CHN_F_WRITING;
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
index 0c13b65..23e31df 100644
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -81,9 +81,10 @@ extern pcm_feeder feeder_root;
#define CHN_F_NBIO 0x00004000 /* do non-blocking i/o */
#define CHN_F_INIT 0x00008000 /* changed parameters. need init */
#define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */
+#define CHN_F_DEAD 0x00020000
-#define CHN_F_RESET (CHN_F_BUSY)
+#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD)
/*
* This should be large enough to hold all pcm data between
diff --git a/sys/dev/sound/pcm/datatypes.h b/sys/dev/sound/pcm/datatypes.h
index 67e1923..31df62b 100644
--- a/sys/dev/sound/pcm/datatypes.h
+++ b/sys/dev/sound/pcm/datatypes.h
@@ -121,6 +121,7 @@ struct _pcm_channel {
int direction;
snd_dbuf buffer, buffer2nd;
+ snddev_info *parent;
void *devinfo;
};
@@ -136,6 +137,7 @@ struct _snddev_info {
unsigned flags;
void *devinfo;
pcm_swap_t *swap;
+ device_t dev;
char status[SND_STATUSLEN];
};
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 6dfae6a..903de31 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -41,7 +41,7 @@ allocchn(snddev_info *d, int direction)
pcm_channel *chns = (direction == PCMDIR_PLAY)? d->play : d->rec;
int i, cnt = (direction == PCMDIR_PLAY)? d->playcount : d->reccount;
for (i = 0; i < cnt; i++) {
- if (!(chns[i].flags & CHN_F_BUSY)) {
+ if (!(chns[i].flags & (CHN_F_BUSY | CHN_F_DEAD))) {
chns[i].flags |= CHN_F_BUSY;
return &chns[i];
}
@@ -94,8 +94,9 @@ dsp_open(snddev_info *d, int chan, int oflags, int devtype)
if (oflags & FWRITE) {
if (wrch == NULL) {
wrch = allocchn(d, PCMDIR_PLAY);
- if (!wrch && (oflags & FREAD)) {
- rdch->flags &= ~CHN_F_BUSY;
+ if (!wrch) {
+ if (rdch && (oflags & FREAD))
+ rdch->flags &= ~CHN_F_BUSY;
return EBUSY;
}
} else return EBUSY;
@@ -171,7 +172,7 @@ dsp_read(snddev_info *d, int chan, struct uio *buf, int flag)
getchns(d, chan, &rdch, &wrch);
KASSERT(rdch, ("dsp_read: nonexistant channel"));
KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
- if (rdch->flags & CHN_F_MAPPED) return EINVAL;
+ if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) return EINVAL;
if (!(rdch->flags & CHN_F_RUNNING))
rdch->flags |= CHN_F_RUNNING;
return chn_read(rdch, buf);
@@ -187,7 +188,7 @@ dsp_write(snddev_info *d, int chan, struct uio *buf, int flag)
getchns(d, chan, &rdch, &wrch);
KASSERT(wrch, ("dsp_write: nonexistant channel"));
KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
- if (wrch->flags & CHN_F_MAPPED) return EINVAL;
+ if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) return EINVAL;
if (!(wrch->flags & CHN_F_RUNNING))
wrch->flags |= CHN_F_RUNNING;
return chn_write(wrch, buf);
@@ -203,6 +204,12 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
rdch = d->arec[chan];
wrch = d->aplay[chan];
+ if (rdch && (rdch->flags & CHN_F_DEAD))
+ rdch = NULL;
+ if (wrch && (wrch->flags & CHN_F_DEAD))
+ wrch = NULL;
+ if (!(rdch || wrch))
+ return EINVAL;
/*
* all routines are called with int. blocked. Make sure that
* ints are re-enabled when calling slow or blocking functions!
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
index 0d3a84c..c5c0b6d 100644
--- a/sys/dev/sound/pcm/sound.c
+++ b/sys/dev/sound/pcm/sound.c
@@ -106,6 +106,7 @@ pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo)
}
ch = (dir == PCMDIR_PLAY)? &d->play[d->playcount] : &d->rec[d->reccount];
*ch = *templ;
+ ch->parent = d;
if (chn_init(ch, devinfo, dir)) {
device_printf(dev, "chn_init() for %s:%d failed\n",
(dir == PCMDIR_PLAY)? "play" : "record",
@@ -174,6 +175,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
+ d->dev = dev;
d->devinfo = devinfo;
d->chancount = d->playcount = d->reccount = 0;
sz = (numplay + numrec) * sizeof(pcm_channel *);
OpenPOWER on IntegriCloud