[PARPORT] trustirq patch for 2.1.127


Andrea Arcangeli (andrea@e-mind.com)
Wed, 11 Nov 1998 17:22:16 +0100 (CET)


In the last days I had reports from people that tried my new trust-irq lp
code. It worked for most of people except someone that had to comment out
_only_ the trustirq check to print with irqs. So the trustirq thing has to
be a per /dev/lp? settable flag via ioctl.

Here the new patch against 2.1.127, it adds also some docs, remove the
CAREFUL flag (see my discussion with Phil on linux-kernel) and return to
add the udelay(1) for all arch as default (not done by me but that I agree
completly). It also warn if there is a printer with a buggy handshake that
could need the trustirq thing to be optimized...

And btw the lp_table[minor].flags seems to have something to do with POSIX
(reading the comment above the flag #definitions). I don' t know POSIX,
but such .flags fields are not accessible from userspace so I don' t think
that I am breaking POSIX adding my LP_TRUST_IRQ flag in such place. Am I
breaking POSIX removing LP_CAREFUL btw? I don' t think that POSIX has
defined a LP_CAREFUL flag since it never made a lot of sense.

If everything with POSIX is OK, this could be applyed to 2.1.127 I think.

Index: linux/include/linux/lp.h
diff -u linux/include/linux/lp.h:1.1.1.5 linux/include/linux/lp.h:1.1.1.1.12.9
--- linux/include/linux/lp.h:1.1.1.5 Sun Nov 8 02:38:45 1998
+++ linux/include/linux/lp.h Wed Nov 11 17:00:58 1998
@@ -25,10 +25,13 @@
 #define LP_NOPA 0x0010
 #define LP_ERR 0x0020
 #define LP_ABORT 0x0040
-#ifdef LP_NEED_CAREFUL
-#define LP_CAREFUL 0x0080
-#endif
+#define LP_CAREFUL 0x0080 /* obsoleted -arca */
 #define LP_ABORTOPEN 0x0100
+/*
+ * It has nothing to do with POSIX but this is sure the best place for the
+ * LP_TRUST_IRQ flag ... -arca
+ */
+#define LP_TRUST_IRQ 0x0200
 
 /* timeout for each character. This is relative to bus cycles -- it
  * is the count in a busy loop. THIS IS THE VALUE TO CHANGE if you
@@ -41,13 +44,10 @@
 #define LP_INIT_CHAR 1000
 
 /* The parallel port specs apparently say that there needs to be
- * a .5usec wait before and after the strobe. Since there are wildly
- * different computers running linux, I can't come up with a perfect
- * value so if 20 is not good for you use `tunelp /dev/lp? -w ?`.
- * You can also set it to 0 if your printer handle that.
+ * a .5usec wait before and after the strobe.
  */
 
-#define LP_INIT_WAIT 20
+#define LP_INIT_WAIT 1
 
 /* This is the amount of time that the driver waits for the printer to
  * catch up when the printer's buffer appears to be filled. If you
@@ -70,11 +70,10 @@
                             or 0 for polling (no IRQ) */
 #define LPGETIRQ 0x0606 /* get the current IRQ number */
 #define LPWAIT 0x0608 /* corresponds to LP_INIT_WAIT */
-#ifdef LP_NEED_CAREFUL
+/* NOTE: LPCAREFUL is obsoleted and it' s always the default right now -arca */
 #define LPCAREFUL 0x0609 /* call with TRUE arg to require out-of-paper, off-
                             line, and error indicators good on all writes,
                             FALSE to ignore them. Default is ignore. */
-#endif
 #define LPABORTOPEN 0x060a /* call with TRUE arg to abort open() on error,
                             FALSE to ignore error. Default is ignore. */
 #define LPGETSTATUS 0x060b /* return LP_S(minor) */
@@ -83,6 +82,7 @@
 #define LPGETSTATS 0x060d /* get statistics (struct lp_stats) */
 #endif
 #define LPGETFLAGS 0x060e /* get status flags */
+#define LPTRUSTIRQ 0x060f /* set/unset the LP_TRUST_IRQ flag */
 
 /* timeout for printk'ing a timeout, in jiffies (100ths of a second).
    This is also used for re-checking error conditions if LP_ABORT is
@@ -96,7 +96,7 @@
 #define LP_TIME(minor) lp_table[(minor)].time /* wait time */
 #define LP_WAIT(minor) lp_table[(minor)].wait /* strobe wait */
 #define LP_IRQ(minor) lp_table[(minor)].dev->port->irq /* interrupt # */
- /* 0 means polled */
+ /* PARPORT_IRQ_NONE means polled */
 #ifdef LP_STATS
 #define LP_STAT(minor) lp_table[(minor)].stats /* statistics area */
 #endif
Index: linux/drivers/char/lp.c
diff -u linux/drivers/char/lp.c:1.1.1.5 linux/drivers/char/lp.c:1.1.1.1.12.19
--- linux/drivers/char/lp.c:1.1.1.5 Sun Nov 8 02:35:21 1998
+++ linux/drivers/char/lp.c Wed Nov 11 17:00:52 1998
@@ -16,8 +16,17 @@
  * 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. To enable
+ * the new TRUST_IRQ mode read the `LP OPTIMIZATION' section below...
+ * Fixed the irq on the rising edge of the strobe case.
+ * Obsoleted the CAREFUL flag since a printer that doesn' t work with
+ * CAREFUL will block a bit after in lp_check_status().
+ * Andrea Arcangeli, 15 Oct 1998
  */
 
 /* This driver should, in theory, work with any parallel port that has an
@@ -49,6 +58,71 @@
  * # insmod lp.o reset=1
  */
 
+/*
+ * LP OPTIMIZATIONS
+ *
+ * - TRUST_IRQ flag
+ *
+ * Epson Stylus Color, HP and many other new printers want the TRUST_IRQ flag
+ * set when printing with interrupts. This is a long story. Such printers
+ * use a broken handshake (see the timing graph below) when printing with
+ * interrupts. The lp driver as default is just able to handle such bogus
+ * handshake, but setting such flag cause lp to go faster and probably do
+ * what such printers want (even if not documented).
+ *
+ * NOTE that setting the TRUST_IRQ flag in some printer can cause the irq
+ * printing to fail completly. You must try, to know if your printer
+ * will handle it. I suggest a graphics printing to force a major flow of
+ * characters to the printer for do the test. NOTE also that the TRUST_IRQ
+ * flag _should_ be fine everywhere but there is a lot of buggy hardware out
+ * there, so I am forced to implement it as a not-default thing.
+ * WARNING: before to do the test, be sure to have not played with the
+ * `-w' parameter of tunelp!
+ *
+ * Note also that lp automagically warn you (with a KERN_WARNING) if it
+ * detects that you could _try_ to set the TRUST_IRQ flag to speed up the
+ * printing and decrease the CPU load.
+ *
+ * To set the TRUST_IRQ flag you can use this command:
+ *
+ * tunelp /dev/lp? -T on
+ *
+ * If you have an old tunelp executable you can (hack and) use this simple
+ * C lazy proggy to set the flag in the lp driver:
+
+-------------------------- cut here -------------------------------------
+#define LPTRUSTIRQ 0x060f
+
+int main(int argc, char **argv)
+{
+ int fd = open("/dev/lp0");
+ ioctl(fd, LPTRUSTIRQ, argc - 1);
+ if (argc - 1)
+ printf("trusting the irq\n");
+ else
+ printf("untrusting the irq\n");
+ return 0;
+}
+-------------------------- cut here -------------------------------------
+
+ * - LP_WAIT time
+ *
+ * You can use this setting if your printer is fast enough and/or your
+ * machine is slow enough ;-).
+ *
+ * tunelp /dev/lp? -w 0
+ *
+ * - LP_CHAR tries
+ *
+ * If you print with irqs probably you can decrease the CPU load a lot using
+ * this setting. This is not the default because the printing is reported to
+ * be jerky somewhere...
+ *
+ * tunelp /dev/lp? -c 1
+ *
+ * 11 Nov 1998, Andrea Arcangeli
+ */
+
 /* COMPATIBILITY WITH OLD KERNELS
  *
  * Under Linux 2.0 and previous versions, lp devices were bound to ports at
@@ -79,6 +153,12 @@
  * 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.
+ *
+ * 15 Oct 1998, Andrea Arcangeli
  */
 
 #include <linux/module.h>
@@ -95,7 +175,6 @@
 
 #include <linux/parport.h>
 #undef LP_STATS
-#undef LP_NEED_CAREFUL
 #include <linux/lp.h>
 
 #include <asm/irq.h>
@@ -115,16 +194,14 @@
                            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) \
- ((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 printer is ready */
+#define LP_READY(status) ((status) & LP_PBUSY)
+/* Test if the printer is not acking the strobe */
+#define LP_NO_ACKING(status) ((status) & LP_PACK)
+/* Test if the printer has error conditions */
+#define LP_NO_ERROR(status) \
+ (((status) & (LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
+ (LP_PSELECD|LP_PERRORP))
 
 #undef LP_DEBUG
 #undef LP_READ_DEBUG
@@ -187,60 +264,122 @@
         return retval;
 }
 
+#define lp_wait(minor) udelay(LP_WAIT(minor))
+
 static inline int lp_char(char lpchar, int minor)
 {
- unsigned int wait = 0;
         unsigned long count = 0;
 #ifdef LP_STATS
         struct lp_stats *stats;
 #endif
 
+ if (signal_pending(current))
+ return 0;
+
         for (;;)
         {
+ unsigned char status;
+
+ /*
+ * Give a chance to other pardevice to run in the meantime.
+ */
                 lp_yield(minor);
- if (LP_READY(minor, r_str(minor)))
- break;
- if (++count == LP_CHAR(minor) || signal_pending(current))
- return 0;
+
+ status = r_str(minor);
+ if (LP_NO_ERROR(status))
+ {
+ if (LP_READY(status))
+ break;
+ /*
+ * This is a crude hack that should be well known
+ * at least by Epson device driver developers. -arca
+ */
+ if ((LP_F(minor) & LP_TRUST_IRQ) &&
+ !LP_POLLED(minor) &&
+ LP_NO_ACKING(status) &&
+ lp_table[minor].irq_detected)
+ break;
+ }
+ /*
+ * NOTE: if you run with irqs you _must_ use
+ * `tunelp /dev/lp? -c 1' to be rasonable efficient!
+ */
+ if (++count == LP_CHAR(minor))
+ {
+ if (!LP_POLLED(minor) && lp_table[minor].irq_detected)
+ {
+ static int first_time = 1;
+ /*
+ * The printer is using a buggy handshake, so
+ * revert to polling to not overload the
+ * machine and warn the user that its printer
+ * could get optimized trusting the irq. -arca
+ */
+ lp_table[minor].irq_missed = 1;
+ if (first_time)
+ {
+ first_time = 0;
+ printk(KERN_WARNING "lp%d: the "
+ "printing could be optimized "
+ "using the TRUST_IRQ flag, "
+ "see the top of "
+ "linux/drivers/char/lp.c\n",
+ minor);
+ }
+ }
+ return 0;
+ }
         }
 
         w_dtr(minor, lpchar);
+
 #ifdef LP_STATS
         stats = &LP_STAT(minor);
         stats->chars++;
 #endif
+
         /* 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
         {
+ w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
+ lp_wait(minor);
+ w_ctr(minor, LP_PSELECP | LP_PINITP);
+ } else {
+ /*
+ * 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 instructions. -arca
+ */
+ mb();
+ w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE | LP_PINTEN);
+ lp_wait(minor);
                 w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
         }
 
+ /*
+ * Give to the printer a chance to put BUSY low. Really we could
+ * remove this because we could _guess_ that we are slower to reach
+ * again lp_char() than the printer to put BUSY low, but I' d like
+ * to remove this variable from the function I go solve
+ * when I read bug reports ;-). -arca
+ */
+ lp_wait(minor);
+
 #ifdef LP_STATS
         /* 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,7 +464,10 @@
         lp_table[minor].irq_detected = 0;
         lp_table[minor].irq_missed = 1;
 
- 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;
@@ -396,9 +538,7 @@
                                                 goto lp_polling;
                                         }
                                         if (!lp_table[minor].irq_detected)
- {
                                                 interruptible_sleep_on_timeout(&lp->wait_q, LP_TIMEOUT_INTERRUPT);
- }
                                         sti();
                                 }
                         }
@@ -645,7 +785,7 @@
                         else
                                 LP_F(minor) &= ~LP_ABORTOPEN;
                         break;
-#ifdef LP_NEED_CAREFUL
+#ifdef OBSOLETED
                 case LPCAREFUL:
                         if (arg)
                                 LP_F(minor) |= LP_CAREFUL;
@@ -653,6 +793,12 @@
                                 LP_F(minor) &= ~LP_CAREFUL;
                         break;
 #endif
+ case LPTRUSTIRQ:
+ if (arg)
+ LP_F(minor) |= LP_TRUST_IRQ;
+ else
+ LP_F(minor) &= ~LP_TRUST_IRQ;
+ break;
                 case LPWAIT:
                         LP_WAIT(minor) = arg;
                         break;
@@ -695,7 +841,6 @@
         }
         return retval;
 }
-
 
 static struct file_operations lp_fops = {
         lp_lseek,

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