patch-2.1.104 linux/drivers/char/lp.c
Next file: linux/drivers/char/mem.c
Previous file: linux/drivers/char/istallion.c
Back to the patch index
Back to the overall index
- Lines: 233
- Date:
Sun May 24 12:09:36 1998
- Orig file:
v2.1.103/linux/drivers/char/lp.c
- Orig date:
Thu May 7 22:51:49 1998
diff -u --recursive --new-file v2.1.103/linux/drivers/char/lp.c linux/drivers/char/lp.c
@@ -16,6 +16,8 @@
* Parport sharing hacking by Andrea Arcangeli <arcangeli@mbox.queen.it>
* 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
*/
/* This driver should, in theory, work with any parallel port that has an
@@ -62,6 +64,23 @@
* can force it using the parameters described above.
*/
+/*
+ * The new interrupt handling code take care of the buggy handshake
+ * of some HP and Epson printer:
+ * ___
+ * ACK _______________ ___________
+ * |__|
+ * ____
+ * BUSY _________ _______
+ * |____________|
+ *
+ * I discovered this using the printer scanner:
+ *
+ * http://www.cs.unibo.it/~arcangel/pscan/pscan-0.4.tar.gz
+ *
+ * 11 May 98, Andrea Arcangeli
+ */
+
#include <linux/module.h>
#include <linux/init.h>
@@ -93,7 +112,7 @@
#ifdef LP_STATS
0, 0, {0},
#endif
- NULL, 0}
+ NULL, 0, 0}
};
/* Test if printer is ready (and optionally has no error conditions) */
@@ -135,8 +154,12 @@
static __inline__ void lp_yield (int minor)
{
- if (!parport_yield_blocking (lp_table[minor].dev) && need_resched)
- schedule ();
+ if (!parport_yield_blocking (lp_table[minor].dev))
+ {
+ if (need_resched)
+ schedule ();
+ } else
+ lp_table[minor].irq_missed = 1;
}
static __inline__ void lp_schedule(int minor)
@@ -145,6 +168,7 @@
register unsigned long int timeslip = (jiffies - dev->time);
if ((timeslip > dev->timeslice) && (dev->port->waithead != NULL)) {
lp_parport_release(minor);
+ lp_table[minor].irq_missed = 1;
schedule ();
lp_parport_claim(minor);
} else
@@ -165,7 +189,6 @@
static inline int lp_char(char lpchar, int minor)
{
- unsigned char status;
unsigned int wait = 0;
unsigned long count = 0;
#ifdef LP_STATS
@@ -175,12 +198,10 @@
for (;;)
{
lp_yield(minor);
- status = r_str (minor);
- if (LP_READY(minor, status))
+ if (LP_READY(minor, r_str(minor)))
break;
- if (!LP_POLLED(minor) || ++count == LP_CHAR(minor) ||
- signal_pending(current))
- return 0;
+ if (++count == LP_CHAR(minor) || signal_pending(current))
+ return 0;
}
w_dtr(minor, lpchar);
@@ -205,7 +226,15 @@
udelay(1);
#endif
/* take strobe low */
- w_ctr(minor, LP_PSELECP | LP_PINITP);
+ 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_PINTEN);
+ }
#ifdef LP_STATS
/* update waittime statistics */
@@ -231,6 +260,9 @@
if (waitqueue_active (&lp_dev->wait_q))
wake_up_interruptible(&lp_dev->wait_q);
+
+ lp_dev->irq_detected = 1;
+ lp_dev->irq_missed = 0;
}
static void lp_error(int minor)
@@ -241,10 +273,12 @@
lp_parport_release(minor);
schedule();
lp_parport_claim(minor);
+ lp_table[minor].irq_missed = 1;
}
}
-static int lp_check_status(int minor) {
+static int lp_check_status(int minor)
+{
unsigned int last = lp_table[minor].last_error;
unsigned char status = r_str(minor);
if ((status & LP_POUTPA)) {
@@ -282,7 +316,6 @@
unsigned long total_bytes_written = 0;
unsigned long bytes_written;
struct lp_struct *lp = &lp_table[minor];
- unsigned char status;
if (minor >= LP_NO)
return -ENXIO;
@@ -290,13 +323,21 @@
return -ENXIO;
lp_table[minor].last_error = 0;
+ lp_table[minor].irq_detected = 0;
+ lp_table[minor].irq_missed = 0;
+
+ if (!LP_POLLED(minor))
+ w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
do {
bytes_written = 0;
copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
if (copy_from_user(lp->lp_buffer, buf, copy_size))
+ {
+ w_ctr(minor, LP_PINITP);
return -EFAULT;
+ }
while (copy_size) {
if (lp_char(lp->lp_buffer[bytes_written], minor)) {
@@ -314,7 +355,9 @@
LP_STAT(minor).sleeps++;
#endif
- if (signal_pending(current)) {
+ if (signal_pending(current))
+ {
+ w_ctr(minor, LP_PINITP);
if (total_bytes_written + bytes_written)
return total_bytes_written + bytes_written;
else
@@ -325,9 +368,15 @@
lp->runchars = 0;
#endif
- if (LP_POLLED(minor)) {
- if (lp_check_status(minor))
- return rc ? rc : -EIO;
+ if (lp_check_status(minor))
+ {
+ w_ctr(minor, LP_PINITP);
+ return rc ? rc : -EIO;
+ }
+
+ if (LP_POLLED(minor) ||
+ lp_table[minor].irq_missed)
+ {
lp_polling:
#if defined(LP_DEBUG) && defined(LP_STATS)
printk(KERN_DEBUG "lp%d sleeping at %d characters for %d jiffies\n", minor, lp->runchars, LP_TIME(minor));
@@ -342,28 +391,19 @@
/*
* We can' t sleep on the interrupt
* since another pardevice need the port.
+ * We must check this in a cli() protected
+ * envinroment to avoid parport sharing
+ * starvation.
*/
sti();
goto lp_polling;
}
- w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
- status = r_str(minor);
- if (!(status & LP_PACK) || (status & LP_PBUSY))
+ if (!lp_table[minor].irq_detected)
{
- /*
- * The interrupt is happened in the
- * meantime so don' t wait for it.
- */
- w_ctr(minor, LP_PSELECP | LP_PINITP);
- sti();
- continue;
+ current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
+ interruptible_sleep_on(&lp->wait_q);
}
- current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
- interruptible_sleep_on(&lp->wait_q);
- w_ctr(minor, LP_PSELECP | LP_PINITP);
sti();
- if (lp_check_status(minor))
- return rc ? rc : -EIO;
}
}
}
@@ -374,6 +414,7 @@
} while (count > 0);
+ w_ctr(minor, LP_PINITP);
return total_bytes_written;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov