[PARPORT] [PATCH] 2.4.3 parport/ppdev hacks and fixes (attempt #2)

From: Fred Barnes (frmb2@ukc.ac.uk)
Date: Sun Apr 22 2001 - 10:39:36 EDT

  • Next message: Eric Tang: "[PARPORT] Some Basic Questions on Parport driver"

    Hi,

    Here's the second version of the patch, incorporating the changes
    you suggested from the last one. I undid all that 0x80000000 stuff
    for fast EPP, and used your PARPORT_EPP_FAST flag (as I ought to have
    done the first time round). A quick re-iteration of what the patch changes:

    * support for fast EPP reads/writes.
            This makes EPP behave something like the ppa/imm drivers, in
            that a timeout is only checked for after we've in'd/out'd the
            data. This assumes that the device can source/sink the required
            amount. If a timeout happens, -EIO is returned, as (afaik) it's
            impossible to tell how much was read/written OK (well, maybe 1
            byte is the exception). ppdev has been hacked to support this.

    * various new IOCTLs added to ppdev, to:
            - get hardware supported modes of parport
            - get the current IEEE mode/phase
            - get/set user-visible flags, which currently consist of
              PP_FASTREAD and PP_FASTWRITE for fast (block) EPP transfers.

    * change in ppdev, so that if a device releases the port while
            in non-compatability mode, it will be put back into compatability
            mode when close()d. This caused some problems with our
            hardware, as it was still in EPP/ECP the next time we ran the
            client software.

    * BYTE mode will be used over preference to NIBBLE for reverse
            transfers (if the peripheral supports it) -- when in compatability
            mode. I ought to store this information somewhere so if the
            peripheral can't do BYTE mode, it won't keep trying.

    * software emulated EPP fixed (and relevant chunks added to
            parport_{read,write}. Problems caused by wrong polarities
            here and there. This is for *data*, not addresses, although
            I think the address stuff should work now -- no hardware to
            test it on however.

    * added some debugging aids (dump_parport_state) to parport_pc.h
            (dumps control register from parport_pc_private->ctr and the
            hardware register)

    Things which are still broken/unfinished:

    * those added fast reads/writes only work on the 8-bit FIFO at
            the moment. We could probably allow for 16/32-bit transfers
            for some more throughput.

    * parport_pc_ecp_read_block_pio() is still royally broken. I've
            been able to get some data transferred, but it's just not
            quite correct yet. If anyone has any insights into what the
            problem is, I'd be interested to know.

    Cheers,

    -- Fred

    diff -u -p linux-2.4.3/CREDITS.orig linux-2.4.3/CREDITS
    --- linux-2.4.3/CREDITS.orig Mon Mar 26 04:14:20 2001
    +++ linux-2.4.3/CREDITS Tue Apr 17 00:12:27 2001
    @@ -221,6 +221,14 @@ S: ul. Koscielna 12a
     S: 62-300 Wrzesnia
     S: Poland
     
    +N: Fred Barnes
    +E: frmb2@ukc.ac.uk
    +D: Various parport/ppdev hacks and fixes
    +S: Computing Lab, The University
    +S: Canterbury, KENT
    +S: CT2 7NF
    +S: England
    +
     N: Paul Barton-Davis
     E: pbd@op.net
     D: Driver for WaveFront soundcards (Turtle Beach Maui, Tropez, Tropez+)
    @@ -1658,6 +1666,7 @@ S: Ecole Nationale Superieure des Teleco
     N: Jamie Lokier
     E: jamie@imbolc.ucc.ie
     D: Reboot-through-BIOS for broken 486 motherboards
    +D: Some parport fixes
     S: 11 Goodson Walk
     S: Marston
     S: Oxford
    diff -u -p linux-2.4.3/drivers/char/ppdev.c.orig linux-2.4.3/drivers/char/ppdev.c
    --- linux-2.4.3/drivers/char/ppdev.c.orig Mon Oct 16 21:58:51 2000
    +++ linux-2.4.3/drivers/char/ppdev.c Sun Apr 22 16:23:46 2001
    @@ -35,17 +35,25 @@
      * WCTLONIRQ on interrupt, set control lines
      * CLRIRQ clear (and return) interrupt count
      * SETTIME sets device timeout (struct timeval)
    - * GETTIME gets device timeout (struct timeval)
    + * GETTIME gets device timeout (struct timeval)
    + * GETMODES gets hardware supported modes (unsigned int)
    + * GETMODE gets the current IEEE1284 mode
    + * GETPHASE gets the current IEEE1284 phase
    + * GETFLAGS gets current (user-visible) flags
    + * SETFLAGS sets current (user-visible) flags
      * read/write read or write in current IEEE 1284 protocol
      * select wait for interrupt (in readfds)
      *
      * Changes:
    - * Added SETTIME/GETTIME ioctl, Fred Barnes 1999.
    + * Added SETTIME/GETTIME ioctl, Fred Barnes, 1999.
      *
      * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 2000/08/25
      * - On error, copy_from_user and copy_to_user do not return -EFAULT,
      * They return the positive number of bytes *not* copied due to address
      * space errors.
    + *
    + * Added GETMODES/GETMODE/GETPHASE ioctls, Fred Barnes <frmb2@ukc.ac.uk>, 03/01/2001.
    + * Added GETFLAGS/SETFLAGS ioctls, Fred Barnes, 04/2001
      */
     
     #include <linux/module.h>
    @@ -109,6 +117,8 @@ static ssize_t pp_read (struct file * fi
             char * kbuffer;
             ssize_t bytes_read = 0;
             ssize_t got = 0;
    + struct parport *pport;
    + int mode;
     
             if (!(pp->flags & PP_CLAIMED)) {
                     /* Don't have the port claimed */
    @@ -118,18 +128,32 @@ static ssize_t pp_read (struct file * fi
             }
     
             kbuffer = kmalloc (min (count, PP_BUFFER_SIZE), GFP_KERNEL);
    - if (!kbuffer)
    + if (!kbuffer) {
                     return -ENOMEM;
    + }
    + pport = pp->pdev->port;
    + mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR);
     
             while (bytes_read < count) {
                     ssize_t need = min(count - bytes_read, PP_BUFFER_SIZE);
     
    - got = parport_read (pp->pdev->port, kbuffer, need);
    + if ((pp->flags & PP_FASTREAD) && (mode == IEEE1284_MODE_EPP)) {
    + /* do a fast EPP read */
    + if (pport->ieee1284.mode & IEEE1284_ADDR) {
    + got = pport->ops->epp_read_addr (pport, kbuffer,
    + need, PARPORT_EPP_FAST);
    + } else {
    + got = pport->ops->epp_read_data (pport, kbuffer,
    + need, PARPORT_EPP_FAST);
    + }
    + } else {
    + got = parport_read (pport, kbuffer, need);
    + }
     
                     if (got <= 0) {
    - if (!bytes_read)
    + if (!bytes_read) {
                                     bytes_read = got;
    -
    + }
                             break;
                     }
     
    @@ -141,13 +165,15 @@ static ssize_t pp_read (struct file * fi
                     bytes_read += got;
     
                     if (signal_pending (current)) {
    - if (!bytes_read)
    + if (!bytes_read) {
                                     bytes_read = -EINTR;
    + }
                             break;
                     }
     
    - if (current->need_resched)
    + if (current->need_resched) {
                             schedule ();
    + }
             }
     
             kfree (kbuffer);
    @@ -163,6 +189,8 @@ static ssize_t pp_write (struct file * f
             char * kbuffer;
             ssize_t bytes_written = 0;
             ssize_t wrote;
    + int mode;
    + struct parport *pport;
     
             if (!(pp->flags & PP_CLAIMED)) {
                     /* Don't have the port claimed */
    @@ -172,8 +200,11 @@ static ssize_t pp_write (struct file * f
             }
     
             kbuffer = kmalloc (min (count, PP_BUFFER_SIZE), GFP_KERNEL);
    - if (!kbuffer)
    + if (!kbuffer) {
                     return -ENOMEM;
    + }
    + pport = pp->pdev->port;
    + mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR);
     
             while (bytes_written < count) {
                     ssize_t n = min(count - bytes_written, PP_BUFFER_SIZE);
    @@ -183,24 +214,38 @@ static ssize_t pp_write (struct file * f
                             break;
                     }
     
    - wrote = parport_write (pp->pdev->port, kbuffer, n);
    + if ((pp->flags & PP_FASTWRITE) && (mode == IEEE1284_MODE_EPP)) {
    + /* do a fast EPP write */
    + if (pport->ieee1284.mode & IEEE1284_ADDR) {
    + wrote = pport->ops->epp_write_addr (pport,
    + kbuffer, n, PARPORT_EPP_FAST);
    + } else {
    + wrote = pport->ops->epp_write_data (pport,
    + kbuffer, n, PARPORT_EPP_FAST);
    + }
    + } else {
    + wrote = parport_write (pp->pdev->port, kbuffer, n);
    + }
     
                     if (wrote <= 0) {
    - if (!bytes_written)
    + if (!bytes_written) {
                                     bytes_written = wrote;
    + }
                             break;
                     }
     
                     bytes_written += wrote;
     
                     if (signal_pending (current)) {
    - if (!bytes_written)
    + if (!bytes_written) {
                                     bytes_written = -EINTR;
    + }
                             break;
                     }
     
    - if (current->need_resched)
    + if (current->need_resched) {
                             schedule ();
    + }
             }
     
             kfree (kbuffer);
    @@ -276,7 +321,9 @@ static int pp_ioctl(struct inode *inode,
             struct parport * port;
     
             /* First handle the cases that don't take arguments. */
    - if (cmd == PPCLAIM) {
    + switch (cmd) {
    + case PPCLAIM:
    + {
                     struct ieee1284_info *info;
     
                     if (pp->flags & PP_CLAIMED) {
    @@ -288,8 +335,9 @@ static int pp_ioctl(struct inode *inode,
                     /* Deferred device registration. */
                     if (!pp->pdev) {
                             int err = register_device (minor, pp);
    - if (err)
    + if (err) {
                                     return err;
    + }
                     }
     
                     parport_claim_or_block (pp->pdev);
    @@ -307,9 +355,8 @@ static int pp_ioctl(struct inode *inode,
                     info->phase = pp->state.phase;
     
                     return 0;
    - }
    -
    - if (cmd == PPEXCL) {
    + }
    + case PPEXCL:
                     if (pp->pdev) {
                             printk (KERN_DEBUG CHRDEV "%x: too late for PPEXCL; "
                                     "already registered\n", minor);
    @@ -324,9 +371,8 @@ static int pp_ioctl(struct inode *inode,
                      * when we finally do the registration. */
                     pp->flags |= PP_EXCL;
                     return 0;
    - }
    -
    - if (cmd == PPSETMODE) {
    + case PPSETMODE:
    + {
                     int mode;
                     if (copy_from_user (&mode, (int *) arg, sizeof (mode)))
                             return -EFAULT;
    @@ -340,20 +386,82 @@ static int pp_ioctl(struct inode *inode,
                     }
     
                     return 0;
    - }
    + }
    + case PPGETMODE:
    + {
    + int mode;
     
    - if (cmd == PPSETPHASE) {
    + if (pp->flags & PP_CLAIMED) {
    + mode = pp->pdev->port->ieee1284.mode;
    + } else {
    + mode = pp->state.mode;
    + }
    + if (copy_to_user ((int *)arg, &mode, sizeof (mode))) {
    + return -EFAULT;
    + }
    + return 0;
    + }
    + case PPSETPHASE:
    + {
                     int phase;
    - if (copy_from_user (&phase, (int *) arg, sizeof (phase)))
    + if (copy_from_user (&phase, (int *) arg, sizeof (phase))) {
                             return -EFAULT;
    + }
                     /* FIXME: validate phase */
                     pp->state.phase = phase;
     
    - if (pp->flags & PP_CLAIMED)
    + if (pp->flags & PP_CLAIMED) {
                             pp->pdev->port->ieee1284.phase = phase;
    + }
     
                     return 0;
    - }
    + }
    + case PPGETPHASE:
    + {
    + int phase;
    +
    + if (pp->flags & PP_CLAIMED) {
    + phase = pp->pdev->port->ieee1284.phase;
    + } else {
    + phase = pp->state.phase;
    + }
    + if (copy_to_user ((int *)arg, &phase, sizeof (phase))) {
    + return -EFAULT;
    + }
    + return 0;
    + }
    + case PPGETMODES:
    + {
    + unsigned int modes;
    +
    + modes = pp->pdev->port->modes;
    + if (copy_to_user ((unsigned int *)arg, &modes, sizeof (port->modes))) {
    + return -EFAULT;
    + }
    + return 0;
    + }
    + case PPSETFLAGS:
    + {
    + int uflags;
    +
    + if (copy_from_user (&uflags, (int *)arg, sizeof (uflags))) {
    + return -EFAULT;
    + }
    + pp->flags &= ~PP_FLAGMASK;
    + pp->flags |= (uflags & PP_FLAGMASK);
    + return 0;
    + }
    + case PPGETFLAGS:
    + {
    + int uflags;
    +
    + uflags = pp->flags & PP_FLAGMASK;
    + if (copy_to_user ((int *)arg, &uflags, sizeof (uflags))) {
    + return -EFAULT;
    + }
    + return 0;
    + }
    + } /* end switch() */
     
             /* Everything else requires the port to be claimed, so check
              * that now. */
    @@ -537,13 +645,28 @@ static int pp_release (struct inode * in
     {
             unsigned int minor = MINOR (inode->i_rdev);
             struct pp_struct *pp = file->private_data;
    + int compat_negot;
     
             lock_kernel();
    - if (pp->pdev && pp->pdev->port->ieee1284.mode != IEEE1284_MODE_COMPAT) {
    - if (!(pp->flags & PP_CLAIMED)) {
    - parport_claim_or_block (pp->pdev);
    - pp->flags |= PP_CLAIMED;
    - }
    + compat_negot = 0;
    + if (!(pp->flags & PP_CLAIMED) && pp->pdev &&
    + (pp->state.mode != IEEE1284_MODE_COMPAT)) {
    + struct ieee1284_info *info;
    +
    + /* parport released, but not in compatability mode */
    + parport_claim_or_block (pp->pdev);
    + pp->flags |= PP_CLAIMED;
    + info = &pp->pdev->port->ieee1284;
    + pp->saved_state.mode = info->mode;
    + pp->saved_state.phase = info->phase;
    + info->mode = pp->state.mode;
    + info->phase = pp->state.phase;
    + compat_negot = 1;
    + } else if ((pp->flags & PP_CLAIMED) && pp->pdev &&
    + (pp->pdev->port->ieee1284.mode != IEEE1284_MODE_COMPAT)) {
    + compat_negot = 2;
    + }
    + if (compat_negot) {
                     parport_negotiate (pp->pdev->port, IEEE1284_MODE_COMPAT);
                     printk (KERN_DEBUG CHRDEV
                             "%x: negotiated back to compatibility mode because "
    @@ -551,9 +674,18 @@ static int pp_release (struct inode * in
             }
     
             if (pp->flags & PP_CLAIMED) {
    + struct ieee1284_info *info;
    +
    + info = &pp->pdev->port->ieee1284;
    + pp->state.mode = info->mode;
    + pp->state.phase = info->phase;
    + info->mode = pp->saved_state.mode;
    + info->phase = pp->saved_state.phase;
                     parport_release (pp->pdev);
    - printk (KERN_DEBUG CHRDEV "%x: released pardevice because "
    - "user-space forgot\n", minor);
    + if (compat_negot != 1) {
    + printk (KERN_DEBUG CHRDEV "%x: released pardevice "
    + "because user-space forgot\n", minor);
    + }
             }
     
             if (pp->pdev) {
    diff -u -p linux-2.4.3/drivers/parport/ieee1284.c.orig linux-2.4.3/drivers/parport/ieee1284.c
    --- linux-2.4.3/drivers/parport/ieee1284.c.orig Thu Jan 4 22:00:55 2001
    +++ linux-2.4.3/drivers/parport/ieee1284.c Sun Apr 22 15:11:39 2001
    @@ -12,6 +12,8 @@
      * Any part of this program may be used in documents licensed under
      * the GNU Free Documentation License, Version 1.1 or any later version
      * published by the Free Software Foundation.
    + *
    + * Various hacks, Fred Barnes <frmb2@ukc.ac.uk>, 04/2000
      */
     
     #include <linux/config.h>
    @@ -523,9 +525,10 @@ int parport_negotiate (struct parport *p
                     r = parport_wait_peripheral (port,
                                                  PARPORT_STATUS_PAPEROUT,
                                                  PARPORT_STATUS_PAPEROUT);
    - if (r)
    + if (r) {
                             DPRINTK (KERN_INFO "%s: Timeout at event 31\n",
    - port->name);
    + port->name);
    + }
     
                     port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
                     DPRINTK (KERN_DEBUG "%s: ECP direction: forward\n",
    @@ -593,7 +596,7 @@ void parport_ieee1284_interrupt (int whi
      *
      * It is the caller's responsibility to ensure that the first
      * @len bytes of @buffer are valid.
    - *
    + *
      * This function returns the number of bytes transferred (if zero
      * or positive), or else an error code.
      */
    @@ -614,6 +617,7 @@ ssize_t parport_write (struct parport *p
             /* Use the mode we're in. */
             switch (mode) {
             case IEEE1284_MODE_NIBBLE:
    + case IEEE1284_MODE_BYTE:
                     parport_negotiate (port, IEEE1284_MODE_COMPAT);
             case IEEE1284_MODE_COMPAT:
                     DPRINTK (KERN_DEBUG "%s: Using compatibility mode\n",
    @@ -623,19 +627,29 @@ ssize_t parport_write (struct parport *p
     
             case IEEE1284_MODE_EPP:
                     DPRINTK (KERN_DEBUG "%s: Using EPP mode\n", port->name);
    - if (addr)
    + if (addr) {
                             fn = port->ops->epp_write_addr;
    - else
    + } else {
                             fn = port->ops->epp_write_data;
    + }
    + break;
    + case IEEE1284_MODE_EPPSWE:
    + DPRINTK (KERN_DEBUG "%s: Using software-emulated EPP mode\n",
    + port->name);
    + if (addr) {
    + fn = parport_ieee1284_epp_write_addr;
    + } else {
    + fn = parport_ieee1284_epp_write_data;
    + }
                     break;
    -
             case IEEE1284_MODE_ECP:
             case IEEE1284_MODE_ECPRLE:
                     DPRINTK (KERN_DEBUG "%s: Using ECP mode\n", port->name);
    - if (addr)
    + if (addr) {
                             fn = port->ops->ecp_write_addr;
    - else
    + } else {
                             fn = port->ops->ecp_write_data;
    + }
                     break;
     
             case IEEE1284_MODE_ECPSWE:
    @@ -643,10 +657,11 @@ ssize_t parport_write (struct parport *p
                              port->name);
                     /* The caller has specified that it must be emulated,
                      * even if we have ECP hardware! */
    - if (addr)
    + if (addr) {
                             fn = parport_ieee1284_ecp_write_addr;
    - else
    + } else {
                             fn = parport_ieee1284_ecp_write_data;
    + }
                     break;
     
             default:
    @@ -656,8 +671,7 @@ ssize_t parport_write (struct parport *p
             }
     
             retval = (*fn) (port, buffer, len, 0);
    - DPRINTK (KERN_DEBUG "%s: wrote %d/%d bytes\n", port->name, retval,
    - len);
    + DPRINTK (KERN_DEBUG "%s: wrote %d/%d bytes\n", port->name, retval, len);
             return retval;
     #endif /* IEEE1284 support */
     }
    @@ -696,8 +710,22 @@ ssize_t parport_read (struct parport *po
             /* Use the mode we're in. */
             switch (mode) {
             case IEEE1284_MODE_COMPAT:
    - if (parport_negotiate (port, IEEE1284_MODE_NIBBLE))
    + /* if we can tri-state use BYTE mode instead of NIBBLE mode,
    + * if that fails, revert to NIBBLE mode -- ought to store somewhere
    + * the device's ability to do BYTE mode reverse transfers, so we don't
    + * end up needlessly calling negotiate(BYTE) repeately.. (fb)
    + */
    + if ((port->physport->modes & PARPORT_MODE_TRISTATE) &&
    + !parport_negotiate (port, IEEE1284_MODE_BYTE)) {
    + /* got into BYTE mode OK */
    + DPRINTK (KERN_DEBUG "%s: Using byte mode\n", port->name);
    + fn = port->ops->byte_read_data;
    + break;
    + }
    + if (parport_negotiate (port, IEEE1284_MODE_NIBBLE)) {
                             return -EIO;
    + }
    + /* fall through to NIBBLE */
             case IEEE1284_MODE_NIBBLE:
                     DPRINTK (KERN_DEBUG "%s: Using nibble mode\n", port->name);
                     fn = port->ops->nibble_read_data;
    @@ -710,12 +738,21 @@ ssize_t parport_read (struct parport *po
     
             case IEEE1284_MODE_EPP:
                     DPRINTK (KERN_DEBUG "%s: Using EPP mode\n", port->name);
    - if (addr)
    + if (addr) {
                             fn = port->ops->epp_read_addr;
    - else
    + } else {
                             fn = port->ops->epp_read_data;
    + }
    + break;
    + case IEEE1284_MODE_EPPSWE:
    + DPRINTK (KERN_DEBUG "%s: Using software-emulated EPP mode\n",
    + port->name);
    + if (addr) {
    + fn = parport_ieee1284_epp_read_addr;
    + } else {
    + fn = parport_ieee1284_epp_read_data;
    + }
                     break;
    -
             case IEEE1284_MODE_ECP:
             case IEEE1284_MODE_ECPRLE:
                     DPRINTK (KERN_DEBUG "%s: Using ECP mode\n", port->name);
    diff -u -p linux-2.4.3/drivers/parport/ieee1284_ops.c.orig linux-2.4.3/drivers/parport/ieee1284_ops.c
    --- linux-2.4.3/drivers/parport/ieee1284_ops.c.orig Sat Mar 3 03:43:03 2001
    +++ linux-2.4.3/drivers/parport/ieee1284_ops.c Sun Apr 22 16:21:53 2001
    @@ -10,6 +10,7 @@
      *
      * Author: Tim Waugh <tim@cyberelk.demon.co.uk>
      * Fixed AUTOFD polarity in ecp_forward_to_reverse(). Fred Barnes, 1999
    + * Software emulated EPP fixes, Fred Barnes, 04/2001.
      */
     
     
    @@ -18,7 +19,7 @@
     #include <linux/delay.h>
     #include <asm/uaccess.h>
     
    -#define DEBUG /* undef me for production */
    +#undef DEBUG /* undef me for production */
     
     #ifdef CONFIG_LP_CONSOLE
     #undef DEBUG /* Don't want a garbled console */
    @@ -725,31 +726,32 @@ size_t parport_ieee1284_epp_write_data (
                                             const void *buffer, size_t len,
                                             int flags)
     {
    - /* This is untested */
             unsigned char *bp = (unsigned char *) buffer;
             size_t ret = 0;
     
    + /* set EPP idle state (just to make sure) with strobe low */
             parport_frob_control (port,
                                   PARPORT_CONTROL_STROBE |
                                   PARPORT_CONTROL_AUTOFD |
    - PARPORT_CONTROL_SELECT,
    + PARPORT_CONTROL_SELECT |
    + PARPORT_CONTROL_INIT,
                                   PARPORT_CONTROL_STROBE |
    - PARPORT_CONTROL_SELECT);
    + PARPORT_CONTROL_INIT);
             port->ops->data_forward (port);
             for (; len > 0; len--, bp++) {
    - /* Event 62: Write data and strobe data */
    + /* Event 62: Write data and set autofd low */
                     parport_write_data (port, *bp);
                     parport_frob_control (port, PARPORT_CONTROL_AUTOFD,
                                           PARPORT_CONTROL_AUTOFD);
     
    - /* Event 58 */
    + /* Event 58: wait for busy (nWait) to go high */
                     if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY, 0, 10))
                             break;
     
    - /* Event 63 */
    + /* Event 63: set nAutoFd (nDStrb) high */
                     parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
     
    - /* Event 60 */
    + /* Event 60: wait for busy (nWait) to go low */
                     if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
                                                  PARPORT_STATUS_BUSY, 5))
                             break;
    @@ -757,7 +759,7 @@ size_t parport_ieee1284_epp_write_data (
                     ret++;
             }
     
    - /* Event 61 */
    + /* Event 61: set strobe (nWrite) high */
             parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
     
             return ret;
    @@ -768,29 +770,37 @@ size_t parport_ieee1284_epp_read_data (s
                                            void *buffer, size_t len,
                                            int flags)
     {
    - /* This is untested. */
             unsigned char *bp = (unsigned char *) buffer;
             unsigned ret = 0;
     
    + /* set EPP idle state (just to make sure) with strobe high */
             parport_frob_control (port,
                                   PARPORT_CONTROL_STROBE |
    - PARPORT_CONTROL_SELECT, 0);
    + PARPORT_CONTROL_AUTOFD |
    + PARPORT_CONTROL_SELECT |
    + PARPORT_CONTROL_INIT,
    + PARPORT_CONTROL_INIT);
             port->ops->data_reverse (port);
             for (; len > 0; len--, bp++) {
    - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
    -
    - /* Event 58 */
    - if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
    - PARPORT_STATUS_BUSY, 10))
    + /* Event 67: set nAutoFd (nDStrb) low */
    + parport_frob_control (port,
    + PARPORT_CONTROL_AUTOFD,
    + PARPORT_CONTROL_AUTOFD);
    + /* Event 58: wait for Busy to go high */
    + if (parport_wait_peripheral (port, PARPORT_STATUS_BUSY, 0)) {
                             break;
    + }
     
                     *bp = parport_read_data (port);
     
    - parport_frob_control (port, PARPORT_CONTROL_AUTOFD,
    - PARPORT_CONTROL_AUTOFD);
    + /* Event 63: set nAutoFd (nDStrb) high */
    + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
     
    - if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY, 0, 5))
    + /* Event 60: wait for Busy to go low */
    + if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
    + PARPORT_STATUS_BUSY, 5)) {
                             break;
    + }
     
                     ret++;
             }
    @@ -873,3 +883,5 @@ size_t parport_ieee1284_epp_read_addr (s
     
             return ret;
     }
    +
    +
    diff -u -p linux-2.4.3/drivers/parport/parport_pc.c.orig linux-2.4.3/drivers/parport/parport_pc.c
    --- linux-2.4.3/drivers/parport/parport_pc.c.orig Tue Mar 27 01:41:19 2001
    +++ linux-2.4.3/drivers/parport/parport_pc.c Sun Apr 22 16:04:05 2001
    @@ -11,6 +11,7 @@
      * Cleaned up include files - Russell King <linux@arm.uk.linux.org>
      * DMA support - Bert De Jonghe <bert@sophis.be>
      * Many ECP bugs fixed. Fred Barnes & Jamie Lokier, 1999
    + * Various hacks, Fred Barnes, 04/2001
      */
     
     /* This driver should work with any hardware that is broadly compatible
    @@ -210,6 +211,7 @@ static int get_fifo_residue (struct parp
             /* Back to PS2 mode. */
             frob_econtrol (p, 0xe0, ECR_PS2 << 5);
     
    + DPRINTK (KERN_DEBUG "*** get_fifo_residue: done residue collecting (ecr = 0x%2.2x)\n", inb (ECONTROL (p)));
             return residue;
     }
     #endif /* IEEE 1284 support */
    @@ -365,6 +367,15 @@ static size_t parport_pc_epp_read_data (
                                             size_t length, int flags)
     {
             size_t got = 0;
    +
    + if (flags & PARPORT_EPP_FAST) {
    + insb (EPPDATA (port), buf, length);
    + if (inb (STATUS (port)) & 0x01) {
    + clear_epp_timeout (port);
    + return -EIO; /* dunno, broke */
    + }
    + return length;
    + }
             for (; got < length; got++) {
                     *((char*)buf)++ = inb (EPPDATA(port));
                     if (inb (STATUS(port)) & 0x01) {
    @@ -380,6 +391,15 @@ static size_t parport_pc_epp_write_data
                                              size_t length, int flags)
     {
             size_t written = 0;
    +
    + if (flags & PARPORT_EPP_FAST) {
    + outsb (EPPDATA (port), buf, length);
    + if (inb (STATUS (port)) & 0x01) {
    + clear_epp_timeout (port);
    + return -EIO; /* dunno, broke.. */
    + }
    + return length;
    + }
             for (; written < length; written++) {
                     outb (*((char*)buf)++, EPPDATA(port));
                     if (inb (STATUS(port)) & 0x01) {
    @@ -395,6 +415,7 @@ static size_t parport_pc_epp_read_addr (
                                             size_t length, int flags)
     {
             size_t got = 0;
    +
             for (; got < length; got++) {
                     *((char*)buf)++ = inb (EPPADDR (port));
                     if (inb (STATUS (port)) & 0x01) {
    @@ -411,6 +432,7 @@ static size_t parport_pc_epp_write_addr
                                              int flags)
     {
             size_t written = 0;
    +
             for (; written < length; written++) {
                     outb (*((char*)buf)++, EPPADDR (port));
                     if (inb (STATUS (port)) & 0x01) {
    @@ -588,6 +610,7 @@ static size_t parport_pc_fifo_write_bloc
             unsigned long start = (unsigned long) buf;
             unsigned long end = (unsigned long) buf + length - 1;
     
    +dump_parport_state ("enter fifo_write_block_dma", port);
             if (end < MAX_DMA_ADDRESS) {
                     /* If it would cross a 64k boundary, cap it at the end. */
                     if ((start ^ end) & ~0xffffUL)
    @@ -606,7 +629,8 @@ static size_t parport_pc_fifo_write_bloc
     
             /* We don't want to be interrupted every character. */
             parport_pc_disable_irq (port);
    - frob_econtrol (port, (1<<4), (1<<4)); /* nErrIntrEn */
    + /* set nErrIntrEn and serviceIntr */
    + frob_econtrol (port, (1<<4) | (1<<2), (1<<4) | (1<<2));
     
             /* Forward mode. */
             parport_pc_data_forward (port); /* Must be in PS2 mode */
    @@ -696,6 +720,7 @@ static size_t parport_pc_fifo_write_bloc
             if (dma_handle)
                     pci_unmap_single(priv->dev, dma_handle, length, PCI_DMA_TODEVICE);
     
    +dump_parport_state ("leave fifo_write_block_dma", port);
             return length - left;
     }
     
    @@ -789,9 +814,10 @@ size_t parport_pc_ecp_write_block_pio (s
                     r = parport_wait_peripheral (port,
                                                  PARPORT_STATUS_PAPEROUT,
                                                  PARPORT_STATUS_PAPEROUT);
    - if (r)
    + if (r) {
                             printk (KERN_DEBUG "%s: PError timeout (%d) "
                                     "in ecp_write_block_pio\n", port->name, r);
    + }
             }
     
             /* Set up ECP parallel port mode.*/
    @@ -822,10 +848,10 @@ size_t parport_pc_ecp_write_block_pio (s
     
                     /* Adjust for the contents of the FIFO. */
                     for (written -= priv->fifo_depth; ; written++) {
    - if (inb (ECONTROL (port)) & 0x2)
    + if (inb (ECONTROL (port)) & 0x2) {
                                     /* Full up. */
                                     break;
    -
    + }
                             outb (0, FIFO (port));
                     }
     
    @@ -877,58 +903,99 @@ size_t parport_pc_ecp_read_block_pio (st
             char *bufp = buf;
     
             port = port->physport;
    +DPRINTK (KERN_DEBUG "parport_pc: parport_pc_ecp_read_block_pio\n");
    +dump_parport_state ("enter fcn", port);
     
             /* 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);
     
    - fifofull = fifo_depth;
    - if (port->ieee1284.mode == IEEE1284_MODE_ECPRLE)
    + if (port->ieee1284.mode == IEEE1284_MODE_ECPRLE) {
                     /* If the peripheral is allowed to send RLE compressed
                      * data, it is possible for a byte to expand to 128
                      * bytes in the FIFO. */
                     fifofull = 128;
    + } else {
    + fifofull = fifo_depth;
    + }
     
             /* If the caller wants less than a full FIFO's worth of data,
    - * go through software emulation. Otherwise we may have to through
    + * go through software emulation. Otherwise we may have to throw
              * away data. */
             if (length < fifofull)
                     return parport_ieee1284_ecp_read_data (port, buf,
                                                            length, flags);
     
    - /* Switch to reverse mode if necessary. */
    - if ((port->ieee1284.phase != IEEE1284_PH_REV_IDLE) &&
    - (port->ieee1284.phase != IEEE1284_PH_REV_DATA)) {
    - /* Event 38: Set nAutoFd low */
    + 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_CONTROL_STROBE,
                                           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: PError goes low */
    + /* Event 40: Wait for nAckReverse (PError) to go low */
                     r = parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
    - if (r)
    + if (r) {
                             printk (KERN_DEBUG "%s: PE timeout Event 40 (%d) "
                                     "in ecp_read_block_pio\n", port->name, r);
    + return 0;
    + }
             }
     
             /* Set up ECP FIFO mode.*/
    - parport_pc_data_reverse (port); /* Must be in PS2 mode */
    - parport_pc_frob_control (port,
    +/* parport_pc_frob_control (port,
                                      PARPORT_CONTROL_STROBE |
                                      PARPORT_CONTROL_AUTOFD,
    - 0);
    + PARPORT_CONTROL_AUTOFD); */
             r = change_mode (port, ECR_ECP); /* ECP FIFO */
             if (r) printk (KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n", port->name);
    +
             port->ieee1284.phase = IEEE1284_PH_REV_DATA;
     
    + /* 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 "PIO read timed out (initial byte)\n");
    + goto out_no_data;
    + }
    + /* read byte */
    + *bufp++ = 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 */
    +/* r = parport_wait_peripheral (port, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK); */
    +dump_parport_state ("post 45", port);
    +r = 0;
    + if (r) {
    + /* 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;
    + }
    + /* 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);
             /* Do the transfer. */
             while (left > fifofull) {
                     int ret;
    @@ -949,22 +1016,26 @@ size_t parport_pc_ecp_read_block_pio (st
     
                     if (ecrval & 0x01) {
                             /* FIFO is empty. Wait for interrupt. */
    +dump_parport_state ("FIFO empty", port);
     
                             /* Anyone else waiting for the port? */
                             if (port->waithead) {
    - printk (KERN_DEBUG
    - "Somebody wants the port\n");
    + printk (KERN_DEBUG "Somebody wants the port\n");
                                     break;
                             }
     
                             /* Clear serviceIntr */
                             outb (ecrval & ~(1<<2), ECONTROL (port));
                     false_alarm:
    +dump_parport_state ("waiting", port);
                             ret = parport_wait_event (port, HZ);
    - if (ret < 0) break;
    +DPRINTK (KERN_DEBUG "parport_wait_event returned %d\n", ret);
    + if (ret < 0)
    + break;
                             ret = 0;
                             if (!time_before (jiffies, expire)) {
                                     /* Timed out. */
    +dump_parport_state ("timeout", port);
                                     printk (KERN_DEBUG "PIO read timed out\n");
                                     break;
                             }
    @@ -973,36 +1044,55 @@ size_t parport_pc_ecp_read_block_pio (st
                                     if (current->need_resched &&
                                         time_before (jiffies, expire))
                                             schedule ();
    -
    +dump_parport_state ("false alarm", port);
                                     goto false_alarm;
                             }
    +dump_parport_state ("got something", port);
     
                             continue;
                     }
     
                     if (ecrval & 0x02) {
                             /* FIFO is full. */
    +dump_parport_state ("FIFO full", port);
                             insb (fifo, bufp, fifo_depth);
                             bufp += fifo_depth;
                             left -= fifo_depth;
                             continue;
                     }
     
    +DPRINTK (KERN_DEBUG "*** ecp_read_block_pio: reading one byte from the FIFO\n");
    + *bufp++ = inb (fifo);
    + left--;
    + }
    +
    + /* scoop up what got left in the FIFO */
    + while (left) {
    + if (inb (ECONTROL (port)) & 0x01) {
    + /* empty (< 1 PWord) */
    + break;
    + }
                     *bufp++ = inb (fifo);
                     left--;
             }
     
             port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
    +dump_parport_state ("rev idle2", port);
    +
    +
    +out_no_data:
    + /* Go to forward idle mode to shut the peripheral up (event 47). */
    + parport_frob_control (port, PARPORT_CONTROL_INIT, PARPORT_CONTROL_INIT);
     
    - /* Go to forward idle mode to shut the peripheral up. */
    - parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
    + /* event 49: PError goes high */
             r = parport_wait_peripheral (port,
                                          PARPORT_STATUS_PAPEROUT,
                                          PARPORT_STATUS_PAPEROUT);
    - if (r)
    + if (r) {
                     printk (KERN_DEBUG
                             "%s: PE timeout FWDIDLE (%d) in ecp_read_block_pio\n",
                             port->name, r);
    + }
     
             port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
     
    @@ -1015,13 +1105,21 @@ size_t parport_pc_ecp_read_block_pio (st
                                     port->name, lost);
             }
     
    +dump_parport_state ("fwd idle", port);
             return length - left;
     }
     
     #endif /* IEEE 1284 support */
    -
     #endif /* Allowed to use FIFO/DMA */
     
    +
    +/*
    + * ******************************************
    + * INITIALISATION AND MODULE STUFF BELOW HERE
    + * ******************************************
    + */
    +
    +
     void parport_pc_inc_use_count(void)
     {
     #ifdef MODULE
    @@ -1923,10 +2021,11 @@ static int __devinit parport_irq_probe(s
                     pb->irq = programmable_irq_support(pb);
             }
     
    - if (pb->modes & PARPORT_MODE_ECP)
    + if (pb->modes & PARPORT_MODE_ECP) {
                     pb->irq = irq_probe_ECP(pb);
    + }
     
    - if (pb->irq == PARPORT_IRQ_NONE && priv->ecr &&
    + if ((pb->irq == PARPORT_IRQ_NONE) && priv->ecr &&
                 (pb->modes & PARPORT_MODE_EPP))
                     pb->irq = irq_probe_EPP(pb);
     
    @@ -1971,11 +2070,12 @@ static int __devinit parport_dma_probe (
             const struct parport_pc_private *priv = p->private_data;
             if (priv->ecr)
                     p->dma = programmable_dma_support(p); /* ask ECP chipset first */
    - if (p->dma == PARPORT_DMA_NONE)
    + if (p->dma == PARPORT_DMA_NONE) {
                     /* ask known Super-IO chips proper, although these
                        claim ECP compatible, some don't report their DMA
                        conforming to ECP standards */
                     p->dma = get_superio_dma(p);
    + }
     
             return p->dma;
     }
    @@ -2085,6 +2185,8 @@ struct parport *__devinit parport_pc_pro
                     p->ops->compat_write_data = parport_pc_compat_write_block_pio;
     #ifdef CONFIG_PARPORT_1284
                     p->ops->ecp_write_data = parport_pc_ecp_write_block_pio;
    + /* currently broken, but working on it.. (FB) */
    + /* p->ops->ecp_read_data = parport_pc_ecp_read_block_pio; */
     #endif /* IEEE 1284 support */
                     if (p->dma != PARPORT_DMA_NONE) {
                             printk(", dma %d", p->dma);
    diff -u -p linux-2.4.3/include/linux/parport_pc.h.orig linux-2.4.3/include/linux/parport_pc.h
    --- linux-2.4.3/include/linux/parport_pc.h.orig Mon Apr 16 22:09:16 2001
    +++ linux-2.4.3/include/linux/parport_pc.h Sun Apr 22 16:19:02 2001
    @@ -59,6 +59,51 @@ extern __inline__ unsigned char parport_
             return val;
     }
     
    +#ifdef DEBUG_PARPORT
    +extern __inline__ void dump_parport_state (char *str, struct parport *p)
    +{
    + /* here's hoping that reading these ports won't side-effect anything underneath */
    + unsigned char ecr = inb (ECONTROL (p));
    + unsigned char dcr = inb (CONTROL (p));
    + unsigned char dsr = inb (STATUS (p));
    + static char *ecr_modes[] = {"SPP", "PS2", "PPFIFO", "ECP", "xXx", "yYy", "TST", "CFG"};
    + const struct parport_pc_private *priv = (parport_pc_private *)p->physport->private_data;
    + int i;
    +
    + printk (KERN_DEBUG "*** parport state (%s): ecr=[%s", str, ecr_modes[(ecr & 0xe0) >> 5]);
    + if (ecr & 0x10) printk (",nErrIntrEn");
    + if (ecr & 0x08) printk (",dmaEn");
    + if (ecr & 0x04) printk (",serviceIntr");
    + if (ecr & 0x02) printk (",f_full");
    + if (ecr & 0x01) printk (",f_empty");
    + for (i=0; i<2; i++) {
    + printk ("] dcr(%s)=[", i ? "soft" : "hard");
    + dcr = i ? priv->ctr : inb (CONTROL (p));
    +
    + if (dcr & 0x20) {
    + printk ("rev");
    + } else {
    + printk ("fwd");
    + }
    + if (dcr & 0x10) printk (",ackIntEn");
    + if (!(dcr & 0x08)) printk (",N-SELECT-IN");
    + if (dcr & 0x04) printk (",N-INIT");
    + if (!(dcr & 0x02)) printk (",N-AUTOFD");
    + if (!(dcr & 0x01)) printk (",N-STROBE");
    + }
    + printk ("] dsr=[");
    + if (!(dsr & 0x80)) printk ("BUSY");
    + if (dsr & 0x40) printk (",N-ACK");
    + if (dsr & 0x20) printk (",PERROR");
    + if (dsr & 0x10) printk (",SELECT");
    + if (dsr & 0x08) printk (",N-FAULT");
    + printk ("]\n");
    + return;
    +}
    +#else /* !DEBUG_PARPORT */
    +#define dump_parport_state(args...)
    +#endif /* !DEBUG_PARPORT */
    +
     /* __parport_pc_frob_control differs from parport_pc_frob_control in that
      * it doesn't do any extra masking. */
     static __inline__ unsigned char __parport_pc_frob_control (struct parport *p,
    diff -u -p linux-2.4.3/include/linux/ppdev.h.orig linux-2.4.3/include/linux/ppdev.h
    --- linux-2.4.3/include/linux/ppdev.h.orig Mon Oct 11 19:04:02 1999
    +++ linux-2.4.3/include/linux/ppdev.h Sun Apr 22 16:04:46 2001
    @@ -11,6 +11,7 @@
      * 2 of the License, or (at your option) any later version.
      *
      * Added PPGETTIME/PPSETTIME, Fred Barnes, 1999
    + * Added PPGETMODES/PPGETMODE/PPGETPHASE, Fred Barnes <frmb2@ukc.ac.uk>, 03/01/2001
      */
     
     #define PP_MAJOR 99
    @@ -77,5 +78,23 @@ struct ppdev_frob_struct {
     /* Set and get port timeout (struct timeval's) */
     #define PPGETTIME _IOR(PP_IOCTL, 0x95, struct timeval)
     #define PPSETTIME _IOW(PP_IOCTL, 0x96, struct timeval)
    +
    +/* Get available modes (what the hardware can do) */
    +#define PPGETMODES _IOR(PP_IOCTL, 0x97, unsigned int)
    +
    +/* Get the current mode and phaze */
    +#define PPGETMODE _IOR(PP_IOCTL, 0x98, int)
    +#define PPGETPHASE _IOR(PP_IOCTL, 0x99, int)
    +
    +/* get/set flags */
    +#define PPGETFLAGS _IOR(PP_IOCTL, 0x9a, int)
    +#define PPSETFLAGS _IOW(PP_IOCTL, 0x9b, int)
    +
    +/* flags visible to the world */
    +#define PP_FASTWRITE (1<<2)
    +#define PP_FASTREAD (1<<3)
    +
    +/* only masks user-visible flags */
    +#define PP_FLAGMASK (PP_FASTWRITE | PP_FASTREAD)
     
     

    -- 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 : Sun Apr 22 2001 - 10:53:03 EDT