My CS4614 has got only 4KB of the DMA buffer, frequently failing to
play pcm blocks continuously. The following patch adds the secondary
pcm channel buffers with the size independent from that of a DSP. The
patch works quite well with mpg123 and x11amp. For those who has the
similar problem could you please try that?


Index: channel.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/pcm/channel.c,v
retrieving revision 1.9
diff -u -r1.9 channel.c
--- channel.c   1999/12/05 19:09:12     1.9
+++ channel.c   1999/12/11 09:34:25
@@ -33,6 +33,13 @@
 #define        DMA_ALIGN_THRESHOLD     4
 #define        DMA_ALIGN_MASK          (~(DMA_ALIGN_THRESHOLD - 1))
 
+/*
+ * This should be large enough to hold all pcm data between
+ * tsleeps in chn_{read,write} at the highest sample rate.
+ * (which is usually 48kHz * 16bit * stereo = 192000 bytes/sec)
+ */
+#define CHN_2NDBUFSIZE                 (12 * 1024)
+
 #define ISA_DMA(b) (((b)->chan >= 0 && (b)->chan != 4 && (b)->chan < 8))
 #define CANCHANGE(c) (!(c)->buffer.dl)
 
@@ -131,9 +138,8 @@
 }
 
 /*
- * chn_dmadone() updates pointers and wakes up any process sleeping
- * or waiting on a select().
- * Must be called at spltty().
+ * chn_dmadone() updates pointers and wakes up any process waiting
+ * on a select(). Must be called at spltty().
  */
 static void
 chn_dmadone(pcm_channel *c)
@@ -142,12 +148,23 @@
 
        chn_dmaupdate(c);
        if (ISA_DMA(b)) chn_isadmabounce(c); /* sync bounce buffer */
-       wakeup(b);
        b->int_count++;
        if (b->sel.si_pid && chn_polltrigger(c)) selwakeup(&b->sel);
 }
 
 /*
+ * chn_dmawakeup() wakes up any process sleeping.
+ * Must be called at spltty().
+ */
+static void
+chn_dmawakeup(pcm_channel *c)
+{
+       snd_dbuf *b = &c->buffer;
+
+       wakeup(b);
+}
+
+/*
  * chn_dmaupdate() tracks the status of a dma transfer,
  * updating pointers. It must be called at spltty().
  *
@@ -181,8 +198,9 @@
 }
 
 /*
- * Check channel for underflow occured, reset DMA buffer in case of
- * underflow. It must be called at spltty().
+ * Check channel for underflow occured. Reset DMA buffer in case of
+ * underflow, so that new data can go into the buffer. It must be
+ * called at spltty().
  */
 static void
 chn_checkunderflow(pcm_channel *c)
@@ -201,6 +219,61 @@
 }
 
 /*
+ * Feeds new data to the write dma buffer. Can be called in the bottom half.
+ * Must be called at spltty.
+ */
+static int
+chn_wrfeed(pcm_channel *c)
+{
+       snd_dbuf *b = &c->buffer;
+       snd_dbuf *bs = &c->buffer2nd;
+       int a, l, lacc;
+
+       /* ensure we always have a whole number of samples */
+       a = (1 << c->align) - 1;
+       lacc = 0;
+       /* Don't allow write unaligned data */
+       while (bs->rl > a && b->fl > a) {
+               /* ensure we always have a whole number of samples */
+               l = min(min(bs->rl, bs->bufsize - bs->rp), min(b->fl, b->bufsize - 
+b->fp)) & ~a;
+               if (l == 0)
+                       return lacc;
+               bcopy(bs->buf + bs->rp, b->buf + b->fp, l);
+               bs->fl += l;
+               bs->rl -= l;
+               bs->rp = (bs->rp + l) % bs->bufsize;
+               b->rl += l;
+               b->fl -= l;
+               b->fp = (b->fp + l) % b->bufsize;
+               lacc += l;
+       }
+
+       return lacc;
+}
+
+/* Feeds new data to the secondary write buffer. */
+static int
+chn_wrfeed2nd(pcm_channel *c, struct uio *buf)
+{
+       snd_dbuf *bs = &c->buffer2nd;
+       int l, w, wacc;
+
+       /* ensure we always have a whole number of samples */
+       wacc = 0;
+       while (buf->uio_resid > 0 && bs->fl > 0) {
+               l = min(bs->fl, bs->bufsize - bs->fp);
+               w = c->feeder->feed(c->feeder, c, bs->buf + bs->fp, l, buf);
+               if (w == 0) panic("no feed");
+               bs->rl += w;
+               bs->fl -= w;
+               bs->fp = (bs->fp + w) % bs->bufsize;
+               wacc += w;
+       }
+
+       return wacc;
+}
+
+/*
  * Write interrupt routine. Can be called from other places (e.g.
  * to start a paused transfer), but with interrupts disabled.
  */
@@ -222,7 +295,14 @@
        * needed when doing stereo and 16-bit.
        */
        if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED;
-       else start = (b->rl >= DMA_ALIGN_THRESHOLD && !(c->flags & CHN_F_ABORTING));
+       else {
+               /* Fill up the DMA buffer. */
+               if (chn_wrfeed(c) > 0) {
+                       chn_dmawakeup(c);
+                       while(chn_wrfeed(c) > 0);
+               }
+               start = (b->rl >= DMA_ALIGN_THRESHOLD && !(c->flags & CHN_F_ABORTING));
+       }
        if (start) {
                int l;
                chn_dmaupdate(c);
@@ -237,13 +317,13 @@
                        /* Start DMA operation */
                        b->dl = c->blocksize ; /* record new transfer size */
                        chn_trigger(c, PCMTRIG_START);
-               } else if (b->dl != l) {
+               }
+               if (b->dl != l)
                        /*
                         * we are near to underflow condition, so to prevent
                         * audio 'clicks' clear next 1.5*dl bytes
                         */
                         chn_clearbuf(c, (b->dl*3)/2);
-               }
        } else {
                /* cannot start a new dma transfer */
                DEB(printf("cannot start wr-dma flags 0x%08x rp %d rl %d\n",
@@ -274,10 +354,9 @@
 int
 chn_write(pcm_channel *c, struct uio *buf)
 {
-       int             a, l, w, timeout, ret = 0, rc;
+       int             ret = 0, timeout;
        long            s;
        snd_dbuf       *b = &c->buffer;
-       int             threshold, maxthreshold, minthreshold;
 
        if (c->flags & CHN_F_WRITING) {
                /* This shouldn't happen and is actually silly
@@ -286,57 +365,36 @@
                tsleep(&s, PZERO, "pcmwrW", hz);
                return EBUSY;
        }
-       a = (1 << c->align) - 1;
-       maxthreshold = (b->dl / 4 + a) & ~a;
-       minthreshold = a;
        c->flags |= CHN_F_WRITING;
+       /*
+        * Fill up the secondary and DMA buffer.
+        * chn_wrfeed*() takes care of the alignment.
+        */
        s = spltty();
        chn_checkunderflow(c);
-       splx(s);
-       while ((buf->uio_resid + c->smegcnt) > minthreshold ) { /* Don't allow write 
unaligned data */
-               threshold = min((buf->uio_resid + c->smegcnt), maxthreshold);
-               if (b->fl < threshold) {
-                       if (c->flags & CHN_F_NBIO) {
-                               ret = -1;
-                               break;
-                       }
-                       timeout = (buf->uio_resid >= b->dl)? hz : 1;
-                       rc = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout);
-                       if (rc == 0 || rc == EWOULDBLOCK) {
-                               s = spltty();
-                               chn_checkunderflow(c);
-                               splx(s);
-                               if (b->fl < minthreshold) continue; /* write only 
alligned chunk of data */
-                       } else {
-#if 0
-                               if (ret == EINTR) chn_abort(c);
-#endif
-                               ret = rc;
-                               break;
-                       }
-               }
-               /* ensure we always have a whole number of samples */
-               l = min(b->fl, b->bufsize - b->fp) & ~a;
-               if (l == 0) continue;
-               w = c->feeder->feed(c->feeder, c, b->buf + b->fp, l, buf);
-               KASSERT(w, ("chn_write: no feed"));
-               s = spltty();
-               b->rl += w;
-               b->fl -= w;
-               b->fp = (b->fp + w) % b->bufsize;
-               splx(s);
-               DEB(if(1) printf("write %d bytes fp %d rl %d\n",w ,b->fp, b->rl));
-               if (!b->dl) chn_stintr(c);
-       }
-       if ((ret == 0) && (buf->uio_resid > 0)) {
-               s = spltty();
-               l = buf->uio_resid;
-               KASSERT( (c->smegcnt + l) < SMEGBUFSZ, ("resid overflow %d", l));
-               uiomove(c->smegbuf + c->smegcnt, l, buf);
-               c->smegcnt += l;
-               splx(s);
-       }
+       while (chn_wrfeed2nd(c, buf) > 0 || chn_wrfeed(c) > 0);
+       /* Start playing if not yet. */
+       if (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) {
+                       splx(s);
+                       timeout = (buf->uio_resid >= b->dl)? hz / 20 : 1;
+                       ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout);
+                       s = spltty();
+#if notdef
+                       if (ret == EINTR) chn_abort(c);
+#endif /* notdef */
+                       if (ret == EINTR || ret == ERESTART) break;
+                       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;
 }
 
@@ -370,6 +428,56 @@
 
  */
 
+/*
+ * Feed new data from the read buffer. Can be called in the bottom half.
+ * Must be called at spltty.
+ */
+static int
+chn_rdfeed(pcm_channel *c)
+{
+       snd_dbuf *b = &c->buffer;
+       snd_dbuf *bs = &c->buffer2nd;
+       int l, lacc;
+
+       /* ensure we always have a whole number of samples */
+       lacc = 0;
+       while (bs->fl >= DMA_ALIGN_THRESHOLD && b->rl >= DMA_ALIGN_THRESHOLD) {
+               l = min(min(bs->fl, bs->bufsize - bs->fp), min(b->rl, b->bufsize - 
+b->rp)) & DMA_ALIGN_MASK;
+               bcopy(b->buf + b->rp, bs->buf + bs->fp, l);
+               bs->fl -= l;
+               bs->rl += l;
+               bs->fp = (bs->fp + l) % bs->bufsize;
+               b->rl -= l;
+               b->fl += l;
+               b->rp = (b->rp + l) % b->bufsize;
+               lacc += l;
+       }
+
+       return lacc;
+}
+
+/* Feeds new data from the secondary read buffer. */
+static int
+chn_rdfeed2nd(pcm_channel *c, struct uio *buf)
+{
+       snd_dbuf *bs = &c->buffer2nd;
+       int l, w, wacc;
+
+       /* ensure we always have a whole number of samples */
+       wacc = 0;
+       while (buf->uio_resid > 0 && bs->rl > 0) {
+               l = min(bs->rl, bs->bufsize - bs->rp);
+               w = c->feeder->feed(c->feeder, c, bs->buf + bs->rp, l, buf);
+               if (w == 0) panic("no feed");
+               bs->fl += w;
+               bs->rl -= w;
+               bs->rp = (bs->rp + w) % bs->bufsize;
+               wacc += w;
+       }
+
+       return wacc;
+}
+
 /* read interrupt routine. Must be called with interrupts blocked. */
 static void
 chn_rdintr(pcm_channel *c)
@@ -383,7 +491,14 @@
                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 start = (b->fl > 0x200 && !(c->flags & CHN_F_ABORTING));
+       else {
+               /* Suck up the DMA buffer. */
+               if (chn_rdfeed(c) > 0) {
+                       chn_dmawakeup(c);
+                       while (chn_rdfeed(c) > 0);
+               }
+               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;
@@ -431,7 +546,7 @@
 int
 chn_read(pcm_channel *c, struct uio *buf)
 {
-       int             w, l, timeout, limit, ret = 0;
+       int             ret = 0, timeout, limit;
        long            s;
        snd_dbuf       *b = &c->buffer;
 
@@ -441,33 +556,31 @@
                return (EBUSY);
        }
 
-       if (!b->rl & !b->dl) chn_stintr(c);
+       s = spltty();
        c->flags |= CHN_F_READING;
        limit = buf->uio_resid - c->blocksize;
        if (limit < 0) limit = 0;
-       while (buf->uio_resid > limit) {
-               s = spltty();
-               chn_dmaupdate(c);
-               splx(s);
-               if (b->rl < DMA_ALIGN_THRESHOLD) {
-                       if (c->flags & CHN_F_NBIO) break;
-                       timeout = (buf->uio_resid - limit >= b->dl)? hz : 1;
-                       ret = tsleep(b, PRIBIO | PCATCH, "pcmrd", timeout);
+       /* 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 (!(c->flags & CHN_F_NBIO)) {
+               /* Wait until all samples are captured. */
+               while (buf->uio_resid > 0) {
+                       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;
-                       ret = 0;
-                       continue;
+                       /* 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);
                }
-               /* ensure we always have a whole number of samples */
-               l = min(b->rl, b->bufsize - b->rp) & DMA_ALIGN_MASK;
-               w = c->feeder->feed(c->feeder, c, b->buf + b->rp, l, buf);
-               s = spltty();
-               b->rl -= w;
-               b->fl += w;
-               b->rp = (b->rp + w) % b->bufsize;
-               splx(s);
        }
        c->flags &= ~CHN_F_READING;
+       splx(s);
        return ret;
 }
 
@@ -534,6 +647,7 @@
 chn_resetbuf(pcm_channel *c)
 {
        snd_dbuf *b = &c->buffer;
+       snd_dbuf *bs = &c->buffer2nd;
 
        c->smegcnt = 0;
        c->buffer.sample_size = 1;
@@ -547,6 +661,9 @@
        b->prev_int_count = b->int_count = 0;
        b->first_poll = 1;
        b->underflow=0;
+       bs->rp = bs->fp = 0;
+       bs->dl = bs->rl = 0;
+       bs->fl = bs->bufsize;
 }
 
 void
@@ -631,6 +748,7 @@
        long s;
        int missing = 0;
        snd_dbuf *b = &c->buffer;
+       snd_dbuf *bs = &c->buffer2nd;
 
        s = spltty();
        if (b->dl) {
@@ -639,7 +757,7 @@
                chn_trigger(c, PCMTRIG_ABORT);
                chn_dmadone(c);
        }
-       missing = b->rl;
+       missing = b->rl + bs->rl;
        splx(s);
        return missing;
 }
@@ -708,11 +826,18 @@
 int
 chn_init(pcm_channel *c, void *devinfo, int dir)
 {
+       snd_dbuf       *bs = &c->buffer2nd;
+
        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_2NDBUFSIZE;
+       bs->buf = malloc(bs->bufsize, M_DEVBUF, M_NOWAIT);
+       bzero(bs->buf, bs->bufsize);
+       bs->rl = bs->rp = bs->fp = 0;
+       bs->fl = bs->bufsize;
        return 0;
 }
 
Index: datatypes.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/pcm/datatypes.h,v
retrieving revision 1.5
diff -u -r1.5 datatypes.h
--- datatypes.h 1999/12/05 19:09:12     1.5
+++ datatypes.h 1999/12/11 09:34:25
@@ -127,6 +127,7 @@
        u_int8_t smegbuf[SMEGBUFSZ];
        u_int32_t smegcnt;
        void *devinfo;
+       snd_dbuf buffer2nd;
 };
 
 #define SND_STATUSLEN  64

-- 
Seigo Tanimura <[EMAIL PROTECTED]> <[EMAIL PROTECTED]>

Reply via email to