[cpia] DMA mode in 2.4.x

Gleicon S. Moraes gleicon@uol.com.br
Tue, 8 Jan 2002 09:14:44 -0200


Thanks !

----- Original Message -----
From: "Blaise Gassend" <gassend@MIT.EDU>
To: <cpia@risc.uni-linz.ac.at>
Sent: Tuesday, January 08, 2002 1:48 AM
Subject: [cpia] DMA mode in 2.4.x


> 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);
> < }
>
> _______________________________________________
> cpia mailing list  -  cpia@risc.uni-linz.ac.at
> http://mailman.risc.uni-linz.ac.at/mailman/cgi-bin/listinfo/cpia
>