Date: Mon, 17 Feb 1997 23:00:24 +0100 (MET)
From: Roman Hodek <rnhodek@emilio.informatik.uni-erlangen.de>
To: linux-m68k@phil.uni-sb.de
Subject: L68K: Atari keyboard self test
Sender: owner-linux-m68k@phil.uni-sb.de
Reply-To: linux-m68k@phil.uni-sb.de


Another goodie for today: I've finally implemented an evaluation of
the IKBD self test. Until today, every Atari OS ignored this self
test... now Linux is the first to break this tradition :-) Guess why? I
had such a bad key (yea, these ugly smokers... :-), which the IKBD
indeed reports. Now at least Linux is able to determine this and
ignore that key.

In more detail: If a reset command is sent to the IKBD (like on Linux
initialization), it performs an internal self-test and return 0xf1 on
success. If this byte isn't received, the kernel will print a big, fat
warning. Second, if any keys seem to be pressed during the self-test,
the IKBD assumes that key is broken and sends its break code to inform
the OS. Now the Atari keyboard driver notes all these keys (in the
bitmap 'broken_keys'), and will ignore them later, after printing a
warning. Unfortunately, some AT keyboard interfaces (e.g., mine :-)
send *make* codes instead of *break* codes for broken keys...
Therefore, the code simply ignores bit 7. Hope this is the Right Thing
(TM) to do in this case.

Codes for broken keys during self-test and normal scancodes are
distinguished simply by a timeout: After the reset command, the driver
waits until it doesn't receive characters from the IKBD for some time
(0.25s, concretely). Then it assumes the test to be finished.

You can test the feature by simply holding down an arbitrary key (or
even more than one) from the beginning of Linux boot... But don't
choose an key you might need later, you can't use it anymore :-O

The patch below is relative to 2.1.21 (I haven't unpacked .26 yet...),
but should fit into any kernel source since 1.3 :-)

Roman

------------------------------------------------------------------------------

--- linux-2.1.21/arch/m68k/atari/atakeyb.c.orig	Mon Feb 17 19:02:15 1997
+++ linux-2.1.21/arch/m68k/atari/atakeyb.c	Mon Feb 17 22:46:59 1997
@@ -14,6 +14,7 @@
  */
 
 #include <linux/sched.h>
+#include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/errno.h>
 #include <linux/keyboard.h>
@@ -40,6 +41,15 @@
 /* Hook for mouse driver */
 void (*atari_mouse_interrupt_hook) (char *);
 
+/* variables for IKBD self test: */
+
+/* state: 0: off; >0: in progress; >1: 0xf1 received */
+static volatile int ikbd_self_test;
+/* timestamp when last received a char */
+static volatile unsigned long self_test_last_rcv;
+/* bitmap of keys reported as broken */
+static unsigned long broken_keys[128/(sizeof(unsigned long)*8)] = { 0, };
+
 #define BREAK_MASK	(0x80)
 
 /*
@@ -331,12 +341,15 @@
     {
 	/* a very fast typist or a slow system, give a warning */
 	/* ...happens often if interrupts were disabled for too long */
-	printk( "Keyboard overrun\n" );
+	printk( KERN_WARNING "Keyboard overrun\n" );
 	scancode = acia.key_data;
 	/* Turn off autorepeating in case a break code has been lost */
 	del_timer( &atakeyb_rep_timer );
 	rep_scancode = 0;
-	if (IS_SYNC_CODE(scancode)) {
+	if (ikbd_self_test)
+	    /* During self test, don't do resyncing, just process the code */
+	    goto interpret_scancode;
+	else if (IS_SYNC_CODE(scancode)) {
 	    /* This code seem already to be the start of a new packet or a
 	     * single scancode */
 	    kb_state.state = KEYBOARD;
@@ -386,10 +399,47 @@
 		kb_state.buf[0] = scancode;
 		break;
 
+	      case 0xF1:
+		/* during self-test, note that 0xf1 received */
+		if (ikbd_self_test) {
+		    ++ikbd_self_test;
+		    self_test_last_rcv = jiffies;
+		    break;
+		}
+		/* FALL THROUGH */
+		
 	      default:
 		break_flag = scancode & BREAK_MASK;
 		scancode &= ~BREAK_MASK;
 
+		if (ikbd_self_test) {
+		    /* Scancodes sent during the self-test stand for broken
+		     * keys (keys being down). The code *should* be a break
+		     * code, but nevertheless some AT keyboard interfaces send
+		     * make codes instead. Therefore, simply ignore
+		     * break_flag...
+		     * */
+		    int keyval = ataplain_map[scancode], keytyp;
+		    
+		    set_bit( scancode, broken_keys );
+		    self_test_last_rcv = jiffies;
+		    keyval = ataplain_map[scancode];
+		    keytyp = KTYP(keyval) - 0xf0;
+		    keyval = KVAL(keyval);
+		    
+		    printk( KERN_WARNING "Key with scancode %d ", scancode );
+		    if (keytyp == KT_LATIN || KT_LETTER) {
+			if (keyval < ' ')
+			    printk( "('^%c') ", keyval + '@' );
+			else
+			    printk( "('%c') ", keyval );
+		    }
+		    printk( "is broken -- will be ignored.\n" );
+		    break;
+		}
+		else if (test_bit( scancode, broken_keys ))
+		    break;
+		
 		if (break_flag) {
 		    del_timer( &atakeyb_rep_timer );
 		    rep_scancode = 0;
@@ -815,7 +865,18 @@
     mfp.active_edge &= ~0x10;
     atari_turnon_irq(IRQ_MFP_ACIA);
 
+    ikbd_self_test = 1;
     ikbd_reset();
+    /* wait for a period of inactivity (here: 0.25s), then assume the IKBD's
+     * self-test is finished */
+    self_test_last_rcv = jiffies;
+    while( jiffies < self_test_last_rcv + HZ/4 )
+	barrier();
+    /* if not incremented: no 0xf1 received */
+    if (ikbd_self_test == 1)
+	printk( KERN_ERR "WARNING: keyboard self test failed!\n" );
+    ikbd_self_test = 0;
+    
     ikbd_mouse_disable();
     ikbd_joystick_disable();
 

