[PARPORT] ECP mode transfers in 2.4.x kernels (latest patches)

From: Dave Strauss (D.Strauss@motorola.com)
Date: Fri Oct 12 2001 - 17:45:05 EDT

  • Next message: mike501: "[PARPORT] I really need some help with redhat 7.1"

    Here are my latest patches to add ECP mode to lp and to deal with
    hardware that doesn't like to be disturbed while data is in the FIFO.
    parport_pc must be built with CONFIG_PARPORT_PC_FIFO on for these
    patches to take effect, and the parport_pc option "irq" must be set to
    "auto". If the parport_pc option "dma" is set to "auto", DMA will be
    used to write data to the FIFO, otherwise PIO will be used. For
    example, in /etc/modules.conf:

    options parport_pc irq=auto dma=auto
            - or -
    options parport_pc irq=auto dma=none

    The only difference between these patches and the last set of patches
    I posted is in parport_pc.c; relative to that, I've changed
    parport_pc_fifo_write_block_pio() and parport_pc_fifo_write_block_dma()
    back to non-blocking, but added a loop in parport_pc_compat_write_block_pio()
    and parport_pc_ecp_write_block_pio() to wait up to 4 seconds per slot in
    the FIFO before declaring that the FIFO is stuck. This preserves the
    idea of a "timeout" and still works for printers which continuously
    drain the channel at some slow rate. It does *not* work, at least with
    some host hardware, if the printer stops accepting data completely
    Eventually I'd like the FIFO timeout to be a module parameter, but I
    thought I would solicit comments before I did that step.

    Parenthetically, I have not been able to get this FIFO timeout code to
    work on the one host I have with DMA capability, but I'm about ready
    to give up on getting it to work since PIO mode works fine and is
    faster in most if not all cases. In case anyone is interested, this
    is a K7T266 Pro motherboard from Micro-Star International with an
    Apollo KT266 chip set (VT8366 Northbridge and VT8233 Southbridge).

    -- Dave Strauss

    Index: drivers/parport/parport_pc.c
    ===================================================================
    RCS file: drivers/parport/RCS/parport_pc.c,v
    retrieving revision 1.1
    diff -up -r1.1 drivers/parport/parport_pc.c
    --- drivers/parport/parport_pc.c 2001/09/12 16:42:38 1.1
    +++ drivers/parport/parport_pc.c 2001/10/12 15:43:19
    @@ -707,6 +707,9 @@ size_t parport_pc_compat_write_block_pio
     {
             size_t written;
             int r;
    + long int expire;
    + const struct parport_pc_private *priv =
    + port->physport->private_data;
     
             /* Special case: a timeout of zero means we cannot call schedule(). */
             if (!port->physport->cad->timeout)
    @@ -728,9 +731,19 @@ size_t parport_pc_compat_write_block_pio
                     written = parport_pc_fifo_write_block_pio (port, buf, length);
     
             /* Finish up. */
    - if (change_mode (port, ECR_PS2) == -EBUSY) {
    - const struct parport_pc_private *priv =
    - port->physport->private_data;
    + /* For some hardware we don't want to touch the mode until
    + * the FIFO is empty, so allow 4 seconds for each position
    + * in the fifo.
    + */
    + expire = jiffies + (priv->fifo_depth * HZ * 4);
    + do {
    + /* Wait for the FIFO to empty */
    + r = change_mode (port, ECR_PS2);
    + if (r != -EBUSY) {
    + break;
    + }
    + } while (time_before (jiffies, expire));
    + if (r == -EBUSY) {
     
                     printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);
     
    @@ -771,6 +784,9 @@ size_t parport_pc_ecp_write_block_pio (s
     {
             size_t written;
             int r;
    + long int expire;
    + const struct parport_pc_private *priv =
    + port->physport->private_data;
     
             /* Special case: a timeout of zero means we cannot call schedule(). */
             if (!port->physport->cad->timeout)
    @@ -812,9 +828,19 @@ size_t parport_pc_ecp_write_block_pio (s
                     written = parport_pc_fifo_write_block_pio (port, buf, length);
     
             /* Finish up. */
    - if (change_mode (port, ECR_PS2) == -EBUSY) {
    - const struct parport_pc_private *priv =
    - port->physport->private_data;
    + /* For some hardware we don't want to touch the mode until
    + * the FIFO is empty, so allow 4 seconds for each position
    + * in the fifo.
    + */
    + expire = jiffies + (priv->fifo_depth * (HZ * 4));
    + do {
    + /* Wait for the FIFO to empty */
    + r = change_mode (port, ECR_PS2);
    + if (r != -EBUSY) {
    + break;
    + }
    + } while (time_before (jiffies, expire));
    + if (r == -EBUSY) {
     
                     printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);
     
    @@ -1717,7 +1743,6 @@ static int __devinit parport_ECP_support
     
             /* Go back to mode 000 */
             frob_econtrol (pb, 0xe0, ECR_SPP << 5);
    - pb->modes |= PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
     
             return 1;
     }
    @@ -2093,6 +2118,7 @@ struct parport *__devinit parport_pc_pro
     #ifdef CONFIG_PARPORT_PC_FIFO
             if (p->dma != PARPORT_DMA_NOFIFO &&
                 priv->fifo_depth > 0 && p->irq != PARPORT_IRQ_NONE) {
    + p->modes |= PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
                     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;

    Index: drivers/char/lp.c
    ===================================================================
    RCS file: drivers/char/RCS/lp.c,v
    retrieving revision 1.1
    diff -up -r1.1 drivers/char/lp.c
    --- drivers/char/lp.c 2001/08/17 15:28:11 1.1
    +++ drivers/char/lp.c 2001/09/14 17:49:21
    @@ -149,6 +149,10 @@ static unsigned int lp_count = 0;
     
     #undef LP_DEBUG
     
    +/* Bits used to manage claiming the parport device */
    +#define LP_PREEMPT_REQUEST 1
    +#define LP_PARPORT_CLAIMED 2
    +
     /* --- low-level port access ----------------------------------- */
     
     #define r_dtr(x) (parport_read_data(lp_table[(x)].dev->port))
    @@ -156,15 +160,55 @@ static unsigned int lp_count = 0;
     #define w_ctr(x,y) do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)
     #define w_dtr(x,y) do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)
     
    +/* Claim the parport or block trying unless we've already claimed it */
    +static void lp_claim_parport_or_block(struct lp_struct *this_lp)
    +{
    + if (!test_and_set_bit(LP_PARPORT_CLAIMED, &this_lp->bits)) {
    + parport_claim_or_block (this_lp->dev);
    + }
    +}
    +
    +/* Claim the parport or block trying unless we've already claimed it */
    +static void lp_release_parport(struct lp_struct *this_lp)
    +{
    + if (test_and_clear_bit(LP_PARPORT_CLAIMED, &this_lp->bits)) {
    + parport_release (this_lp->dev);
    + }
    +}
    +
    +
    +
    +static int lp_preempt(void *handle)
    +{
    + struct lp_struct *this_lp = (struct lp_struct *)handle;
    + set_bit(LP_PREEMPT_REQUEST, &this_lp->bits);
    + return (1);
    +}
    +
    +
    +/*
    + * Try to negotiate to a new mode; if unsuccessful negotiate to
    + * compatibility mode. Return the mode we ended up in.
    + */
    +static int lp_negotiate(struct parport * port, int mode)
    +{
    + if (parport_negotiate (port, mode) != 0) {
    + mode = IEEE1284_MODE_COMPAT;
    + parport_negotiate (port, mode);
    + }
    +
    + return (mode);
    +}
    +
     static int lp_reset(int minor)
     {
             int retval;
    - parport_claim_or_block (lp_table[minor].dev);
    + lp_claim_parport_or_block (&lp_table[minor]);
             w_ctr(minor, LP_PSELECP);
             udelay (LP_DELAY);
             w_ctr(minor, LP_PSELECP | LP_PINITP);
             retval = r_str(minor);
    - parport_release (lp_table[minor].dev);
    + lp_release_parport (&lp_table[minor]);
             return retval;
     }
     
    @@ -176,10 +220,10 @@ static void lp_error (int minor)
                     return;
     
             polling = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE;
    - if (polling) parport_release (lp_table[minor].dev);
    + if (polling) lp_release_parport (&lp_table[minor]);
             interruptible_sleep_on_timeout (&lp_table[minor].waitq,
                                             LP_TIMEOUT_POLLED);
    - if (polling) parport_claim_or_block (lp_table[minor].dev);
    + if (polling) lp_claim_parport_or_block (&lp_table[minor]);
             else parport_yield_blocking (lp_table[minor].dev);
     }
     
    @@ -225,6 +269,12 @@ static int lp_check_status(int minor)
     static int lp_wait_ready(int minor)
     {
             int error = 0;
    +
    + /* If we're not in compatibility mode, we're ready now! */
    + if (lp_table[minor].current_mode != IEEE1284_MODE_COMPAT) {
    + return (0);
    + }
    +
             do {
                     error = lp_check_status (minor);
                     if (error && (LP_F(minor) & LP_ABORT))
    @@ -266,10 +316,10 @@ static ssize_t lp_write(struct file * fi
     
              /* Claim Parport or sleep until it becomes available
               */
    - parport_claim_or_block (lp_table[minor].dev);
    -
    - /* Go to compatibility mode. */
    - parport_negotiate (port, IEEE1284_MODE_COMPAT);
    + lp_claim_parport_or_block (&lp_table[minor]);
    + /* Go to the proper mode. */
    + lp_table[minor].current_mode = lp_negotiate (port,
    + lp_table[minor].best_mode);
     
             parport_set_timeout (lp_table[minor].dev,
                                  lp_table[minor].timeout);
    @@ -294,7 +344,13 @@ static ssize_t lp_write(struct file * fi
     
                     if (copy_size > 0) {
                             /* incomplete write -> check error ! */
    - int error = lp_wait_ready (minor);
    + int error;
    +
    + parport_negotiate (lp_table[minor].dev->port,
    + IEEE1284_MODE_COMPAT);
    + lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
    +
    + error = lp_wait_ready (minor);
     
                             if (error) {
                                     if (retv == 0)
    @@ -303,6 +359,10 @@ static ssize_t lp_write(struct file * fi
                             }
     
                             parport_yield_blocking (lp_table[minor].dev);
    + lp_table[minor].current_mode
    + = lp_negotiate (port,
    + lp_table[minor].best_mode);
    +
                     } else if (current->need_resched)
                             schedule ();
     
    @@ -319,7 +379,14 @@ static ssize_t lp_write(struct file * fi
                     }
             } while (count > 0);
     
    - parport_release (lp_table[minor].dev);
    + if (test_and_clear_bit(LP_PREEMPT_REQUEST,
    + &lp_table[minor].bits)) {
    + printk(KERN_INFO "lp%d releasing parport\n", minor);
    + parport_negotiate (lp_table[minor].dev->port,
    + IEEE1284_MODE_COMPAT);
    + lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
    + lp_release_parport (&lp_table[minor]);
    + }
     
             up (&lp_table[minor].port_mutex);
     
    @@ -343,9 +410,9 @@ static ssize_t lp_read(struct file * fil
             if (down_interruptible (&lp_table[minor].port_mutex))
                     return -EINTR;
     
    - parport_claim_or_block (lp_table[minor].dev);
    + lp_claim_parport_or_block (&lp_table[minor]);
             retval = parport_read (port, kbuf, count);
    - parport_release (lp_table[minor].dev);
    + lp_release_parport (&lp_table[minor]);
     
             if (retval > 0 && copy_to_user (buf, kbuf, retval))
                     retval = -EFAULT;
    @@ -375,9 +442,9 @@ static int lp_open(struct inode * inode,
                should most likely only ever be used by the tunelp application. */
             if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
                     int status;
    - parport_claim_or_block (lp_table[minor].dev);
    + lp_claim_parport_or_block (&lp_table[minor]);
                     status = r_str(minor);
    - parport_release (lp_table[minor].dev);
    + lp_release_parport (&lp_table[minor]);
                     if (status & LP_POUTPA) {
                             printk(KERN_INFO "lp%d out of paper\n", minor);
                             LP_F(minor) &= ~LP_BUSY;
    @@ -397,6 +464,21 @@ static int lp_open(struct inode * inode,
                     LP_F(minor) &= ~LP_BUSY;
                     return -ENOMEM;
             }
    + /* Determine if the peripheral supports ECP mode */
    + lp_claim_parport_or_block (&lp_table[minor]);
    + if ( (lp_table[minor].dev->port->modes & PARPORT_MODE_ECP) &&
    + !parport_negotiate (lp_table[minor].dev->port,
    + IEEE1284_MODE_ECP)) {
    + printk (KERN_INFO "lp%d: ECP mode\n", minor);
    + lp_table[minor].best_mode = IEEE1284_MODE_ECP;
    + } else {
    + printk (KERN_INFO "lp%d: compatibility mode\n", minor);
    + lp_table[minor].best_mode = IEEE1284_MODE_COMPAT;
    + }
    + /* Leave peripheral in compatibility mode */
    + parport_negotiate (lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
    + lp_release_parport (&lp_table[minor]);
    + lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
             return 0;
     }
     
    @@ -404,6 +486,10 @@ static int lp_release(struct inode * ino
     {
             unsigned int minor = MINOR(inode->i_rdev);
     
    + lp_claim_parport_or_block (&lp_table[minor]);
    + parport_negotiate (lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
    + lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
    + lp_release_parport (&lp_table[minor]);
             lock_kernel();
             kfree(lp_table[minor].lp_buffer);
             lp_table[minor].lp_buffer = NULL;
    @@ -466,9 +552,9 @@ static int lp_ioctl(struct inode *inode,
                                     return -EFAULT;
                             break;
                     case LPGETSTATUS:
    - parport_claim_or_block (lp_table[minor].dev);
    + lp_claim_parport_or_block (&lp_table[minor]);
                             status = r_str(minor);
    - parport_release (lp_table[minor].dev);
    + lp_release_parport (&lp_table[minor]);
     
                             if (copy_to_user((int *) arg, &status, sizeof(int)))
                                     return -EFAULT;
    @@ -657,7 +743,7 @@ static int lp_register(int nr, struct pa
             char name[8];
     
             lp_table[nr].dev = parport_register_device(port, "lp",
    - NULL, NULL, NULL, 0,
    + lp_preempt, NULL, NULL, 0,
                                                        (void *) &lp_table[nr]);
             if (lp_table[nr].dev == NULL)
                     return 1;

    Index: include/linux/lp.h
    ===================================================================
    RCS file: include/linux/RCS/lp.h,v
    retrieving revision 1.1
    diff -up -r1.1 include/linux/lp.h
    --- include/linux/lp.h 2001/08/17 16:52:32 1.1
    +++ include/linux/lp.h 2001/09/10 20:44:13
    @@ -145,6 +145,9 @@ struct lp_struct {
             struct semaphore port_mutex;
             wait_queue_head_t dataq;
             long timeout;
    + unsigned int best_mode;
    + unsigned int current_mode;
    + unsigned long bits;
     };
     
     /*

    -- 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 : Fri Oct 12 2001 - 17:52:25 EDT