Keiichi Sakai (sakai@aial.hiroshima-u.ac.jp)
Mon, 21 Dec 1998 11:03:24 +0900 (JST)
> At the moment, the criteria for using the FIFO are:
> - the port supports ECP mode
> - we can get interrupts from it
>
> Let me know of successes and failures.
I have modified the driver to use the FIFO for 2.0.29 kernel and now
I am using the parallel port FIFO mode lp driver in 2.0.35 kernel.
The driver works fine for me.
I have no time to port it to 2.1.13x kernel.
This is patch for kernel 2.0.35.
-- cut here -- cut here -- cut here
diff -urN linux.orig/drivers/char/lp.c linux/drivers/char/lp.c
--- linux.orig/drivers/char/lp.c Mon Dec 21 10:45:12 1998
+++ linux/drivers/char/lp.c Mon Dec 21 10:49:03 1998
@@ -8,6 +8,7 @@
* LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
* Statistics and support for slow printers by Rob Janssen, rob@knoware.nl
* "lp=" command line parameters added by Grant Guenther, grant@torque.net
+ * Copyright (C) 1997 by Keiichi Sakai (added PPFIFO code for SMC FDC37C66x compatible)
*/
#include <linux/module.h>
@@ -159,14 +160,42 @@
static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct lp_struct *lp = &lp_table[0];
+#define lp ((struct lp_struct *)dev_id)
- while (irq != lp->irq) {
- if (++lp >= &lp_table[LP_NO])
- return;
+ if (lp->lp_wait_q == NULL)
+ return;
+#if 0
+ if (lp->flags & LP_EXIST_ECR) {
+ do {
+ int ecr;
+
+ ecr = inb(lp->base + 0x402);
+ if (ecr & LP_PFull) {
+ outb(LP_ECR_DEFAULT & ~LP_PnServIntrEn, lp->base+0x402);
+ return;
+ } else if (ecr & LP_PEmpty) {
+ asm (" movl $16,%%ecx
+ cmpl %%ecx,%1
+ ja 1f
+ movl %1,%%ecx
+ 1: subl %%ecx,%1
+ cld; rep; outsb "
+ : "=S" (lp->lp_buffer), "=a" (lp->runchars)
+ : "0"(lp->lp_buffer), "1"(lp->runchars), "d"(lp->base+0x400)
+ : "cx"
+ );
+ } else {
+ asm (" outsb
+ decl %1"
+ : "=S" (lp->lp_buffer), "=m" (lp->runchars)
+ : "0"(lp->lp_buffer), "1"(lp->runchars), "d"(lp->base+0x400)
+ );
+ }
+ } while (lp->runchars);
}
-
+#endif
wake_up(&lp->lp_wait_q);
+#undef lp
}
static inline int lp_write_interrupt(unsigned int minor, const char * buf, int count)
@@ -304,6 +333,148 @@
return temp-buf;
}
+static inline int lp_write_ppfifo_polled(unsigned int minor, const char * buf, int count)
+{
+ int status;
+ char c;
+ const char *temp;
+
+ temp = buf;
+ while (count > 0) {
+ c = get_user(temp);
+ if(need_resched)
+ schedule();
+ status = inb(LP_E(minor));
+ if (status & LP_PFull) {
+ if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
+ LP_STAT(minor).maxrun = lp_table[minor].runchars;
+ status = LP_S(minor);
+
+ if (status & LP_POUTPA) {
+ printk(KERN_INFO "lp%d out of paper\n", minor);
+ if(LP_F(minor) & LP_ABORT)
+ return temp-buf?temp-buf:-ENOSPC;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + LP_TIMEOUT_POLLED;
+ schedule();
+ } else if (!(status & LP_PSELECD)) {
+ printk(KERN_INFO "lp%d off-line\n", minor);
+ if(LP_F(minor) & LP_ABORT)
+ return temp-buf?temp-buf:-EIO;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + LP_TIMEOUT_POLLED;
+ schedule();
+ } else if (!(status & LP_PERRORP)) {
+ printk(KERN_ERR "lp%d reported invalid error status (on fire, eh?)\n", minor);
+ if(LP_F(minor) & LP_ABORT)
+ return temp-buf?temp-buf:-EIO;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + LP_TIMEOUT_POLLED;
+ schedule();
+ }
+
+ /* check for signals before going to sleep */
+ if (current->signal & ~current->blocked) {
+ if (temp != buf)
+ return temp-buf;
+ else
+ return -EINTR;
+ }
+ LP_STAT(minor).sleeps++;
+#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);
+ schedule();
+ } else {
+ outb(c, LP_D(minor));
+ count--; temp++;
+ LP_STAT(minor).chars++;
+ lp_table[minor].runchars++;
+ }
+ }
+ return temp-buf;
+}
+
+static inline int lp_write_ppfifo_interrupt(unsigned int minor, const char * buf, int count)
+{
+ char *lp_buffer = lp_table[minor].lp_buffer;
+ unsigned long copy_size, total_bytes_written = 0;
+ int retval = 0;
+ unsigned char dsr, ecr;
+ struct lp_struct *lp = &lp_table[minor];
+
+ while ((count > 0) && !retval) {
+ if (current->signal & ~current->blocked) {
+ retval = -EINTR;
+ break;
+ }
+ copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
+ memcpy_fromfs(lp_buffer, buf, copy_size);
+ lp->runchars = copy_size;
+ do {
+ ecr = inb(lp->base+0x402);
+ if (ecr & LP_PFull) {
+ cli();
+ outb(LP_ECR_DEFAULT & ~LP_PnServIntrEn, lp->base+0x402);
+ current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
+ interruptible_sleep_on(&lp->lp_wait_q);
+ outb(LP_ECR_DEFAULT, lp->base+0x402);
+ sti();
+ } else if (ecr & LP_PEmpty) {
+ asm (" movl $16,%%ecx
+ cmpl %%ecx,%1
+ ja 1f
+ movl %1,%%ecx
+ 1: subl %%ecx,%1
+ cld; rep; outsb "
+ : "=S" (lp->lp_buffer), "=b" (lp->runchars)
+ : "0"(lp->lp_buffer), "1"(lp->runchars), "d"(lp->base+0x400)
+ : "cx"
+ );
+ } else {
+ asm (" outsb
+ decl %1"
+ : "=S" (lp->lp_buffer), "=m" (lp->runchars)
+ : "0"(lp->lp_buffer), "1"(lp->runchars), "d"(lp->base+0x400)
+ );
+ }
+ } while (lp->runchars);
+
+ if (lp->runchars) {
+ dsr = inb(lp->base+0x001);
+ if (!(dsr & LP_PBUSY)) {
+ if ( (dsr & LP_POUTPA)) {
+ printk(KERN_INFO "lp%d out of paper\n", minor);
+ if ( lp->flags & LP_ABORT )
+ retval = -ENOSPC;
+ } else if (!(dsr & LP_PSELECD)) {
+ printk(KERN_INFO "lp%d off-line\n", minor);
+ if ( lp->flags & LP_ABORT )
+ retval = -EIO;
+ } else if (!(dsr & LP_PERRORP)) {
+ printk(KERN_ERR "lp%d printer error\n", minor);
+ if ( lp->flags & LP_ABORT )
+ retval = -EIO;
+#ifdef LP_DEBUG
+ } else {
+ printk(KERN_DEBUG "lp%d printer BUSY(no error)\n", minor);
+#endif /* LP_DEBUG */
+ }
+ }
+ }
+ copy_size -= lp->runchars;
+ total_bytes_written += copy_size;
+ buf += copy_size;
+ count -= copy_size;
+ lp->lp_buffer = lp_buffer;
+ }
+ return (total_bytes_written ? total_bytes_written : retval);
+}
+
static int lp_write(struct inode * inode, struct file * file, const char * buf, int count)
{
unsigned int minor = MINOR(inode->i_rdev);
@@ -313,9 +484,15 @@
lp_table[minor].lastcall = jiffies;
if (LP_IRQ(minor))
- return lp_write_interrupt(minor, buf, count);
+ if (LP_F(minor) & LP_EXIST_ECR)
+ return lp_write_ppfifo_interrupt(minor, buf, count);
+ else
+ return lp_write_interrupt(minor, buf, count);
else
- return lp_write_polled(minor, buf, count);
+ if (LP_F(minor) & LP_EXIST_ECR)
+ return lp_write_ppfifo_polled(minor, buf, count);
+ else
+ return lp_write_polled(minor, buf, count);
}
static int lp_lseek(struct inode * inode, struct file * file,
@@ -368,7 +545,7 @@
return -ENOMEM;
}
- ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer", NULL);
+ ret = request_irq(irq, lp_interrupt, SA_SHIRQ, "printer", &lp_table[minor]);
if (ret) {
kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
lp_table[minor].lp_buffer = NULL;
@@ -388,7 +565,7 @@
unsigned int irq;
if ((irq = LP_IRQ(minor))) {
- free_irq(irq, NULL);
+ free_irq(irq, &lp_table[minor]);
kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
lp_table[minor].lp_buffer = NULL;
}
@@ -457,14 +634,14 @@
}
if (oldirq) {
- free_irq(oldirq, NULL);
+ free_irq(oldirq, lp);
}
if (newirq) {
/* Install new irq */
- if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer", NULL))) {
+ if ((retval = request_irq(newirq, lp_interrupt, SA_SHIRQ, "printer", lp))) {
if (oldirq) {
/* restore old irq */
- request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer", NULL);
+ request_irq(oldirq, lp_interrupt, SA_SHIRQ, "printer", lp);
} else {
/* We don't need the buffer */
kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
@@ -555,6 +732,31 @@
size = (base == 0x3bc)? 3 : 8;
if (check_region(base, size) < 0)
return -1;
+ if ( !check_region(base+0x400, size)) {
+ static const char PPFIFO[] = "Parallel Port FIFO";
+ unsigned char dcr = inb(base+0x002), ecr = inb(base+0x402);
+
+ if ((dcr != ecr)) {
+ outb(LP_PNORMAL|LP_PnServIntrEn|LP_PnErrIntrEn, base+0x402);
+ outb(LP_PSELECP|LP_PINITP, base+0x002);
+ outb(LP_ECR_DEFAULT, base+0x402);
+ dcr = inb(base+0x002) & 0x3F;
+ ecr = inb(base+0x402) & 0xFC;
+ if ((dcr == (LP_PSELECP|LP_PINITP)) && (ecr == LP_ECR_DEFAULT)) {
+ request_region(base+0x000, size, PPFIFO);
+ request_region(base+0x400, size, PPFIFO);
+ LP_F(offset) |= (LP_EXIST|LP_EXIST_ECR);
+ lp_reset(offset);
+ printk(KERN_INFO"lp%d<%s> at %#x,%#x ",
+ offset, PPFIFO, base, base+0x400);
+ if (LP_IRQ(offset))
+ printk("(irq = %d)\n", LP_IRQ(offset));
+ else
+ printk("(polling)\n");
+ return 1;
+ }
+ }
+ }
/* write to port & read back to check */
outb_p(LP_DUMMY, base);
udelay(LP_DELAY);
@@ -588,7 +790,7 @@
built-in driver with lp=0 .
*/
-
+#ifndef MODULE
void lp_setup(char *str, int *ints)
{
@@ -600,7 +802,7 @@
LP_IRQ(2) = ((ints[0] > 5) ? ints[6] : 0 );
}
-#ifdef MODULE
+#else
static int io[] = {0, 0, 0};
static int irq[] = {0, 0, 0};
@@ -680,6 +882,11 @@
size = (base == 0x3bc)? 3 : 8;
if (LP_F(offset) & LP_EXIST)
release_region(LP_B(offset),size);
+ if (LP_F(offset) & LP_EXIST_ECR){
+ outb((LP_PNORMAL|LP_PnServIntrEn|LP_PnErrIntrEn),
+ LP_B(offset)+0x402);
+ release_region(LP_B(offset) + 0x400, size);
+ }
}
}
#endif
diff -urN linux.orig/include/linux/lp.h linux/include/linux/lp.h
--- linux.orig/include/linux/lp.h Mon Dec 21 10:46:08 1998
+++ linux/include/linux/lp.h Mon Dec 21 10:50:18 1998
@@ -5,6 +5,7 @@
* usr/include/linux/lp.h c.1991-1992 James Wiegand
* many modifications copyright (C) 1992 Michael K. Johnson
* Interrupt support added 1993 Nigel Gamble
+ * many modifications for DMA support Copyright (C) 1997 Keiichi Sakai
*/
/*
@@ -20,6 +21,7 @@
#define LP_ABORT 0x0040
#define LP_CAREFUL 0x0080
#define LP_ABORTOPEN 0x0100
+#define LP_EXIST_ECR 0x0200
/* timeout for each character. This is relative to bus cycles -- it
* is the count in a busy loop. THIS IS THE VALUE TO CHANGE if you
@@ -82,6 +84,8 @@
#define LP_F(minor) lp_table[(minor)].flags /* flags for busy, etc. */
#define LP_S(minor) inb_p(LP_B((minor)) + 1) /* status port */
#define LP_C(minor) (lp_table[(minor)].base + 2) /* control port */
+#define LP_D(minor) (lp_table[(minor)].base + 0x400)/* data FIFO port */
+#define LP_E(minor) (lp_table[(minor)].base + 0x402)/* extended control port */
#define LP_CHAR(minor) lp_table[(minor)].chars /* busy timeout */
#define LP_TIME(minor) lp_table[(minor)].time /* wait time */
#define LP_WAIT(minor) lp_table[(minor)].wait /* strobe wait */
@@ -89,7 +93,7 @@
/* 0 means polled */
#define LP_STAT(minor) lp_table[(minor)].stats /* statistics area */
-#define LP_BUFFER_SIZE 256
+#define LP_BUFFER_SIZE 4080
struct lp_stats {
unsigned long chars;
@@ -145,6 +149,25 @@
#define LP_PAUTOLF 0x02 /* inverted output, active low */
#define LP_PSTROBE 0x01 /* short high output on raising edge */
+/*
+ * defines for extended control register
+ * base + 0x0402
+ * accessed with LP_E(minor)
+ */
+#define LP_PNORMAL 0x00
+#define LP_PPS2 0x20
+#define LP_PFIFO 0x40
+#define LP_PECP 0x60
+#define LP_PEPP 0x80
+#define LP_PTEST 0xC0
+#define LP_PCONFIG 0xE0
+#define LP_PnErrIntrEn 0x10
+#define LP_PDmaEn 0x08
+#define LP_PnServIntrEn 0x04
+#define LP_PFull 0x02
+#define LP_PEmpty 0x01
+#define LP_ECR_DEFAULT (LP_PFIFO|LP_PnServIntrEn|LP_PnErrIntrEn)
+
/*
* the value written to ports to test existence. PC-style ports will
* return the value written. AT-style ports will return 0. so why not
@@ -162,6 +185,8 @@
* function prototypes
*/
+#ifndef MODULE
extern int lp_init(void);
+#endif
#endif
-- 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:18:59 EST