[cpia] DMA mode in 2.4.x

Blaise Gassend gassend@MIT.EDU
Mon, 7 Jan 2002 22:48:23 -0500


Hi,

Having been forced to upgrade to a 2.4.x kernel, I finally took the time
to get the DMA going on my parallel port CPiA camera.

A working but not very clean piece of code was produced, it has been
working fine for a couple of weeks now.

I am posting it here for the people who are frustrated with the
hopelessly slow speeds of the non-DMA transfer, and who still want to
move to 2.4.x.

For now, I expect that this code will probably bother Parallel devices
other than the CPIA camera if they happen to do block reads with DMA
turned on. I hope to fix this shortly.

This was taken from a 2.4.6 kernel, but I haven't seen any changes that
should prevent it from working with later kernels. In any case, you are
trying at your own risk, of course.

Cheers,
Blaise

Two patches follow. Sorry if this isn't the approved way of doing
things, I'm new to this list.




The firts patch applies to /usr/src/linux/drivers/media/video/cpia_pp.c


39c39
< /*#define _CPIA_DEBUG_		define for verbose debug output */
---
> /* #define _CPIA_DEBUG_		define for verbose debug output */
205,211c205
<   int ecr = ECONTROL(cam->port);
<   int oecr = inb(ecr);
<   oecr &= ~(7 << 5);
<   oecr |= 1 << 5;
<   outb (oecr, ecr);
< 			  
<   parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
---
> 	parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
309d302
< 	
479d471
< 






The second patch applies to /usr/src/linux/drivers/parport/parport_pc.c


16d15
<  * Added DMA read support, only for CPiA so far. Blaise Gassend 12/01
78c77
< //#define DEBUG
---
> #define DEBUG
680,682c679
< 	DPRINTK (KERN_DEBUG "parport_pc_ecp_write_block_dma\n");
< 
< 	dump_parport_state ("enter fifo_write_block_dma", port);
---
> dump_parport_state ("enter fifo_write_block_dma", port);
796,1032d792
< static size_t parport_pc_fifo_read_block_dma (struct parport *port,
<     void *bufv, size_t length, int flags)
< {
< 	int ret = 0;
< 	unsigned long dmaflag;
< 	size_t left = length;
< 	const struct parport_pc_private *priv = port->physport->private_data;
< 	dma_addr_t dma_addr, dma_handle = 0;
< 	size_t maxlen = 0x10000; /* max 64k per DMA transfer */
< 	char *buf = bufv;
< 	unsigned long start = (unsigned long) buf;
< 	unsigned long end = (unsigned long) buf + length - 1;
< 	size_t count;
< 
< 	DPRINTK (KERN_DEBUG "parport_pc_ecp_read_block_dma\n");
< 	
< 	if (length == 0)
< 	  return 0;
< 	
< 	/* Special case: a timeout of zero means we cannot call schedule(). */
< 	if (!port->cad->timeout)
< 		return parport_ieee1284_ecp_read_data (port, buf,
< 				length, flags);
< 
< 	ret = change_mode (port, ECR_ECP); /* ECP FIFO */
< 	if (ret) printk (KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n", port->name);
<                 
< 	if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE) {
< 		/* change to reverse-idle phase (must be in forward-idle) */
< 
< 		/* Event 38: Set nAutoFd low (also make sure nStrobe is high) */
< 		parport_frob_control (port,
< 				PARPORT_CONTROL_AUTOFD,
< 				PARPORT_CONTROL_AUTOFD);
< 		parport_pc_data_reverse (port); /* Must be in PS2 mode */
< 		udelay (5);
< 		/* Event 39: Set nInit low to initiate bus reversal */
< 		parport_frob_control (port,
< 				PARPORT_CONTROL_INIT,
< 				0);
< 		/* Event 40: Wait for  nAckReverse (PError) to go low */
< 		ret = parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
< 		if (ret) {
< 			printk (KERN_DEBUG "%s: PE timeout Event 40 (%d) "
< 					"in ecp_read_block_dma\n", port->name, ret);
< 			return 0;
< 		}
< 
< 		port->ieee1284.phase = IEEE1284_PH_REV_DATA;
< 
< 		parport_frob_control (port,
< 		    PARPORT_CONTROL_AUTOFD,
< 		    PARPORT_CONTROL_AUTOFD);
< 
< 		/* the first byte must be collected manually */
< 		ret = parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0);
< 		if (ret) {
< 		  /* timed out while reading -- no data */
< 		  printk (KERN_DEBUG "PIO read timed out (initial byte)\n");
< 		  goto out_no_data;
< 		}
< 		/* read byte */ /* Bogus data for some reason. */
< 		/**buf++ =*/ inb (DATA (port));
< 		//	left--;
< 		dump_parport_state ("43-44", port);
< 		/* Event 44: nAutoFd (HostAck) goes high to acknowledge */
< 		parport_pc_frob_control (port,
< 		    PARPORT_CONTROL_AUTOFD,
< 		    0);
< 		dump_parport_state ("pre 45", port);
< 		/* Event 45: Wait for nAck to go high */
< 		ret = parport_wait_peripheral (port, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK); 
< 		dump_parport_state ("post 45", port);
< 		if (ret) {
< 		  /* timed out while waiting for peripheral to respond to ack */
< 		  printk (KERN_DEBUG "ECP PIO read timed out (waiting for nAck)\n");
< 
< 		  /* keep hold of the byte we've got already */
< 		  goto out_no_data;
< 		}
< 	}
< //	
< //	DPRINTK (KERN_DEBUG "first byte has been read\n");
< //	
< //	dump_parport_state ("enter fifo_read_block_dma", port);
< 	if (end < MAX_DMA_ADDRESS) {
< 		/* If it would cross a 64k boundary, cap it at the end. */
< 		if ((start ^ end) & ~0xffffUL)
< 			maxlen = 0x10000 - (start & 0xffff);
< 
< 		dma_addr = dma_handle = pci_map_single(priv->dev, (void *)buf, length,
< 						       PCI_DMA_FROMDEVICE);
<         } else {
< 		/* above 16 MB we use a bounce buffer as ISA-DMA is not possible */
< 		maxlen   = PAGE_SIZE;          /* sizeof(priv->dma_buf) */
< 		dma_addr = priv->dma_handle;
< 	}
< 	
< 	DPRINTK (KERN_DEBUG "DMA buffer is ready\n");
< 
< 	port = port->physport;
< 
< 	/* We don't want to be interrupted every character. */
< 	parport_pc_disable_irq (port);
< 	/* set nErrIntrEn and serviceIntr */
< 	frob_econtrol (port, (1<<4) | (1<<2), (1<<4) | (1<<2));
< 
< 	/* Forward mode. */
< 	parport_pc_data_reverse (port); /* Must be in PS2 mode */
< 
<         if (!left)
< 	  goto out_no_data;
< 	
< 	while (left) {
< 		long expire = jiffies + port->physport->cad->timeout;
< 	
< 		DPRINTK (KERN_DEBUG "start of while %i left\n", left);
< 
< 	        count = left;
< 
< 		if (count > maxlen)
< 			count = maxlen;
< 
< 		dmaflag = claim_dma_lock();
< 		disable_dma(port->dma);
< 		clear_dma_ff(port->dma);
< 		set_dma_mode(port->dma, DMA_MODE_READ);
< 		set_dma_addr(port->dma, dma_addr);
< 		set_dma_count(port->dma, count);
< 
< 		/* Set DMA mode */
< 		frob_econtrol (port, 1<<3, 1<<3);
< 
< 		/* Clear serviceIntr */
< 		frob_econtrol (port, 1<<2, 0);
< 
< 		enable_dma(port->dma);
< 		release_dma_lock(dmaflag);
< 
< 		/* Wait for interrupt. */
< 		DPRINTK (KERN_DEBUG "about to wait\n");
< 	false_alarm:
< 		ret = parport_wait_event (port, HZ);
< 		if (ret < 0) break;
< 		ret = 0;
< 		if (!time_before (jiffies, expire)) {
< 			/* Timed out. */
< 			printk (KERN_DEBUG "DMA read timed out\n");
< 			break;
< 		}
< 		/* Is serviceIntr set? */
< 		if (!(inb (ECONTROL (port)) & (1<<2))) {
< 			if (current->need_resched)
< 				schedule ();
< 
< 			goto false_alarm;
< 		}
< 
< 		dmaflag = claim_dma_lock();
< 		disable_dma(port->dma);
< 		clear_dma_ff(port->dma);
< 		count -= get_dma_residue(port->dma);
< 		release_dma_lock(dmaflag);
< 
< 		if (current->need_resched)
< 			/* Can't yield the port. */
< 			schedule ();
< 
< 		/* Anyone else waiting for the port? */
< 		if (port->waithead) {
< 			printk (KERN_DEBUG "Somebody wants the port\n");
< 			break;
< 		}
< 
< 		/* update for possible DMA residue ! */
< 		DPRINTK (KERN_DEBUG "read %i in loop\n", count);
< 		if (dma_handle) dma_addr += count;
< 		else memcpy(buf, priv->dma_buf, count);
< 		buf  += count;
< 		left -= count;
< 	}
< 
< 	/* Maybe got here through break, so adjust for DMA residue! */
<         if (left)
< 	{
< 	  DPRINTK (KERN_DEBUG "exited on break\n");
< 	  dmaflag = claim_dma_lock();
< 	  disable_dma(port->dma);
< 	  clear_dma_ff(port->dma);
< 	  count -= get_dma_residue(port->dma);
< 	  release_dma_lock(dmaflag);
< 
< 	  if (!dma_handle)
< 	    memcpy(buf, priv->dma_buf, count);
< 	  left -= count;
< 	}
< 
< 	dump_parport_state ("leave fifo_read_block_dma", port);
< out_no_data:
< 
< 	frob_econtrol (port, 1<<2, 1<<2);
< 	/* Turn off DMA mode */
< 	frob_econtrol (port, 1<<3, 0);
< 	
< //	  ret = change_mode (port, ECR_PS2); /* ECP FIFO */
< //          if (ret) printk (KERN_DEBUG "%s: Warning change_mode ECR_PS2 failed\n", port->name);
< 	
< 	if (dma_handle)
< 		pci_unmap_single(priv->dev, dma_handle, length, PCI_DMA_TODEVICE);
< 
< //	/* Go to forward idle mode to shut the peripheral up (event 47). */
< //	parport_frob_control (port, PARPORT_CONTROL_INIT | PARPORT_CONTROL_AUTOFD, 
< //	    PARPORT_CONTROL_INIT | PARPORT_CONTROL_AUTOFD);
< //
< //	/* event 49: PError goes high */
< //	ret = parport_wait_peripheral (port,
< //			PARPORT_STATUS_PAPEROUT,
< //			PARPORT_STATUS_PAPEROUT);
< //	if (ret) {
< //		printk (KERN_DEBUG
< //				"%s: PE timeout FWDIDLE (%d) in ecp_read_block_pio\n",
< //				port->name, ret);
< //	}
< //	else
< //	{
< //	  parport_data_forward (port);        
< //	  port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
< //	}
< //
< 	dump_parport_state ("fwd idle", port);
< 
<         port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
< 
< 	DPRINTK (KERN_DEBUG "returning %i bytes read\n", length - left);
< 	return length - left;
< }
< 
1041,1042d800
< 	DPRINTK (KERN_DEBUG "parport_pc_compat_write_block_pio\n");
< 	
1107,1108d864
< 	DPRINTK (KERN_DEBUG "parport_pc_ecp_write_block_pio\n");
< 	
1216,1217c972,973
< 	DPRINTK (KERN_DEBUG "parport_pc: parport_pc_ecp_read_block_pio\n");
< 	dump_parport_state ("enter fcn", port);
---
> DPRINTK (KERN_DEBUG "parport_pc: parport_pc_ecp_read_block_pio\n");
> dump_parport_state ("enter fcn", port);
1222c978
< 				length, flags);
---
> 						       length, flags);
1238c994
< 				length, flags);
---
> 						       length, flags);
1245,1247c1001,1003
< 				PARPORT_CONTROL_AUTOFD
< 				| PARPORT_CONTROL_STROBE,
< 				PARPORT_CONTROL_AUTOFD);
---
> 				      PARPORT_CONTROL_AUTOFD
> 				      | PARPORT_CONTROL_STROBE,
> 				      PARPORT_CONTROL_AUTOFD);
1252,1253c1008,1009
< 				PARPORT_CONTROL_INIT,
< 				0);
---
> 				      PARPORT_CONTROL_INIT,
> 				      0);
1256,1258c1012,1014
< 		if (r) {
< 			printk (KERN_DEBUG "%s: PE timeout Event 40 (%d) "
< 					"in ecp_read_block_pio\n", port->name, r);
---
>                 if (r) {
>                         printk (KERN_DEBUG "%s: PE timeout Event 40 (%d) "
> 				"in ecp_read_block_pio\n", port->name, r);
1264,1267c1020,1023
< 	/*	parport_pc_frob_control (port,
< 		PARPORT_CONTROL_STROBE |
< 		PARPORT_CONTROL_AUTOFD,
< 		PARPORT_CONTROL_AUTOFD); */
---
> /*	parport_pc_frob_control (port,
> 				 PARPORT_CONTROL_STROBE |
> 				 PARPORT_CONTROL_AUTOFD,
> 				 PARPORT_CONTROL_AUTOFD); */
1274c1030
< 	dump_parport_state ("pre 43", port);
---
> dump_parport_state ("pre 43", port);
1285c1041
< 	dump_parport_state ("43-44", port);
---
> dump_parport_state ("43-44", port);
1288,1290c1044,1046
< 			PARPORT_CONTROL_AUTOFD,
< 			0);
< 	dump_parport_state ("pre 45", port);
---
> 				 PARPORT_CONTROL_AUTOFD,
> 				 0);
> dump_parport_state ("pre 45", port);
1292,1294c1048,1050
< 	/*	r = parport_wait_peripheral (port, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK); */
< 	dump_parport_state ("post 45", port);
< 	r = 0;
---
> /*	r = parport_wait_peripheral (port, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK); */
> dump_parport_state ("post 45", port);
> r = 0;
1304,1305c1060,1061
< 			PARPORT_CONTROL_AUTOFD,
< 			PARPORT_CONTROL_AUTOFD);
---
> 				 PARPORT_CONTROL_AUTOFD,
> 				 PARPORT_CONTROL_AUTOFD);
1308c1064
< 	dump_parport_state ("rev idle", port);
---
> dump_parport_state ("rev idle", port);
1320,1323c1076,1079
< 		 * that case ECP is already holding back the
< 		 * peripheral (assuming proper design) with a delayed
< 		 * handshake.  Work fast to avoid a peripheral
< 		 * timeout.  */
---
>                  * that case ECP is already holding back the
>                  * peripheral (assuming proper design) with a delayed
>                  * handshake.  Work fast to avoid a peripheral
>                  * timeout.  */
1327c1083
< 			dump_parport_state ("FIFO empty", port);
---
> dump_parport_state ("FIFO empty", port);
1337,1338c1093,1094
< false_alarm:
< 			dump_parport_state ("waiting", port);
---
> 		false_alarm:
> dump_parport_state ("waiting", port);
1340c1096
< 			DPRINTK (KERN_DEBUG "parport_wait_event returned %d\n", ret);
---
> DPRINTK (KERN_DEBUG "parport_wait_event returned %d\n", ret);
1346c1102
< 				dump_parport_state ("timeout", port);
---
> dump_parport_state ("timeout", port);
1353c1109
< 						time_before (jiffies, expire)) {
---
> 				    time_before (jiffies, expire)) {
1360,1362c1116,1118
< 			 * set, how long interrupt service took, and
< 			 * how fast the peripheral is, we might be
< 			 * lucky and have a just filled FIFO. */
---
>                          * set, how long interrupt service took, and
>                          * how fast the peripheral is, we might be
>                          * lucky and have a just filled FIFO. */
1368c1124
< 			dump_parport_state ("FIFO full", port);
---
> dump_parport_state ("FIFO full", port);
1375c1131
< 		DPRINTK (KERN_DEBUG "*** ecp_read_block_pio: reading one byte from the FIFO\n");
---
> DPRINTK (KERN_DEBUG "*** ecp_read_block_pio: reading one byte from the FIFO\n");
1378,1379c1134,1135
< 		 * and either the peripheral will fill it faster,
< 		 * tripping a fast empty with insb, or we empty it. */
---
>                  * and either the peripheral will fill it faster,
>                  * tripping a fast empty with insb, or we empty it. */
1391c1147
< 	dump_parport_state ("rev idle2", port);
---
> dump_parport_state ("rev idle2", port);
1400,1401c1156,1157
< 			PARPORT_STATUS_PAPEROUT,
< 			PARPORT_STATUS_PAPEROUT);
---
> 				     PARPORT_STATUS_PAPEROUT,
> 				     PARPORT_STATUS_PAPEROUT);
1404,1405c1160,1161
< 				"%s: PE timeout FWDIDLE (%d) in ecp_read_block_pio\n",
< 				port->name, r);
---
> 			"%s: PE timeout FWDIDLE (%d) in ecp_read_block_pio\n",
> 			port->name, r);
1416c1172
< 					port->name, lost);
---
> 				port->name, lost);
1419c1175
< 	dump_parport_state ("fwd idle", port);
---
> dump_parport_state ("fwd idle", port);
2503,2504c2259
< 		/* p->ops->ecp_read_data = parport_pc_ecp_read_block_pio;
< 		 * */
---
> 		/* p->ops->ecp_read_data = parport_pc_ecp_read_block_pio; */
2509d2263
< 			p->ops->ecp_read_data = parport_pc_fifo_read_block_dma;
2610d2363
< 	{
2612,2613d2364
< 		change_mode (p, ECR_PS2);
< 	}