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:
parent
0790e5cf47
commit
cc71ce279b
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
};
|
||||
|
||||
|
@ -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!
|
||||
|
@ -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 *);
|
||||
|
Loading…
Reference in New Issue
Block a user