[PARPORT] lp driver using Parallel Port FIFO mode for 2.1.132


Keiichi Sakai (sakai@aial.hiroshima-u.ac.jp)
Sat, 9 Jan 1999 11:15:28 +0900 (JST)


I modified the lp driver of 2.1.132 kernel to use Parallel Port FIFO mode
when the driver sends the data to the printer.
This driver doesn't use parport_pc_fifo_write_block_pio() etc..
The driver is tested on my PC(Dual Pentium II, kernel-2.1.132).

I have a problem about the FIFO. If the driver is waiting for the FIFO empty
(ex. number of the data in FIFO is less than write threshold),
there are no way to acknowldge empty or to know rest data in FIFO from the hardware.
My driver is waiting for FIFO empty by busy loop.
This is bad solution. I want to use fill-up method like
parport_pc_ecp_write_block_pio() in patch-2.2.0-pre5-1284.

But I think there are no way to stop sending the data by the hardware
or change the mode to TEST FIFO mode without data loss according to the data sheet.
I think the same problem exist in patch-2.2.0-pre5-1284.

This is patch for lp.c in 2.1.132 kernel.
-- cut here --
diff -ur linux.orig/drivers/char/lp.c linux/drivers/char/lp.c
--- linux.orig/drivers/char/lp.c Sat Jan 9 10:11:06 1999
+++ linux/drivers/char/lp.c Fri Jan 8 20:04:33 1999
@@ -229,8 +229,11 @@
 
 #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 r_ecr(x) (parport_read_econtrol(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)
+#define w_ecr(x,y) do { parport_write_econtrol(lp_table[(x)].dev->port, (y)); } while (0)
+#define w_pfr(x,y) do { parport_write_fifo(lp_table[(x)].dev->port, (y)); } while (0)
 
 static __inline__ void lp_yield (int minor)
 {
@@ -559,6 +562,181 @@
         return total_bytes_written;
 }
 
+#define LP_PSPP 0x00
+#define LP_PPS2 0x20
+#define LP_PFIFO 0x40
+#define LP_PnErrIntrEn 0x10
+#define LP_PnServIntrEn 0x04
+#define LP_PFull 0x02
+#define LP_PEmpty 0x01
+
+static void lp_empty_ppfifo(int minor)
+{
+ int emptying_timeout = LP_CHAR(minor)*16;
+
+ while( !(r_ecr(minor) & LP_PEmpty) ){
+ if (current->need_resched)
+ schedule ();
+ if (emptying_timeout > 0) {
+ emptying_timeout--;
+ } else {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(1);
+ }
+ }
+ w_ecr(minor,LP_PSPP|LP_PnServIntrEn|LP_PnErrIntrEn);
+ w_ctr(minor, LP_PSELECP | LP_PINITP);
+}
+
+static __inline__ void lp_set_ppfifo(int minor)
+{
+ w_ctr(minor, LP_PSELECP | LP_PINITP);
+ w_ecr(minor,LP_PFIFO|LP_PnServIntrEn|LP_PnErrIntrEn);
+}
+
+static __inline__ void lp_sleep_ppfifo(int minor)
+{
+ cli();
+ w_ecr(minor, LP_PFIFO|LP_PnErrIntrEn);
+ interruptible_sleep_on_timeout(&lp_table[minor].wait_q, LP_TIMEOUT_INTERRUPT);
+ sti();
+}
+
+static int lp_write_ppfifo(unsigned int minor, const char *buf, int count)
+{
+ unsigned long copy_size;
+ unsigned long total_bytes_written = 0;
+ unsigned long bytes_written;
+ struct lp_struct *lp = &lp_table[minor];
+ int sending_timeout = LP_CHAR(minor);
+
+ if (minor >= LP_NO)
+ return -ENXIO;
+ if (lp->dev == NULL)
+ return -ENXIO;
+
+ lp_set_ppfifo(minor);
+ lp->last_error = 0;
+ do {
+ bytes_written = 0;
+ copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
+
+ if (copy_from_user(lp->lp_buffer, buf, copy_size)) {
+ lp_empty_ppfifo(minor);
+ return -EFAULT;
+ }
+
+ while (copy_size) {
+ unsigned long int timeslip = (jiffies - lp->dev->time);
+ int rc = total_bytes_written + bytes_written;
+ unsigned char ecr, str;
+
+ if (signal_pending(current)) {
+ lp_empty_ppfifo(minor);
+ return rc ? rc : -EINTR;
+ }
+ if ((lp->dev->port->waithead != NULL) && (timeslip > lp->dev->timeslice)) {
+ lp_empty_ppfifo(minor);
+ lp_parport_release(minor);
+ schedule ();
+ lp_parport_claim(minor);
+ lp_set_ppfifo(minor);
+ lp->last_error = 0;
+ }
+ ecr = r_ecr(minor);
+ if (ecr & LP_PEmpty) {
+ int n = (copy_size < 16) ? copy_size : 16;
+ outsb(lp->dev->port->base+CONFIGA, &lp->lp_buffer[bytes_written], n);
+ bytes_written += n;
+ copy_size -= n;
+ sending_timeout = LP_CHAR(minor);
+#ifdef LP_STATS
+ lp->runchars += n;
+ lp->stats.chars += n;
+#endif
+ continue;
+ }
+ if ( !(ecr & LP_PFull)) {
+ w_pfr(minor,lp->lp_buffer[bytes_written]);
+ copy_size--;
+ bytes_written++;
+ sending_timeout = LP_CHAR(minor);
+#ifdef LP_STATS
+ lp->runchars++;
+ lp->stats.chars++;
+#endif
+ continue;
+ }
+ if (LP_POLLED(minor)) {
+ if (sending_timeout > 0) {
+ sending_timeout--;
+ if (current->need_resched)
+ schedule ();
+ continue;
+ }
+ } else {
+ if (ecr & LP_PnServIntrEn) {
+ lp_sleep_ppfifo(minor);
+ continue;
+ }
+ }
+ /* FIFO is completely full and the data was not sent at all */
+ str = r_str(minor);
+#ifdef LP_STATS
+ lp->stats.sleeps++;
+ if (lp->runchars > lp->stats.maxrun)
+ lp->stats.maxrun = lp->runchars;
+ lp->runchars = 0;
+#endif
+ if (str & LP_PBUSY) {
+ lp->last_error = 0;
+ } else if ((str & LP_POUTPA)) {
+ if (lp->last_error != LP_POUTPA) {
+ lp->last_error = LP_POUTPA;
+ printk(KERN_INFO "lp%d out of paper\n", minor);
+ }
+ if (LP_F(minor) & LP_ABORT) {
+ lp_empty_ppfifo(minor);
+ return rc ? rc : -EIO;
+ }
+ } else if (!(str & LP_PSELECD)) {
+ if (lp->last_error != LP_PSELECD) {
+ lp->last_error = LP_PSELECD;
+ printk(KERN_INFO "lp%d off-line\n", minor);
+ }
+ if (LP_F(minor) & LP_ABORT) {
+ lp_empty_ppfifo(minor);
+ return rc ? rc : -EIO;
+ }
+ } else if (!(str & LP_PERRORP)) {
+ if (lp->last_error != LP_PERRORP) {
+ lp->last_error = LP_PERRORP;
+ printk(KERN_ERR "lp%d on fire!\n", minor);
+ }
+ if (LP_F(minor) & LP_ABORT) {
+ lp_empty_ppfifo(minor);
+ return rc ? rc : -EIO;
+ }
+ } else {
+ lp->last_error = 0;
+ }
+
+ if (LP_POLLED(minor)) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(LP_TIMEOUT_POLLED);
+ } else {
+ lp_sleep_ppfifo(minor);
+ }
+ }
+ total_bytes_written += bytes_written;
+ buf += bytes_written;
+ count -= bytes_written;
+ } while (count > 0);
+
+ lp_empty_ppfifo(minor);
+ return total_bytes_written;
+}
+
 static ssize_t lp_write(struct file * file, const char * buf,
                         size_t count, loff_t *ppos)
 {
@@ -576,7 +754,11 @@
           */
          lp_parport_claim (minor);
 
- retv = lp_write_buf(minor, buf, count);
+ if (lp_table[minor].dev->port->modes & (PARPORT_MODE_PCECR)) {
+ retv = lp_write_ppfifo(minor, buf, count);
+ } else {
+ retv = lp_write_buf(minor, buf, count);
+ }
 
          lp_parport_release (minor);
          return retv;

-- 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 Fri 08 Jan 1999 - 21:17:29 EST