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
- Lines: 1106
- Date:
Thu Jul 1 14:22:57 1999
- Orig file:
v2.3.9/linux/drivers/char/lp.c
- Orig date:
Wed May 12 08:41:12 1999
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)