diff options
-rw-r--r-- | sys/dev/sound/isa/ad1816.c | 1 | ||||
-rw-r--r-- | sys/dev/sound/isa/mss.c | 1 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb.c | 2 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb16.c | 2 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb8.c | 2 | ||||
-rw-r--r-- | sys/dev/sound/pci/aureal.c | 1 | ||||
-rw-r--r-- | sys/dev/sound/pci/csapcm.c | 1 | ||||
-rw-r--r-- | sys/dev/sound/pci/es137x.c | 3 | ||||
-rw-r--r-- | sys/dev/sound/pci/t4dwave.c | 1 | ||||
-rw-r--r-- | sys/dev/sound/pcm/channel.c | 501 | ||||
-rw-r--r-- | sys/dev/sound/pcm/channel.h | 11 | ||||
-rw-r--r-- | sys/dev/sound/pcm/datatypes.h | 2 | ||||
-rw-r--r-- | sys/dev/sound/pcm/dsp.c | 152 | ||||
-rw-r--r-- | sys/dev/sound/pcm/sound.h | 6 |
14 files changed, 486 insertions, 200 deletions
diff --git a/sys/dev/sound/isa/ad1816.c b/sys/dev/sound/isa/ad1816.c index 94e2936..e1bc75e 100644 --- a/sys/dev/sound/isa/ad1816.c +++ b/sys/dev/sound/isa/ad1816.c @@ -403,6 +403,7 @@ ad1816chan_trigger(void *data, int go) struct ad1816_info *ad1816 = ch->parent; int wr, reg; + if (go == PCMTRIG_EMLDMAWR) return 0; buf_isadma(ch->buffer, go); wr = (ch->dir == PCMDIR_PLAY); reg = wr? AD1816_PLAY : AD1816_CAPT; diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c index d812bc9..05dabc3 100644 --- a/sys/dev/sound/isa/mss.c +++ b/sys/dev/sound/isa/mss.c @@ -1720,6 +1720,7 @@ msschan_trigger(void *data, int go) { struct mss_chinfo *ch = data; + if (go == PCMTRIG_EMLDMAWR) return 0; buf_isadma(ch->buffer, go); mss_trigger(ch, go); return 0; diff --git a/sys/dev/sound/isa/sb.c b/sys/dev/sound/isa/sb.c index 734788e..7a0c1f1 100644 --- a/sys/dev/sound/isa/sb.c +++ b/sys/dev/sound/isa/sb.c @@ -860,6 +860,7 @@ static int sbchan_trigger(void *data, int go) { struct sb_chinfo *ch = data; + if (go == PCMTRIG_EMLDMAWR) return 0; buf_isadma(ch->buffer, go); if (go == PCMTRIG_START) sb_start(ch); else sb_stop(ch); return 0; @@ -928,6 +929,7 @@ static int esschan_trigger(void *data, int go) { struct sb_chinfo *ch = data; + if (go == PCMTRIG_EMLDMAWR) return 0; switch (go) { case PCMTRIG_START: if (!ch->ess_dma_started) diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c index 734788e..7a0c1f1 100644 --- a/sys/dev/sound/isa/sb16.c +++ b/sys/dev/sound/isa/sb16.c @@ -860,6 +860,7 @@ static int sbchan_trigger(void *data, int go) { struct sb_chinfo *ch = data; + if (go == PCMTRIG_EMLDMAWR) return 0; buf_isadma(ch->buffer, go); if (go == PCMTRIG_START) sb_start(ch); else sb_stop(ch); return 0; @@ -928,6 +929,7 @@ static int esschan_trigger(void *data, int go) { struct sb_chinfo *ch = data; + if (go == PCMTRIG_EMLDMAWR) return 0; switch (go) { case PCMTRIG_START: if (!ch->ess_dma_started) diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c index 734788e..7a0c1f1 100644 --- a/sys/dev/sound/isa/sb8.c +++ b/sys/dev/sound/isa/sb8.c @@ -860,6 +860,7 @@ static int sbchan_trigger(void *data, int go) { struct sb_chinfo *ch = data; + if (go == PCMTRIG_EMLDMAWR) return 0; buf_isadma(ch->buffer, go); if (go == PCMTRIG_START) sb_start(ch); else sb_stop(ch); return 0; @@ -928,6 +929,7 @@ static int esschan_trigger(void *data, int go) { struct sb_chinfo *ch = data; + if (go == PCMTRIG_EMLDMAWR) return 0; switch (go) { case PCMTRIG_START: if (!ch->ess_dma_started) diff --git a/sys/dev/sound/pci/aureal.c b/sys/dev/sound/pci/aureal.c index 5c8e122..1be8364 100644 --- a/sys/dev/sound/pci/aureal.c +++ b/sys/dev/sound/pci/aureal.c @@ -353,6 +353,7 @@ auchan_trigger(void *data, int go) { struct au_chinfo *ch = data; struct au_info *au = ch->parent; + if (go == PCMTRIG_EMLDMAWR) return 0; if (ch->dir == PCMDIR_PLAY) { au_setadb(au, 0x11, (go)? 1 : 0); if (!go) { diff --git a/sys/dev/sound/pci/csapcm.c b/sys/dev/sound/pci/csapcm.c index f2b37c0..b67f2c9 100644 --- a/sys/dev/sound/pci/csapcm.c +++ b/sys/dev/sound/pci/csapcm.c @@ -386,6 +386,7 @@ csachan_trigger(void *data, int go) struct csa_chinfo *ch = data; struct csa_info *csa = ch->parent; + if (go == PCMTRIG_EMLDMAWR) return 0; if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) csa_startplaydma(csa); diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c index f525d92..1573332 100644 --- a/sys/dev/sound/pci/es137x.c +++ b/sys/dev/sound/pci/es137x.c @@ -367,6 +367,7 @@ eschan_trigger(void *data, int go) struct es_info *es = ch->parent; unsigned cnt = ch->buffer->dl / ch->buffer->sample_size - 1; + if (go == PCMTRIG_EMLDMAWR) return 0; if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) { int b = (ch->fmt & AFMT_S16_LE)? 2 : 1; @@ -479,7 +480,7 @@ es1371_init(struct es_info *es) /* AC'97 warm reset to start the bitclk */ bus_space_write_4(es->st, es->sh, ES1371_REG_LEGACY, es->ctrl | ES1371_SYNC_RES); DELAY(2000); - bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL,es->ctrl); + bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->ctrl); /* Init the sample rate converter */ bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, ES1371_DIS_SRC); for (idx = 0; idx < 0x80; idx++) diff --git a/sys/dev/sound/pci/t4dwave.c b/sys/dev/sound/pci/t4dwave.c index 7539da7..c5c7fbb 100644 --- a/sys/dev/sound/pci/t4dwave.c +++ b/sys/dev/sound/pci/t4dwave.c @@ -490,6 +490,7 @@ trchan_trigger(void *data, int go) { struct tr_chinfo *ch = data; struct tr_info *tr = ch->parent; + if (go == PCMTRIG_EMLDMAWR) return 0; if (ch->index >= 0) { if (go == PCMTRIG_START) tr_startch(tr, ch->index); else tr_stopch(tr, ch->index); diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c index b74efd9..dc63bd5 100644 --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -35,8 +35,13 @@ #define ISA_DMA(b) (((b)->chan >= 0 && (b)->chan != 4 && (b)->chan < 8)) #define CANCHANGE(c) (!(c)->buffer.dl) - -static void chn_clearbuf(pcm_channel *c, int length); +/* +#define DEB(x) x +*/ +static void chn_clearbuf(pcm_channel *c, snd_dbuf *b, int length); +static void chn_dmaupdate(pcm_channel *c); +static void chn_wrintr(pcm_channel *c); +static void chn_rdintr(pcm_channel *c); /* * SOUND OUTPUT @@ -109,23 +114,23 @@ chn_isadmabounce(pcm_channel *c) static int chn_polltrigger(pcm_channel *c) { - snd_dbuf *b = &c->buffer; - unsigned lim = (c->flags & CHN_F_HAS_SIZE)? c->blocksize : 1; + snd_dbuf *bs = &c->buffer2nd; + unsigned lim = (c->flags & CHN_F_HAS_SIZE)? c->blocksize2nd : 1; int trig = 0; if (c->flags & CHN_F_MAPPED) - trig = ((b->int_count > b->prev_int_count) || b->first_poll); - else trig = (((c->direction == PCMDIR_PLAY)? b->fl : b->rl) >= lim); + trig = ((bs->int_count > bs->prev_int_count) || bs->first_poll); + else trig = (((c->direction == PCMDIR_PLAY)? bs->rl : bs->fl) < lim); return trig; } static int chn_pollreset(pcm_channel *c) { - snd_dbuf *b = &c->buffer; + snd_dbuf *bs = &c->buffer; - if (c->flags & CHN_F_MAPPED) b->prev_int_count = b->int_count; - b->first_poll = 0; + if (c->flags & CHN_F_MAPPED) bs->prev_int_count = bs->int_count; + bs->first_poll = 0; return 1; } @@ -138,15 +143,17 @@ chn_dmadone(pcm_channel *c) { snd_dbuf *b = &c->buffer; - chn_dmaupdate(c); + if (c->direction == PCMDIR_PLAY) + chn_checkunderflow(c); + else + chn_dmaupdate(c); if (ISA_DMA(b)) chn_isadmabounce(c); /* sync bounce buffer */ b->int_count++; - if (b->sel.si_pid && chn_polltrigger(c)) selwakeup(&b->sel); } /* * chn_dmawakeup() wakes up any process sleeping. Separated from - * chn_dma_done() so that wakeup occurs only when feed from a + * chn_dmadone() so that wakeup occurs only when feed from a * secondary buffer to a DMA buffer takes place. Must be called * at spltty(). */ @@ -167,7 +174,7 @@ chn_dmawakeup(pcm_channel *c) */ DEB (static int chn_updatecount=0); -void +static void chn_dmaupdate(pcm_channel *c) { snd_dbuf *b = &c->buffer; @@ -180,13 +187,18 @@ chn_dmaupdate(pcm_channel *c) b->rp = hwptr; b->rl -= delta; b->fl += delta; - DEB(if (b->rl<0) printf("OUCH!(%d) rl %d(%d) delta %d bufsize %d hwptr %d rp %d(%d)\n", chn_updatecount++, b->rl, b_rl, delta, b->bufsize, hwptr, b->rp, b_rp)); + + if (b->rl < 0) { + DEB(printf("OUCH!(%d) rl %d(%d) delta %d bufsize %d hwptr %d rp %d(%d)\n", chn_updatecount++, b->rl, b_rl, delta, b->bufsize, hwptr, b->rp, b_rp)); + } } else { delta = (b->bufsize + hwptr - b->fp) % b->bufsize; b->fp = hwptr; b->rl += delta; b->fl -= delta; - DEB(if (b->fl<0) printf("OUCH!(%d) fl %d(%d) delta %d bufsize %d hwptr %d fp %d(%d)\n", chn_updatecount++, b->fl, b_fl, delta, b->bufsize, hwptr, b->fp, b_fp)); + if (b->fl < 0) { + DEB(printf("OUCH!(%d) fl %d(%d) delta %d bufsize %d hwptr %d fp %d(%d)\n", chn_updatecount++, b->fl, b_fl, delta, b->bufsize, hwptr, b->fp, b_fp)); + } } b->total += delta; } @@ -196,7 +208,7 @@ chn_dmaupdate(pcm_channel *c) * underflow, so that new data can go into the buffer. It must be * called at spltty(). */ -static void +void chn_checkunderflow(pcm_channel *c) { snd_dbuf *b = &c->buffer; @@ -216,7 +228,7 @@ chn_checkunderflow(pcm_channel *c) b->fp = (b->rp + b->bufsize / 4) % b->bufsize; b->rl = b->bufsize / 4; b->fl = b->bufsize - b->rl; - b->underflow=0; + b->underflow = 0; } else { chn_dmaupdate(c); } @@ -226,7 +238,7 @@ chn_checkunderflow(pcm_channel *c) * Feeds new data to the write dma buffer. Can be called in the bottom half. * Hence must be called at spltty. */ -static int +int chn_wrfeed(pcm_channel *c) { snd_dbuf *b = &c->buffer; @@ -250,8 +262,13 @@ chn_wrfeed(pcm_channel *c) b->rl += l; b->fl -= l; b->fp = (b->fp + l) % b->bufsize; + /* Clear the new space in the secondary buffer. */ + chn_clearbuf(c, bs, l); /* Accumulate the total bytes of the moved samples. */ lacc += l; + /* A feed to the DMA buffer is equivalent to an interrupt. */ + bs->int_count++; + if (bs->sel.si_pid && chn_polltrigger(c)) selwakeup(&bs->sel); } return lacc; @@ -264,6 +281,9 @@ chn_wrfeed2nd(pcm_channel *c, struct uio *buf) snd_dbuf *bs = &c->buffer2nd; int l, w, wacc; + /* The DMA buffer may have some space. */ + while (chn_wrfeed(c) > 0); + /* ensure we always have a whole number of samples */ wacc = 0; while (buf->uio_resid > 0 && bs->fl > 0) { @@ -280,7 +300,12 @@ chn_wrfeed2nd(pcm_channel *c, struct uio *buf) 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. */ + if (w > 0) + while (chn_wrfeed(c) > 0); } return wacc; @@ -294,34 +319,52 @@ static void chn_wrintr(pcm_channel *c) { snd_dbuf *b = &c->buffer; - int start; + int start, dl, l; - if (b->underflow && !(c->flags & CHN_F_MAPPED)) return; /* nothing new happened */ + if (b->underflow && !(c->flags & CHN_F_MAPPED)) { +/* printf("underflow return\n"); +*/ return; /* nothing new happened */ + } if (b->dl) chn_dmadone(c); /* - * start another dma operation only if have ready data in the buffer, - * there is no pending abort, have a full-duplex device, or have a - * half duplex device and there is no pending op on the other side. - * - * Force transfers to be aligned to a boundary of 4, which is - * needed when doing stereo and 16-bit. - */ - if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED; - else { - /* - * Fill up the DMA buffer. This may result in making new - * free space in the secondary buffer, thus we can wake up - * the top half if feed occurs. - */ - if (chn_wrfeed(c) > 0) { - chn_dmawakeup(c); - while (chn_wrfeed(c) > 0); - } - start = (b->rl >= DMA_ALIGN_THRESHOLD && !(c->flags & CHN_F_ABORTING)); + * start another dma operation only if have ready data in the buffer, + * there is no pending abort, have a full-duplex device, or have a + * half duplex device and there is no pending op on the other side. + * + * Force transfers to be aligned to a boundary of 4, which is + * 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; + chn_clearbuf(c, b, dl); + } + + /* Check underflow and update the pointers. */ + chn_checkunderflow(c); + + /* + * Fill up the DMA buffer, followed by waking up the top half. + * If some of the pcm data in uio are still left, the top half + * goes to sleep by itself. + */ + while (chn_wrfeed(c) > 0); + 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 | CHN_F_CLOSING))); } if (start) { - int l; chn_dmaupdate(c); if (c->flags & CHN_F_MAPPED) l = c->blocksize; else l = min(b->rl, c->blocksize) & DMA_ALIGN_MASK; @@ -334,21 +377,25 @@ chn_wrintr(pcm_channel *c) b->dl = c->blocksize; /* record new transfer size */ chn_trigger(c, PCMTRIG_START); } - if (b->dl != l) + /* + * Emulate writing by DMA, i.e. transfer the pcm data from + * 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)); /* * we are near to underflow condition, so to prevent * audio 'clicks' clear next b->fl bytes */ - chn_clearbuf(c, b->fl); + chn_clearbuf(c, b, b->fl); + } } else { /* cannot start a new dma transfer */ - DEB(printf("cannot start wr-dma flags 0x%08x rp %d rl %d\n", - c->flags, b->rp, b->rl)); + DEB(printf("underflow, flags 0x%08x rp %d rl %d\n", c->flags, b->rp, b->rl)); if (b->dl) { /* DMA was active */ - chn_trigger(c, PCMTRIG_STOP); - b->dl = 0; b->underflow = 1; /* set underflow flag */ - chn_clearbuf(c, b->bufsize); /* and clear all DMA buffer */ + chn_clearbuf(c, b, b->bufsize); /* and clear all DMA buffer */ } } } @@ -372,9 +419,10 @@ chn_wrintr(pcm_channel *c) int chn_write(pcm_channel *c, struct uio *buf) { - int ret = 0, timeout; + int ret = 0, timeout, res, newsize; long s; snd_dbuf *b = &c->buffer; + snd_dbuf *bs = &c->buffer2nd; if (c->flags & CHN_F_WRITING) { /* This shouldn't happen and is actually silly @@ -384,37 +432,70 @@ chn_write(pcm_channel *c, struct uio *buf) return EBUSY; } c->flags |= CHN_F_WRITING; + c->flags &= ~CHN_F_ABORTING; + s = spltty(); + + /* + * XXX Certain applications attempt to write larger size + * of pcm data than c->blocksize2nd without blocking, + * 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) { + for (newsize = 1 ; newsize < min(buf->uio_resid, CHN_2NDBUFWHOLESIZE) ; newsize <<= 1); + chn_setblocksize(c, newsize * c->fragments); + c->blocksize2nd = newsize; + c->fragments = bs->bufsize / c->blocksize2nd; + } + + /* 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. */ - s = spltty(); + /* Check for underflow before writing into the buffers. */ chn_checkunderflow(c); - while (chn_wrfeed2nd(c, buf) > 0 || chn_wrfeed(c) > 0); + while (chn_wrfeed2nd(c, buf) > 0); + /* Start playing if not yet. */ - if (b->rl && !b->dl) chn_wrintr(c); - if (!(c->flags & CHN_F_NBIO)) { + if ((bs->rl || b->rl) && !b->dl) { + chn_wrintr(c); + } + + if (c->flags & CHN_F_NBIO) { + /* If no pcm data was written on nonblocking, return EAGAIN. */ + if (buf->uio_resid == res) + ret = EAGAIN; + } else { /* Wait until all samples are played in blocking mode. */ while (buf->uio_resid > 0) { - splx(s); + /* Check for underflow before writing into the buffers. */ + chn_checkunderflow(c); + /* 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_wrintr(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); timeout = (buf->uio_resid >= b->dl)? hz / 20 : 1; ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout); s = spltty(); - if (ret == EINTR) chn_abort(c); + /* if (ret == EINTR) chn_abort(c); */ if (ret == EINTR || ret == ERESTART) break; - /* Check for underflow before writing into the buffers. */ - chn_checkunderflow(c); - /* Fill up the buffers with new pcm data. */ - while (chn_wrfeed2nd(c, buf) > 0 || chn_wrfeed(c) > 0); - /* Start playing if necessary. */ - if (b->rl && !b->dl) chn_wrintr(c); } } c->flags &= ~CHN_F_WRITING; splx(s); - return (ret > 0)? ret : 0; + return ret; } /* @@ -451,7 +532,7 @@ rec_blocksize, and fallback to smaller sizes if no space is available. * Feed new data from the read buffer. Can be called in the bottom half. * Hence must be called at spltty. */ -static int +int chn_rdfeed(pcm_channel *c) { snd_dbuf *b = &c->buffer; @@ -470,8 +551,13 @@ 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. */ + chn_clearbuf(c, b, 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); } return lacc; @@ -484,6 +570,9 @@ chn_rdfeed2nd(pcm_channel *c, struct uio *buf) snd_dbuf *bs = &c->buffer2nd; int l, w, wacc; + /* The DMA buffer may have pcm data. */ + while(chn_rdfeed(c) > 0); + /* ensure we always have a whole number of samples */ wacc = 0; while (buf->uio_resid > 0 && bs->rl > 0) { @@ -499,8 +588,15 @@ chn_rdfeed2nd(pcm_channel *c, struct uio *buf) bs->fl += w; bs->rl -= w; bs->rp = (bs->rp + w) % bs->bufsize; + /* Clear the new space in the secondary buffer. */ + chn_clearbuf(c, bs, l); /* Accumulate the total bytes of the moved samples. */ + bs->total += w; wacc += w; + + /* If any pcm data gets moved, suck up the DMA buffer. */ + if (w > 0) + while (chn_rdfeed(c) > 0); } return wacc; @@ -511,26 +607,41 @@ static void chn_rdintr(pcm_channel *c) { snd_dbuf *b = &c->buffer; - int start; + 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 */ - if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED; - else { - /* - * Suck up the DMA buffer. This may result in making new - * captured data in the secondary buffer, thus we can wake - * up the top half if feed occurs. - */ - if (chn_rdfeed(c) > 0) { - chn_dmawakeup(c); - while (chn_rdfeed(c) > 0); - } - start = (b->fl > 0x200 && !(c->flags & CHN_F_ABORTING)); + + /* + * 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; + chn_clearbuf(c, bs, dl); } + + /* Update the pointers. */ + chn_dmaupdate(c); + + /* + * Suck up the DMA buffer, followed by waking up the top half. + * If some of the pcm data in the secondary buffer are still left, + * the top half goes to sleep by itself. + */ + 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; @@ -551,8 +662,8 @@ chn_rdintr(pcm_channel *c) } else { if (b->dl) { /* was active */ b->dl = 0; - chn_dmaupdate(c); chn_trigger(c, PCMTRIG_STOP); + chn_dmaupdate(c); } } } @@ -578,9 +689,10 @@ chn_rdintr(pcm_channel *c) int chn_read(pcm_channel *c, struct uio *buf) { - int ret = 0, timeout, limit; + int ret = 0, timeout, limit, res; long s; snd_dbuf *b = &c->buffer; + snd_dbuf *bs = &c->buffer2nd; if (c->flags & CHN_F_READING) { /* This shouldn't happen and is actually silly */ @@ -589,28 +701,48 @@ chn_read(pcm_channel *c, struct uio *buf) } s = spltty(); + + /* Store the initial size in the uio. */ + res = buf->uio_resid; + c->flags |= CHN_F_READING; + c->flags &= ~CHN_F_ABORTING; limit = buf->uio_resid - c->blocksize; 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 (!b->rl & !b->dl) chn_rdintr(c); - /* Suck up the DMA and secondary buffers. */ - while (chn_rdfeed(c) > 0 || chn_rdfeed2nd(c, buf) > 0); + if ((!bs->rl || !b->rl) && !b->dl) chn_rdintr(c); + if (!(c->flags & CHN_F_NBIO)) { /* Wait until all samples are captured. */ while (buf->uio_resid > 0) { - splx(s); + /* Suck up the DMA and secondary buffers. */ + chn_dmaupdate(c); + while (chn_rdfeed2nd(c, buf) > 0); + + /* Start capturing if necessary. */ + if ((!bs->rl || !b->rl) && !b->dl) chn_rdintr(c); + + /* Have we finished to feed the uio? */ + if (buf->uio_resid == 0) + break; + /* Wait for new pcm samples. */ + 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; - /* Start capturing if necessary. */ - if (!b->rl & !b->dl) chn_rdintr(c); - /* Suck up the DMA and secondary buffers. */ - while (chn_rdfeed(c) > 0 || chn_rdfeed2nd(c, buf) > 0); } + } else { + /* If no pcm data was read on nonblocking, return EAGAIN. */ + if (buf->uio_resid == res) + ret = EAGAIN; } c->flags &= ~CHN_F_READING; splx(s); @@ -635,6 +767,10 @@ chn_dma_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) } } +/* + * Allocate memory for DMA buffer. If the device do not perform DMA transfer, + * the drvier can call malloc(9) by its own. + */ int chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat) { @@ -646,12 +782,11 @@ chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat) } static void -chn_clearbuf(pcm_channel *c, int length) +chn_clearbuf(pcm_channel *c, snd_dbuf *b, int length) { -int i; -u_int16_t data, *p; + int i; + u_int16_t data, *p; - snd_dbuf *b = &c->buffer; /* rely on length & DMA_ALIGN_MASK == 0 */ length&=DMA_ALIGN_MASK; if (c->hwfmt & AFMT_SIGNED) data = 0x00; else data = 0x80; @@ -673,35 +808,47 @@ chn_resetbuf(pcm_channel *c) snd_dbuf *bs = &c->buffer2nd; c->smegcnt = 0; - c->buffer.sample_size = 1; - c->buffer.sample_size <<= (c->hwfmt & AFMT_STEREO)? 1 : 0; - c->buffer.sample_size <<= (c->hwfmt & AFMT_16BIT)? 1 : 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; - chn_clearbuf(c, b->bufsize); b->prev_total = b->total = 0; b->prev_int_count = b->int_count = 0; b->first_poll = 1; - b->underflow=0; + b->underflow = 0; + chn_clearbuf(c, b, 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; + chn_clearbuf(c, bs, bs->bufsize); } void buf_isadma(snd_dbuf *b, int go) { if (ISA_DMA(b)) { - if (go == PCMTRIG_START) { + switch (go) { + case PCMTRIG_START: DEB(printf("buf 0x%p ISA DMA started\n", b)); isa_dmastart(b->dir | B_RAW, b->buf, b->bufsize, b->chan); - } else { + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: DEB(printf("buf 0x%p ISA DMA stopped\n", b)); isa_dmastop(b->chan); isa_dmadone(b->dir | B_RAW, b->buf, b->bufsize, b->chan); + break; } } else KASSERT(1, ("buf_isadma called on invalid channel")); } @@ -718,7 +865,7 @@ buf_isadmaptr(snd_dbuf *b) } /* - * snd_sync waits until the space in the given channel goes above + * chn_sync waits until the space in the given channel goes above * a threshold. The threshold is checked against fl or rl respectively. * Assume that the condition can become true, do not check here... */ @@ -728,11 +875,13 @@ chn_sync(pcm_channel *c, int threshold) u_long s, rdy; int ret; snd_dbuf *b = &c->buffer; + snd_dbuf *bs = &c->buffer2nd; for (;;) { s = spltty(); - chn_dmaupdate(c); - rdy = (c->direction == PCMDIR_PLAY)? b->fl : b->rl; + chn_checkunderflow(c); + while (chn_wrfeed(c) > 0); + rdy = (c->direction == PCMDIR_PLAY)? bs->fl : bs->rl; if (rdy <= threshold) { ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmsyn", 1); splx(s); @@ -750,14 +899,31 @@ int chn_poll(pcm_channel *c, int ev, struct proc *p) { snd_dbuf *b = &c->buffer; - u_long s = spltty(); - if (b->dl) chn_dmaupdate(c); - splx(s); - if (chn_polltrigger(c) && chn_pollreset(c)) return ev; + snd_dbuf *bs = &c->buffer2nd; + u_long s; + int ret; + + s = spltty(); + if (c->direction == PCMDIR_PLAY) { + /* Fill up the DMA buffer. */ + chn_checkunderflow(c); + while(chn_wrfeed(c) > 0); + if (!b->dl) chn_wrintr(c); + } else { + /* Suck up the DMA buffer. */ + chn_dmaupdate(c); + while(chn_rdfeed(c) > 0); + if (!b->dl) chn_rdintr(c); + } + ret = 0; + if (chn_polltrigger(c) && chn_pollreset(c)) + ret = ev; else { - selrecord(p, &b->sel); - return 0; + selrecord(p, &bs->sel); + ret = 0; } + splx(s); + return ret; } /* @@ -780,7 +946,11 @@ chn_abort(pcm_channel *c) } chn_trigger(c, PCMTRIG_ABORT); b->dl = 0; - missing = b->rl + bs->rl; + if (c->direction == PCMDIR_PLAY) + chn_checkunderflow(c); + else + chn_dmaupdate(c); + missing = bs->rl; return missing; } @@ -793,27 +963,26 @@ chn_abort(pcm_channel *c) int chn_flush(pcm_channel *c) { - int ret, count = 10; + int ret, count = 50; snd_dbuf *b = &c->buffer; - DEB(printf("snd_flush c->flags 0x%08x\n", c->flags)); + DEB(printf("chn_flush c->flags 0x%08x\n", c->flags)); c->flags |= CHN_F_CLOSING; - if (c->direction != PCMDIR_PLAY) chn_abort(c); - else if (b->dl) while (!b->underflow) { - /* still pending output data. */ - ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmflu", hz); - DEB(chn_dmaupdate(c)); - DEB(printf("snd_sync: now rl : fl %d : %d\n", b->rl, b->fl)); - if (ret == EINTR || ret == ERESTART) { - DEB(printf("chn_flush: tsleep returns %d\n", ret)); - return -1; - } - if (ret && --count == 0) { - DEB(printf("chn_flush: timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n",\ - b->rl, c->flags)); - break; - } - } + if (c->direction == PCMDIR_REC) chn_abort(c); + else if (b->dl) { + while (!b->underflow && (count-- > 0)) { + /* still pending output data. */ + ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmflu", hz/10); + DEB(chn_dmaupdate(c)); + DEB(printf("chn_flush: now rl = %d, fl = %d\n", b->rl, b->fl)); + if (ret == EINTR || ret == ERESTART) { + DEB(printf("chn_flush: tsleep returns %d\n", ret)); + return ret; + } + } + } + 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) chn_abort(c); return 0; @@ -835,13 +1004,10 @@ chn_reinit(pcm_channel *c) if ((c->flags & CHN_F_INIT) && CANCHANGE(c)) { chn_setformat(c, c->format); chn_setspeed(c, c->speed); - chn_setblocksize(c, (c->flags & CHN_F_HAS_SIZE) ? c->blocksize : 0); chn_setvolume(c, (c->volume >> 8) & 0xff, c->volume & 0xff); c->flags &= ~CHN_F_INIT; return 1; } - if (CANCHANGE(c) && !(c->flags & CHN_F_HAS_SIZE) ) - chn_setblocksize(c, 0); /* Apply new block size */ return 0; } @@ -850,13 +1016,20 @@ chn_init(pcm_channel *c, void *devinfo, int dir) { snd_dbuf *bs = &c->buffer2nd; + /* Initialize the hardware and DMA buffer first. */ c->flags = 0; c->feeder = &feeder_root; c->buffer.chan = -1; c->devinfo = c->init(devinfo, &c->buffer, c, dir); chn_setdir(c, dir); - bs->bufsize = CHN_2NDBUFBLKSIZE * CHN_2NDBUFBLKNUM; + + /* And the secondary buffer. */ + c->blocksize2nd = CHN_2NDBUFBLKSIZE; + c->fragments = CHN_2NDBUFBLKNUM; + bs->bufsize = c->blocksize2nd * c->fragments; bs->buf = malloc(bs->bufsize, M_DEVBUF, M_NOWAIT); + if (bs->buf == NULL) + return 1; bzero(bs->buf, bs->bufsize); bs->rl = bs->rp = bs->fp = 0; bs->fl = bs->bufsize; @@ -866,10 +1039,13 @@ chn_init(pcm_channel *c, void *devinfo, int dir) int chn_setdir(pcm_channel *c, int dir) { + int r; + c->direction = dir; - if (ISA_DMA(&c->buffer)) + r = c->setdir(c->devinfo, c->direction); + if (!r && ISA_DMA(&c->buffer)) c->buffer.dir = (dir == PCMDIR_PLAY)? B_WRITE : B_READ; - return c->setdir(c->devinfo, c->direction); + return r; } int @@ -912,22 +1088,63 @@ chn_setformat(pcm_channel *c, u_int32_t fmt) return 0; } +/* + * The seconday buffer is modified only during interrupt. + * Hence the size of the secondary buffer can be changed + * at any time as long as an interrupt is disabled. + */ int chn_setblocksize(pcm_channel *c, int blksz) { - if (CANCHANGE(c)) { - c->flags &= ~CHN_F_HAS_SIZE; - if (blksz >= 2) c->flags |= CHN_F_HAS_SIZE; - if (blksz < 0) blksz = -blksz; - if (blksz < 2) blksz = c->buffer.sample_size * (c->speed >> 2); - RANGE(blksz, 1024, c->buffer.bufsize / 4); - blksz &= DMA_ALIGN_MASK; - c->blocksize = c->setblocksize(c->devinfo, blksz) & DMA_ALIGN_MASK; - return c->blocksize; + snd_dbuf *bs = &c->buffer2nd; + u_int8_t *tmpbuf; + int s, tmpbuf_fl, tmpbuf_fp, l; + + c->flags &= ~CHN_F_HAS_SIZE; + if (blksz >= 2) c->flags |= CHN_F_HAS_SIZE; + if (blksz < 0) blksz = -blksz; + if (blksz < 2) blksz = c->buffer.sample_size * (c->speed >> 2); + /* XXX How small can the lower bound be? */ + RANGE(blksz, 64, CHN_2NDBUFWHOLESIZE); + + /* + * Allocate a temporary buffer. It holds the pcm data + * until the size of the secondary buffer gets changed. + * bs->buf is not affected, so mmap should work fine. + */ + tmpbuf = malloc(blksz, M_TEMP, M_NOWAIT); + if (tmpbuf == NULL) { + DEB(printf("chn_setblocksize: out of memory.")); + return 1; } - c->blocksize = blksz; - c->flags |= CHN_F_INIT; - return 0; + bzero(tmpbuf, blksz); + tmpbuf_fl = blksz; + tmpbuf_fp = 0; + s = spltty(); + while (bs->rl > 0 && tmpbuf_fl > 0) { + l = min(min(bs->rl, bs->bufsize - bs->rp), tmpbuf_fl); + bcopy(bs->buf + bs->rp, tmpbuf + tmpbuf_fp, l); + tmpbuf_fl -= l; + tmpbuf_fp = (tmpbuf_fp + l) % blksz; + bs->rl -= l; + bs->fl += l; + bs->rp = (bs->rp + l) % bs->bufsize; + } + /* Change the size of the seconary buffer. */ + bs->bufsize = blksz; + c->fragments = CHN_2NDBUFBLKNUM; + c->blocksize2nd = bs->bufsize / c->fragments; + /* Clear the secondary buffer and restore the pcm data. */ + bzero(bs->buf, bs->bufsize); + bs->rl = bs->bufsize - tmpbuf_fl; + bs->rp = 0; + bs->fl = tmpbuf_fl; + bs->fp = tmpbuf_fp; + bcopy(tmpbuf, bs->buf, bs->rl); + + free(tmpbuf, M_TEMP); + splx(s); + return c->blocksize2nd; } int diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h index 64994ad..157d353 100644 --- a/sys/dev/sound/pcm/channel.h +++ b/sys/dev/sound/pcm/channel.h @@ -47,7 +47,9 @@ pcmchan_caps *chn_getcaps(pcm_channel *c); int chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat); void chn_resetbuf(pcm_channel *c); void chn_intr(pcm_channel *c); -void chn_dmaupdate(pcm_channel *c); +void chn_checkunderflow(pcm_channel *c); +int chn_wrfeed(pcm_channel *c); +int chn_rdfeed(pcm_channel *c); int chn_abort(pcm_channel *c); void buf_isadma(snd_dbuf *b, int go); @@ -60,6 +62,7 @@ extern pcm_feeder feeder_root; #define PCMDIR_REC -1 #define PCMTRIG_START 1 +#define PCMTRIG_EMLDMAWR 2 #define PCMTRIG_STOP 0 #define PCMTRIG_ABORT -1 @@ -85,6 +88,8 @@ extern pcm_feeder feeder_root; * tsleeps in chn_{read,write} at the highest sample rate. * (which is usually 48kHz * 16bit * stereo = 192000 bytes/sec) */ -#define CHN_2NDBUFBLKSIZE (12 * 1024) +#define CHN_2NDBUFBLKSIZE (2 * 1024) /* The total number of blocks per secondary buffer. */ -#define CHN_2NDBUFBLKNUM (3) +#define CHN_2NDBUFBLKNUM (8) +/* The size of a whole secondary buffer. */ +#define CHN_2NDBUFWHOLESIZE (CHN_2NDBUFBLKSIZE * CHN_2NDBUFBLKNUM) diff --git a/sys/dev/sound/pcm/datatypes.h b/sys/dev/sound/pcm/datatypes.h index 1fb86f2..7ea9390 100644 --- a/sys/dev/sound/pcm/datatypes.h +++ b/sys/dev/sound/pcm/datatypes.h @@ -120,6 +120,8 @@ struct _pcm_channel { u_int32_t flags; u_int32_t format, hwfmt; u_int32_t blocksize; + u_int32_t blocksize2nd; + u_int32_t fragments; int direction; snd_dbuf buffer; diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c index f69edac..79f6ef1 100644 --- a/sys/dev/sound/pcm/dsp.c +++ b/sys/dev/sound/pcm/dsp.c @@ -217,14 +217,14 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) * we start with the new ioctl interface. */ case AIONWRITE: /* how many bytes can write ? */ - if (wrch && wrch->buffer.dl) chn_dmaupdate(wrch); - *arg_i = wrch? wrch->buffer.fl : 0; + if (wrch && wrch->buffer.dl) + while (chn_wrfeed(wrch) > 0); + *arg_i = wrch? wrch->buffer2nd.fl : 0; break; case AIOSSIZE: /* set the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; - splx(s); if (wrch) chn_setblocksize(wrch, p->play_size); if (rdch) chn_setblocksize(rdch, p->rec_size); } @@ -232,15 +232,14 @@ 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->blocksize; - if (rdch) p->rec_size = rdch->blocksize; + if (wrch) p->play_size = wrch->blocksize2nd; + if (rdch) p->rec_size = rdch->blocksize2nd; } break; case AIOSFMT: { snd_chan_param *p = (snd_chan_param *)arg; - splx(s); if (wrch) { chn_setformat(wrch, p->play_format); chn_setspeed(wrch, p->play_rate); @@ -272,8 +271,8 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) pcaps? pcaps->minspeed : 0); p->rate_max = min(rcaps? rcaps->maxspeed : 1000000, pcaps? pcaps->maxspeed : 1000000); - p->bufsize = min(rdch? rdch->buffer.bufsize : 1000000, - wrch? wrch->buffer.bufsize : 1000000); + p->bufsize = min(rdch? rdch->buffer2nd.bufsize : 1000000, + wrch? wrch->buffer2nd.bufsize : 1000000); /* XXX bad on sb16 */ p->formats = (rcaps? rcaps->formats : 0xffffffff) & (pcaps? pcaps->formats : 0xffffffff); @@ -287,7 +286,6 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) if (*arg_i == AIOSYNC_PLAY && wrch) *arg_i = chn_abort(wrch); else if (*arg_i == AIOSYNC_CAPTURE && rdch) *arg_i = chn_abort(rdch); else { - splx(s); printf("AIOSTOP: bad channel 0x%x\n", *arg_i); *arg_i = 0; } @@ -301,8 +299,9 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) * here follow the standard ioctls (filio.h etc.) */ case FIONREAD: /* get # bytes to read */ - if (rdch && rdch->buffer.dl) chn_dmaupdate(rdch); - *arg_i = rdch? rdch->buffer.rl : 0; + if (rdch && rdch->buffer.dl) + while (chn_rdfeed(rdch) > 0); + *arg_i = rdch? rdch->buffer2nd.rl : 0; break; case FIOASYNC: /*set/clear async i/o */ @@ -325,17 +324,22 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) case THE_REAL_SNDCTL_DSP_GETBLKSIZE: case SNDCTL_DSP_GETBLKSIZE: - *arg_i = CHN_2NDBUFBLKSIZE; + if (wrch) + *arg_i = wrch->blocksize2nd; + else if (rdch) + *arg_i = rdch->blocksize2nd; + else + *arg_i = 0; break ; case SNDCTL_DSP_SETBLKSIZE: - splx(s); if (wrch) chn_setblocksize(wrch, *arg_i); if (rdch) chn_setblocksize(rdch, *arg_i); break; case SNDCTL_DSP_RESET: DEB(printf("dsp reset\n")); + splx(s); if (wrch) chn_abort(wrch); if (rdch) chn_abort(rdch); break; @@ -343,7 +347,7 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) case SNDCTL_DSP_SYNC: DEB(printf("dsp sync\n")); splx(s); - if (wrch) chn_sync(wrch, wrch->buffer.bufsize - 4); + if (wrch) chn_sync(wrch, wrch->buffer2nd.bufsize - 4); break; case SNDCTL_DSP_SPEED: @@ -400,30 +404,49 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) int bytes = 1 << min(*arg_i & 0xffff, 16); int count = (*arg_i >> 16) & 0xffff; pcm_channel *c = wrch? wrch : rdch; - splx(s); - if (rdch) chn_setblocksize(rdch, bytes); - if (wrch) chn_setblocksize(wrch, bytes); + if (count == 0) + count = CHN_2NDBUFWHOLESIZE / bytes; + if (count < 2) { + ret = EINVAL; + break; + } + if (rdch) { + chn_setblocksize(rdch, bytes * count); + rdch->blocksize2nd = bytes; + rdch->fragments = rdch->buffer2nd.bufsize / rdch->blocksize2nd; + } + if (wrch) { + chn_setblocksize(wrch, bytes * count); + wrch->blocksize2nd = bytes; + wrch->fragments = wrch->buffer2nd.bufsize / wrch->blocksize2nd; + } /* eg: 4dwave can only interrupt at buffer midpoint, so * it will force blocksize == bufsize/2 */ - count = c->buffer.bufsize / c->blocksize; - bytes = ffs(c->blocksize) - 1; + count = c->buffer2nd.bufsize / c->blocksize2nd; + bytes = ffs(c->blocksize2nd) - 1; *arg_i = (count << 16) | bytes; } break; - case SNDCTL_DSP_GETISPACE: - /* return space available in the input queue */ + case SNDCTL_DSP_GETISPACE: /* XXX Space for reading? Makes no sense... */ + /* return the size of data available in the input queue */ { audio_buf_info *a = (audio_buf_info *)arg; if (rdch) { snd_dbuf *b = &rdch->buffer; - if (b->dl) chn_dmaupdate(rdch); - a->bytes = b->fl; - a->fragments = 1; - a->fragstotal = b->bufsize / rdch->blocksize; - a->fragsize = rdch->blocksize; + snd_dbuf *bs = &rdch->buffer2nd; + if (b->dl) + /* + * Suck up the secondary and DMA buffer. + * chn_rdfeed*() takes care of the alignment. + */ + while (chn_rdfeed(rdch) > 0); + a->bytes = bs->rl; + a->fragments = a->bytes / rdch->blocksize2nd; + a->fragstotal = bs->bufsize / rdch->blocksize2nd; + a->fragsize = rdch->blocksize2nd; } } break; @@ -434,11 +457,20 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) audio_buf_info *a = (audio_buf_info *)arg; if (wrch) { snd_dbuf *b = &wrch->buffer; - if (b->dl) chn_dmaupdate(wrch); - a->bytes = b->fl; - a->fragments = 1; - a->fragstotal = b->bufsize / wrch->blocksize; - a->fragsize = wrch->blocksize; + snd_dbuf *bs = &wrch->buffer2nd; + if (b->dl) { + /* + * Fill up the secondary and DMA buffer. + * chn_wrfeed*() takes care of the alignment. + * Check for underflow before writing into the buffers. + */ + chn_checkunderflow(wrch); + while (chn_wrfeed(wrch) > 0); + } + a->bytes = bs->fl; + a->fragments = a->bytes / wrch->blocksize2nd; + a->fragstotal = bs->bufsize / wrch->blocksize2nd; + a->fragsize = wrch->blocksize2nd; } } break; @@ -447,12 +479,17 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) { count_info *a = (count_info *)arg; if (rdch) { - snd_dbuf *b = &rdch->buffer; - if (b->dl) chn_dmaupdate(rdch); - a->bytes = b->total; - a->blocks = (b->total - b->prev_total) / rdch->blocksize; - a->ptr = b->fp; - b->prev_total += a->blocks * rdch->blocksize; + snd_dbuf *b = &rdch->buffer; + snd_dbuf *bs = &rdch->buffer2nd; + if (b->dl) + /* + * Suck up the secondary and DMA buffer. + * chn_rdfeed*() takes care of the alignment. + */ + while (chn_rdfeed(rdch) > 0); + a->bytes = bs->total; + a->blocks = bs->rl / rdch->blocksize2nd; + a->ptr = bs->rl % rdch->blocksize2nd; } else ret = EINVAL; } break; @@ -462,11 +499,19 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) count_info *a = (count_info *)arg; if (wrch) { snd_dbuf *b = &wrch->buffer; - if (b->dl) chn_dmaupdate(wrch); - a->bytes = b->total; - a->blocks = (b->total - b->prev_total) / wrch->blocksize; - a->ptr = b->rp; - b->prev_total += a->blocks * wrch->blocksize; + snd_dbuf *bs = &wrch->buffer2nd; + if (b->dl) { + /* + * Fill up the secondary and DMA buffer. + * chn_wrfeed*() takes care of the alignment. + * Check for underflow before writing into the buffers. + */ + chn_checkunderflow(wrch); + while (chn_wrfeed(wrch) > 0); + } + a->bytes = bs->total; + a->blocks = bs->rl / wrch->blocksize2nd; + a->ptr = bs->fl % wrch->blocksize2nd; } else ret = EINVAL; } break; @@ -504,15 +549,17 @@ dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) *arg_i |= PCM_ENABLE_INPUT; break; - case SNDCTL_DSP_GETODELAY: - if (wrch) { - snd_dbuf *b = &wrch->buffer; - if (b->dl) - chn_dmaupdate(wrch); - *arg = b->total; - } else - ret = EINVAL; - break; + case SNDCTL_DSP_GETODELAY: + if (wrch) { + snd_dbuf *b = &wrch->buffer; + if (b->dl) { + chn_checkunderflow(wrch); + while (chn_wrfeed(wrch) > 0); + } + *arg = b->total; + } else + ret = EINVAL; + break; case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: @@ -556,8 +603,9 @@ dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot) if (1 || (wrch && (nprot & PROT_WRITE))) c = wrch; else if (rdch && (nprot & PROT_READ)) c = rdch; if (c) { + printf("dsp_mmap.\n"); c->flags |= CHN_F_MAPPED; - return atop(vtophys(c->buffer.buf + offset)); + return atop(vtophys(c->buffer2nd.buf + offset)); } return -1; } diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h index d2409ef..5f34e50 100644 --- a/sys/dev/sound/pcm/sound.h +++ b/sys/dev/sound/pcm/sound.h @@ -106,8 +106,10 @@ struct isa_device { int dummy; }; /* many variables should be reduced to a range. Here define a macro */ #define RANGE(var, low, high) (var) = \ (((var)<(low))? (low) : ((var)>(high))? (high) : (var)) - -#define DSP_BUFFSIZE (65536 - 256) /* XXX */ +/* +#define DSP_BUFFSIZE (65536 - 256) +*/ +#define DSP_BUFFSIZE (8192) /* the last 256 bytes are room for buggy soundcard to overflow. */ /* make figuring out what a format is easier. got AFMT_STEREO already */ |