Re: [PARPORT] ECP mode transfers in 2.4.x kernels

From: Dave Strauss (D.Strauss@motorola.com)
Date: Mon Sep 17 2001 - 16:17:49 EDT

  • Next message: Rafael Diniz: "Re: [PARPORT] which distro for an old i486 laptop"

    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

    -- 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/09/14 21:28:57
    @@ -531,7 +531,7 @@ static size_t parport_pc_fifo_write_bloc
                             if (!time_before (jiffies, expire)) {
                                     /* Timed out. */
                                     printk (KERN_DEBUG "FIFO write timed out\n");
    - break;
    + expire = jiffies + port->cad->timeout;
                             }
                             ecrval = inb (ECONTROL (port));
                             if (!(ecrval & (1<<2))) {
    @@ -652,7 +652,7 @@ static size_t parport_pc_fifo_write_bloc
                     if (!time_before (jiffies, expire)) {
                             /* Timed out. */
                             printk (KERN_DEBUG "DMA write timed out\n");
    - break;
    + expire = jiffies + port->cad->timeout;
                     }
                     /* Is serviceIntr set? */
                     if (!(inb (ECONTROL (port)) & (1<<2))) {
    @@ -1717,7 +1717,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 +2092,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 : Mon Sep 17 2001 - 16:25:05 EDT