[PARPORT] [PATCH] 2.4.3 parport/ppdev hacks and fixes

From: Fred Barnes (frmb2@ukc.ac.uk)
Date: Sat Apr 21 2001 - 21:38:29 EDT

  • Next message: Tim Waugh: "Re: [PARPORT] [PATCH] 2.4.3 parport/ppdev hacks and fixes"

    Hi,

    Appended to this message is said promised patch for parport/ppdev/etc.
    In breif, the things it adds/changes/fixes are:

    * support for fast reads/writes (only in EPP mode at the moment).
            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). To do this, something calls
            parport_{read,write} with (len | 0x80000000). I guess this is OK
            since we're not likely to want to transfer >2gb in one go, and
            the return type of these is signed anyway.

    * various new IOCTLs added to ppdev, to:
            - get hardware supported modes of parport
            - get the current IEEE mode/phase
            - enable/disable fast reads/writes as described above

    * 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

    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.

    If you're happy with the patches as they stand, I'm happy for them to
    go into the kernel. I'll continue to fix/better things, but now seems
    a good a point as any to synchronise with Linus/etc.

    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 Sat Apr 21 21:37:34 2001 @@ -35,17 +35,27 @@ * 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 + * GETFASTW gets status of "fast" writes + * GETFASTR gets status of "fast" reads + * SETFASTW sets "fast" writes + * SETFASTR sets "fast" reads * 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 {GET,SET}FAST{R,W} ioctls, Fred Barnes, 04/2001 */ #include <linux/module.h> @@ -81,6 +91,8 @@ struct pp_struct { /* pp_struct.flags bitfields */ #define PP_CLAIMED (1<<0) #define PP_EXCL (1<<1) +#define PP_FASTWRITE (1<<2) +#define PP_FASTREAD (1<<3) /* Other constants */ #define PP_INTERRUPT_TIMEOUT (10 * HZ) /* 10s */ @@ -118,18 +130,23 @@ static ssize_t pp_read (struct file * fi } kbuffer = kmalloc (min (count, PP_BUFFER_SIZE), GFP_KERNEL); - if (!kbuffer) + if (!kbuffer) { return -ENOMEM; + } 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) { + got = parport_read (pp->pdev->port, kbuffer, (need | 0x80000000)); + } else { + got = parport_read (pp->pdev->port, kbuffer, need); + } if (got <= 0) { - if (!bytes_read) + if (!bytes_read) { bytes_read = got; - + } break; } @@ -141,13 +158,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); @@ -183,24 +202,31 @@ static ssize_t pp_write (struct file * f break; } - wrote = parport_write (pp->pdev->port, kbuffer, n); + if (pp->flags & PP_FASTWRITE) { + wrote = parport_write (pp->pdev->port, kbuffer, (n | 0x80000000)); + } 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 +302,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 +316,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 +336,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 +352,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 +367,99 @@ 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 PPSETFASTR: + case PPSETFASTW: + { + int fastx; + + if (copy_from_user (&fastx, (int *)arg, sizeof (fastx))) { + return -EFAULT; + } + if (cmd == PPSETFASTR) { + if (fastx) { + pp->flags |= PP_FASTREAD; + } else { + pp->flags &= ~PP_FASTREAD; + } + } else { + if (fastx) { + pp->flags |= PP_FASTWRITE; + } else { + pp->flags &= ~PP_FASTWRITE; + } + } + return 0; + } + case PPGETFASTR: + case PPGETFASTW: + { + int fastx; + + if (cmd == PPGETFASTR) { + fastx = pp->flags & PP_FASTREAD; + } else { + fastx = pp->flags & PP_FASTWRITE; + } + if (copy_to_user ((int *)arg, &fastx, sizeof (fastx))) { + return -EFAULT; + } + return 0; + } + } /* end switch() */ /* Everything else requires the port to be claimed, so check * that now. */ @@ -537,13 +643,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 +672,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 Sat Apr 21 21:31:35 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,6 +596,9 @@ void parport_ieee1284_interrupt (int whi * * It is the caller's responsibility to ensure that the first * @len bytes of @buffer are valid. + * + * If the high bit of @len is set, this indicates a fast-write, ie + * do not check for timeouts. -- only used for EPP/ECP * * This function returns the number of bytes transferred (if zero * or positive), or else an error code. @@ -601,7 +607,8 @@ void parport_ieee1284_interrupt (int whi ssize_t parport_write (struct parport *port, const void *buffer, size_t len) { #ifndef CONFIG_PARPORT_1284 - return port->ops->compat_write_data (port, buffer, len, 0); + /* meaningless to attempt a fast-write here */ + return port->ops->compat_write_data (port, buffer, (len & ~0x80000000), 0); #else ssize_t retval; int mode = port->ieee1284.mode; @@ -614,28 +621,40 @@ 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", port->name); fn = port->ops->compat_write_data; + len &= ~0x80000000; break; 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 +662,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: @@ -657,7 +677,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); + (len & ~0x80000000)); return retval; #endif /* IEEE1284 support */ } @@ -676,6 +696,10 @@ ssize_t parport_write (struct parport *p * It is the caller's responsibility to ensure that the first * @len bytes of @buffer are available to write to. * + * If the high-bit of @len is set, do a fast read. (ie, just try + * and read the data without checking for timeouts). -- only used + * for EPP/ECP modes. + * * This function returns the number of bytes transferred (if zero * or positive), or else an error code. */ @@ -696,26 +720,52 @@ 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) + */ + len &= ~0x80000000; + 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); + len &= ~0x80000000; fn = port->ops->nibble_read_data; break; case IEEE1284_MODE_BYTE: DPRINTK (KERN_DEBUG "%s: Using byte mode\n", port->name); + len &= ~0x80000000; fn = port->ops->byte_read_data; break; 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 02:58:22 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 */ @@ -404,6 +405,7 @@ size_t parport_ieee1284_ecp_write_data ( size_t written; int retry; + len &= ~0x80000000; /* clear high-bit */ port = port->physport; if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE) @@ -492,6 +494,7 @@ size_t parport_ieee1284_ecp_read_data (s int rle = 0; ssize_t count = 0; + len &= ~0x80000000; /* clear high-bit */ port = port->physport; if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE) @@ -640,6 +643,7 @@ size_t parport_ieee1284_ecp_write_addr ( size_t written; int retry; + len &= ~0x80000000; /* clear high-bit */ port = port->physport; if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE) @@ -725,31 +729,33 @@ 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; + len &= ~0x80000000; /* clear high-bit */ + /* 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 +763,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 +774,38 @@ 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; + len &= ~0x80000000; + /* 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++; } @@ -808,6 +823,7 @@ size_t parport_ieee1284_epp_write_addr ( unsigned char *bp = (unsigned char *) buffer; size_t ret = 0; + len &= ~0x80000000; /* clear high-bit */ parport_frob_control (port, PARPORT_CONTROL_STROBE | PARPORT_CONTROL_SELECT | @@ -847,6 +863,7 @@ size_t parport_ieee1284_epp_read_addr (s unsigned char *bp = (unsigned char *) buffer; unsigned ret = 0; + len &= ~0x80000000; /* clear high-bit */ parport_frob_control (port, PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD, 0); @@ -873,3 +890,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 03:29:40 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,19 @@ static size_t parport_pc_epp_read_data ( size_t length, int flags) { size_t got = 0; + + if (length & 0x80000000) { + /* do a fast read */ + length &= ~0x80000000; + + insb (EPPDATA (port), buf, length); + if (inb (STATUS (port)) & 0x01) { + clear_epp_timeout (port); + return -EIO; /* dunno, broke */ + } else { + return length; + } + } for (; got < length; got++) { *((char*)buf)++ = inb (EPPDATA(port)); if (inb (STATUS(port)) & 0x01) { @@ -380,6 +395,19 @@ static size_t parport_pc_epp_write_data size_t length, int flags) { size_t written = 0; + + if (length & 0x80000000) { + /* do a fast write */ + length &= ~0x80000000; + + outsb (EPPDATA (port), buf, length); + if (inb (STATUS (port)) & 0x01) { + clear_epp_timeout (port); + return -EIO; /* dunno, broke.. */ + } else { + return length; + } + } for (; written < length; written++) { outb (*((char*)buf)++, EPPDATA(port)); if (inb (STATUS(port)) & 0x01) { @@ -395,6 +423,10 @@ static size_t parport_pc_epp_read_addr ( size_t length, int flags) { size_t got = 0; + + if (length & 0x80000000) { + length &= ~0x80000000; /* not yet.. */ + } for (; got < length; got++) { *((char*)buf)++ = inb (EPPADDR (port)); if (inb (STATUS (port)) & 0x01) { @@ -411,6 +443,10 @@ static size_t parport_pc_epp_write_addr int flags) { size_t written = 0; + + if (length & 0x80000000) { + length &= ~0x80000000; /* not yet.. */ + } for (; written < length; written++) { outb (*((char*)buf)++, EPPADDR (port)); if (inb (STATUS (port)) & 0x01) { @@ -496,6 +532,11 @@ static size_t parport_pc_fifo_write_bloc port = port->physport; + if (length & 0x8000000) { + /* not done here yet.. */ + length &= ~0x80000000; + left = length; + } /* We don't want to be interrupted every character. */ parport_pc_disable_irq (port); frob_econtrol (port, (1<<4), (1<<4)); /* nErrIntrEn */ @@ -588,6 +629,13 @@ 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 (length & 0x80000000) { + /* DMA driven is as fast as can be anyway */ + length &= ~0x80000000; + left = length; + end = (unsigned long)buf + length - 1; + } if (end < MAX_DMA_ADDRESS) { /* If it would cross a 64k boundary, cap it at the end. */ if ((start ^ end) & ~0xffffUL) @@ -606,7 +654,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 +745,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; } @@ -707,6 +757,10 @@ size_t parport_pc_compat_write_block_pio size_t written; int r; + if (length & 0x80000000) { + /* not done here, yet */ + length &= ~0x80000000; + } /* Special case: a timeout of zero means we cannot call schedule(). */ if (!port->physport->cad->timeout) return parport_ieee1284_write_compat (port, buf, @@ -771,6 +825,10 @@ size_t parport_pc_ecp_write_block_pio (s size_t written; int r; + if (length & 0x80000000) { + /* not done here, yet */ + length &= ~0x80000000; + } /* Special case: a timeout of zero means we cannot call schedule(). */ if (!port->physport->cad->timeout) return parport_ieee1284_ecp_write_data (port, buf, @@ -789,9 +847,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 +881,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 +936,103 @@ 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); + if (length & 0x80000000) { + length &= ~0x80000000; + left = length; + } /* 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 +1053,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 +1081,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); - /* Go to forward idle mode to shut the peripheral up. */ - parport_frob_control (port, PARPORT_CONTROL_INIT, 0); + +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); + + /* 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 +1142,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 +2058,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 +2107,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 +2222,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 Sat Apr 21 19:08:05 2001 @@ -59,6 +59,45 @@ 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"}; + + 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"); + printk ("] dcr=["); + 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 Sat Apr 21 20:34:05 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 @@ -78,4 +79,16 @@ struct ppdev_frob_struct { #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) + +/* For fast reads/writes */ +#define PPGETFASTR _IOR(PP_IOCTL, 0x9a, int) +#define PPGETFASTW _IOR(PP_IOCTL, 0x9b, int) +#define PPSETFASTR _IOW(PP_IOCTL, 0x9c, int) +#define PPSETFASTW _IOW(PP_IOCTL, 0x9d, int)

    -- 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 - 05:55:08 EDT