patch-2.2.8 linux/drivers/misc/parport_pc.c

Next file: linux/drivers/net/3c507.c
Previous file: linux/drivers/misc/parport_mfc3.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.7/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
@@ -53,6 +53,8 @@
    than PARPORT_MAX (in <linux/parport.h>).  */
 #define PARPORT_PC_MAX_PORTS  8
 
+static int user_specified = 0;
+
 static void parport_pc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	parport_generic_irq(irq, (struct parport *) dev_id, regs);
@@ -103,19 +105,24 @@
 
 void parport_pc_write_control(struct parport *p, unsigned char d)
 {
+	struct parport_pc_private *priv = p->private_data;
+	priv->ctr = d;/* update soft copy */
 	outb(d, p->base+CONTROL);
 }
 
 unsigned char parport_pc_read_control(struct parport *p)
 {
-	return inb(p->base+CONTROL);
+	struct parport_pc_private *priv = p->private_data;
+	return priv->ctr;
 }
 
 unsigned char parport_pc_frob_control(struct parport *p, unsigned char mask,  unsigned char val)
 {
-	unsigned char old = inb(p->base+CONTROL);
-	outb(((old & ~mask) ^ val), p->base+CONTROL);
-	return old;
+	struct parport_pc_private *priv = p->private_data;
+	unsigned char ctr = priv->ctr;
+	ctr = (ctr & ~mask) ^ val;
+	outb (ctr, p->base+CONTROL);
+	return priv->ctr = ctr; /* update soft copy */
 }
 
 void parport_pc_write_status(struct parport *p, unsigned char d)
@@ -345,6 +352,8 @@
  */
 static int parport_SPP_supported(struct parport *pb)
 {
+	unsigned char r, w;
+
 	/*
 	 * first clear an eventually pending EPP timeout 
 	 * I (sailer@ife.ee.ethz.ch) have an SMSC chipset
@@ -354,14 +363,54 @@
 	parport_pc_epp_clear_timeout(pb);
 
 	/* Do a simple read-write test to make sure the port exists. */
-	parport_pc_write_control(pb, 0xc);
-	parport_pc_write_data(pb, 0xaa);
-	if (parport_pc_read_data(pb) != 0xaa) return 0;
-	
-	parport_pc_write_data(pb, 0x55);
-	if (parport_pc_read_data(pb) != 0x55) return 0;
+	w = 0xc;
+	parport_pc_write_control(pb, w);
+
+	/* Can we read from the control register?  Some ports don't
+	 * allow reads, so read_control just returns a software
+	 * copy. Some ports _do_ allow reads, so bypass the software
+	 * copy here.  In addition, some bits aren't writable. */
+	r = inb (pb->base+CONTROL);
+	if ((r & 0x3f) == w) {
+		w = 0xe;
+		parport_pc_write_control (pb, w);
+		r = inb (pb->base+CONTROL);
+		parport_pc_write_control (pb, 0xc);
+		if ((r & 0x3f) == w)
+			return PARPORT_MODE_PCSPP;
+	}
+
+	if (user_specified)
+		/* That didn't work, but the user thinks there's a
+		 * port here. */
+		printk (KERN_DEBUG "0x%lx: CTR: wrote 0x%02x, read 0x%02x\n",
+			pb->base, w, r);
+
+	/* Try the data register.  The data lines aren't tri-stated at
+	 * this stage, so we expect back what we wrote. */
+	w = 0xaa;
+	parport_pc_write_data (pb, w);
+	r = parport_pc_read_data (pb);
+	if (r == w) {
+		w = 0x55;
+		parport_pc_write_data (pb, w);
+		r = parport_pc_read_data (pb);
+		if (r == w)
+			return PARPORT_MODE_PCSPP;
+	}
+
+	if (user_specified)
+		/* Didn't work with 0xaa, but the user is convinced
+		 * this is the place. */
+		printk (KERN_DEBUG "0x%lx: DATA: wrote 0x%02x, read 0x%02x\n",
+			pb->base, w, r);
+
+	/* It's possible that we can't read the control register or
+	   the data register.  In that case just believe the user. */
+	if (user_specified)
+		return PARPORT_MODE_PCSPP;
 
-	return PARPORT_MODE_PCSPP;
+	return 0;
 }
 
 /* Check for ECP
@@ -712,6 +761,15 @@
 	if (check_region(base, 3)) return 0;
 	if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops)))
 		return 0;
+	p->private_data = kmalloc (sizeof (struct parport_pc_private),
+				   GFP_KERNEL);
+	if (!p->private_data) {
+		/* Not enough memory. */
+		printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base);
+		parport_unregister_port (p);
+		return 0;
+	}
+	((struct parport_pc_private *) (p->private_data))->ctr = 0xc;
 	if (p->base != 0x3bc) {
 		if (!check_region(base+0x400,3)) {
 			p->modes |= parport_ECR_present(p);	
@@ -725,6 +783,7 @@
 	}
 	if (!parport_SPP_supported(p)) {
 		/* No port. */
+		kfree (p->private_data);
 		parport_unregister_port (p);
 		return 0;
 	}
@@ -787,6 +846,7 @@
 	int count = 0, i = 0;
 	if (io && *io) {
 		/* Only probe the ports we were given. */
+		user_specified = 1;
 		do {
 			count += probe_one_port(*(io++), *(irq++), *(dma++));
 		} while (*io && (++i < PARPORT_PC_MAX_PORTS));
@@ -829,6 +889,7 @@
 			if (!(p->flags & PARPORT_FLAG_COMA)) 
 				parport_quiesce(p);
 			parport_proc_unregister(p);
+			kfree (p->private_data);
 			parport_unregister_port(p);
 		}
 		p = tmp;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)