[PARPORT] ECP and DMA

From: Duncan Haldane (f.duncan.m.haldane@worldnet.att.net)
Date: Tue Mar 11 2003 - 03:10:57 EST

  • Next message: Blaise Gassend: "Re: [PARPORT] ECP and DMA"

    Hi, the IEEE1284 DMA ECP read support was absent in parport_pc.c,
    but I got it working in 2.4.x (x > 18), at least for CPiA-based webcams.
    Here is the patch which probably needs some further hacking..

    I couldnt get PIO (non DMA) ECP working, the systems just crashes in that case,
    seems to be an interrupt issue....

    But DMA ECP can be made to work with parport_pc.c.

    It would be good if the Linux IEEE1284 ECP support could be completed.

    Duncan
    On Mon 10 March 2003 mark anderson <andersom@frii.com> wrote:
    >I've found a note in the parport bug list (Suse, 2.4.10-4.GB):
    >
    >o parport_pc_ecp_read_block_pio() is broken. parport will revert to theI've
    found a note in the parport bug list (Suse, 2.4.10-4.GB):

    o parport_pc_ecp_read_block_pio() is broken. parport will revert to the
      software-driven mode in ieee1284_ops.c

    Is this telling me that DMA reads under ECP are broken? As I read
    various parallel port documents I'm lead to believe that only ECP
    supports DMA.

    Any thoughts would be appreciated. Thanks.

    > software-driven mode in ieee1284_ops.c
    >
    >Is this telling me that DMA reads under ECP are broken? As I read
    >various parallel port documents I'm lead to believe that only ECP
    >supports DMA.

    >Any thoughts would be appreciated. Thanks.
    >
    >--mark

    -----FW: <XFMail.20030106045700.f.duncan.m.haldane@worldnet.att.net>-----

    Date: Mon, 06 Jan 2003 05:00:19 -0500 (EST)
    Sender: owner-linux-parport@gear.torque.net
    From: Duncan Haldane <f.duncan.m.haldane@worldnet.att.net>
    To: linux-parport@torque.net
    Subject: [PARPORT] [PATCH] DMA ieee1284 read support in parport_pc.c
    Cc: blaise@gassend.com

    Hi,

    The ECP DMA read support in parport_pc.c is missing, but I've got it working
    with the CPiA Webcam driver, using a patch sent to me by Blaise Gassend
    (<blaise@gassend.com>) who has managed to blend elements of the two functions

     parport_pc_ecp_read_block_pio() (for ieee1284 stuff)
     parport_pc_fifo_write_block_dma() (for dma stuff)

    to create a "parport_pc_ecp_read_block_dma()" to do IEEE1284 DMA reads.

    There are still some unsolved issues (see the #if 0.. #endif and
    #if 1 .. #endif changes from the "expected" merge of the IEEE1284 stuff
    with the DMA stuff). Can anyone give any advice?

    The cpia driver doesnt need to know anything about the DMA support, its all
    done transparently through the IEEE1284 support, except that some cleanup
    is done by:

    -------------from cpia_pp.c-(modified)------------------------------
    static void EndTransferMode(struct pp_cam_entry *cam)
    {
            if (cam->port->dma != PARPORT_DMA_NONE){
                    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);
    }
    -------------------------------------------------------------
    I guess this could go into some place in parport_pc.c so the cpia driver
    would have no involvement at all with lowlevel ECP setup/putdown stuff.

    (where ?)

    In parport_pc_ecp_read_block_dma, the final ieee384 stuff where
    one goes from the REV_IDLE to FWD_IDLE phase needs to be commented
    out for the cpia driver to work (I dont yet know why), and
    the manually-read 1st byte seems to be discarded. Also moving the
    change_mode(port, ECR_ECP) to before the initial change to reverse_idle
    was necessary (maybe because the final change to fwd idle was dropped)?

    Also the switch from ECR_ECP mode to ECR_PS2 mode is postponed till
    parport_pc_unregister_port in the hacked parport_pc.c (this is the only
    change apart from adding the new function).
     

    Since an (undated) comment in tne code implies that
    parport_pc_ecp_read_block_pio()
    is "currently broken but FB is working on it...."
    some of the discrepancies may be due to this breakage.

    Does anyone on this list remember the details?

    I would like to get Blaise's patch into better shape for
    possible inclusion in parport_pc.c.

    I append my version of it below...

    Duncan

    ------------adapted from a patch by Blaise Gassend-----------------

    --- parport_pc.c.orig Sun Nov 10 11:34:29 2002
    +++ parport_pc.c Sun Jan 5 23:40:46 2003
    @@ -1223,6 +1223,277 @@
     dump_parport_state ("fwd idle", port);
            return length - left;
     }
    +#if 0
    +size_t parport_pc_ecp_write_block_dma (struct parport *port,
    + const void *buf, size_t length,
    + int flags)
    +{
    + /* TODO !!! */
    +}
    +#endif
    +static size_t parport_pc_ecp_read_block_dma (struct parport *port,
    + void *bufv, size_t length, int flags)
    +{
    + int r = 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 = length;
    + struct parport *dma_port;
    +
    + 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);
    +
    +#if 1
    + r = change_mode (port, ECR_ECP); /* ECP FIFO */
    + if (r) printk (KERN_DEBUG "%s: Warning change_mode ECR_ECP
    failed\n", port->name);
    +#endif
    +
    + 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_STROBE
    |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 */
    + r = parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
    + if (r) {
    + printk (KERN_DEBUG "%s: PE timeout Event 40 (%d) "
    + "in ecp_read_block_dma\n", port->name, r);
    + return 0;
    + }
    +
    +
    + port->ieee1284.phase = IEEE1284_PH_REV_DATA;
    +
    + parport_frob_control (port,
    + PARPORT_CONTROL_STROBE
    |PARPORT_CONTROL_AUTOFD,
    + PARPORT_CONTROL_AUTOFD);
    +
    +#if 0
    + r = change_mode (port, ECR_ECP); /* ECP FIFO */
    + if (r) printk (KERN_DEBUG "%s: Warning change_mode ECR_ECP
    failed\n", port->name);
    +#endif
    + /* the first byte must be collected manually */
    + dump_parport_state ("pre 43", port);
    + /* Event 43: Wait for nAck to go low */
    + r = parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0);
    + if (r) {
    + /* timed out while reading -- no data */
    + printk (KERN_DEBUG "DMA read timed out (initial
    byte)\n");
    + goto out_no_data;
    + }
    +#if 0 /* this is active in parport_pc_ecp_read_block_pio */
    + /* read byte */
    + *buf++ = inb (DATA (port));
    + left--;
    +#endif
    + dump_parport_state ("43-44", port);
    + /* Event 44: nAutoFd (HostAck) goes high to acknowledge */
    + parport_pc_frob_control (port,
    + PARPORT_CONTROL_AUTOFD,
    + 0);
    +#if 1 /* this is commented out in parport_pc_ecp_read_block_pio */
    + dump_parport_state ("pre 45", port);
    + /* Event 45: Wait for nAck to go high */
    + r = parport_wait_peripheral (port, PARPORT_STATUS_ACK,
    PARPORT_STATUS_ACK);
    + dump_parport_state ("post 45", port);
    + if (r) {
    + /* timed out while waiting for peripheral to respond to
    ack */
    + printk (KERN_DEBUG "ECP DMA read timed out (waiting for
    nAck)\n");
    +
    + /* keep hold of the byte we've got already */
    + goto out_no_data;
    + }
    +#endif
    + }
    +
    +#if 0 /* active in parport_pc_ecp_read_block_pio */
    + /* Event 46: nAutoFd (HostAck) goes low to accept more data */
    + parport_pc_frob_control (port,
    + PARPORT_CONTROL_AUTOFD,
    + PARPORT_CONTROL_AUTOFD);
    +
    +
    + dump_parport_state ("rev idle", port);
    +#endif
    +
    +
    + if (!left)
    + goto out_no_data;
    +
    + /* now do the DMA transfer (adapted from
    parport_pc_fifo_write_block_dma) */
    +
    + 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");
    +
    + dma_port = port->physport;
    +
    + /* We don't want to be interrupted every character. */
    + parport_pc_disable_irq (dma_port);
    + /* set nErrIntrEn and serviceIntr */
    + frob_econtrol (dma_port, (1<<4) | (1<<2), (1<<4) | (1<<2));
    +
    + /* Reverse mode. */
    + parport_pc_data_reverse (dma_port); /* Must be in PS2 mode */
    +
    + dump_parport_state ("begin ecp_read_block_dma transfer", port);
    + while (left) {
    + long expire = jiffies + dma_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(dma_port->dma);
    + clear_dma_ff(dma_port->dma);
    + set_dma_mode(dma_port->dma, DMA_MODE_READ);
    + set_dma_addr(dma_port->dma, dma_addr);
    + set_dma_count(dma_port->dma, count);
    +
    + /* Set DMA mode */
    + frob_econtrol (dma_port, 1<<3, 1<<3);
    +
    + /* Clear serviceIntr */
    + frob_econtrol (dma_port, 1<<2, 0);
    +
    + enable_dma(dma_port->dma);
    + release_dma_lock(dmaflag);
    +
    + /* Wait for interrupt. */
    + DPRINTK (KERN_DEBUG "about to wait\n");
    + false_alarm:
    + r = parport_wait_event (dma_port, HZ);
    + if (r < 0) break;
    + r = 0;
    + if (!time_before (jiffies, expire)) {
    + /* Timed out. */
    + printk (KERN_DEBUG "DMA read timed out\n");
    + break;
    + }
    + /* Is serviceIntr set? */
    + if (!(inb (ECONTROL (dma_port)) & (1<<2))) {
    + if (current->need_resched)
    + schedule ();
    +
    + goto false_alarm;
    + }
    +
    + dmaflag = claim_dma_lock();
    + disable_dma(dma_port->dma);
    + clear_dma_ff(dma_port->dma);
    + count -= get_dma_residue(dma_port->dma);
    + release_dma_lock(dmaflag);
    +
    + if (current->need_resched)
    + /* Can't yield the port. */
    + schedule ();
    +
    + /* Anyone else waiting for the port? */
    + if (dma_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(dma_port->dma);
    + clear_dma_ff(dma_port->dma);
    + count -= get_dma_residue(dma_port->dma);
    + release_dma_lock(dmaflag);
    +
    + if (!dma_handle)
    + memcpy(buf, priv->dma_buf, count);
    + left -= count;
    + }
    +
    + frob_econtrol (dma_port, 1<<2, 1<<2);
    + /* Turn off DMA mode */
    + frob_econtrol (dma_port, 1<<3, 0);
    +
    + if (dma_handle)
    + pci_unmap_single(priv->dev, dma_handle, length,
    PCI_DMA_TODEVICE);
    +
    + dump_parport_state ("end ecp_read_block_dma transfer",port);
    +
    + out_no_data:
    +
    +#if 0 /* active in parport_pc_ecp_read_block_pio */
    + r = change_mode (port, ECR_PS2); /* ECP FIFO */
    + if (r) printk (KERN_DEBUG "%s: Warning change_mode ECR_PS2 failed\n",
    port->name);
    +
    + /* 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 */
    + r = parport_wait_peripheral (port,
    + PARPORT_STATUS_PAPEROUT,
    + PARPORT_STATUS_PAPEROUT);
    + if (r) {
    + printk (KERN_DEBUG
    + "%s: PE timeout FWDIDLE (%d) in ecp_read_block_dma\n",
    + port->name, r);
    + }
    + else
    + {
    + parport_data_forward (port);
    + port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
    + }
    + dump_parport_state ("fwd idle", port);
    +#else
    + port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
    + dump_parport_state ("rev idle", port);
    +#endif
    + DPRINTK (KERN_DEBUG "returning %i bytes read\n", length - left);
    + return length - left;
    +}
     
     #endif /* IEEE 1284 support */
     #endif /* Allowed to use FIFO/DMA */
    @@ -2319,6 +2590,9 @@
                    if (p->dma != PARPORT_DMA_NONE) {
                            printk(", dma %d", p->dma);
                            p->modes |= PARPORT_MODE_DMA;
    +#ifdef CONFIG_PARPORT_1284
    + p->ops->ecp_read_data = parport_pc_ecp_read_block_dma;
    +#endif /* IEEE 1284 support */
                    }
                    else printk(", using FIFO");
            }
    @@ -2420,8 +2694,10 @@
            release_region(p->base, 3);
            if (p->size > 3)
                    release_region(p->base + 3, p->size - 3);
    - if (p->modes & PARPORT_MODE_ECP)
    + if (p->modes & PARPORT_MODE_ECP){
                    release_region(p->base_hi, 3);
    + change_mode (p, ECR_PS2);
    + }
            parport_proc_unregister(p);
     #ifdef CONFIG_PARPORT_PC_FIFO
            if (priv->dma_buf)

     

    ----------------------------------
    E-Mail: Duncan Haldane <f.duncan.m.haldane@worldnet.att.net>
    Date: 06-Jan-2003
    Time: 04:10:13

    This message was sent by XFMail
    ----------------------------------

    -- To unsubscribe, send mail to: linux-parport-request@torque.net --
    -- with the single word "unsubscribe" in the body of the message. --

    --------------End of forwarded message-------------------------

    ----------------------------------
    E-Mail: Duncan Haldane <f.duncan.m.haldane@worldnet.att.net>
    Date: 11-Mar-2003
    Time: 02:57:19

    This message was sent by XFMail
    ----------------------------------

    -- To unsubscribe, send mail to: linux-parport-request@torque.net --
    -- with the single word "unsubscribe" in the body of the message. --



    This archive was generated by hypermail 2b29 : Tue Mar 11 2003 - 03:15:57 EST