Philip Blundell (Philip.Blundell@pobox.com)
Sun, 14 Dec 1997 19:12:29 +0000
Hi.
I did some cosmetic surgery on the lp driver. In theory all the PC-specific
port access code is gone now, and parameters (both module and kernel) should
work properly now.
The patch is appended. As with all great changes, I haven't tested it at all
(due to not having a machine to do so on right now). It compiles OK, though -
please check it out.
p.
Index: lp.c
===================================================================
RCS file: /disks/c7/repository/linux/drivers/char/lp.c,v
retrieving revision 1.4
diff -u -r1.4 lp.c
--- lp.c 1997/12/07 16:29:42 1.4
+++ lp.c 1997/12/14 19:08:51
@@ -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
@@ -16,7 +18,52 @@
* 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. It should be possible to eliminate or
+ * reduce this overhead in the common case, as well.
+ *
+ * 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>
@@ -26,31 +73,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, NULL, 0, 0, 0, 0, {0}}
+};
/* Test if printer is ready (and optionally has no error conditions) */
#define LP_READY(minor, status) \
@@ -64,10 +104,7 @@
#undef LP_DEBUG
#undef LP_READ_DEBUG
-/* Magic numbers */
-#define AUTO -3
-#define OFF -2
-#define UNSPEC -1
+/* --- parport support functions ------------------------------- */
static inline void lp_parport_release (int minor)
{
@@ -81,19 +118,21 @@
sleep_on (&lp_table[minor].lp_wait_q);
}
-static inline void lp_schedule (int minor)
+static void lp_wakeup(void *ref)
{
- if (lp_table[minor].should_relinquish) {
- lp_parport_release (minor);
- schedule ();
- lp_parport_claim (minor);
- }
- else
- schedule ();
-}
+ struct lp_struct *lp_dev = (struct lp_struct *) ref;
+
+ if (!waitqueue_active (&lp_dev->lp_wait_q))
+ return; /* Wake up whom? */
+ /* Claim the Parport */
+ if (parport_claim(lp_dev->dev))
+ return; /* Shouldn't happen */
+
+ wake_up(&lp_dev->lp_wait_q);
+}
-static int lp_preempt (void *handle)
+static int lp_preempt(void *handle)
{
struct lp_struct *lps = (struct lp_struct *)handle;
@@ -104,6 +143,24 @@
return 1;
}
+/* --- 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_schedule (int minor)
+{
+ if (lp_table[minor].should_relinquish) {
+ lp_parport_release (minor);
+ schedule ();
+ lp_parport_claim (minor);
+ }
+ else
+ schedule ();
+}
+
static int lp_reset(int minor)
{
w_ctr(minor, LP_PSELECP);
@@ -307,14 +364,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 */
@@ -351,26 +409,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);
@@ -571,19 +630,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 +659,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 +676,92 @@
#endif
-void lp_wakeup(void *ref)
+int lp_register(int nr, struct parport *port)
{
- struct lp_struct *lp_dev = (struct lp_struct *) ref;
-
- if (!waitqueue_active (&lp_dev->lp_wait_q))
- return; /* Wake up whom? */
-
- /* Claim the Parport */
- if (parport_claim(lp_dev->dev))
- return; /* Shouldn't happen */
-
- wake_up(&lp_dev->lp_wait_q);
-}
+ lp_table[nr].dev = parport_register_device(port, "lp",
+ lp_preempt, lp_wakeup,
+ lp_interrupt,
+ PARPORT_DEV_TRAN,
+ (void *) &lp_table[nr]);
+ lp_table[nr].flags |= LP_EXIST;
+ init_waitqueue (&lp_table[nr].lp_wait_q);
+ lp_parport_claim(nr);
+
+ if (reset)
+ lp_reset(nr);
+
+ lp_parport_release(nr);
+ printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name,
+ (port->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven");
-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++) {
Index: lp.h
===================================================================
RCS file: /disks/c7/repository/linux/include/linux/lp.h,v
retrieving revision 1.1.1.2
diff -u -r1.1.1.2 lp.h
--- lp.h 1997/10/06 17:52:28 1.1.1.2
+++ lp.h 1997/12/14 18:38:54
@@ -7,6 +7,11 @@
* Interrupt support added 1993 Nigel Gamble
*/
+/* Magic numbers for defining port-device mappings */
+#define LP_PARPORT_AUTO -3
+#define LP_PARPORT_OFF -2
+#define LP_PARPORT_UNSPEC -1
+
/*
* Per POSIX guidelines, this module reserves the LP and lp prefixes
* These are the lp_table[minor].flags flags...
@@ -88,20 +93,6 @@
#define LP_BUFFER_SIZE 256
#define LP_BASE(x) lp_table[(x)].dev->port->base
-
-#define r_dtr(x) inb(LP_BASE(x))
-#define r_str(x) inb(LP_BASE(x)+1)
-#define r_ctr(x) inb(LP_BASE(x)+2)
-#define r_epp(x) inb(LP_BASE(x)+4)
-#define r_fifo(x) inb(LP_BASE(x)+0x400)
-#define r_ecr(x) inb(LP_BASE(x)+0x402)
-
-#define w_dtr(x,y) outb((y), LP_BASE(x))
-#define w_str(x,y) outb((y), LP_BASE(x)+1)
-#define w_ctr(x,y) outb((y), LP_BASE(x)+2)
-#define w_epp(x,y) outb((y), LP_BASE(x)+4)
-#define w_fifo(x,y) outb((y), LP_BASE(x)+0x400)
-#define w_ecr(x,y) outb((y), LP_BASE(x)+0x402)
struct lp_stats {
unsigned long chars;
-- To unsubscribe, send mail to: linux-parport-request@torque.net --
-- with the single word "unsubscribe" in the body of the message. --
This archive was generated by hypermail 2.0b3 on Wed 30 Dec 1998 - 10:17:11 EST