[PARPORT] [patch] new lp code (to support Stylus Color irq driven)


Andrea Arcangeli (andrea@e-mind.com)
Sat, 24 Oct 1998 19:49:32 +0200 (CEST)


I updated my lp bugfix-patch to take care of the performance problem you
reported Tim. People that has fast hardware (as me now ;-) must care to
use `tunelp /dev/lp0 -c 1'. This patch is against clean 2.1.126.

Index: linux/drivers/char/lp.c
diff -u linux/drivers/char/lp.c:1.1.1.2 linux/drivers/char/lp.c:1.1.1.1.2.11
--- linux/drivers/char/lp.c:1.1.1.2 Sat Oct 24 15:39:41 1998
+++ linux/drivers/char/lp.c Sat Oct 24 17:25:29 1998
@@ -16,8 +16,14 @@
  * Parport sharing hacking by Andrea Arcangeli
  * Fixed kernel_(to/from)_user memory copy to check for errors
  * by Riccardo Facchetti <fizban@tin.it>
- * Interrupt handling workaround for printers with buggy handshake
- * by Andrea Arcangeli, 11 May 98
+ * Redesigned interrupt handling for handle printers with buggy handshake
+ * by Andrea Arcangeli, 11 May 1998
+ * Full efficient handling of printer with buggy irq handshake (now I have
+ * understood the meaning of the strange handshake). This is done sending new
+ * characters if the interrupt is just happened, even if the printer say to
+ * be still BUSY. This is needed at least with Epson Stylus Color.
+ * I also fixed the irq on the rising edge of the strobe problem.
+ * Andrea Arcangeli, 15 Oct 1998
  */
 
 /* This driver should, in theory, work with any parallel port that has an
@@ -78,7 +84,14 @@
  *
  * ftp://e-mind.com/pub/linux/pscan/
  *
- * 11 May 98, Andrea Arcangeli
+ * My printer scanner run on an Epson Stylus Color show that such printer
+ * generates the irq on the _rising_ edge of the STROBE. Now lp handle
+ * this case fine too.
+ *
+ * I also understood that on such printer we are just allowed to send
+ * new characters after the interrupt even if the BUSY line is still active.
+ *
+ * 15 Oct 1998, Andrea Arcangeli
  */
 
 #include <linux/module.h>
@@ -95,7 +108,6 @@
 
 #include <linux/parport.h>
 #undef LP_STATS
-#undef LP_NEED_CAREFUL
 #include <linux/lp.h>
 
 #include <asm/irq.h>
@@ -115,17 +127,22 @@
                            NULL, 0, 0, 0}
 };
 
-/* Test if printer is ready (and optionally has no error conditions) */
-#ifdef LP_NEED_CAREFUL
-#define LP_READY(minor, status) \
- ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : ((status) & LP_PBUSY))
-#define _LP_CAREFUL_READY(status) \
+/*
+ * Test if printer is ready.
+ */
+#define LP_READY(status) \
    ((status) & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
       (LP_PBUSY|LP_PSELECD|LP_PERRORP)
-#else
-#define LP_READY(minor, status) ((status) & LP_PBUSY)
-#endif
 
+/*
+ * Test if the printer has error conditions.
+ */
+#define LP_NO_ERROR(status) \
+ ((status) & (LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
+ (LP_PSELECD|LP_PERRORP)
+
+#define LP_NO_ACKING(status) ((status) & LP_PACK)
+
 #undef LP_DEBUG
 #undef LP_READ_DEBUG
 
@@ -187,52 +204,88 @@
         return retval;
 }
 
-static inline int lp_char(char lpchar, int minor)
+static inline void lp_wait(int minor)
 {
         unsigned int wait = 0;
+#ifndef __sparc__
+ /* FIXME: should be function(time) */
+ while (wait++ != LP_WAIT(minor));
+#else
+ udelay(1);
+#endif
+
+}
+
+static inline int lp_char(char lpchar, int minor)
+{
         unsigned long count = 0;
 #ifdef LP_STATS
         struct lp_stats *stats;
 #endif
 
+ if (signal_pending(current))
+ return 0;
+
         for (;;)
         {
+ unsigned char status;
                 lp_yield(minor);
- if (LP_READY(minor, r_str(minor)))
+
+ status = r_str(minor);
+ /*
+ * On Epson Stylus Color we must continue even if LP_READY()
+ * is false to be efficient. This way is backwards
+ * compatible with old not-buggy printers. -arca
+ */
+ if (LP_NO_ERROR(status) &&
+ ((lp_table[minor].irq_detected && LP_NO_ACKING(status)) ||
+ LP_READY(status)))
                         break;
- if (++count == LP_CHAR(minor) || signal_pending(current))
- return 0;
+ /*
+ * To have a chance to sleep on the interrupt we should break
+ * the polling loop ASAP. Unfortunately there seems to be
+ * some hardware that underperform so we leave this
+ * configurable at runtime. So when printing with irqs
+ * `tunelp /dev/lp0 -c 1' is a must to take the full
+ * advantage of the irq. -arca
+ */
+ if (++count == LP_CHAR(minor))
+ return 0;
         }
 
         w_dtr(minor, lpchar);
+
 #ifdef LP_STATS
         stats = &LP_STAT(minor);
         stats->chars++;
 #endif
+
+ /*
+ * Epson Stylus Color generate the IRQ on the rising edge of
+ * strobe so clean the irq's information before playing with
+ * the strobe. -arca
+ */
+ lp_table[minor].irq_detected = 0;
+ lp_table[minor].irq_missed = 0;
+ /*
+ * Be sure that the CPU doesn' t reorder instruction. I am not sure
+ * if it' s needed also before an outb(). If not tell me ;-). -arca
+ */
+ mb();
+
         /* must wait before taking strobe high, and after taking strobe
            low, according spec. Some printers need it, others don't. */
-#ifndef __sparc__
- while (wait != LP_WAIT(minor)) /* FIXME: should be a udelay() */
- wait++;
-#else
- udelay(1);
-#endif
+ lp_wait(minor);
+
         /* control port takes strobe high */
- w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
-#ifndef __sparc__
- while (wait) /* FIXME: should be a udelay() */
- wait--;
-#else
- udelay(1);
-#endif
- /* take strobe low */
         if (LP_POLLED(minor))
- /* take strobe low */
- w_ctr(minor, LP_PSELECP | LP_PINITP);
- else
         {
- lp_table[minor].irq_detected = 0;
- lp_table[minor].irq_missed = 0;
+ w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
+ lp_wait(minor);
+ w_ctr(minor, LP_PSELECP | LP_PINITP);
+ } else {
+ w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE | LP_PINTEN);
+ lp_wait(minor);
                 w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
         }
 
@@ -240,7 +293,8 @@
         /* update waittime statistics */
         if (count > stats->maxwait) {
 #ifdef LP_DEBUG
- printk(KERN_DEBUG "lp%d success after %d counts.\n", minor, count);
+ printk(KERN_DEBUG "lp%d success after %d counts.\n",
+ minor, count);
 #endif
                 stats->maxwait = count;
         }
@@ -325,8 +379,12 @@
         lp_table[minor].last_error = 0;
         lp_table[minor].irq_detected = 0;
         lp_table[minor].irq_missed = 1;
+ LP_POLLED(minor) = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE;
 
- w_ctr(minor, LP_PSELECP | LP_PINITP);
+ if (LP_POLLED(minor))
+ w_ctr(minor, LP_PSELECP | LP_PINITP);
+ else
+ w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
 
         do {
                 bytes_written = 0;
@@ -649,14 +707,6 @@
                         else
                                 LP_F(minor) &= ~LP_ABORTOPEN;
                         break;
-#ifdef LP_NEED_CAREFUL
- case LPCAREFUL:
- if (arg)
- LP_F(minor) |= LP_CAREFUL;
- else
- LP_F(minor) &= ~LP_CAREFUL;
- break;
-#endif
                 case LPWAIT:
                         LP_WAIT(minor) = arg;
                         break;
Index: linux/include/linux/lp.h
diff -u linux/include/linux/lp.h:1.1.1.2 linux/include/linux/lp.h:1.1.1.1.2.4
--- linux/include/linux/lp.h:1.1.1.2 Sat Oct 24 15:42:34 1998
+++ linux/include/linux/lp.h Sat Oct 17 16:03:23 1998
@@ -25,7 +25,7 @@
 #define LP_NOPA 0x0010
 #define LP_ERR 0x0020
 #define LP_ABORT 0x0040
-#ifdef LP_NEED_CAREFUL
+#if 0
 #define LP_CAREFUL 0x0080
 #endif
 #define LP_ABORTOPEN 0x0100
@@ -131,6 +131,7 @@
         unsigned int last_error;
         volatile unsigned int irq_detected:1;
         volatile unsigned int irq_missed:1;
+ unsigned int polled:1;
 };
 
 /*
@@ -176,7 +177,7 @@
  */
 #define LP_DELAY 50
 
-#define LP_POLLED(minor) (lp_table[(minor)].dev->port->irq == PARPORT_IRQ_NONE)
+#define LP_POLLED(minor) (lp_table[(minor)].polled)
 #define LP_PREEMPTED(minor) (lp_table[(minor)].dev->port->waithead != NULL)
 
 /*

Andrea Arcangeli

-- 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 2.0b3 on Wed 30 Dec 1998 - 10:18:40 EST