[PARPORT] ECP + other bug fixed to parport driver


Frederick Barnes (frmb@mail.cern.ch)
Sat, 25 Sep 1999 21:44:26 +0200 (CEST)


All,

Due to a somewhat urgent need for a working parport device driver, myself
and Jamie Lokier have been fixing some bugs. We don't claim that we've
fixed them properly, but it's made ECP transfers work :-). Tacked onto
the mail are the patches. The bugs (if they were really bugs) fixed are:
    ecp_forward_to_reverse() incorrecly setting nAutoFeed
    __frob_control() masking direction bit. (and inline version)
    change_mode() testing direction bit incorrectly. (and inline version)
    parport_pc_frob_control() now calls data_forward and data_reverse
        depending on situation.
    ECP bi-directional port detection broken, fixed now
    Some incorrect polarities, fixed
    Some places attempt to change direction when not in PS/2 mode, and
        fail, fixed
    Instances of ((HZ + 99) / 25) replaced with ((HZ + 24) / 25). [if
        indeed this is correct]

If there any problems, please mail me (Frederick.Barnes@cern.ch), or Jamie
Lokier (Jamie.Lokier@cern.ch).

Enjoy :-),
  Fred.

+--------------------------------------------------------------------------+
| Fred Barnes |
| Frederick.Barnes@cern.ch http://teddy.xylene.com/ |
+--------------------------------------------------------------------------+

diff -u linux-pcetb/drivers/parport/ieee1284_ops.c.orig linux-pcetb/drivers/parport/ieee1284_ops.c
--- linux-pcetb/drivers/parport/ieee1284_ops.c.orig Sat Sep 25 21:13:35 1999
+++ linux-pcetb/drivers/parport/ieee1284_ops.c Sat Sep 25 22:42:36 1999
@@ -9,8 +9,10 @@
  * Note: Make no assumptions about hardware or architecture in this file!
  *
  * Author: Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Fixed AUTOFD polarity in ecp_forward_to_reverse(). Fred Barnes, 1999
  */
 
+
 #include <linux/config.h>
 #include <linux/parport.h>
 #include <linux/delay.h>
@@ -336,7 +338,7 @@
         /* Event 38: Set nAutoFd low */
         parport_frob_control (port,
                               PARPORT_CONTROL_AUTOFD,
- 0);
+ PARPORT_CONTROL_AUTOFD);
         parport_data_reverse (port);
         udelay (5);
 
@@ -524,12 +526,12 @@
                         if (count && dev->port->irq != PARPORT_IRQ_NONE) {
                                 parport_release (dev);
                                 current->state = TASK_INTERRUPTIBLE;
- schedule_timeout ((HZ + 99) / 25);
+ schedule_timeout ((HZ + 24) / 25);
                                 parport_claim_or_block (dev);
                         }
                         else
                                 /* We must have the device claimed here. */
- parport_wait_event (port, (HZ + 99) / 25);
+ parport_wait_event (port, (HZ + 24) / 25);
 
                         /* Is there a signal pending? */
                         if (signal_pending (current))
@@ -610,10 +612,11 @@
                         count += rle_count;
                         DPRINTK (KERN_DEBUG "%s: decompressed to %d bytes\n",
                                  port->name, rle_count);
- }
- else
+ } else {
                         /* Normal data byte. */
- *buf++ = byte, count++;
+ *buf = byte;
+ buf++, count++;
+ }
         }
 
  out:
diff -u linux-pcetb/drivers/parport/parport_pc.c.orig linux-pcetb/drivers/parport/parport_pc.c
--- linux-pcetb/drivers/parport/parport_pc.c.orig Sat Sep 25 21:12:49 1999
+++ linux-pcetb/drivers/parport/parport_pc.c Sat Sep 25 22:42:19 1999
@@ -10,6 +10,7 @@
  *
  * Cleaned up include files - Russell King <linux@arm.uk.linux.org>
  * DMA support - Bert De Jonghe <bert@sophis.be>
+ * Many ECP bugs fixed. Fred Barnes & Jamie Lokier, 1999
  */
 
 /* This driver should work with any hardware that is broadly compatible
@@ -73,7 +74,12 @@
 static void frob_econtrol (struct parport *pb, unsigned char m,
                            unsigned char v)
 {
- outb ((inb (ECONTROL (pb)) & ~m) ^ v, ECONTROL (pb));
+ unsigned char ectr = inb (ECONTROL (pb));
+#ifdef DEBUG_PARPORT
+ printk (KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n",
+ m, v, ectr, (ectr & ~m) ^ v);
+#endif
+ outb ((ectr & ~m) ^ v, ECONTROL (pb));
 }
 
 #ifdef CONFIG_PARPORT_PC_FIFO
@@ -94,11 +100,8 @@
         oecr = inb (ecr);
         mode = (oecr >> 5) & 0x7;
         if (mode == m) return 0;
- if (mode && m)
- /* We have to go through mode 000 */
- change_mode (p, ECR_SPP);
 
- if (m < 2 && !(parport_read_control (p) & 0x20)) {
+ if (mode >= 2 && !(priv->ctr & 0x20)) {
                 /* This mode resets the FIFO, so we may
                  * have to wait for it to drain first. */
                 long expire = jiffies + p->physport->cad->timeout;
@@ -127,6 +130,13 @@
                 }
         }
 
+ if (mode >= 2 && m >= 2) {
+ /* We have to go through mode 001 */
+ oecr &= ~(7 << 5);
+ oecr |= ECR_PS2 << 5;
+ outb (oecr, ecr);
+ }
+
         /* Set the mode. */
         oecr &= ~(7 << 5);
         oecr |= m << 5;
@@ -160,11 +170,11 @@
                 residue);
 
         /* Reset the FIFO. */
- frob_econtrol (p, 0xe0, 0x20);
+ frob_econtrol (p, 0xe0, ECR_PS2 << 5);
         parport_frob_control (p, PARPORT_CONTROL_STROBE, 0);
 
         /* Now change to config mode and clean up. FIXME */
- frob_econtrol (p, 0xe0, 0xe0);
+ frob_econtrol (p, 0xe0, ECR_CNF << 5);
         cnfga = inb (CONFIGA (p));
         printk (KERN_DEBUG "%s: cnfgA contains 0x%02x\n", p->name, cnfga);
 
@@ -177,7 +187,7 @@
          * PWord != 1 byte. */
 
         /* Back to PS2 mode. */
- frob_econtrol (p, 0xe0, 0x20);
+ frob_econtrol (p, 0xe0, ECR_PS2 << 5);
 
         return residue;
 }
@@ -229,21 +239,6 @@
         return inb (DATA (p));
 }
 
-unsigned char __frob_control (struct parport *p, unsigned char mask,
- unsigned char val)
-{
- const unsigned char wm = (PARPORT_CONTROL_STROBE |
- PARPORT_CONTROL_AUTOFD |
- PARPORT_CONTROL_INIT |
- PARPORT_CONTROL_SELECT);
- struct parport_pc_private *priv = p->physport->private_data;
- unsigned char ctr = priv->ctr;
- ctr = (ctr & ~mask) ^ val;
- ctr &= priv->ctr_writable; /* only write writable bits. */
- outb (ctr, CONTROL (p));
- return priv->ctr = ctr & wm; /* update soft copy */
-}
-
 void parport_pc_write_control(struct parport *p, unsigned char d)
 {
         const unsigned char wm = (PARPORT_CONTROL_STROBE |
@@ -253,9 +248,9 @@
 
         /* Take this out when drivers have adapted to the newer interface. */
         if (d & 0x20) {
- printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
- p->name, p->cad->name);
- parport_pc_data_reverse (p);
+ printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
+ p->name, p->cad->name);
+ parport_pc_data_reverse (p);
         }
 
         __frob_control (p, wm, d & wm);
@@ -263,8 +258,12 @@
 
 unsigned char parport_pc_read_control(struct parport *p)
 {
+ const unsigned char wm = (PARPORT_CONTROL_STROBE |
+ PARPORT_CONTROL_AUTOFD |
+ PARPORT_CONTROL_INIT |
+ PARPORT_CONTROL_SELECT);
         const struct parport_pc_private *priv = p->physport->private_data;
- return priv->ctr; /* Use soft copy */
+ return priv->ctr & wm; /* Use soft copy */
 }
 
 unsigned char parport_pc_frob_control (struct parport *p, unsigned char mask,
@@ -277,9 +276,13 @@
 
         /* Take this out when drivers have adapted to the newer interface. */
         if (mask & 0x20) {
- printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
- p->name, p->cad->name);
+ printk (KERN_DEBUG "%s (%s): use data_%s for this!\n",
+ p->name, p->cad->name,
+ (val & 0x20) ? "reverse" : "forward");
+ if (val & 0x20)
                         parport_pc_data_reverse (p);
+ else
+ parport_pc_data_forward (p);
         }
 
         /* Restrict mask and val to control lines. */
@@ -469,7 +472,7 @@
         frob_econtrol (port, (1<<4), (1<<4)); /* nErrIntrEn */
 
         /* Forward mode. */
- parport_pc_data_forward (port);
+ parport_pc_data_forward (port); /* Must be in PS2 mode */
 
         while (left) {
                 unsigned char byte;
@@ -559,7 +562,7 @@
         frob_econtrol (port, (1<<4), (1<<4)); /* nErrIntrEn */
 
         /* Forward mode. */
- parport_pc_data_forward (port);
+ parport_pc_data_forward (port); /* Must be in PS2 mode */
 
         while (left) {
                 long expire = jiffies + port->physport->cad->timeout;
@@ -656,8 +659,8 @@
                                                       length, flags);
 
         /* Set up parallel port FIFO mode.*/
+ parport_pc_data_forward (port); /* Must be in PS2 mode */
         change_mode (port, ECR_PPF); /* Parallel port FIFO */
- parport_pc_data_forward (port);
         port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
 
         /* Write the data to the FIFO. */
@@ -687,8 +690,8 @@
                         outb (0, FIFO (port));
                 }
 
- /* Reset the FIFO. */
- frob_econtrol (port, 0xe0, 0);
+ /* Reset the FIFO and return to PS2 mode. */
+ frob_econtrol (port, 0xe0, ECR_PS2 << 5);
 
                 /* De-assert strobe. */
                 parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
@@ -727,8 +730,8 @@
         }
 
         /* Set up ECP parallel port mode.*/
+ parport_pc_data_forward (port); /* Must be in PS2 mode */
         change_mode (port, ECR_ECP); /* ECP FIFO */
- parport_pc_data_forward (port);
         port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
 
         /* Write the data to the FIFO. */
@@ -758,17 +761,20 @@
                         outb (0, FIFO (port));
                 }
 
- /* Reset the FIFO. */
- frob_econtrol (port, 0xe0, 0);
+ /* Reset the FIFO and return to PS2 mode. */
+ frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+
+ /* De-assert strobe. */
                 parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
 
                 /* Host transfer recovery. */
+ parport_pc_data_reverse (port); /* Must be in PS2 mode */
+ udelay (5);
+ parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
+ parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
                 parport_frob_control (port,
                                       PARPORT_CONTROL_INIT,
                                       PARPORT_CONTROL_INIT);
- parport_pc_data_reverse (port);
- parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
- parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
                 parport_wait_peripheral (port,
                                          PARPORT_STATUS_PAPEROUT,
                                          PARPORT_STATUS_PAPEROUT);
@@ -819,21 +825,21 @@
                 parport_frob_control (port,
                                       PARPORT_CONTROL_AUTOFD,
                                       PARPORT_CONTROL_AUTOFD);
- parport_pc_data_reverse (port);
+ parport_pc_data_reverse (port); /* Must be in PS2 mode */
                 udelay (5);
 
                 /* Event 39: Set nInit low to initiate bus reversal */
                 parport_frob_control (port,
                                       PARPORT_CONTROL_INIT,
- PARPORT_CONTROL_INIT);
+ 0);
 
                 /* Event 40: PError goes low */
                 parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
         }
 
         /* Set up ECP parallel port mode.*/
+ parport_pc_data_reverse (port); /* Must be in PS2 mode */
         change_mode (port, ECR_ECP); /* ECP FIFO */
- parport_pc_data_reverse (port);
         port->ieee1284.phase = IEEE1284_PH_REV_DATA;
 
         /* Do the transfer. */
@@ -1054,7 +1060,6 @@
         struct parport_pc_private *priv = pb->private_data;
         unsigned char r = 0xc;
 
- priv->ecr = 0;
         outb (r, CONTROL (pb));
         if ((inb (ECONTROL (pb)) & 0x3) == (r & 0x3)) {
                 outb (r ^ 0x2, CONTROL (pb)); /* Toggle bit 1 */
@@ -1120,9 +1125,9 @@
         /* cancel input mode */
         parport_pc_data_forward (pb);
 
- if (ok)
+ if (ok) {
                 pb->modes |= PARPORT_MODE_TRISTATE;
- else {
+ } else {
                 struct parport_pc_private *priv = pb->private_data;
                 priv->ctr_writable &= ~0x20;
         }
@@ -1180,8 +1185,8 @@
         priv->writeIntrThreshold = i;
 
         /* Find out readIntrThreshold */
- frob_econtrol (pb, 0xe0, ECR_PS2 << 5); /* Reset FIFO */
- parport_pc_data_reverse (pb);
+ frob_econtrol (pb, 0xe0, ECR_PS2 << 5); /* Reset FIFO and enable PS2 */
+ parport_pc_data_reverse (pb); /* Must be in PS2 mode */
         frob_econtrol (pb, 0xe0, ECR_TST << 5); /* Test FIFO */
         frob_econtrol (pb, 1<<2, 1<<2);
         frob_econtrol (pb, 1<<2, 0);
@@ -1544,12 +1549,10 @@
         if (base_hi && !check_region(base_hi,3)) {
                 parport_ECR_present(p);
                 parport_ECP_supported(p);
- parport_ECPPS2_supported(p);
         }
         if (base != 0x3bc) {
                 if (!check_region(base+0x3, 5)) {
- parport_EPP_supported(p);
- if (!(p->modes & PARPORT_MODE_EPP))
+ if (!parport_EPP_supported(p))
                                 parport_ECPEPP_supported(p);
                 }
         }
@@ -1558,8 +1561,10 @@
                 kfree (priv);
                 return NULL;
         }
-
- parport_PS2_supported (p);
+ if (priv->ecr)
+ parport_ECPPS2_supported(p);
+ else
+ parport_PS2_supported (p);
 
         if (!(p = parport_register_port(base, PARPORT_IRQ_NONE,
                                         PARPORT_DMA_NONE, ops))) {
@@ -1672,9 +1677,10 @@
         /* Done probing. Now put the port into a sensible start-up state.
          * SELECT | INIT also puts IEEE1284-compliant devices into
          * compatibility mode. */
- if (p->modes & PARPORT_MODE_ECP)
+ if (priv->ecr)
                 /*
                  * Put the ECP detected port in PS2 mode.
+ * Do this also for ports that have ECR but don't do ECP.
                  */
                 outb (0x34, ECONTROL (p));
 
diff -u linux-pcetb/include/linux/parport_pc.h.orig linux-pcetb/include/linux/parport_pc.h
--- linux-pcetb/include/linux/parport_pc.h.orig Sat Sep 25 21:37:25 1999
+++ linux-pcetb/include/linux/parport_pc.h Sat Sep 25 21:37:54 1999
@@ -41,28 +41,37 @@
 
 extern __inline__ void parport_pc_write_data(struct parport *p, unsigned char d)
 {
+#ifdef DEBUG_PARPORT
+ printk (KERN_DEBUG "parport_pc_write_data(%p,0x%02x)\n", p, d);
+#endif
         outb(d, DATA(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_data(struct parport *p)
 {
- return inb(DATA(p));
+ unsigned char val = inb (DATA (p));
+#ifdef DEBUG_PARPORT
+ printk (KERN_DEBUG "parport_pc_read_data(%p) = 0x%02x\n",
+ p, val);
+#endif
+ return val;
 }
 
 extern __inline__ unsigned char __frob_control (struct parport *p,
                                                 unsigned char mask,
                                                 unsigned char val)
 {
- const unsigned char wm = (PARPORT_CONTROL_STROBE |
- PARPORT_CONTROL_AUTOFD |
- PARPORT_CONTROL_INIT |
- PARPORT_CONTROL_SELECT);
         struct parport_pc_private *priv = p->physport->private_data;
         unsigned char ctr = priv->ctr;
+#ifdef DEBUG_PARPORT
+ printk (KERN_DEBUG "__frob_control(%02x,%02x): %02x -> %02x\n",
+ mask, val, ctr, ((ctr & ~mask) ^ val) & priv->ctr_writable);
+#endif
         ctr = (ctr & ~mask) ^ val;
         ctr &= priv->ctr_writable; /* only write writable bits. */
         outb (ctr, CONTROL (p));
- return priv->ctr = ctr & wm; /* update soft copy */
+ priv->ctr = ctr; /* Update soft copy */
+ return ctr;
 }
 
 extern __inline__ void parport_pc_data_reverse (struct parport *p)
@@ -70,6 +79,11 @@
         __frob_control (p, 0x20, 0x20);
 }
 
+extern __inline__ void parport_pc_data_forward (struct parport *p)
+{
+ __frob_control (p, 0x20, 0x00);
+}
+
 extern __inline__ void parport_pc_write_control (struct parport *p,
                                                  unsigned char d)
 {
@@ -80,9 +94,9 @@
 
         /* Take this out when drivers have adapted to newer interface. */
         if (d & 0x20) {
- printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
- p->name, p->cad->name);
- parport_pc_data_reverse (p);
+ printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
+ p->name, p->cad->name);
+ parport_pc_data_reverse (p);
         }
 
         __frob_control (p, wm, d & wm);
@@ -90,8 +104,12 @@
 
 extern __inline__ unsigned char parport_pc_read_control(struct parport *p)
 {
+ const unsigned char wm = (PARPORT_CONTROL_STROBE |
+ PARPORT_CONTROL_AUTOFD |
+ PARPORT_CONTROL_INIT |
+ PARPORT_CONTROL_SELECT);
         const struct parport_pc_private *priv = p->physport->private_data;
- return priv->ctr; /* Use soft copy */
+ return priv->ctr & wm; /* Use soft copy */
 }
 
 extern __inline__ unsigned char parport_pc_frob_control (struct parport *p,
@@ -105,9 +123,13 @@
 
         /* Take this out when drivers have adapted to newer interface. */
         if (mask & 0x20) {
- printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
- p->name, p->cad->name);
+ printk (KERN_DEBUG "%s (%s): use data_%s for this!\n",
+ p->name, p->cad->name,
+ (val & 0x20) ? "reverse" : "forward");
+ if (val & 0x20)
                         parport_pc_data_reverse (p);
+ else
+ parport_pc_data_forward (p);
         }
 
         /* Restrict mask and val to control lines. */
@@ -122,10 +144,6 @@
         return inb(STATUS(p));
 }
 
-extern __inline__ void parport_pc_data_forward (struct parport *p)
-{
- __frob_control (p, 0x20, 0x00);
-}
 
 extern __inline__ void parport_pc_disable_irq(struct parport *p)
 {

-- 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 Sat 25 Sep 1999 - 15:46:00 EDT