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

From: Dave Strauss (D.Strauss@motorola.com)
Date: Mon Aug 27 2001 - 11:49:31 EDT

  • Next message: Philip Blundell: "Re: [PARPORT] ECP mode transfers in 2.4.x kernels"

    I'm attaching below my patches for getting ECP mode to work in 2.4.x
    kernels. The patches are based on the versions which shipped with
    the Redhat 7.1 distribution.

    A couple of notes about the patches:

    1) For ECP mode to work properly, the parport_pc module must be
        compiled with CONFIG_PARPORT_PC_FIFO set and with the "irq=auto"
        and "dma=auto" options set. For the latter, you need to put
        "options parport_pc irq=auto dma=auto" in /etc/modules.conf if
        you are compiling parport_pc as a module. I don't know the
        kernel switches if parport_pc is compiled into the kernel.

    2) I've implemented ECP mode transfer only for writing to the
        peripheral; however, extending the technique to reading from the
        peripheral should be fairly straightforward.

    3) Philip's original patch used the results from the parport "probe"
        code to determine whether the peripheral supports ECP mode. I
        have found this to be unreliable with some printers, so instead I
        attempt to put the port into ECP mode during the lp_open() call;
        it this succeeds, then I attempt to use ECP mode for writing.
        There should probably be some sort of option that can be passed to
        the driver to tell it which modes to try, or whether it should
        even bother.

    4) I haven't tried EPP mode (since I don't have any peripheral that
        supports it) but extending the technique to EPP shouldn't be
        difficult.

    -- Dave Strauss

    Index: drivers/char/lp.c
    ===================================================================
    RCS file: RCS/lp.c,v
    retrieving revision 1.1
    diff -up -r1.1 lp.c
    --- drivers/char/lp.c 2001/08/17 15:28:11 1.1
    +++ drivers/char/lp.c 2001/08/27 15:38:08
    @@ -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,24 @@ static int lp_open(struct inode * inode,
                     LP_F(minor) &= ~LP_BUSY;
                     return -ENOMEM;
             }
    +#ifdef CONFIG_PARPORT_PC_FIFO
    + /* Determine if the peripheral supports ECP mode */
    + lp_claim_parport_or_block (&lp_table[minor]);
    + if (!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]);
    +#else /* CONFIG_PARPORT_PC_FIFO */
    + printk (KERN_INFO "lp%d: compatibility mode\n", minor);
    + lp_table[minor].best_mode = IEEE1284_MODE_COMPAT;
    +#endif /* CONFIG_PARPORT_PC_FIFO */
    + lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
             return 0;
     }
     
    @@ -404,6 +489,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 +555,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 +746,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/08/27 15:45:33
    @@ -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 Aug 27 2001 - 11:51:45 EDT