Subject: port-i386/700: i386 dma interface lacks functionality
To: None <gnats-admin@NetBSD.ORG>
From: None <kenh@entropic.com>
List: netbsd-bugs
Date: 01/04/1995 21:05:10
>Number:         700
>Category:       port-i386
>Synopsis:       The i386 interface to the PC DMA controller lacks functionality
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    gnats-admin (GNATS administrator)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Wed Jan  4 21:05:05 1995
>Originator:     Ken Hornstein
>Organization:
"	"
>Release:        1.0
>Environment:
	
System: NetBSD excalibur 1.0 NetBSD 1.0 (EXCALIBUR) #39: Wed Jan 4 23:44:48 EST 1995 kenh@excalibur:/usr/src/sys/arch/i386/compile/EXCALIBUR i386


>Description:
The "flags" argument to isa_dmastart is more than slightly bogus.  It uses the
same flags as the kernel buffers to indicate read or write, and doesn't provide
a way to get at some of the more useful features of the DMA controller (like
auto-initialization).
	
>How-To-Repeat:
Try writing a device driver that requires the DMA controller to use auto-
initialization.
	
>Fix:
	
The following patch should solve this problem.  Instead of using the kernel
buf flags B_READ and B_WRITE to indicate a DMA read or write, you now use the
flags DMA_READ and DMA_WRITE.  You also can specify other flags (for example,
DMA_AUTOINIT, or DMAMODE_BLOCK).  This allows the isa_dmastart interface to
be easily extended.

Note - I tested this with the floppy drive, and as far as I can tell, it works
fine; both reads and writes to my floppy drive are ok.  Since I don't have
a QIC tape drive, I couldn't test that.  I think the changes I made to wt.c
are correct, but I can't be 100% sure.  I am pretty sure the changes made to
dma.c are correct, at least.

--- dma.c.orig	Wed Jan  4 23:28:01 1995
+++ dma.c	Wed Jan  4 23:37:29 1995
@@ -62,6 +62,7 @@
 	vm_offset_t phys;
 	int waport;
 	caddr_t newaddr;
+	int dmaflags;
 
 #ifdef DIAGNOSTIC
 	if (chan < 0 || chan > 7 ||
@@ -79,7 +80,7 @@
 		newaddr = dma_bounce[chan];
 		*(int *) newaddr = 0;	/* XXX */
 		/* copy bounce buffer on write */
-		if ((flags & B_READ) == 0)
+		if ((flags & DMA_READ) == 0)
 			bcopy(addr, newaddr, nbytes);
 		addr = newaddr;
 	}
@@ -87,16 +88,27 @@
 	/* translate to physical */
 	phys = pmap_extract(kernel_pmap, (vm_offset_t)addr);
 
+	/* set flags for DMA controller based on what we were given */
+
+	dmaflags = (flags & DMA_READ) ? DMA37MD_WRITE : DMA37MD_READ;
+
+	if (flags & DMAMODE_DEMAND)
+		dmaflags |= DMA37MD_DEMAND;
+	else if (flags & DMAMODE_BLOCK)
+		dmaflags |= DMA37MD_BLOCK;
+	else
+		dmaflags |= DMA37MD_SINGLE;
+
+	if (flags & DMA_AUTOINIT)
+		dmaflags |= DMA37MD_AUTO;
+
 	if ((chan & 4) == 0) {
 		/*
 		 * Program one of DMA channels 0..3.  These are
 		 * byte mode channels.
 		 */
 		/* set dma channel mode, and reset address ff */
-		if (flags & B_READ)
-			outb(DMA1_MODE, chan | DMA37MD_SINGLE | DMA37MD_WRITE);
-		else
-			outb(DMA1_MODE, chan | DMA37MD_SINGLE | DMA37MD_READ);
+		outb(DMA1_MODE, chan | dmaflags);
 		outb(DMA1_FFC, 0);
 
 		/* send start address */
@@ -117,10 +129,7 @@
 		 * word mode channels.
 		 */
 		/* set dma channel mode, and reset address ff */
-		if (flags & B_READ)
-			outb(DMA2_MODE, (chan & 3) | DMA37MD_SINGLE | DMA37MD_WRITE);
-		else
-			outb(DMA2_MODE, (chan & 3) | DMA37MD_SINGLE | DMA37MD_READ);
+		outb(DMA2_MODE, (chan & 3) | dmaflags);
 		outb(DMA2_FFC, 0);
 
 		/* send start address */
--- dmavar.h.orig	Wed Jan  4 23:17:25 1995
+++ dmavar.h	Wed Jan  4 23:37:24 1995
@@ -2,3 +2,17 @@
 void isa_dmastart __P((int, caddr_t, vm_size_t, int));
 void isa_dmaabort __P((int));
 void isa_dmadone __P((int, caddr_t, vm_size_t, int));
+
+/*
+ * Flags for isa_dmastart.  To make old code easier to port, set up single-step
+ * mode as a default and DMA_READ/WRITE like the buf B_READ/WRITE flags
+ */
+
+#define DMA_READ	0x01		/* Device read, memory write */
+#define DMA_WRITE	0x00		/* Device write, memory read */
+
+#define DMAMODE_SINGLE	0x00		/* Single-step mode - the default */
+#define DMAMODE_DEMAND	0x02		/* Demand mode */
+#define DMAMODE_BLOCK	0x04		/* Block mode */
+
+#define DMA_AUTOINIT	0x08		/* Auto-initialization */
--- fd.c.orig	Wed Jan  4 23:38:17 1995
+++ fd.c	Wed Jan  4 23:39:06 1995
@@ -883,7 +883,7 @@
 #endif
 		 }}
 #endif
-		read = bp->b_flags & B_READ;
+		read = (bp->b_flags & B_READ) ? DMA_READ : DMA_WRITE;
 #ifdef NEWCONFIG
 		at_dma(read, bp->b_data + fd->sc_skip, nblks * FDC_BSIZE,
 		    fdc->sc_drq);
--- wt.c.orig	Wed Jan  4 23:55:34 1995
+++ wt.c	Wed Jan  4 23:41:51 1995
@@ -648,7 +648,7 @@
 	/*
 	 * Clean up dma.
 	 */
-	if ((sc->dmaflags & B_READ) &&
+	if ((sc->dmaflags & DMA_READ) &&
 	    (sc->dmatotal - sc->dmacount) < sc->bsize) {
 		/* If reading short block, copy the internal buffer
 		 * to the user memory. */
@@ -662,7 +662,7 @@
 	 */
 	if ((x & sc->NOEXCEP) == 0) {
 		DEBUG(("i/o exception\n"));
-		wtsense(sc, 1, (sc->dmaflags & B_READ) ? TP_WRP : 0);
+		wtsense(sc, 1, (sc->dmaflags & DMA_READ) ? TP_WRP : 0);
 		if (sc->error & (TP_EOM | TP_FIL))
 			sc->flags |= TPVOL;	/* end of file */
 		else
@@ -837,7 +837,7 @@
 		outb(sc->SDMAPORT, 0);
 	}
 
-	if ((sc->dmaflags & B_READ) &&
+	if ((sc->dmaflags & DMA_READ) &&
 	    (sc->dmatotal - sc->dmacount) < sc->bsize) {
 		/* Reading short block; do it through the internal buffer. */
 		isa_dmastart(sc->dmaflags, sc->buf, sc->bsize, sc->chan);
@@ -865,7 +865,7 @@
 	sc->dmavaddr = vaddr;
 	sc->dmatotal = len;
 	sc->dmacount = 0;
-	sc->dmaflags = flag;
+	sc->dmaflags = flag & B_READ ? DMA_READ : DMA_WRITE;
 	wtdma(sc);
 	return 1;
 }
--- ic/i8237.h.orig	Wed Jan  4 23:25:02 1995
+++ ic/i8237.h	Wed Jan  4 23:27:47 1995
@@ -4,8 +4,11 @@
  *	$Id: i8237.h,v 1.3 1994/03/01 18:18:07 mycroft Exp $
  */
 
+#define DMA37MD_DEMAND	0x00	/* demand mode */
 #define	DMA37MD_SINGLE	0x40	/* single pass mode */
+#define DMA37MD_BLOCK	0x80	/* block mode */
 #define	DMA37MD_CASCADE	0xc0	/* cascade mode */
+#define DMA37MD_AUTO	0x10	/* autoinit upon completed transfer */
 #define	DMA37MD_WRITE	0x04	/* read the device, write memory operation */
 #define	DMA37MD_READ	0x08	/* write the device, read memory operation */
 	
>Audit-Trail:
>Unformatted: