[patch] Re: [patch] Epson Stylus Color 740 & lp irq driven

Andrea Arcangeli (andrea@e-mind.com)
Sat, 17 Oct 1998 18:14:09 +0200 (CEST)


On Fri, 16 Oct 1998, Andrea Arcangeli wrote:

>On Fri, 16 Oct 1998, David Campbell wrote:
>
>>Date sent: Thu, 15 Oct 1998 18:23:29 +0200 (CEST)
>>From: Andrea Arcangeli <andrea@e-mind.com>
>>
>>> 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.
>>
>>I think we need to settle on a standard and then declare some printers
>>broken.
>
>This is impossible in this case I think, since the way Epson printer works
>better broke the specs ;-). I am very interested in _which_ printer will

Woops it does not broke backwards compatibilty. This because when the irq
happens (on the lower or upper edge of ACK) the printer should be just
ready (BUSY high) on not buggy hardware.

____ _______ ___________
BUSY |_____|
___ _____________ _________
ACK |__|
|
\ here we can just send a new chars also with the
right handshake

If eventually (for timing issues) it will happen that irq happens on the
lower edge of ACK and ACK goes low while the printer is not yet ready we
could have missed chars (with my last patch). For this reason I produced a
new patch that will handle this case too (look at the lp_char() part of
the patch).

I also removed the LP_CAREFUL stuff (since it' s slower to check for such
flag than being careful in all cases). So now lp handle the poweron case
fine on Epson Stylus Color (and everywhere I guess).

This patch should go in I think. Comments?

Index: linux/drivers/char/lp.c
diff -u linux/drivers/char/lp.c:1.1.1.1.2.1 linux/drivers/char/lp.c:1.1.1.1.2.8
--- linux/drivers/char/lp.c:1.1.1.1.2.1 Fri Oct 2 19:39:14 1998
+++ linux/drivers/char/lp.c Sat Oct 17 18:10:00 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,84 @@
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 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 +289,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 +375,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 +703,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.1.2.2 linux/include/linux/lp.h:1.1.1.1.2.4
--- linux/include/linux/lp.h:1.1.1.1.2.2 Mon Oct 5 00:12:06 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/