[parisc-linux-cvs] keyboard patch

Richard Hirst rhirst@linuxcare.com
Thu, 13 Sep 2001 19:56:07 +0100


Hopefully this change wont make life too hard for Thomas when he
comes integrate his proper fix, but I figured it was time we had
something in cvs that worked again.

Hack Thomas's patch to apply to 2.4.9, and change it to remove all
potentially infinite loops.  Make init_keyb() grab the spinlock to
prevent the interrupt handler being invoked by input data.  
Tested on B180 and C360 with serial console, with and without
keyboard attached.  Tested with STI console on B180 and 712 with 
both an HP keyboard and a non-name PC keyboard.



Index: Makefile
===================================================================
RCS file: /home/cvs/parisc/linux/Makefile,v
retrieving revision 1.130
diff -u -r1.130 Makefile
--- Makefile	2001/09/10 12:53:38	1.130
+++ Makefile	2001/09/13 17:54:42
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 9
-EXTRAVERSION = -pa18
+EXTRAVERSION = -pa19
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
Index: drivers/char/hp_psaux.c
===================================================================
RCS file: /home/cvs/parisc/linux/drivers/char/hp_psaux.c,v
retrieving revision 1.13
diff -u -r1.13 hp_psaux.c
--- hp_psaux.c	2001/08/28 07:07:59	1.13
+++ hp_psaux.c	2001/09/13 17:54:43
@@ -43,6 +43,7 @@
 
 /* HP specific LASI PS/2 keyboard and psaux constants */
 #define	AUX_REPLY_ACK	0xFA	/* Command byte ACK. */
+#define	AUX_RESEND	0xFE	/* Sent by the keyb. Asking for resending the last command. */
 #define	AUX_RECONNECT	0xAA	/* scancode when ps2 device is plugged (back) in */
 
 #define	LASI_PSAUX_OFFSET 0x0100 /* offset from keyboard to psaux port */
@@ -70,9 +71,11 @@
 #define LASI_STAT_DATSHD 0x40
 #define LASI_STAT_CLKSHD 0x80
 
+static spinlock_t	kbd_controller_lock = SPIN_LOCK_UNLOCKED;
 static unsigned long lasikbd_hpa;
 static unsigned long lasips2_hpa;
 
+static volatile int cmd_status;
 
 static inline u8 read_input(unsigned long hpa)
 {
@@ -94,26 +97,40 @@
         return gsc_readb(hpa+LASI_STATUS);
 }
 
+/* XXX should this grab the spinlock? */
+
 static int write_output(u8 val, unsigned long hpa)
 {
-	int wait = 0;
+	int wait = 250;
 
 	while (read_status(hpa) & LASI_STAT_TBNE) {
-		wait++;
-		if (wait>10000) {
-			/* printk(KERN_WARNING "Lasi PS/2 transmit buffer timeout\n"); */
+		if (!--wait) {
+			printk(KERN_WARNING "Lasi PS/2 transmit buffer timeout\n");
 			return 0;
 		}
+		mdelay(1);
 	}
-
-	if (wait)
-		printk(KERN_DEBUG "Lasi PS/2 wait %d\n", wait);
-	
 	gsc_writeb(val, hpa+LASI_XMTDATA);
 
 	return 1;
 }
 
+/* XXX should this grab the spinlock? */
+
+static u8 wait_input(unsigned long hpa)
+{
+	int wait = 250;
+
+	while (!(read_status(hpa) & LASI_STAT_RBNE)) {
+		if (!--wait) {
+			printk(KERN_WARNING "Lasi PS/2 receive buffer timeout\n");
+			return 0;
+		}
+		mdelay(1);
+	}
+	return read_input(hpa);      
+}
+
 /* This function is the PA-RISC adaptation of i386 source */
 
 static inline int aux_write_ack(u8 val)
@@ -121,11 +138,34 @@
       return write_output(val, lasikbd_hpa+LASI_PSAUX_OFFSET);
 }
 
+/* This is wrong, should do something like the pc driver, which sends
+ * the command up to 3 times at 1 second intervals, checking once
+ * per millisecond for an acknowledge.
+ */
+
 static void lasikbd_leds(unsigned char leds)
 {
-	write_output(KBD_CMD_SET_LEDS, lasikbd_hpa);
-	write_output(leds, lasikbd_hpa);
-	write_output(KBD_CMD_ENABLE, lasikbd_hpa);
+	int loop = 1000;
+
+	cmd_status=2;
+	while (cmd_status!=0 && --loop > 0) {
+		write_output(KBD_CMD_SET_LEDS, lasikbd_hpa);
+		mdelay(5); 
+	}
+   
+	cmd_status=2;
+	while (cmd_status!=0 && --loop > 0) {
+		write_output(leds, lasikbd_hpa);
+		mdelay(5);
+	}
+
+	cmd_status=2;
+	while (cmd_status!=0 && --loop > 0) {
+	   write_output(KBD_CMD_ENABLE, lasikbd_hpa);   
+	   mdelay(5);
+	}
+	if (loop <= 0)
+		printk("lasikbd_leds: timeout\n");
 }
 
 #if 0
@@ -154,11 +194,31 @@
 	return ret;
 }
 #endif 
+
+static int init_keyb(unsigned long hpa)
+{
+	int res = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kbd_controller_lock, flags);
+
+	if (write_output(KBD_CMD_SET_LEDS, hpa) &&
+			wait_input(hpa) == AUX_REPLY_ACK &&
+			write_output(0, hpa) &&
+			wait_input(hpa) == AUX_REPLY_ACK &&
+			write_output(KBD_CMD_ENABLE, hpa) &&
+			wait_input(hpa) == AUX_REPLY_ACK)
+		res = 1;
+
+	spin_unlock_irqrestore(&kbd_controller_lock, flags);
+
+	return res;
+}
+
 
-static int __init lasi_ps2_reset(unsigned long hpa, int id)
+static void __init lasi_ps2_reset(unsigned long hpa)
 {
 	u8 control;
-	int ret = 1;
 
 	/* reset the interface */
 	gsc_writeb(0xff, hpa+LASI_RESET);
@@ -167,25 +227,8 @@
 	/* enable it */
 	control = read_control(hpa);
 	write_control(control | LASI_CTRL_ENBL, hpa);
-
-        /* initializes the leds at the default state */
-        if (id==0) {
-           write_output(KBD_CMD_SET_LEDS, hpa);
-	   write_output(0, hpa);
-	   ret = write_output(KBD_CMD_ENABLE, hpa);
-	}
-
-	return ret;
 }
 
-static int inited;
-
-static void lasi_ps2_init_hw(void)
-{
-	++inited;
-}
-
-
 /* Greatly inspired by pc_keyb.c */
 
 /*
@@ -203,7 +246,6 @@
 #ifdef CONFIG_PSMOUSE
 
 static struct aux_queue	*queue;
-static spinlock_t	kbd_controller_lock = SPIN_LOCK_UNLOCKED;
 static unsigned char	mouse_reply_expected;
 static int 		aux_count;
 
@@ -363,7 +405,7 @@
 	lock_kernel();
 	fasync_aux(-1, file, 0);
 	if (--aux_count) {
-	   unlock_kernel();
+		unlock_kernel();
 		return 0;
 	}
 	unlock_kernel();
@@ -399,46 +441,47 @@
         id = gsc_readb(hpa+LASI_ID) & 0x0f;
         
         if (id==1) 
-           hpa -= LASI_PSAUX_OFFSET; 
-        lasikbd_hpa = hpa;
-        
-
+		hpa -= LASI_PSAUX_OFFSET; 
+	
         status_keyb = read_status(hpa);
         status_mouse = read_status(hpa+LASI_PSAUX_OFFSET);
 
         while ((status_keyb|status_mouse) & LASI_STAT_RBNE){
            
-           while (status_keyb & LASI_STAT_RBNE) {
+		while (status_keyb & LASI_STAT_RBNE) {
 	      
-              scancode = read_input(hpa);
+		scancode = read_input(hpa);
 
-	      /* XXX don't know if this is a valid fix, but filtering
-	       * 0xfa avoids 'unknown scancode' errors on, eg, capslock
-	       * on some keyboards.
-	       */
-	      if (inited && scancode != 0xfa)
-		 handle_at_scancode(scancode); 
+	        /* XXX don't know if this is a valid fix, but filtering
+	         * 0xfa avoids 'unknown scancode' errors on, eg, capslock
+	         * on some keyboards.
+	         */
+	      	      
+		if (scancode == AUX_REPLY_ACK) 
+			cmd_status=0;
+			
+		else if (scancode == AUX_RESEND)
+			cmd_status=1;
+		else 
+			handle_at_scancode(scancode); 
 	      
-	      status_keyb =read_status(hpa);
-           }
+		status_keyb =read_status(hpa);
+		}
 	   
 #ifdef CONFIG_PSMOUSE
-           while (status_mouse & LASI_STAT_RBNE) {
-	      scancode = read_input(hpa+LASI_PSAUX_OFFSET);
-	      handle_mouse_scancode(scancode);
-              status_mouse = read_status(hpa+LASI_PSAUX_OFFSET);
-	   }
-           status_mouse = read_status(hpa+LASI_PSAUX_OFFSET);
+		while (status_mouse & LASI_STAT_RBNE) {
+			scancode = read_input(hpa+LASI_PSAUX_OFFSET);
+			handle_mouse_scancode(scancode);
+			status_mouse = read_status(hpa+LASI_PSAUX_OFFSET);
+		}
+		status_mouse = read_status(hpa+LASI_PSAUX_OFFSET);
 #endif /* CONFIG_PSMOUSE */
-           status_keyb = read_status(hpa);
+		status_keyb = read_status(hpa);
         }
 
         tasklet_schedule(&keyboard_tasklet);
         return (status_keyb|status_mouse);
 }
-
-
-
 	
 extern struct pt_regs *kbd_pt_regs;
 
@@ -449,12 +492,10 @@
 	handle_lasikbd_event(lasips2_hpa);
 }
 
-
 extern int pckbd_translate(unsigned char, unsigned char *, char);
 
 static struct kbd_ops gsc_ps2_kbd_ops = {
 	translate:	pckbd_translate,
-	init_hw:	lasi_ps2_init_hw,
 	leds:		lasikbd_leds,
 #ifdef CONFIG_MAGIC_SYSRQ
 	sysrq_key:	0x54,
@@ -468,7 +509,7 @@
 	unsigned long hpa = dev->hpa;
 	unsigned int irq;
 	char *name;
-	int device_found;
+	int device_found = 0;
 	u8 id;
 
 	id = gsc_readb(hpa+LASI_ID) & 0x0f;
@@ -488,7 +529,7 @@
 	}
 	
 	/* reset the PS/2 port */
-	device_found = lasi_ps2_reset(hpa,id);
+	lasi_ps2_reset(hpa);
 
 	/* allocate the irq and memory region for that device */
 	if (!(irq = busdevice_alloc_irq(dev)))
@@ -502,6 +543,7 @@
 
 	switch (id) {
 	case 0:	
+	        device_found = init_keyb(hpa);
 		register_kbd_ops(&gsc_ps2_kbd_ops);
 		break;
 	case 1:
@@ -527,6 +569,13 @@
 #endif
 	} /* of case */
 	
+        /* allocate the irq and memory region for that device */
+	if (!(irq = busdevice_alloc_irq(dev)))
+		return -ENODEV;
+		    
+	if (request_irq(irq, lasikbd_interrupt, 0, name, (void *)hpa))
+		return -ENODEV;
+	
 	printk(KERN_INFO "PS/2 %s port at 0x%08lx (irq %d) found, "
 			 "%sdevice attached.\n",
 			name, hpa, irq, device_found ? "":"no ");
@@ -553,4 +602,3 @@
 }
 
 module_init(gsc_ps2_init);
-