Date: Mon, 20 Dec 1999 13:44:02 +0900 From: Seigo Tanimura <tanimura@r.dl.itc.u-tokyo.ac.jp> To: current@freebsd.org Subject: more patch for secondary buffer, polling and DMA emulation in Message-ID: <14429.46098.1195.72159A@rina.r.dl.itc.u-tokyo.ac.jp>
next in thread | raw e-mail | index | archive | help
pcm cc: Seigo Tanimura <tanimura> User-Agent: Wanderlust/1.0.3 (Notorious) SEMI/1.13.4 (Terai) FLIM/1.12.7 (=?ISO-8859-4?Q?Y=FEzaki?=) MULE XEmacs/21.1 (patch 8) (Bryce Canyon) (i386--freebsd) Organization: Digital Library Research Division, Information Techinology Centre, The University of Tokyo MIME-Version: 1.0 (generated by SEMI 1.13.4 - "Terai") Content-Type: multipart/mixed; boundary="Multipart_Mon_Dec_20_13:44:01_1999-1" --Multipart_Mon_Dec_20_13:44:01_1999-1 Content-Type: text/plain; charset=US-ASCII If you have a trouble playing pcm with some applications, could you please try the patch attached below? The patch does the following things: - All ioctl(2)s go to see the secondary buffer(if I have forget nothing). - chn_setblocksize() changes the size of the secondary buffer. - chn_mmap() maps the secondary buffer. - chn_poll() invokes DMA. - chn_wrintr() performs DMA emulation for pcm devices with no DMA functionality(requested by nyan). With the patch, snes9x(w/-r 7) and RealPlayer G2 for Linux worked fine. The range of blksz in chn_setblocksize() may need further tuning... --Multipart_Mon_Dec_20_13:44:01_1999-1 Content-Type: text/plain; type=patch; charset=US-ASCII Content-Disposition: attachment; filename="2ndbuf-19991220.diff" Content-Transfer-Encoding: 7bit diff -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/isa/ad1816.c sys.2ndbuf/dev/sound/isa/ad1816.c --- sys.apictimer/dev/sound/isa/ad1816.c Mon Dec 20 11:13:56 1999 +++ sys.2ndbuf/dev/sound/isa/ad1816.c Mon Dec 20 11:45:28 1999 @@ -403,6 +403,7 @@ 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 -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/isa/mss.c sys.2ndbuf/dev/sound/isa/mss.c --- sys.apictimer/dev/sound/isa/mss.c Mon Dec 20 11:44:09 1999 +++ sys.2ndbuf/dev/sound/isa/mss.c Mon Dec 20 11:45:28 1999 @@ -1772,6 +1772,7 @@ { struct mss_chinfo *ch = data; + if (go == PCMTRIG_EMLDMAWR) return 0; buf_isadma(ch->buffer, go); mss_trigger(ch, go); return 0; diff -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/isa/sb.c sys.2ndbuf/dev/sound/isa/sb.c --- sys.apictimer/dev/sound/isa/sb.c Mon Dec 20 11:13:58 1999 +++ sys.2ndbuf/dev/sound/isa/sb.c Mon Dec 20 11:45:28 1999 @@ -860,6 +860,7 @@ 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 @@ 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 -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/pci/aureal.c sys.2ndbuf/dev/sound/pci/aureal.c --- sys.apictimer/dev/sound/pci/aureal.c Fri Dec 10 10:17:22 1999 +++ sys.2ndbuf/dev/sound/pci/aureal.c Mon Dec 20 11:45:28 1999 @@ -353,6 +353,7 @@ { 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 -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/pci/csapcm.c sys.2ndbuf/dev/sound/pci/csapcm.c --- sys.apictimer/dev/sound/pci/csapcm.c Fri Dec 10 10:20:07 1999 +++ sys.2ndbuf/dev/sound/pci/csapcm.c Mon Dec 20 11:45:28 1999 @@ -386,6 +386,7 @@ 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 -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/pci/es137x.c sys.2ndbuf/dev/sound/pci/es137x.c --- sys.apictimer/dev/sound/pci/es137x.c Mon Dec 20 11:14:02 1999 +++ sys.2ndbuf/dev/sound/pci/es137x.c Mon Dec 20 11:45:28 1999 @@ -367,6 +367,7 @@ 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; diff -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/pci/t4dwave.c sys.2ndbuf/dev/sound/pci/t4dwave.c --- sys.apictimer/dev/sound/pci/t4dwave.c Mon Dec 20 11:14:04 1999 +++ sys.2ndbuf/dev/sound/pci/t4dwave.c Mon Dec 20 11:45:28 1999 @@ -490,6 +490,7 @@ { 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 -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/pcm/channel.c sys.2ndbuf/dev/sound/pcm/channel.c --- sys.apictimer/dev/sound/pcm/channel.c Mon Dec 20 11:14:04 1999 +++ sys.2ndbuf/dev/sound/pcm/channel.c Mon Dec 20 11:47:01 1999 @@ -37,7 +37,10 @@ #define CANCHANGE(c) (!(c)->buffer.dl) static void chn_stintr(pcm_channel *c); -static void chn_clearbuf(pcm_channel *c, int length); +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 @@ -110,23 +113,23 @@ 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->fl : bs->rl) >= 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; } @@ -142,7 +145,6 @@ 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); } /* @@ -168,7 +170,7 @@ */ DEB (static int chn_updatecount=0); -void +static void chn_dmaupdate(pcm_channel *c) { snd_dbuf *b = &c->buffer; @@ -197,7 +199,7 @@ * 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; @@ -227,7 +229,7 @@ * 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; @@ -251,8 +253,13 @@ 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; @@ -281,6 +288,7 @@ bs->fl -= w; bs->fp = (bs->fp + w) % bs->bufsize; /* Accumulate the total bytes of the moved samples. */ + bs->total += w; wacc += w; } @@ -295,32 +303,45 @@ chn_wrintr(pcm_channel *c) { snd_dbuf *b = &c->buffer; - int start; + int start, dl; if (b->underflow && !(c->flags & CHN_F_MAPPED)) 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); } + + /* + * 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); + } + if (c->flags & CHN_F_MAPPED) + start = c->flags & CHN_F_TRIGGERED; + else + start = (b->rl >= DMA_ALIGN_THRESHOLD && !(c->flags & CHN_F_ABORTING)); if (start) { int l; chn_dmaupdate(c); @@ -336,12 +357,17 @@ b->dl = c->blocksize ; /* 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_EMLDMAWR); if (b->dl != l) /* * 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", @@ -350,7 +376,7 @@ 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 */ } } } @@ -377,6 +403,7 @@ int ret = 0, timeout; 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 @@ -386,6 +413,7 @@ return EBUSY; } c->flags |= CHN_F_WRITING; + c->flags &= ~CHN_F_ABORTING; /* * Fill up the secondary and DMA buffer. * chn_wrfeed*() takes care of the alignment. @@ -395,7 +423,7 @@ chn_checkunderflow(c); while (chn_wrfeed2nd(c, buf) > 0 || chn_wrfeed(c) > 0); /* Start playing if not yet. */ - if (b->rl && !b->dl) chn_wrintr(c); + if ((bs->rl || b->rl) && !b->dl) chn_wrintr(c); if (!(c->flags & CHN_F_NBIO)) { /* Wait until all samples are played in blocking mode. */ while (buf->uio_resid > 0) { @@ -411,7 +439,7 @@ /* 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); + if ((bs->rl || b->rl) && !b->dl) chn_wrintr(c); } } c->flags &= ~CHN_F_WRITING; @@ -453,7 +481,7 @@ * 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; @@ -472,8 +500,13 @@ 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; @@ -501,7 +534,10 @@ 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; } @@ -513,26 +549,39 @@ 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); } + /* + * 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); + } + 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; @@ -583,6 +632,7 @@ int ret = 0, timeout, limit; 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 */ @@ -592,10 +642,11 @@ s = spltty(); c->flags |= CHN_F_READING; + c->flags &= ~CHN_F_ABORTING; limit = buf->uio_resid - c->blocksize; if (limit < 0) limit = 0; /* Start capturing if not yet. */ - if (!b->rl & !b->dl) chn_rdintr(c); + if ((!bs->rl || !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 (!(c->flags & CHN_F_NBIO)) { @@ -609,7 +660,7 @@ if (ret == EINTR) chn_abort(c); if (ret == EINTR || ret == ERESTART) break; /* Start capturing if necessary. */ - if (!b->rl & !b->dl) chn_rdintr(c); + if ((!bs->rl || !b->rl) && !b->dl) chn_rdintr(c); /* Suck up the DMA and secondary buffers. */ while (chn_rdfeed(c) > 0 || chn_rdfeed2nd(c, buf) > 0); } @@ -647,6 +698,10 @@ } } +/* + * 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) { @@ -658,12 +713,11 @@ } 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; @@ -691,29 +745,38 @@ b->rp = b->fp = 0; b->dl = b->rl = 0; b->fl = b->bufsize; - chn_clearbuf(c, b->bufsize); + chn_clearbuf(c, b, b->bufsize); b->prev_total = b->total = 0; b->prev_int_count = b->int_count = 0; b->first_poll = 1; b->underflow=0; + c->blocksize2nd = CHN_2NDBUFBLKSIZE; + bs->bufsize = c->blocksize2nd * CHN_2NDBUFBLKNUM; bs->rp = bs->fp = 0; bs->dl = bs->rl = 0; bs->fl = bs->bufsize; + bs->prev_total = bs->total = 0; + bs->first_poll = 1; + 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")); } @@ -730,7 +793,7 @@ } /* - * 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... */ @@ -740,11 +803,13 @@ 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); @@ -762,14 +827,29 @@ 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(); + ret = 0; + if (chn_polltrigger(c) && chn_pollreset(c)) + ret = ev; else { - selrecord(p, &b->sel); - return 0; + selrecord(p, &bs->sel); + if (c->direction == PCMDIR_PLAY) { + /* Start playing if not yet. */ + c->flags &= ~CHN_F_ABORTING; + chn_checkunderflow(c); + if (!b->dl) chn_wrintr(c); + } else { + /* Start capturing if not yet. */ + if (!b->dl) chn_rdintr(c); + } + ret = 0; } + splx(s); + return ret; } /* @@ -792,7 +872,7 @@ } chn_trigger(c, PCMTRIG_ABORT); b->dl = 0; - missing = b->rl + bs->rl; + missing = bs->rl; return missing; } @@ -862,13 +942,19 @@ { 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; + bs->bufsize = c->blocksize2nd * CHN_2NDBUFBLKNUM; 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; @@ -924,22 +1010,60 @@ 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); + /* blksz less than c->blocksize makes nothing but noise. */ + RANGE(blksz, c->blocksize, CHN_2NDBUFBLKSIZE); + + /* + * 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) + return 1; + 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; } - c->blocksize = blksz; - c->flags |= CHN_F_INIT; - return 0; + /* Change the size of the seconary buffer. */ + bs->bufsize = blksz; + c->blocksize2nd = bs->bufsize / CHN_2NDBUFBLKNUM; + /* 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 -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/pcm/channel.h sys.2ndbuf/dev/sound/pcm/channel.h --- sys.apictimer/dev/sound/pcm/channel.h Mon Dec 20 11:14:05 1999 +++ sys.2ndbuf/dev/sound/pcm/channel.h Mon Dec 20 11:45:28 1999 @@ -47,7 +47,9 @@ 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 @@ #define PCMDIR_REC -1 #define PCMTRIG_START 1 +#define PCMTRIG_EMLDMAWR 2 #define PCMTRIG_STOP 0 #define PCMTRIG_ABORT -1 @@ -87,4 +90,4 @@ */ #define CHN_2NDBUFBLKSIZE (12 * 1024) /* The total number of blocks per secondary buffer. */ -#define CHN_2NDBUFBLKNUM (3) +#define CHN_2NDBUFBLKNUM (2) diff -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/pcm/datatypes.h sys.2ndbuf/dev/sound/pcm/datatypes.h --- sys.apictimer/dev/sound/pcm/datatypes.h Mon Dec 20 11:14:05 1999 +++ sys.2ndbuf/dev/sound/pcm/datatypes.h Mon Dec 20 11:45:28 1999 @@ -120,6 +120,7 @@ u_int32_t flags; u_int32_t format, hwfmt; u_int32_t blocksize; + u_int32_t blocksize2nd; int direction; snd_dbuf buffer; diff -urN -x CVS -x compile -x SILVER -x RINA -x aic7xxx -x ata sys.apictimer/dev/sound/pcm/dsp.c sys.2ndbuf/dev/sound/pcm/dsp.c --- sys.apictimer/dev/sound/pcm/dsp.c Mon Dec 20 11:14:06 1999 +++ sys.2ndbuf/dev/sound/pcm/dsp.c Mon Dec 20 11:45:28 1999 @@ -217,8 +217,9 @@ * 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 */ @@ -301,8 +302,9 @@ * 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,7 +327,12 @@ #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: @@ -343,7 +350,7 @@ 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: @@ -407,23 +414,29 @@ /* 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; + 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 = max(bs->rl, rdch->blocksize2nd); a->fragments = 1; - a->fragstotal = b->bufsize / rdch->blocksize; - a->fragsize = rdch->blocksize; + a->fragstotal = bs->bufsize / rdch->blocksize2nd; + a->fragsize = rdch->blocksize2nd; } } break; @@ -434,11 +447,20 @@ 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; + 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 = max(bs->fl, wrch->blocksize2nd); a->fragments = 1; - a->fragstotal = b->bufsize / wrch->blocksize; - a->fragsize = wrch->blocksize; + a->fragstotal = bs->bufsize / wrch->blocksize2nd; + a->fragsize = wrch->blocksize2nd; } } break; @@ -447,12 +469,18 @@ { 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->total - bs->prev_total) / rdch->blocksize2nd; + a->ptr = bs->rp; /* If mmaped, read from this point. */ + bs->prev_total += a->blocks * rdch->blocksize2nd; } else ret = EINVAL; } break; @@ -462,11 +490,20 @@ 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->total - bs->prev_total) / wrch->blocksize2nd; + a->ptr = bs->fp; /* If mmaped, write from this point. */ + bs->prev_total += a->blocks * wrch->blocksize; } else ret = EINVAL; } break; @@ -504,15 +541,17 @@ *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 +595,9 @@ 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; } --Multipart_Mon_Dec_20_13:44:01_1999-1 Content-Type: text/plain; charset=US-ASCII -- Seigo Tanimura <tanimura@r.dl.itc.u-tokyo.ac.jp> <tanimura@FreeBSD.org> --Multipart_Mon_Dec_20_13:44:01_1999-1-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-current" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?14429.46098.1195.72159A>