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
This commit is contained in:
Cameron Grant 2000-06-20 23:27:12 +00:00
parent 04e58856a6
commit e4d5b2502d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=61886
5 changed files with 30 additions and 11 deletions

View File

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

View File

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

View File

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

View File

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

View File

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