patch-2.1.82 linux/drivers/char/lp.c
Next file: linux/drivers/isdn/avmb1/b1capi.c
Previous file: linux/drivers/char/keyboard.c
Back to the patch index
Back to the overall index
- Lines: 680
- Date:
Fri Jan 23 20:04:03 1998
- Orig file:
v2.1.81/linux/drivers/char/lp.c
- Orig date:
Sun Nov 30 14:00:38 1997
diff -u --recursive --new-file v2.1.81/linux/drivers/char/lp.c linux/drivers/char/lp.c
@@ -1,4 +1,6 @@
/*
+ * Generic parallel printer driver
+ *
* Copyright (C) 1992 by Jim Weigand and Linus Torvalds
* Copyright (C) 1992,1993 by Michael K. Johnson
* - Thanks much to Gunter Windau for pointing out to me where the error
@@ -11,14 +13,58 @@
* lp_read (Status readback) support added by Carsten Gross,
* carsten@sol.wohnheim.uni-ulm.de
* Support for parport by Philip Blundell <Philip.Blundell@pobox.com>
- * Reverted interrupt to polling at runtime if more than one device is parport
- * registered and joined the interrupt and polling code.
- * by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ * parport_sharing hacking by Andrea Arcangeli <arcangeli@mbox.queen.it>
*/
-/* This driver is about due for a rewrite. */
+/* This driver should, in theory, work with any parallel port that has an
+ * appropriate low-level driver; all I/O is done through the parport
+ * abstraction layer. There is a performance penalty for this, but parallel
+ * ports are comparitively low-speed devices anyway.
+ *
+ * If this driver is built into the kernel, you can configure it using the
+ * kernel command-line. For example:
+ *
+ * lp=parport1,none,parport2 (bind lp0 to parport1, disable lp1 and
+ * bind lp2 to parport2)
+ *
+ * lp=auto (assign lp devices to all ports that
+ * have printers attached, as determined
+ * by the IEEE-1284 autoprobe)
+ *
+ * lp=reset (reset the printer during
+ * initialisation)
+ *
+ * lp=off (disable the printer driver entirely)
+ *
+ * If the driver is loaded as a module, similar functionality is available
+ * using module parameters. The equivalent of the above commands would be:
+ *
+ * # insmod lp.o parport=1,-1,2 (use -1 for disabled ports, since
+ * module parameters do not allow you
+ * to mix textual and numeric values)
+ *
+ * # insmod lp.o autoprobe=1
+ *
+ * # insmod lp.0 reset=1
+ */
+
+/* COMPATIBILITY WITH OLD KERNELS
+ *
+ * Under Linux 2.0 and previous versions, lp devices were bound to ports at
+ * particular I/O addresses, as follows:
+ *
+ * lp0 0x3bc
+ * lp1 0x378
+ * lp2 0x278
+ *
+ * The new driver, by default, binds lp devices to parport devices as it
+ * finds them. This means that if you only have one port, it will be bound
+ * to lp0 regardless of its I/O address. If you need the old behaviour, you
+ * can force it using the parameters described above.
+ */
#include <linux/module.h>
+#include <linux/init.h>
#include <linux/config.h>
#include <linux/errno.h>
@@ -26,31 +72,24 @@
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/malloc.h>
-#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
+#include <linux/parport.h>
+#include <linux/lp.h>
+
#include <asm/irq.h>
-#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
-#include <linux/parport.h>
-#include <linux/lp.h>
/* if you have more than 3 printers, remember to increase LP_NO */
-struct lp_struct lp_table[] =
-{
- {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, 0,
- {0}},
- {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, 0,
- {0}},
- {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, 0,
- {0}}
-};
#define LP_NO 3
-/* Device name */
-static char *dev_name = "lp";
+struct lp_struct lp_table[LP_NO] =
+{
+ [0 ... LP_NO-1] = {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT,
+ NULL, 0, 0, 0, {0}}
+};
/* Test if printer is ready (and optionally has no error conditions) */
#define LP_READY(minor, status) \
@@ -64,61 +103,60 @@
#undef LP_DEBUG
#undef LP_READ_DEBUG
-/* Magic numbers */
-#define AUTO -3
-#define OFF -2
-#define UNSPEC -1
+/* --- parport support ----------------------------------------- */
-static inline void lp_parport_release (int minor)
+static int lp_preempt(void *handle)
{
- parport_release (lp_table[minor].dev);
- lp_table[minor].should_relinquish = 0;
-}
+ struct lp_struct *lps = (struct lp_struct *)handle;
-static inline void lp_parport_claim (int minor)
-{
- if (parport_claim (lp_table[minor].dev))
- sleep_on (&lp_table[minor].lp_wait_q);
+ if (waitqueue_active (&lps->dev->wait_q))
+ wake_up_interruptible(&lps->dev->wait_q);
+
+ /* Don't actually release the port now */
+ return 1;
}
-static inline void lp_schedule (int minor)
+#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);
+
+/* --- low-level port access ----------------------------------- */
+
+#define r_dtr(x) (parport_read_data(lp_table[(x)].dev->port))
+#define r_str(x) (parport_read_status(lp_table[(x)].dev->port))
+#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 (lp_table[minor].should_relinquish) {
- lp_parport_release (minor);
- schedule ();
- lp_parport_claim (minor);
- }
- else
+ if (parport_yield (lp_table[minor].dev, 1) == 1 && need_resched)
schedule ();
}
-
-static int lp_preempt (void *handle)
+static __inline__ void lp_schedule(int minor)
{
- struct lp_struct *lps = (struct lp_struct *)handle;
-
- /* Just remember that someone wants the port */
- lps->should_relinquish = 1;
-
- /* Don't actually release the port now */
- return 1;
+ 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);
+ schedule ();
+ lp_parport_claim(minor);
+ } else
+ schedule();
}
static int lp_reset(int minor)
{
+ int retval;
+ lp_parport_claim (minor);
w_ctr(minor, LP_PSELECP);
- udelay(LP_DELAY);
+ udelay (LP_DELAY);
w_ctr(minor, LP_PSELECP | LP_PINITP);
- return r_str(minor);
-}
-
-static inline int must_use_polling(int minor)
-{
- return lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE ||
- lp_table[minor].dev->port->devices->next;
+ retval = r_str(minor);
+ lp_parport_release (minor);
+ return retval;
}
-static inline int lp_char(char lpchar, int minor, int use_polling)
+static inline int lp_char(char lpchar, int minor)
{
int status;
unsigned int wait = 0;
@@ -126,28 +164,33 @@
struct lp_stats *stats;
do {
- status = r_str(minor);
+ status = r_str (minor);
count++;
- if (need_resched)
- lp_schedule (minor);
- } while (((use_polling && !LP_READY(minor, status)) ||
- (!use_polling && !(status & LP_PBUSY))) &&
- (count < LP_CHAR(minor)));
+ lp_yield(minor);
+ } while (!LP_READY(minor, status) && count < LP_CHAR(minor));
- if (count == LP_CHAR(minor) ||
- (!use_polling && !LP_CAREFUL_READY(minor, status)))
+ if (count == LP_CHAR(minor))
return 0;
+
w_dtr(minor, lpchar);
stats = &LP_STAT(minor);
stats->chars++;
/* 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
/* 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 */
w_ctr(minor, LP_PSELECP | LP_PINITP);
/* update waittime statistics */
@@ -170,17 +213,40 @@
{
struct lp_struct *lp_dev = (struct lp_struct *) dev_id;
- if (waitqueue_active (&lp_dev->lp_wait_q))
- wake_up(&lp_dev->lp_wait_q);
+ if (waitqueue_active (&lp_dev->dev->wait_q))
+ wake_up_interruptible(&lp_dev->dev->wait_q);
}
static void lp_error(int minor)
{
- if (must_use_polling(minor)) {
+ if (LP_POLLING(minor) || LP_PREEMPTED(minor)) {
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
- lp_schedule (minor);
+ lp_parport_release(minor);
+ schedule();
+ lp_parport_claim(minor);
+ }
+}
+
+static int lp_check_status(int minor) {
+ unsigned char status = r_str(minor);
+ if ((status & LP_POUTPA)) {
+ printk(KERN_INFO "lp%d out of paper\n", minor);
+ if (LP_F(minor) & LP_ABORT)
+ return 1;
+ lp_error(minor);
+ } else if (!(status & LP_PSELECD)) {
+ printk(KERN_INFO "lp%d off-line\n", minor);
+ if (LP_F(minor) & LP_ABORT)
+ return 1;
+ lp_error(minor);
+ } else if (!(status & LP_PERRORP)) {
+ printk(KERN_ERR "lp%d printer error\n", minor);
+ if (LP_F(minor) & LP_ABORT)
+ return 1;
+ lp_error(minor);
}
+ return 0;
}
static inline int lp_write_buf(unsigned int minor, const char *buf, int count)
@@ -202,7 +268,7 @@
copy_from_user(lp->lp_buffer, buf, copy_size);
while (copy_size) {
- if (lp_char(lp->lp_buffer[bytes_written], minor, must_use_polling(minor))) {
+ if (lp_char(lp->lp_buffer[bytes_written], minor)) {
--copy_size;
++bytes_written;
lp_table[minor].runchars++;
@@ -210,36 +276,24 @@
int rc = total_bytes_written + bytes_written;
if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
LP_STAT(minor).maxrun = lp_table[minor].runchars;
- status = r_str(minor);
- if ((status & LP_POUTPA)) {
- printk(KERN_INFO "lp%d out of paper\n", minor);
- if (LP_F(minor) & LP_ABORT)
- return rc ? rc : -ENOSPC;
- lp_error(minor);
- } else if (!(status & LP_PSELECD)) {
- printk(KERN_INFO "lp%d off-line\n", minor);
- if (LP_F(minor) & LP_ABORT)
- return rc ? rc : -EIO;
- lp_error(minor);
- } else if (!(status & LP_PERRORP)) {
- printk(KERN_ERR "lp%d printer error\n", minor);
- if (LP_F(minor) & LP_ABORT)
- return rc ? rc : -EIO;
- lp_error(minor);
- }
-
LP_STAT(minor).sleeps++;
- if (must_use_polling(minor)) {
+ if (LP_POLLING(minor)) {
+ lp_polling:
+ if (lp_check_status(minor))
+ return rc ? rc : -EIO;
#ifdef LP_DEBUG
printk(KERN_DEBUG "lp%d sleeping at %d characters for %d jiffies\n", minor, lp_table[minor].runchars, LP_TIME(minor));
#endif
- lp_table[minor].runchars = 0;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIME(minor);
lp_schedule (minor);
} else {
cli();
+ if (LP_PREEMPTED(minor)) {
+ sti();
+ goto lp_polling;
+ }
enable_irq(lp->dev->port->irq);
w_ctr(minor, LP_PSELECP|LP_PINITP|LP_PINTEN);
status = r_str(minor);
@@ -249,14 +303,16 @@
sti();
continue;
}
- lp_table[minor].runchars = 0;
current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
- interruptible_sleep_on(&lp->lp_wait_q);
-
+ interruptible_sleep_on(&lp->dev->wait_q);
w_ctr(minor, LP_PSELECP | LP_PINITP);
sti();
+ if (lp_check_status(minor))
+ return rc ? rc : -EIO;
}
+ lp_table[minor].runchars = 0;
+
if (signal_pending(current)) {
if (total_bytes_written + bytes_written)
return total_bytes_written + bytes_written;
@@ -287,12 +343,11 @@
lp_table[minor].lastcall = jiffies;
/* Claim Parport or sleep until it becomes available
- * (see lp_wakeup() for details)
*/
lp_parport_claim (minor);
retv = lp_write_buf(minor, buf, count);
-
+
lp_parport_release (minor);
return retv;
}
@@ -307,14 +362,15 @@
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);
+ i = r_str(minor)>>3;
+ i &= ~8;
+ if ((i & 0x10) == 0) i |= 8;
+ return (i & 0x0f);
}
-static void lp_select_in_high(int minor) {
- w_ctr(minor, (r_ctr(minor) | 8));
+static inline void lp_select_in_high(int minor)
+{
+ parport_frob_control(lp_table[minor].dev->port, 8, 8);
}
/* Status readback confirming to ieee1284 */
@@ -329,7 +385,6 @@
unsigned int minor=MINOR(file->f_dentry->d_inode->i_rdev);
/* Claim Parport or sleep until it becomes available
- * (see lp_wakeup() for details)
*/
lp_parport_claim (minor);
@@ -351,26 +406,27 @@
return temp-buf; /* End of file */
}
for (i=0; i<=(count*2); i++) {
- w_ctr(minor, r_ctr(minor) | 2); /* AutoFeed high */
+ parport_frob_control(lp_table[minor].dev->port, 2, 2); /* AutoFeed high */
do {
- status=(r_str(minor) & 0x40);
+ status = (r_str(minor) & 0x40);
udelay(50);
counter++;
if (need_resched)
schedule ();
- } while ( (status == 0x40) && (counter < 20) );
- if ( counter == 20 ) { /* Timeout */
+ } while ((status == 0x40) && (counter < 20));
+ if (counter == 20) {
+ /* Timeout */
#ifdef LP_READ_DEBUG
printk(KERN_DEBUG "lp_read: (Autofeed high) timeout\n");
-#endif
- w_ctr(minor, r_ctr(minor) & ~2);
+#endif
+ parport_frob_control(lp_table[minor].dev->port, 2, 0);
lp_select_in_high(minor);
parport_release(lp_table[minor].dev);
return temp-buf; /* end the read at timeout */
}
counter=0;
- z=lp_read_nibble(minor);
- w_ctr(minor, r_ctr(minor) & ~2); /* AutoFeed low */
+ z = lp_read_nibble(minor);
+ parport_frob_control(lp_table[minor].dev->port, 2, 0); /* AutoFeed low */
do {
status=(r_str(minor) & 0x40);
udelay(20);
@@ -427,7 +483,10 @@
a non-standard manner. This is strictly a Linux hack, and
should most likely only ever be used by the tunelp application. */
if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
- int status = r_str(minor);
+ int status;
+ lp_parport_claim (minor);
+ status = r_str(minor);
+ lp_parport_release (minor);
if (status & LP_POUTPA) {
printk(KERN_INFO "lp%d out of paper\n", minor);
MOD_DEC_USE_COUNT;
@@ -462,7 +521,6 @@
return 0;
}
-
static int lp_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
@@ -520,7 +578,10 @@
if (retval)
return retval;
else {
- int status = r_str(minor);
+ int status;
+ lp_parport_claim (minor);
+ status = r_str(minor);
+ lp_parport_release (minor);
copy_to_user((int *) arg, &status, sizeof(int));
}
break;
@@ -571,19 +632,27 @@
lp_release
};
-static int parport[LP_NO] = { UNSPEC, };
+/* --- initialisation code ------------------------------------- */
#ifdef MODULE
-#define lp_init init_module
+
+static int parport[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };
+static int reset = 0;
+static int autoprobe = 0;
+
MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "i");
+MODULE_PARM(reset, "i");
+MODULE_PARM(autoprobe, "i");
#else
+static int parport[LP_NO] __initdata = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };
+static int reset __initdata = 0;
+
static int parport_ptr = 0;
-void lp_setup(char *str, int *ints)
+__initfunc(void lp_setup(char *str, int *ints))
{
- /* Ugh. */
if (!strncmp(str, "parport", 7)) {
int n = simple_strtoul(str+7, NULL, 10);
if (parport_ptr < LP_NO)
@@ -592,11 +661,15 @@
printk(KERN_INFO "lp: too many ports, %s ignored.\n",
str);
} else if (!strcmp(str, "auto")) {
- parport[0] = AUTO;
+ parport[0] = LP_PARPORT_AUTO;
+ } else if (!strcmp(str, "none")) {
+ parport_ptr++;
+ } else if (!strcmp(str, "reset")) {
+ reset = 1;
} else {
if (ints[0] == 0 || ints[1] == 0) {
/* disable driver on "lp=" or "lp=0" */
- parport[0] = OFF;
+ parport[0] = LP_PARPORT_OFF;
} else {
printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", ints[1]);
}
@@ -605,87 +678,91 @@
#endif
-void lp_wakeup(void *ref)
+int lp_register(int nr, struct parport *port)
{
- struct lp_struct *lp_dev = (struct lp_struct *) ref;
+ lp_table[nr].dev = parport_register_device(port, "lp",
+ lp_preempt, NULL,
+ lp_interrupt,
+ PARPORT_DEV_TRAN,
+ (void *) &lp_table[nr]);
+ if (lp_table[nr].dev == NULL)
+ return 1;
+ lp_table[nr].flags |= LP_EXIST;
- if (!waitqueue_active (&lp_dev->lp_wait_q))
- return; /* Wake up whom? */
+ if (reset)
+ lp_reset(nr);
- /* Claim the Parport */
- if (parport_claim(lp_dev->dev))
- return; /* Shouldn't happen */
+ printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name,
+ (port->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven");
- wake_up(&lp_dev->lp_wait_q);
-}
-
-static int inline lp_searchfor(int list[], int a)
-{
- int i;
- for (i = 0; i < LP_NO && list[i] != UNSPEC; i++) {
- if (list[i] == a) return 1;
- }
return 0;
}
int lp_init(void)
{
- int count = 0;
- struct parport *pb;
-
- if (parport[0] == OFF) return 0;
-
- pb = parport_enumerate();
-
- while (pb) {
- /* We only understand PC-style ports. */
- if (pb->modes & PARPORT_MODE_PCSPP) {
- if (parport[0] == UNSPEC ||
- lp_searchfor(parport, count) ||
- (parport[0] == AUTO &&
- pb->probe_info.class == PARPORT_CLASS_PRINTER)) {
- lp_table[count].dev =
- parport_register_device(pb, dev_name,
- lp_preempt, lp_wakeup,
- lp_interrupt, PARPORT_DEV_TRAN,
- (void *) &lp_table[count]);
- lp_table[count].flags |= LP_EXIST;
- init_waitqueue (&lp_table[count].lp_wait_q);
- lp_parport_claim (count);
- lp_reset (count);
- lp_parport_release (count);
- printk(KERN_INFO "lp%d: using %s (%s).\n",
- count, pb->name, (pb->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven");
+ unsigned int count = 0;
+ struct parport *port;
+
+ switch (parport[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[0] == LP_PARPORT_AUTO &&
+ port->probe_info.class != PARPORT_CLASS_PRINTER)
+ continue;
+
+ if (!lp_register(count, port))
+ if (++count == LP_NO)
+ break;
+ }
+ break;
+
+ default:
+ for (count = 0; count < LP_NO; count++) {
+ if (parport[count] != LP_PARPORT_UNSPEC) {
+ char buffer[16];
+ sprintf(buffer, "parport%d", parport[count]);
+ for (port = parport_enumerate(); port;
+ port = port->next) {
+ if (!strcmp(port->name, buffer)) {
+ (void) lp_register(count, port);
+ break;
+ }
+ }
}
- if (++count == LP_NO)
- break;
}
- pb = pb->next;
- }
+ break;
+ }
- /* Successful specified devices increase count
- * Unsuccessful specified devices increase failed
- */
- if (count) {
+ if (count) {
if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
printk("lp: unable to get major %d\n", LP_MAJOR);
return -EIO;
}
- return 0;
+ } else {
+ printk(KERN_INFO "lp: driver loaded but no devices found\n");
}
- printk(KERN_INFO "lp: driver loaded but no devices found\n");
-#ifdef MODULE
return 0;
-#else
- return 1;
-#endif
}
#ifdef MODULE
+int init_module(void)
+{
+ if (autoprobe)
+ parport[0] = LP_PARPORT_AUTO;
+
+ return lp_init();
+}
+
void cleanup_module(void)
{
- int offset;
+ unsigned int offset;
unregister_chrdev(LP_MAJOR, "lp");
for (offset = 0; offset < LP_NO; offset++) {
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov