Re: [PARPORT] Parallel Port FIFO mode


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