Skip site navigation (1)Skip section navigation (2)
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>