[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 from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/