move various fields from pcm_channel to snd_dbuf

improve chn_read code- don't stop/restart on overrun, just dump data

more error checking on ioctls
This commit is contained in:
Cameron Grant 2000-05-26 21:55:13 +00:00
parent 04553e63a5
commit d28089a10d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=60961
3 changed files with 209 additions and 173 deletions

View file

@ -42,6 +42,7 @@ static void buf_clear(snd_dbuf *b, u_int32_t fmt, int length);
static void chn_dmaupdate(pcm_channel *c);
static void chn_wrintr(pcm_channel *c);
static void chn_rdintr(pcm_channel *c);
static u_int32_t chn_start(pcm_channel *c);
/*
* SOUND OUTPUT
@ -115,11 +116,11 @@ static int
chn_polltrigger(pcm_channel *c)
{
snd_dbuf *bs = &c->buffer2nd;
unsigned lim = (c->flags & CHN_F_HAS_SIZE)? c->blocksize2nd : 0;
unsigned lim = (c->flags & CHN_F_HAS_SIZE)? bs->blksz : 0;
int trig = 0;
if (c->flags & CHN_F_MAPPED)
trig = ((bs->int_count > bs->prev_int_count) || bs->first_poll);
trig = ((bs->int_count > bs->prev_int_count) || bs->prev_int_count == 0);
else
trig = (((c->direction == PCMDIR_PLAY)? bs->fl : bs->rl) > lim);
return trig;
@ -130,8 +131,8 @@ chn_pollreset(pcm_channel *c)
{
snd_dbuf *bs = &c->buffer2nd;
if (c->flags & CHN_F_MAPPED) bs->prev_int_count = bs->int_count;
bs->first_poll = 0;
if (c->flags & CHN_F_MAPPED)
bs->prev_int_count = bs->int_count;
return 1;
}
@ -148,7 +149,8 @@ chn_dmadone(pcm_channel *c)
chn_checkunderflow(c);
else
chn_dmaupdate(c);
if (ISA_DMA(b)) chn_isadmabounce(c); /* sync bounce buffer */
if (ISA_DMA(b))
chn_isadmabounce(c); /* sync bounce buffer */
b->int_count++;
}
@ -250,7 +252,7 @@ chn_wrfeed(pcm_channel *c)
a = (1 << c->align) - 1;
lacc = 0;
if (c->flags & CHN_F_MAPPED) {
bs->rl = min(c->blocksize, b->fl);
bs->rl = min(b->blksz, b->fl);
bs->fl = 0;
a = 0;
}
@ -273,20 +275,21 @@ chn_wrfeed(pcm_channel *c)
b->fl -= l;
b->fp = (b->fp + l) % b->bufsize;
/* Clear the new space in the secondary buffer. */
buf_clear(bs, c->hwfmt, l);
buf_clear(bs, bs->fmt, l);
/* Accumulate the total bytes of the moved samples. */
lacc += l;
/* A feed to the DMA buffer is equivalent to an interrupt. */
bs->total += l;
if (c->flags & CHN_F_MAPPED) {
if (bs->total - bs->prev_total >= c->blocksize2nd) {
if (bs->total - bs->prev_total >= bs->blksz) {
bs->prev_total = bs->total;
bs->int_count++;
c->blocks++;
}
} else
bs->int_count++;
if (bs->sel.si_pid && chn_polltrigger(c)) selwakeup(&bs->sel);
if (bs->sel.si_pid && chn_polltrigger(c))
selwakeup(&bs->sel);
}
return lacc;
@ -313,12 +316,12 @@ chn_wrfeed2nd(pcm_channel *c, struct uio *buf)
l = min(bs->fl, bs->bufsize - bs->fp);
/* Move the samples, update the markers and pointers. */
w = c->feeder->feed(c->feeder, c, bs->buf + bs->fp, l, buf);
if (w == 0) panic("no feed");
if (w == 0)
panic("no feed");
bs->rl += w;
bs->fl -= w;
bs->fp = (bs->fp + w) % bs->bufsize;
/* Accumulate the total bytes of the moved samples. */
bs->total += w;
wacc += w;
/* If any pcm data gets moved, push it to the DMA buffer. */
@ -337,13 +340,13 @@ static void
chn_wrintr(pcm_channel *c)
{
snd_dbuf *b = &c->buffer;
int start, l;
if (b->underflow && !(c->flags & CHN_F_MAPPED)) {
/* printf("underflow return\n");
*/ return; /* nothing new happened */
}
if (b->dl) chn_dmadone(c);
if (b->dl)
chn_dmadone(c);
/*
* start another dma operation only if have ready data in the buffer,
@ -354,19 +357,6 @@ chn_wrintr(pcm_channel *c)
* needed when doing stereo and 16-bit.
*/
/*
* Prepare new space of at least c->blocksize in the DMA
* buffer for mmap.
*/
/*
if (c->flags & CHN_F_MAPPED && b->fl < c->blocksize) {
dl = c->blocksize - b->fl;
b->fl += dl;
b->rl -= dl;
b->rp = (b->rp + dl) % b->bufsize;
buf_clear(b, c->hwfmt, dl);
}
*/
/* Check underflow and update the pointers. */
chn_checkunderflow(c);
@ -379,26 +369,18 @@ chn_wrintr(pcm_channel *c)
chn_wrfeed(c);
else {
while (chn_wrfeed(c) > 0);
buf_clear(b, c->hwfmt, b->fl);
buf_clear(b, b->fmt, b->fl);
}
chn_dmawakeup(c);
if (c->flags & CHN_F_MAPPED)
start = c->flags & CHN_F_TRIGGERED;
else {
/* printf("%d >= %d && !(%x & %x)\n", b->rl, DMA_ALIGN_THRESHOLD, c->flags, CHN_F_ABORTING | CHN_F_CLOSING);
*/ start = (b->rl >= DMA_ALIGN_THRESHOLD && !(c->flags & CHN_F_ABORTING));
}
if (start & !(c->flags & CHN_F_NOTRIGGER)) {
if (c->flags & CHN_F_TRIGGERED) {
chn_dmaupdate(c);
if (c->flags & CHN_F_MAPPED) l = c->blocksize;
else l = min(b->rl, c->blocksize) & DMA_ALIGN_MASK;
/*
* check if we need to reprogram the DMA on the sound card.
* This happens if the size has changed from zero
*/
if (b->dl == 0) {
/* Start DMA operation */
b->dl = c->blocksize; /* record new transfer size */
b->dl = b->blksz; /* record new transfer size */
chn_trigger(c, PCMTRIG_START);
}
/*
@ -406,20 +388,22 @@ chn_wrintr(pcm_channel *c)
* the emulated-DMA buffer to the device itself.
*/
chn_trigger(c, PCMTRIG_EMLDMAWR);
if (b->dl != l) {
DEB(printf("near underflow %d, %d, %d\n", l, b->dl, b->fl));
if (b->rl < b->dl) {
DEB(printf("near underflow (%d < %d), %d\n", b->rl, b->dl, b->fl));
/*
* we are near to underflow condition, so to prevent
* audio 'clicks' clear next b->fl bytes
*/
buf_clear(b, c->hwfmt, b->fl);
buf_clear(b, b->fmt, b->fl);
if (b->rl < DMA_ALIGN_THRESHOLD)
b->underflow = 1;
}
} else {
/* cannot start a new dma transfer */
DEB(printf("underflow, flags 0x%08x rp %d rl %d\n", c->flags, b->rp, b->rl));
if (b->dl) { /* DMA was active */
b->underflow = 1; /* set underflow flag */
buf_clear(b, c->hwfmt, b->bufsize);
buf_clear(b, b->fmt, b->bufsize);
}
}
}
@ -465,14 +449,14 @@ chn_write(pcm_channel *c, struct uio *buf)
* resulting partial write. Expand the block size so that
* the write operation avoids blocking.
*/
if ((c->flags & CHN_F_NBIO) && buf->uio_resid > c->blocksize2nd) {
if ((c->flags & CHN_F_NBIO) && buf->uio_resid > bs->blksz) {
DEB(printf("pcm warning: broken app, nbio and tried to write %d bytes with fragsz %d\n",
buf->uio_resid, c->blocksize2nd));
buf->uio_resid, bs->blksz));
newsize = 16;
while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2))
newsize <<= 1;
chn_setblocksize(c, c->fragments, newsize);
DEB(printf("pcm warning: frags reset to %d x %d\n", c->fragments, c->blocksize2nd));
chn_setblocksize(c, bs->blkcnt, newsize);
DEB(printf("pcm warning: frags reset to %d x %d\n", bs->blkcnt, bs->blksz));
}
/* Store the initial size in the uio. */
@ -486,12 +470,12 @@ chn_write(pcm_channel *c, struct uio *buf)
/* Check for underflow before writing into the buffers. */
chn_checkunderflow(c);
while (chn_wrfeed2nd(c, buf) > 0);
if (c->flags & CHN_F_NBIO && buf->uio_resid > 0)
if ((c->flags & CHN_F_NBIO) && (buf->uio_resid > 0))
ret = EAGAIN;
/* Start playing if not yet. */
if ((bs->rl || b->rl) && !b->dl)
chn_intr(c);
if (!b->dl)
chn_start(c);
if (ret == 0) {
/* Wait until all samples are played in blocking mode. */
@ -501,19 +485,15 @@ chn_write(pcm_channel *c, struct uio *buf)
/* Fill up the buffers with new pcm data. */
while (chn_wrfeed2nd(c, buf) > 0);
/* Start playing if necessary. */
if ((bs->rl || b->rl) && !b->dl)
chn_intr(c);
/* Have we finished to feed the secondary buffer? */
if (buf->uio_resid == 0)
break;
/* Wait for new free space to write new pcm samples. */
splx(s);
/* splx(s); */
timeout = 1; /*(buf->uio_resid >= b->dl)? hz / 20 : 1; */
ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout);
s = spltty();
/* s = spltty(); */
/* if (ret == EINTR) chn_abort(c); */
if (ret == EINTR || ret == ERESTART)
break;
@ -555,6 +535,17 @@ rec_blocksize, and fallback to smaller sizes if no space is available.
*/
static int
chn_rddump(pcm_channel *c, int cnt)
{
snd_dbuf *b = &c->buffer;
b->rl -= cnt;
b->fl += cnt;
b->rp = (b->rp + cnt) % b->bufsize;
return cnt;
}
/*
* Feed new data from the read buffer. Can be called in the bottom half.
* Hence must be called at spltty.
@ -578,13 +569,12 @@ chn_rdfeed(pcm_channel *c)
b->rl -= l;
b->fl += l;
b->rp = (b->rp + l) % b->bufsize;
/* Clear the new space in the DMA buffer. */
buf_clear(b, c->hwfmt, l);
/* Accumulate the total bytes of the moved samples. */
lacc += l;
/* A feed from the DMA buffer is equivalent to an interrupt. */
bs->int_count++;
if (bs->sel.si_pid && chn_polltrigger(c)) selwakeup(&bs->sel);
if (bs->sel.si_pid && chn_polltrigger(c))
selwakeup(&bs->sel);
}
return lacc;
@ -616,7 +606,7 @@ chn_rdfeed2nd(pcm_channel *c, struct uio *buf)
bs->rl -= w;
bs->rp = (bs->rp + w) % bs->bufsize;
/* Clear the new space in the secondary buffer. */
buf_clear(bs, c->hwfmt, l);
buf_clear(bs, bs->fmt, l);
/* Accumulate the total bytes of the moved samples. */
bs->total += w;
wacc += w;
@ -634,26 +624,11 @@ static void
chn_rdintr(pcm_channel *c)
{
snd_dbuf *b = &c->buffer;
snd_dbuf *bs = &c->buffer2nd;
int start, dl;
if (b->dl) chn_dmadone(c);
DEB(printf("rdintr: start dl %d, rp:rl %d:%d, fp:fl %d:%d\n",
b->dl, b->rp, b->rl, b->fp, b->fl));
/* Restart if have enough free space to absorb overruns */
/*
* Prepare new space of at least c->blocksize in the secondary
* buffer for mmap.
*/
if (c->flags & CHN_F_MAPPED && bs->fl < c->blocksize) {
dl = c->blocksize - bs->fl;
bs->fl += dl;
bs->rl -= dl;
bs->rp = (bs->rp + dl) % bs->bufsize;
buf_clear(bs, c->hwfmt, dl);
}
/* Update the pointers. */
chn_dmaupdate(c);
@ -665,27 +640,27 @@ chn_rdintr(pcm_channel *c)
*/
while(chn_rdfeed(c) > 0);
chn_dmawakeup(c);
if (c->flags & CHN_F_MAPPED)
start = c->flags & CHN_F_TRIGGERED;
else
start = (b->fl > 0x200 && !(c->flags & CHN_F_ABORTING));
if (start) {
int l = min(b->fl - 0x100, c->blocksize);
if (c->flags & CHN_F_MAPPED) l = c->blocksize;
l &= DMA_ALIGN_MASK ; /* realign sizes */
DEB(printf("rdintr: dl %d -> %d\n", b->dl, l);)
if (l != b->dl) {
/* size has changed. Stop and restart */
if (b->dl) {
chn_trigger(c, PCMTRIG_STOP);
chn_dmaupdate(c);
l = min(b->fl - 0x100, c->blocksize);
l &= DMA_ALIGN_MASK ; /* realign sizes */
}
b->dl = l;
if (b->fl < b->dl) {
DEB(printf("near overflow (%d < %d), %d\n", b->fl, b->dl, b->rl));
chn_rddump(c, b->blksz - b->fl);
}
if (c->flags & CHN_F_TRIGGERED) {
/*
* check if we need to reprogram the DMA on the sound card.
* This happens if the size has changed from zero
*/
if (b->dl == 0) {
/* Start DMA operation */
b->dl = b->blksz; /* record new transfer size */
chn_trigger(c, PCMTRIG_START);
}
/*
* Emulate writing by DMA, i.e. transfer the pcm data from
* the emulated-DMA buffer to the device itself.
*/
chn_trigger(c, PCMTRIG_EMLDMARD);
} else {
if (b->dl) { /* was active */
b->dl = 0;
@ -734,15 +709,17 @@ chn_read(pcm_channel *c, struct uio *buf)
c->flags |= CHN_F_READING;
c->flags &= ~CHN_F_ABORTING;
limit = buf->uio_resid - c->blocksize;
if (limit < 0) limit = 0;
limit = buf->uio_resid - b->blksz;
if (limit < 0)
limit = 0;
/* Update the pointers and suck up the DMA and secondary buffers. */
chn_dmaupdate(c);
while (chn_rdfeed2nd(c, buf) > 0);
/* Start capturing if not yet. */
if ((!bs->rl || !b->rl) && !b->dl) chn_intr(c);
if ((!bs->rl || !b->rl) && !b->dl)
chn_start(c);
if (!(c->flags & CHN_F_NBIO)) {
/* Wait until all samples are captured. */
@ -751,20 +728,18 @@ chn_read(pcm_channel *c, struct uio *buf)
chn_dmaupdate(c);
while (chn_rdfeed2nd(c, buf) > 0);
/* Start capturing if necessary. */
if ((!bs->rl || !b->rl) && !b->dl) chn_intr(c);
/* Have we finished to feed the uio? */
if (buf->uio_resid == 0)
break;
/* Wait for new pcm samples. */
splx(s);
/* splx(s); */
timeout = (buf->uio_resid - limit >= b->dl)? hz / 20 : 1;
ret = tsleep(b, PRIBIO | PCATCH, "pcmrd", timeout);
s = spltty();
if (ret == EINTR) chn_abort(c);
if (ret == EINTR || ret == ERESTART) break;
/* s = spltty(); */
/* if (ret == EINTR) chn_abort(c); */
if (ret == EINTR || ret == ERESTART)
break;
}
} else {
/* If no pcm data was read on nonblocking, return EAGAIN. */
@ -787,6 +762,33 @@ chn_intr(pcm_channel *c)
chn_rdintr(c);
}
u_int32_t
chn_start(pcm_channel *c)
{
u_int32_t r, s;
snd_dbuf *b = &c->buffer;
r = 0;
s = spltty();
if (b->dl == 0 && !(c->flags & (CHN_F_MAPPED | CHN_F_NOTRIGGER))) {
if (c->direction == PCMDIR_PLAY) {
/* Fill up the DMA buffer. */
while (chn_wrfeed(c) > 0);
if (b->rl >= b->blksz)
r = CHN_F_TRIGGERED;
} else {
/* Suck up the DMA buffer. */
while (chn_rdfeed(c) > 0);
if (b->fl >= b->blksz)
r = CHN_F_TRIGGERED;
}
c->flags |= r;
chn_intr(c);
}
splx(s);
return r;
}
static void
chn_dma_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
@ -857,29 +859,23 @@ chn_resetbuf(pcm_channel *c)
snd_dbuf *bs = &c->buffer2nd;
c->blocks = 0;
b->sample_size = 1;
b->sample_size <<= (c->hwfmt & AFMT_STEREO)? 1 : 0;
b->sample_size <<= (c->hwfmt & AFMT_16BIT)? 1 : 0;
b->rp = b->fp = 0;
b->dl = b->rl = 0;
b->fl = b->bufsize;
b->prev_total = b->total = 0;
b->prev_int_count = b->int_count = 0;
b->first_poll = 1;
b->underflow = 0;
if (b->buf && b->bufsize > 0)
buf_clear(b, c->hwfmt, b->bufsize);
buf_clear(b, b->fmt, b->bufsize);
bs->rp = bs->fp = 0;
bs->dl = bs->rl = 0;
bs->fl = bs->bufsize;
bs->prev_total = bs->total = 0;
b->prev_int_count = b->int_count = 0;
b->first_poll = 1;
b->underflow = 0;
bs->prev_int_count = bs->int_count = 0;
bs->underflow = 0;
if (bs->buf && bs->bufsize > 0)
buf_clear(bs, c->hwfmt, bs->bufsize);
buf_clear(bs, bs->fmt, bs->bufsize);
}
void
@ -908,7 +904,8 @@ buf_isadmaptr(snd_dbuf *b)
{
if (ISA_DMA(b)) {
int i = b->dl? isa_dmastatus(b->chan) : b->bufsize;
if (i < 0) i = 0;
if (i < 0)
i = 0;
return b->bufsize - i;
} else KASSERT(1, ("buf_isadmaptr called on invalid channel"));
return -1;
@ -965,7 +962,7 @@ chn_poll(pcm_channel *c, int ev, struct proc *p)
while (chn_rdfeed(c) > 0);
}
if (!b->dl)
chn_intr(c);
chn_start(c);
}
ret = 0;
if (chn_polltrigger(c) && chn_pollreset(c))
@ -990,6 +987,7 @@ chn_abort(pcm_channel *c)
if (!b->dl) return 0;
c->flags |= CHN_F_ABORTING;
c->flags &= ~CHN_F_TRIGGERED;
while (!b->underflow && (b->dl > 0) && (cnt < 20)) {
tsleep((caddr_t)b, PRIBIO, "pcmabr", hz / 20);
cnt++;
@ -1038,14 +1036,15 @@ chn_flush(pcm_channel *c)
if (count == 0)
DEB(printf("chn_flush: timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n", b->rl, c->flags));
c->flags &= ~CHN_F_CLOSING;
if (c->direction == PCMDIR_PLAY && b->dl) chn_abort(c);
if (c->direction == PCMDIR_PLAY && b->dl)
chn_abort(c);
return 0;
}
int
chn_reset(pcm_channel *c, u_int32_t fmt)
{
int r;
int r = 0;
chn_abort(c);
c->flags &= CHN_F_RESET;
@ -1053,12 +1052,14 @@ chn_reset(pcm_channel *c, u_int32_t fmt)
if (r)
return r;
if (fmt) {
c->format = fmt;
c->speed = DSP_DEFAULT_SPEED;
c->volume = (100 << 8) | 100;
r = chn_setformat(c, fmt);
if (r == 0)
r = chn_setspeed(c, DSP_DEFAULT_SPEED);
if (r == 0)
r = chn_setvolume(c, 100, 100);
}
chn_resetbuf(c);
c->flags |= CHN_F_INIT;
/* c->flags |= CHN_F_INIT; */
return 0;
}
@ -1112,7 +1113,8 @@ chn_setvolume(pcm_channel *c, int left, int right)
{
/* could add a feeder for volume changing if channel returns -1 */
if (CANCHANGE(c)) {
return -1;
c->volume = (left << 8) | right;
return 0;
}
c->volume = (left << 8) | right;
c->flags |= CHN_F_INIT;
@ -1122,10 +1124,12 @@ chn_setvolume(pcm_channel *c, int left, int right)
int
chn_setspeed(pcm_channel *c, int speed)
{
if (speed <= 0)
return EINVAL;
/* could add a feeder for rate conversion */
if (CANCHANGE(c)) {
c->speed = c->setspeed(c->devinfo, speed);
return c->speed;
return 0;
}
c->speed = speed;
c->flags |= CHN_F_INIT;
@ -1135,12 +1139,20 @@ chn_setspeed(pcm_channel *c, int speed)
int
chn_setformat(pcm_channel *c, u_int32_t fmt)
{
snd_dbuf *b = &c->buffer;
snd_dbuf *bs = &c->buffer2nd;
u_int32_t hwfmt;
if (CANCHANGE(c)) {
c->hwfmt = c->format = fmt;
c->hwfmt = chn_feedchain(c);
c->format = fmt;
hwfmt = chn_feedchain(c);
if ((c->flags & CHN_F_MAPPED) && c->format != hwfmt)
return EINVAL;
b->fmt = hwfmt;
bs->fmt = hwfmt;
chn_resetbuf(c);
c->setformat(c->devinfo, c->hwfmt);
return fmt;
c->setformat(c->devinfo, hwfmt);
return 0;
}
c->format = fmt;
c->flags |= CHN_F_INIT;
@ -1150,16 +1162,22 @@ chn_setformat(pcm_channel *c, u_int32_t fmt)
int
chn_setblocksize(pcm_channel *c, int blkcnt, int blksz)
{
snd_dbuf *b = &c->buffer;
snd_dbuf *bs = &c->buffer2nd;
int s, bufsz;
int s, ss, bufsz;
if (c->fragments == blkcnt && c->blocksize2nd == blksz)
if (bs->blkcnt == blkcnt && bs->blksz == blksz)
return 0;
if (c->flags & CHN_F_MAPPED) {
DEB(printf("chn_setblocksize: can't work on mapped channel"));
return EINVAL;
}
c->flags &= ~CHN_F_HAS_SIZE;
ss = 1;
ss <<= (bs->fmt & AFMT_STEREO)? 1 : 0;
ss <<= (bs->fmt & AFMT_16BIT)? 1 : 0;
if (blksz >= 2)
c->flags |= CHN_F_HAS_SIZE;
/* let us specify blksz without setting CHN_F_HAS_SIZE */
@ -1167,7 +1185,7 @@ chn_setblocksize(pcm_channel *c, int blkcnt, int blksz)
blksz = -blksz;
/* default to blksz = ~0.25s */
if (blksz < 16)
blksz = (c->buffer.sample_size * c->speed) >> 2;
blksz = (ss * c->speed) >> 2;
if (blkcnt * blksz > CHN_2NDBUFMAXSIZE)
blkcnt = CHN_2NDBUFMAXSIZE / blksz;
@ -1185,11 +1203,11 @@ chn_setblocksize(pcm_channel *c, int blkcnt, int blksz)
bs->bufsize = bufsz;
bs->rl = bs->rp = bs->fp = 0;
bs->fl = bs->bufsize;
buf_clear(bs, c->hwfmt, bs->bufsize);
c->fragments = blkcnt;
c->blocksize2nd = blksz;
RANGE(blksz, 16, c->buffer.bufsize / 2);
c->blocksize = c->setblocksize(c->devinfo, blksz);
buf_clear(bs, bs->fmt, bs->bufsize);
bs->blkcnt = blkcnt;
bs->blksz = blksz;
RANGE(blksz, 16, b->bufsize / 2);
b->blksz = c->setblocksize(c->devinfo, blksz);
splx(s);
return 0;

View file

@ -60,18 +60,16 @@ struct _snd_mixer {
struct _snd_dbuf {
u_int8_t *buf;
int bufsize;
volatile int rp, fp; /* pointers to the ready and free area */
volatile int dl; /* transfer size */
volatile int rp, fp; /* pointers to the ready and free area */
volatile int rl, fl; /* lenght of ready and free areas. */
volatile u_int32_t int_count, prev_int_count;
volatile u_int32_t total, prev_total;
int chan, dir; /* dma channel */
int sample_size; /* 1, 2, 4 */
struct selinfo sel;
u_long total; /* total bytes processed */
u_long prev_total; /* copy of the above when GETxPTR called */
int first_poll;
bus_dmamap_t dmamap;
int fmt, blksz, blkcnt;
int underflow;
bus_dmamap_t dmamap;
struct selinfo sel;
};
typedef int (pcmfeed_init_t)(pcm_feeder *feeder);
@ -118,9 +116,8 @@ struct _pcm_channel {
int volume;
u_int32_t speed;
u_int32_t flags;
u_int32_t format, hwfmt;
u_int32_t blocksize, blocksize2nd;
u_int32_t fragments, blocks;
u_int32_t format;
u_int32_t blocks;
int direction;
snd_dbuf buffer, buffer2nd;

View file

@ -227,8 +227,8 @@ dsp_ioctl(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 = wrch->blocksize2nd;
if (rdch) p->rec_size = rdch->blocksize2nd;
if (wrch) p->play_size = wrch->buffer2nd.blksz;
if (rdch) p->rec_size = rdch->buffer2nd.blksz;
}
break;
@ -321,9 +321,9 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
case SNDCTL_DSP_GETBLKSIZE:
if (wrch)
*arg_i = wrch->blocksize2nd;
*arg_i = wrch->buffer2nd.blksz;
else if (rdch)
*arg_i = rdch->blocksize2nd;
*arg_i = rdch->buffer2nd.blksz;
else
*arg_i = 0;
break ;
@ -349,8 +349,10 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
case SNDCTL_DSP_SPEED:
splx(s);
if (wrch) chn_setspeed(wrch, *arg_i);
if (rdch) chn_setspeed(rdch, *arg_i);
if (wrch)
ret = chn_setspeed(wrch, *arg_i);
if (rdch && ret == 0)
ret = chn_setspeed(rdch, *arg_i);
/* fallthru */
case SOUND_PCM_READ_RATE:
@ -359,20 +361,29 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
case SNDCTL_DSP_STEREO:
splx(s);
if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
if (wrch)
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
((*arg_i)? AFMT_STEREO : 0));
if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
if (rdch && ret == 0)
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
((*arg_i)? AFMT_STEREO : 0));
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 1 : 0;
break;
case SOUND_PCM_WRITE_CHANNELS:
/* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
splx(s);
if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
if (*arg_i == 1 || *arg_i == 2) {
if (wrch)
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
((*arg_i == 2)? AFMT_STEREO : 0));
if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
((*arg_i == 2)? AFMT_STEREO : 0));
/* fallthru */
if (rdch && ret == 0)
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
((*arg_i == 2)? AFMT_STEREO : 0));
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 1 : 0;
} else
*arg_i = 0;
break;
case SOUND_PCM_READ_CHANNELS:
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
@ -384,18 +395,19 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
case SNDCTL_DSP_SETFMT: /* sets _one_ format */
splx(s);
if (wrch) chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
if (rdch) chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
if (wrch)
ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
if (rdch && ret == 0)
ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
*arg_i = (wrch? wrch->format: rdch->format) & ~AFMT_STEREO;
break;
case SNDCTL_DSP_SUBDIVIDE:
/* XXX watch out, this is RW! */
DEB(printf("SNDCTL_DSP_SUBDIVIDE unimplemented\n");)
printf("SNDCTL_DSP_SUBDIVIDE unimplemented\n");
break;
case SNDCTL_DSP_SETFRAGMENT:
/* XXX watch out, this is RW! */
DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
{
pcm_channel *c = wrch? wrch : rdch;
@ -421,7 +433,13 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
if (wrch && ret == 0)
ret = chn_setblocksize(wrch, maxfrags, fragsz);
*arg_i = (c->fragments << 16) | c->blocksize;
fragsz = c->buffer2nd.blksz;
fragln = 0;
while (fragsz > 1) {
fragln++;
fragsz >>= 1;
}
*arg_i = (c->buffer2nd.blkcnt << 16) | fragln;
}
break;
@ -439,9 +457,9 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
*/
while (chn_rdfeed(rdch) > 0);
a->bytes = bs->rl;
a->fragments = a->bytes / rdch->blocksize2nd;
a->fragstotal = rdch->fragments;
a->fragsize = rdch->blocksize2nd;
a->fragments = a->bytes / rdch->buffer2nd.blksz;
a->fragstotal = rdch->buffer2nd.blkcnt;
a->fragsize = rdch->buffer2nd.blksz;
}
}
break;
@ -463,9 +481,9 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
while (chn_wrfeed(wrch) > 0);
}
a->bytes = bs->fl;
a->fragments = a->bytes / wrch->blocksize2nd;
a->fragstotal = wrch->fragments;
a->fragsize = wrch->blocksize2nd;
a->fragments = a->bytes / wrch->buffer2nd.blksz;
a->fragstotal = wrch->buffer2nd.blkcnt;
a->fragsize = wrch->buffer2nd.blksz;
}
}
break;
@ -483,8 +501,9 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
*/
while (chn_rdfeed(rdch) > 0);
a->bytes = bs->total;
a->blocks = bs->rl / rdch->blocksize2nd;
a->ptr = bs->rl % rdch->blocksize2nd;
a->blocks = rdch->blocks;
a->ptr = bs->rp;
rdch->blocks = 0;
} else ret = EINVAL;
}
break;
@ -601,12 +620,14 @@ dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot)
getchns(d, chan, &rdch, &wrch);
/* XXX this is broken by line 204 of vm/device_pager.c, so force write buffer */
if (1 || (wrch && (nprot & PROT_WRITE))) c = wrch;
else if (rdch && (nprot & PROT_READ)) c = rdch;
if (c) {
if (1 || (wrch && (nprot & PROT_WRITE)))
c = wrch;
else if (rdch && (nprot & PROT_READ))
c = rdch;
if (c && (c->format == c->buffer.fmt)) {
c->flags |= CHN_F_MAPPED;
return atop(vtophys(c->buffer2nd.buf + offset));
}
return -1;
} else
return -1;
}