[PARPORT] [patch] Epson Stylus Color 740 & lp irq driven


Andrea Arcangeli (andrea@e-mind.com)
Thu, 15 Oct 1998 18:23:29 +0200 (CEST)


Finally some days ago I bought a new printer (the one in the subject).
The lp in the stock kernel fail to print in irq mode on it. Today I taken
the time to understand why.

1. My new printer generate the irq on the rising edge of the strobe
   and right now lp is not able to handle this. This cause lp to miss
   irqs. Once this bug is fixed lp overload the CPU (see point 2) but
   works.
2. When my new printer when generate the irq the busy line is still set,
   but the printer is _not_ busy!! Infact if I wait for the printer to
   become ready according the BUSY flag, I _always_
   get the irq before lp can reach the sleep_on_interruptible() (with
   `tunelp /dev/lp0 -w 0' and `tunelp /dev/lp0 -c 1' set).
   My new printer is just able to eat new chars once it has generated
   the irq. Trusting the irq cause also lp to sleep on the irq sometimes
   (this seems to prove that it' s the right thing to do). So lp doesn't
   overload the CPU this way.
   It would be nice to know if trusting the irq works also for other
   hardware (it' s sure the best thing to do here).

This my new patch fix the irq at rising edge of the strobe thing and add
the interrupt==printer_ready feature. If I don' t trust the irq a polled
printing is more efficient.

An implementation that would make irq printing a bit less efficient than
polling here and that sure works everywhere is to do this check in the
polling loop of lp_char:

        if (++times >= LP_CHARS())
        {
                if (irq_happened)
                    ^^^^^^^^^^^^
                        irq_missed = 1
                        ^^^^^^^^^^
                break;
        }

This is ugly but that would be the only way to avoid lp to eat all my CPU
here, remaining backwards compatibile with the specs. That check basically
means: "if the interrupt happened, but also after a whole loop of `times'
tries the printer is still busy, go in polling mode and poll-sleep, since
the interrupt really make no sense now".

Here instead my new lp (Epson Stylus Color) patch against [2.1.125 + my
parport update sent to Linus some days ago]:

Index: linux/drivers/char/lp.c
diff -u linux/drivers/char/lp.c:1.1.1.1.2.1.2.2 linux/drivers/char/lp.c:1.1.1.1.2.2
--- linux/drivers/char/lp.c:1.1.1.1.2.1.2.2 Tue Oct 13 18:21:42 1998
+++ linux/drivers/char/lp.c Thu Oct 15 17:19:26 1998
@@ -78,7 +78,14 @@
  *
  * ftp://e-mind.com/pub/linux/pscan/
  *
- * 11 May 98, Andrea Arcangeli
+ * 11 May 1998, Andrea Arcangeli
+ *
+ * The 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.
+ *
+ * 15 Oct 1998, Andrea Arcangeli
+ *
  */
 
 #include <linux/module.h>
@@ -187,52 +194,80 @@
         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 (;;)
         {
                 lp_yield(minor);
- if (LP_READY(minor, r_str(minor)))
+ /*
+ * On Epson Stylus Color we can trust the interrupt as
+ * the busy flag. If somewhere this will break the printing
+ * let me know. -arca
+ */
+ if (lp_table[minor].irq_detected ||
+ LP_READY(minor, r_str(minor)))
                         break;
- if (++count == LP_CHAR(minor) || signal_pending(current))
+ /*
+ * On fast printer to have a chance to sleep on the interrupt
+ * we must break the polling loop ASAP. -arca.
+ */
+ if (!LP_POLLED(minor) || ++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 +275,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 +361,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;
Index: linux/include/linux/lp.h
diff -u linux/include/linux/lp.h:1.1.1.1.2.2 linux/include/linux/lp.h:1.1.1.1.2.3
--- linux/include/linux/lp.h:1.1.1.1.2.2 Mon Oct 5 00:12:06 1998
+++ linux/include/linux/lp.h Thu Oct 15 17:19:38 1998
@@ -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)
 
 /*

The irq on the rising of the strobe is a needed and safe bugfix for all
printers.

I' d like if people with any kind of hardware would try my patch and
feedback if it cause missed characters or broken printing. It would be
still more interesting to try this patch with HP hardware that was
generating the irq some time before the printer was ready too...

With this my new patch the printer here produce 30000/40000 irq/sec
(30/40kbyte of troughtput) and the load of the CPU is still very low (as
or less than polling mode) and the printing scale better than in polling.

Comments?

Andrea Arcangeli

PS. CC'ed to linux-kernel too hoping to enlarge the testing (and hoping to
reach somebody at Epson that could that I' m right ;-).

-- 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:35 EST