patch-2.3.10 linux/drivers/char/lp.c

Next file: linux/drivers/char/lp_intern.c
Previous file: linux/drivers/char/joystick/joy-db9.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.9/linux/drivers/char/lp.c linux/drivers/char/lp.c
@@ -27,6 +27,9 @@
  * 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
+ * Obsoleted and removed all the lowlevel stuff implemented in the last
+ * month to use the IEEE1284 functions (that handle the _new_ compatibilty
+ * mode fine).
  */
 
 /* This driver should, in theory, work with any parallel port that has an
@@ -58,74 +61,6 @@
  *	# 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 -------------------------------------
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#define	LPTRUSTIRQ  0x060f
-
-int main(int argc, char **argv)
-{
-	int fd = open("/dev/lp0", O_RDONLY);
-	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
@@ -162,6 +97,15 @@
  * this case fine too.
  *
  *					15 Oct 1998, Andrea Arcangeli
+ *
+ * The so called `buggy' handshake is really the well documented
+ * compatibility mode IEEE1284 handshake. They changed the well known
+ * Centronics handshake acking in the middle of busy expecting to not
+ * break drivers or legacy application, while they broken linux lp
+ * until I fixed it reverse engineering the protocol by hand some
+ * month ago...
+ *
+ *                                     14 Dec 1998, Andrea Arcangeli
  */
 
 #include <linux/module.h>
@@ -175,6 +119,8 @@
 #include <linux/malloc.h>
 #include <linux/fcntl.h>
 #include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/console.h>
 
 #include <linux/parport.h>
 #undef LP_STATS
@@ -189,6 +135,8 @@
 
 struct lp_struct lp_table[LP_NO];
 
+static unsigned int lp_count = 0;
+
 /* Test if printer is ready */
 #define	LP_READY(status)	((status) & LP_PBUSY)
 /* Test if the printer is not acking the strobe */
@@ -197,7 +145,9 @@
 #define LP_NO_ERROR(status)	((status) & LP_PERRORP)
 
 #undef LP_DEBUG
-#undef LP_READ_DEBUG
+
+/* If you want to see if you can get lp_poll working, define this. */
+#undef SUPPORT_POLL
 
 /* --- parport support ----------------------------------------- */
 
@@ -205,15 +155,62 @@
 {
        struct lp_struct *lps = (struct lp_struct *)handle;
 
-       if (waitqueue_active (&lps->wait_q))
-               wake_up_interruptible(&lps->wait_q);
+       if (!(lps->flags & LP_PORT_BUSY)) {
+	       /* Let the port go. */
+	       clear_bit (LP_HAVE_PORT_BIT, &lps->flags);
+	       return 0;
+       }
+
+       if (!(lps->flags & LP_PORT_BUSY)) {
+	       /* Let the port go. */
+	       clear_bit (LP_HAVE_PORT_BIT, &lps->flags);
+	       return 0;
+       }
 
        /* Don't actually release the port now */
        return 1;
 }
 
-#define lp_parport_release(x)	do { parport_release(lp_table[(x)].dev); } while (0);
-#define lp_parport_claim(x)	do { parport_claim_or_block(lp_table[(x)].dev); } while (0);
+static void lp_check_data (struct lp_struct *lp)
+{
+#if !defined(CONFIG_PARPORT_1284) || !defined (SUPPORT_POLL)
+	return;
+#else
+	struct pardevice *dev = lp->dev;
+	if (!(lp->flags & LP_NO_REVERSE)) {
+		int err = parport_negotiate (dev->port, IEEE1284_MODE_NIBBLE);
+		if (err)
+			lp->flags |= LP_NO_REVERSE;
+		else {
+			unsigned char s = parport_read_status (dev->port);
+			if (s & PARPORT_STATUS_ERROR)
+				lp->flags &= ~LP_DATA_AVAIL;
+			else {
+				lp->flags |= LP_DATA_AVAIL;
+				if (waitqueue_active (&lp->dataq))
+					wake_up_interruptible (&lp->dataq);
+			}
+		}
+	}
+#endif /* IEEE 1284 support */
+}
+
+static void lp_parport_release (int minor)
+{
+	lp_check_data (&lp_table[minor]);
+	if (test_and_clear_bit (LP_HAVE_PORT_BIT, &lp_table[minor].flags))
+		parport_release (lp_table[minor].dev);
+
+	lp_table[minor].flags &= ~LP_PORT_BUSY;
+}
+
+static void lp_parport_claim (int minor)
+{
+	if (!test_and_set_bit (LP_HAVE_PORT_BIT, &lp_table[minor].flags))
+		parport_claim_or_block (lp_table[minor].dev);
+
+	lp_table[minor].flags |= LP_PORT_BUSY;
+}
 
 /* --- low-level port access ----------------------------------- */
 
@@ -222,29 +219,6 @@
 #define w_ctr(x,y)	do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)
 #define w_dtr(x,y)	do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)
 
-static __inline__ void lp_yield (int minor)
-{
-	if (!parport_yield_blocking (lp_table[minor].dev))
-	{
-		if (current->need_resched)
-			schedule ();
-	} else
-		lp_table[minor].irq_missed = 1;
-}
-
-static __inline__ void lp_schedule(int minor, long timeout)
-{
-	struct pardevice *dev = lp_table[minor].dev;
-	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_timeout(timeout);
-		lp_parport_claim(minor);
-	} else
-		schedule_timeout(timeout);
-}
-
 static int lp_reset(int minor)
 {
 	int retval;
@@ -257,161 +231,51 @@
 	return retval;
 }
 
-#define	lp_wait(minor)	udelay(LP_WAIT(minor))
-
-static inline int lp_char(char lpchar, int minor)
+static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	unsigned long count = 0;
-#ifdef LP_STATS
-	struct lp_stats *stats;
-#endif
-
-	if (signal_pending(current))
-		return 0;
-
-	for (;;)
-	{
-		unsigned char status;
-		int irq_ok = 0;
-
-		/*
-		 * Give a chance to other pardevice to run in the meantime.
-		 */
-		lp_yield(minor);
-
-		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
-			 */
-			irq_ok = (!LP_POLLED(minor) &&
-				  LP_NO_ACKING(status) &&
-				  lp_table[minor].irq_detected);
-			if ((LP_F(minor) & LP_TRUST_IRQ) && irq_ok)
-				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 (irq_ok)
-			{
-				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
+	struct lp_struct *lp_dev = (struct lp_struct *) dev_id;
+	if (!(lp_dev->flags & LP_PORT_BUSY))
+		/* We must have the port since we got an interrupt. */
+		lp_check_data (lp_dev);
+	if (waitqueue_active (&lp_dev->waitq))
+		wake_up_interruptible (&lp_dev->waitq);
+}
 
-	/* must wait before taking strobe high, and after taking strobe
-	   low, according spec.  Some printers need it, others don't. */
-	lp_wait(minor);
+static void lp_wakeup (void *handle)
+{
+	struct lp_struct *lp_dev = handle;
 
-	/* control port takes strobe high */
-	if (LP_POLLED(minor))
-	{
-		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);
+	if (lp_dev->flags & LP_PORT_BUSY)
+		return;
 
-#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);
-#endif
-		stats->maxwait = count;
+	/* Grab the port if it can help (i.e. reverse mode is possible). */
+	if (!(lp_dev->flags & LP_NO_REVERSE)) {
+		parport_claim (lp_dev->dev);
+		set_bit (LP_HAVE_PORT_BIT, &lp_dev->flags);
+		lp_check_data (lp_dev);
+		if (waitqueue_active (&lp_dev->waitq))
+			wake_up_interruptible (&lp_dev->waitq);
 	}
-	count *= 256;
-	wait = (count > stats->meanwait) ? count - stats->meanwait :
-	    stats->meanwait - count;
-	stats->meanwait = (255 * stats->meanwait + count + 128) / 256;
-	stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
-#endif
-
-	return 1;
 }
 
-static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void lp_error (int minor)
 {
-	struct lp_struct *lp_dev = (struct lp_struct *) dev_id;
+	int polling;
 
-	if (waitqueue_active (&lp_dev->wait_q))
-		wake_up_interruptible(&lp_dev->wait_q);
-
-	lp_dev->irq_detected = 1;
-	lp_dev->irq_missed = 0;
-}
+	if (LP_F(minor) & LP_ABORT)
+		return;
 
-static void lp_error(int minor)
-{
-	if (LP_POLLED(minor) || LP_PREEMPTED(minor)) {
-		current->state = TASK_INTERRUPTIBLE;
-		lp_parport_release(minor);
-		schedule_timeout(LP_TIMEOUT_POLLED);
-		lp_parport_claim(minor);
-		lp_table[minor].irq_missed = 1;
-	}
+	polling = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE;
+	if (polling) lp_parport_release (minor);
+	interruptible_sleep_on_timeout (&lp_table[minor].waitq,
+					LP_TIMEOUT_POLLED);
+	if (polling) lp_parport_claim (minor);
+	else parport_yield_blocking (lp_table[minor].dev);
 }
 
 static int lp_check_status(int minor)
 {
+	int error = 0;
 	unsigned int last = lp_table[minor].last_error;
 	unsigned char status = r_str(minor);
 	if (status & LP_PERRORP)
@@ -422,155 +286,112 @@
 			last = LP_POUTPA;
 			printk(KERN_INFO "lp%d out of paper\n", minor);
 		}
+		error = -ENOSPC;
 	} else if (!(status & LP_PSELECD)) {
 		if (last != LP_PSELECD) {
 			last = LP_PSELECD;
 			printk(KERN_INFO "lp%d off-line\n", minor);
 		}
+		error = -EIO;
 	} else {
 		if (last != LP_PERRORP) {
 			last = LP_PERRORP;
 			printk(KERN_INFO "lp%d on fire\n", minor);
 		}
+		error = -EIO;
 	}
 
 	lp_table[minor].last_error = last;
 
-	if (last != 0) {
-		if (LP_F(minor) & LP_ABORT)
-			return 1;
+	if (last != 0)
 		lp_error(minor);
-	}
 
-	return 0;
+	return error;
 }
 
-static int lp_write_buf(unsigned int minor, const char *buf, int count)
+static ssize_t lp_write(struct file * file, const char * buf,
+		        size_t count, loff_t *ppos)
 {
-	unsigned long copy_size;
-	unsigned long total_bytes_written = 0;
-	unsigned long bytes_written;
-	struct lp_struct *lp = &lp_table[minor];
-
-	if (minor >= LP_NO)
-		return -ENXIO;
-	if (lp->dev == NULL)
-		return -ENXIO;
-
-	lp_table[minor].last_error = 0;
-	lp_table[minor].irq_detected = 0;
-	lp_table[minor].irq_missed = 1;
-
-	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;
-		copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
-
-		if (copy_from_user(lp->lp_buffer, buf, copy_size))
-		{
-			w_ctr(minor, LP_PSELECP | LP_PINITP);
-			return -EFAULT;
-		}
+	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+	struct parport *port = lp_table[minor].dev->port;
+	char *kbuf = lp_table[minor].lp_buffer;
+	ssize_t retv = 0;
+	ssize_t written;
+	size_t copy_size = count;
 
-		while (copy_size) {
-			if (lp_char(lp->lp_buffer[bytes_written], minor)) {
-				--copy_size;
-				++bytes_written;
 #ifdef LP_STATS
-				lp->runchars++;
-#endif
-			} else {
-				int rc = total_bytes_written + bytes_written;
+	if (jiffies-lp_table[minor].lastcall > LP_TIME(minor))
+		lp_table[minor].runchars = 0;
 
-#ifdef LP_STATS
-				if (lp->runchars > LP_STAT(minor).maxrun)
-					LP_STAT(minor).maxrun = lp->runchars;
-				LP_STAT(minor).sleeps++;
+	lp_table[minor].lastcall = jiffies;
 #endif
 
-				if (signal_pending(current))
-				{
-					w_ctr(minor, LP_PSELECP | LP_PINITP);
-					if (total_bytes_written + bytes_written)
-						return total_bytes_written + bytes_written;
-					else
-						return -EINTR;
-				}
+	/* Need to copy the data from user-space. */
+	if (copy_size > LP_BUFFER_SIZE)
+		copy_size = LP_BUFFER_SIZE;
 
-#ifdef LP_STATS
-				lp->runchars = 0;
-#endif
+	if (copy_from_user (kbuf, buf, copy_size))
+		return -EFAULT;
 
-				if (lp_check_status(minor))
-				{
-					w_ctr(minor, LP_PSELECP | LP_PINITP);
-					return rc ? rc : -EIO;
-				}
+ 	/* Claim Parport or sleep until it becomes available
+ 	 */
+ 	lp_parport_claim (minor);
 
-				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));
-#endif
-					current->state = TASK_INTERRUPTIBLE;
-					lp_schedule(minor, LP_TIME(minor));
-				} else {
-					cli();
-					if (LP_PREEMPTED(minor))
-					{
-						/*
-						 * 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;
-					}
-					if (!lp_table[minor].irq_detected)
-						interruptible_sleep_on_timeout(&lp->wait_q, LP_TIMEOUT_INTERRUPT);
-					sti();
-				}
-			}
-		}
+	/* Go to compatibility mode. */
+	parport_negotiate (port, IEEE1284_MODE_COMPAT);
 
-		total_bytes_written += bytes_written;
-		buf += bytes_written;
-		count -= bytes_written;
+	do {
+		/* Wait until lp_read has finished. */
+		if (down_interruptible (&lp_table[minor].port_mutex))
+			break;
 
-	} while (count > 0);
+		/* Write the data. */
+		written = parport_write (port, kbuf, copy_size);
+		if (written >= 0) {
+			copy_size -= written;
+			count -= written;
+			buf  += written;
+			retv += written;
+		}
 
-	w_ctr(minor, LP_PSELECP | LP_PINITP);
-	return total_bytes_written;
-}
+		up (&lp_table[minor].port_mutex);
 
-static ssize_t lp_write(struct file * file, const char * buf,
-		        size_t count, loff_t *ppos)
-{
-	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
-	ssize_t retv;
+		if (signal_pending (current)) {
+			if (retv == 0)
+				retv = -EINTR;
 
-#ifdef LP_STATS
-	if (jiffies-lp_table[minor].lastcall > LP_TIME(minor))
-		lp_table[minor].runchars = 0;
+			break;
+		}
 
-	lp_table[minor].lastcall = jiffies;
-#endif
+		if (copy_size > 0) {
+			/* incomplete write -> check error ! */
+			int error = lp_check_status (minor);
 
- 	/* Claim Parport or sleep until it becomes available
- 	 */
- 	lp_parport_claim (minor);
+			if (LP_F(minor) & LP_ABORT) {
+				if (retv == 0)
+					retv = error;
+				break;
+			}
 
-	retv = lp_write_buf(minor, buf, count);
+			parport_yield_blocking (lp_table[minor].dev);
+		} else if (current->need_resched)
+			schedule ();
+
+		if (count) {
+			copy_size = count;
+			if (copy_size > LP_BUFFER_SIZE)
+				copy_size = LP_BUFFER_SIZE;
+
+			if (copy_from_user(kbuf, buf, copy_size)) {
+				if (retv == 0)
+					retv = -EFAULT;
+				break;
+			}
+		}	
+	} while (count > 0);
 
  	lp_parport_release (minor);
+
  	return retv;
 }
 
@@ -579,109 +400,54 @@
 	return -ESPIPE;
 }
 
-#ifdef CONFIG_PRINTER_READBACK
+#ifdef CONFIG_PARPORT_1284
 
-static int lp_read_nibble(int minor) 
-{
-	unsigned char i;
-	i = r_str(minor)>>3;
-	i &= ~8;
-	if ((i & 0x10) == 0) i |= 8;
-	return (i & 0x0f);
-}
-
-static void lp_read_terminate(struct parport *port) {
-	parport_write_control(port, (parport_read_control(port) & ~2) | 8);
-	/* SelectIN high, AutoFeed low */
-	if (parport_wait_peripheral(port, 0x80, 0)) 
-		/* timeout, SelectIN high, Autofeed low */
-		return;
-	parport_write_control(port, parport_read_control(port) | 2);
-	/* AutoFeed high */
-	parport_wait_peripheral(port, 0x80, 0x80);
-	/* no timeout possible, Autofeed low, SelectIN high */
-	parport_write_control(port, (parport_read_control(port) & ~2) | 8);
-}
-
-/* Status readback confirming to ieee1284 */
+/* Status readback conforming to ieee1284 */
 static ssize_t lp_read(struct file * file, char * buf,
-		       size_t length, loff_t *ppos)
+		       size_t count, loff_t *ppos)
 {
-	int i;
 	unsigned int minor=MINOR(file->f_dentry->d_inode->i_rdev);
-	char *temp = buf;
-	ssize_t count = 0;
-	unsigned char z = 0;
-	unsigned char Byte = 0;
 	struct parport *port = lp_table[minor].dev->port;
+	ssize_t retval = 0;
+	char *kbuf = lp_table[minor].lp_buffer;
+
+	if (count > LP_BUFFER_SIZE)
+		count = LP_BUFFER_SIZE;
 
 	lp_parport_claim (minor);
 
-	switch (parport_ieee1284_nibble_mode_ok(port, 0))
-	{
-	case 0:
-		/* Handshake failed. */
-		lp_read_terminate(port);
-		lp_parport_release (minor);
-		return -EIO;
-	case 1:
-		/* No data. */
-		lp_read_terminate(port);
-		lp_parport_release (minor);
-		return 0;
-	default:
-		/* Data available. */
+	if (!down_interruptible (&lp_table[minor].port_mutex)) {
+		for (;;) {
+			retval = parport_read (port, kbuf, count);
 
-		/* Hack: Wait 10ms (between events 6 and 7) */
-                schedule_timeout((HZ+99)/100);
-                break;
-	}
+			if (retval)
+				break;
 
-	for (i=0; ; i++) {
-		parport_frob_control(port, 2, 2); /* AutoFeed high */
-		if (parport_wait_peripheral(port, 0x40, 0)) {
-#ifdef LP_READ_DEBUG
-			/* Some peripherals just time out when they've sent
-			   all their data.  */
-			printk("%s: read1 timeout.\n", port->name);
-#endif
-			parport_frob_control(port, 2, 0); /* AutoFeed low */
-			break;
-		}
-		z = lp_read_nibble(minor);
-		parport_frob_control(port, 2, 0); /* AutoFeed low */
-		if (parport_wait_peripheral(port, 0x40, 0x40)) {
-			printk("%s: read2 timeout.\n", port->name);
-			break;
-		}
-		if ((i & 1) != 0) {
-			Byte |= (z<<4);
-			if (__put_user (Byte, temp))
-			{
-				count = -EFAULT;
+			if (file->f_flags & O_NONBLOCK)
 				break;
-			} else {
-				temp++;
 
-				if (++count == length)
-					break;
-			}
-			/* Does the error line indicate end of data? */
-			if ((parport_read_status(port) & LP_PERRORP) == 
-			    LP_PERRORP) 
+			/* Wait for an interrupt. */
+			interruptible_sleep_on_timeout (&lp_table[minor].waitq,
+							LP_TIMEOUT_POLLED);
+
+			if (signal_pending (current)) {
+				retval = -EINTR;
 				break;
-		} else 
-			Byte=z;
-	}
+			}
+		}
 
-	lp_read_terminate(port);
+		up (&lp_table[minor].port_mutex);
+	}
 
 	lp_parport_release (minor);
 
-	return count;
+	if (retval > 0 && copy_to_user (buf, kbuf, retval))
+		retval = -EFAULT;
+
+	return retval;
 }
 
-#endif
+#endif /* IEEE 1284 support */
 
 static int lp_open(struct inode * inode, struct file * file)
 {
@@ -729,7 +495,6 @@
 		LP_F(minor) &= ~LP_BUSY;
 		return -ENOMEM;
 	}
-	init_waitqueue_head(&(lp_table[minor].wait_q));
 	return 0;
 }
 
@@ -752,7 +517,7 @@
 	int retval = 0;
 
 #ifdef LP_DEBUG
-	printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
+	printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%lx\n", minor, cmd, arg);
 #endif
 	if (minor >= LP_NO)
 		return -ENODEV;
@@ -785,12 +550,6 @@
 				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;
@@ -834,16 +593,35 @@
 	return retval;
 }
 
+#ifdef CONFIG_PARPORT_1284
+static unsigned int lp_poll (struct file *filp, struct poll_table_struct *wait)
+{
+	unsigned int minor = MINOR (filp->f_dentry->d_inode->i_rdev);
+	unsigned int mask = POLLOUT | POLLWRNORM; /* always writable */
+
+	poll_wait (filp, &lp_table[minor].dataq, wait);
+
+	if (lp_table[minor].flags & LP_DATA_AVAIL)
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+#endif /* IEEE 1284 support */
+
 static struct file_operations lp_fops = {
 	lp_lseek,
-#ifdef CONFIG_PRINTER_READBACK
+#ifdef CONFIG_PARPORT_1284
 	lp_read,
 #else
 	NULL,
 #endif
 	lp_write,
 	NULL,		/* lp_readdir */
-	NULL,		/* lp_poll */
+#ifdef CONFIG_PARPORT_1284
+	lp_poll,
+#else
+	NULL,
+#endif
 	lp_ioctl,
 	NULL,		/* lp_mmap */
 	lp_open,
@@ -851,6 +629,70 @@
 	lp_release
 };
 
+/* --- support for console on the line printer ----------------- */
+
+#ifdef CONFIG_LP_CONSOLE
+
+#define CONSOLE_LP 0
+
+/* If the printer is out of paper, we can either lose the messages or
+ * stall until the printer is happy again.  Define CONSOLE_LP_STRICT
+ * non-zero to get the latter behaviour. */
+#define CONSOLE_LP_STRICT 1
+
+static void lp_console_write (struct console *co, const char *s,
+			      unsigned count)
+{
+	struct pardevice *dev = lp_table[CONSOLE_LP].dev;
+	struct parport *port = dev->port;
+	ssize_t written;
+	signed long old_to;
+
+	if (!(lp_table[CONSOLE_LP].flags & (1<<LP_HAVE_PORT_BIT))) {
+		if (parport_claim (dev))
+			/* Nothing we can do. */
+			return;
+		set_bit (LP_HAVE_PORT_BIT, &lp_table[CONSOLE_LP].flags);
+	}
+
+	old_to = parport_set_timeout (dev, 0);
+
+	/* Go to compatibility mode. */
+	parport_negotiate (port, IEEE1284_MODE_COMPAT);
+
+	do {
+		/* Write the data. */
+		written = parport_write (port, s, count);
+		if (written > 0) {
+			s += written;
+			count -= written;
+		}
+	} while (count > 0 && (CONSOLE_LP_STRICT || written > 0));
+
+	parport_set_timeout (dev, old_to);
+}
+
+static kdev_t lp_console_device (struct console *c)
+{
+	return MKDEV(LP_MAJOR, CONSOLE_LP);
+}
+
+static struct console lpcons = {
+	"lp",
+	lp_console_write,
+	NULL,
+	lp_console_device,
+	NULL,
+	NULL,
+	NULL,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+#endif /* console on line printer */
+
 /* --- initialisation code ------------------------------------- */
 
 #ifdef MODULE
@@ -864,8 +706,8 @@
 
 #else
 
-static int parport_nr[LP_NO] __initdata = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };
-static int reset __initdata = 0;
+static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };
+static int reset = 0;
 
 static int parport_ptr = 0;
 
@@ -896,10 +738,10 @@
 
 #endif
 
-int lp_register(int nr, struct parport *port)
+static int lp_register(int nr, struct parport *port)
 {
 	lp_table[nr].dev = parport_register_device(port, "lp", 
-						   lp_preempt, NULL,
+						   lp_preempt, lp_wakeup,
 						   lp_interrupt, 
 						   0,
 						   (void *) &lp_table[nr]);
@@ -916,13 +758,56 @@
 	return 0;
 }
 
-int lp_init(void)
+static void lp_attach (struct parport *port)
 {
-	unsigned int count = 0;
 	unsigned int i;
-	struct parport *port;
 
-	for(i = 0; i < LP_NO; i++) {
+	switch (parport_nr[0])
+	{
+	case LP_PARPORT_UNSPEC:
+	case LP_PARPORT_AUTO:
+		if (parport_nr[0] == LP_PARPORT_AUTO &&
+		    port->probe_info[0].class != PARPORT_CLASS_PRINTER)
+			return;
+
+		if (!lp_register(lp_count, port))
+			if (++lp_count == LP_NO)
+				break;
+
+		break;
+
+	default:
+		for (i = 0; i < LP_NO; i++) {
+			if (port->number == parport_nr[i]) {
+				if (!lp_register(i, port))
+					lp_count++;
+				break;
+			}
+		}
+		break;
+	}
+}
+
+static void lp_detach (struct parport *port)
+{
+	/* Write this some day. */
+}
+
+static struct parport_driver lp_driver = {
+	"lp",
+	lp_attach,
+	lp_detach,
+	NULL
+};
+
+int __init lp_init (void)
+{
+	int i;
+
+	if (parport_nr[0] == LP_PARPORT_OFF)
+		return 0;
+
+	for (i = 0; i < LP_NO; i++) {
 		lp_table[i].dev = NULL;
 		lp_table[i].flags = 0;
 		lp_table[i].chars = LP_INIT_CHAR;
@@ -932,55 +817,37 @@
 #ifdef LP_STATS
 		lp_table[i].lastcall = 0;
 		lp_table[i].runchars = 0;
-		memset(&lp_table[i].stats, 0, sizeof(struct lp_stats));
+		memset (&lp_table[i].stats, 0, sizeof (struct lp_stats));
 #endif
-		init_waitqueue_head(&lp_table[i].wait_q);
 		lp_table[i].last_error = 0;
-		lp_table[i].irq_detected = 0;
-		lp_table[i].irq_missed = 0;
+		init_waitqueue_head (&lp_table[i].waitq);
+		init_waitqueue_head (&lp_table[i].dataq);
+		init_MUTEX (&lp_table[i].port_mutex);
 	}
 
-	switch (parport_nr[0])
-	{
-	case LP_PARPORT_OFF:
-		return 0;
-
-	case LP_PARPORT_UNSPEC:
-	case LP_PARPORT_AUTO:
-	        for (port = parport_enumerate(); port; port = port->next) {
-
-			if (parport_nr[0] == LP_PARPORT_AUTO &&
-			    port->probe_info.class != PARPORT_CLASS_PRINTER)
-				continue;
-
-			if (!lp_register(count, port))
-				if (++count == LP_NO)
-					break;
-		}
-		break;
+	if (register_chrdev (LP_MAJOR, "lp", &lp_fops)) {
+		printk ("lp: unable to get major %d\n", LP_MAJOR);
+		return -EIO;
+	}
 
-	default:
-		for (i = 0; i < LP_NO; i++) {
-			for (port = parport_enumerate(); port; 
-			     port = port->next) {
-				if (port->number == parport_nr[i]) {
-					if (!lp_register(i, port))
-						count++;
-					break;
-				}
-			}
-		}
-		break;
+	if (parport_register_driver (&lp_driver)) {
+		printk ("lp: unable to register with parport\n");
+		return -EIO;
 	}
 
-	if (count) {
-		if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
-			printk("lp: unable to get major %d\n", LP_MAJOR);
-			return -EIO;
-		}
-	} else {
-		printk(KERN_INFO "lp: driver loaded but no devices found\n");
+	if (!lp_count) {
+		printk (KERN_INFO "lp: driver loaded but no devices found\n");
+#ifndef CONFIG_PARPORT_12843
+		if (parport_nr[0] == LP_PARPORT_AUTO)
+			printk (KERN_INFO "lp: (is IEEE 1284.3 support enabled?)\n");
+#endif
+	}
+#ifdef CONFIG_LP_CONSOLE
+	else {
+		register_console (&lpcons);
+		printk (KERN_INFO "lp%d: console ready\n", CONSOLE_LP);
 	}
+#endif
 
 	return 0;
 }
@@ -1018,10 +885,18 @@
 {
 	unsigned int offset;
 
+	parport_unregister_driver (&lp_driver);
+
+#ifdef CONFIG_LP_CONSOLE
+	unregister_console (&lpcons);
+#endif
+
 	unregister_chrdev(LP_MAJOR, "lp");
 	for (offset = 0; offset < LP_NO; offset++) {
 		if (lp_table[offset].dev == NULL)
 			continue;
+		if (lp_table[offset].flags & (1<<LP_HAVE_PORT_BIT))
+			parport_release (lp_table[offset].dev);
 		parport_unregister_device(lp_table[offset].dev);
 	}
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)