Introduce cpu_relax() and use it in busy loops.
[seabios.git] / src / kbd.c
index e1f01a5b6d17d7f133762544c36830626d0b37ea..3770305b213897a7840f374a7929294922468737 100644 (file)
--- a/src/kbd.c
+++ b/src/kbd.c
@@ -9,6 +9,154 @@
 #include "util.h" // debug_enter
 #include "config.h" // CONFIG_*
 
+//--------------------------------------------------------------------------
+// keyboard_panic
+//--------------------------------------------------------------------------
+static void
+keyboard_panic(u16 status)
+{
+    // If you're getting a 993 keyboard panic here,
+    // please see the comment in keyboard_init
+
+    BX_PANIC("Keyboard error:%u\n",status);
+}
+
+static void
+kbd_flush(u8 code)
+{
+    u16 max = 0xffff;
+    while ((inb(PORT_PS2_STATUS) & 0x02) && (--max > 0))
+        outb(code, PORT_DIAG);
+    if (!max && code != 0xff)
+        keyboard_panic(code);
+}
+
+static void
+kbd_waitdata(u8 code)
+{
+    u16 max = 0xffff;
+    while ( ((inb(PORT_PS2_STATUS) & 0x01) == 0) && (--max>0) )
+        outb(code, PORT_DIAG);
+    if (!max)
+        keyboard_panic(code);
+}
+
+//--------------------------------------------------------------------------
+// keyboard_init
+//--------------------------------------------------------------------------
+// this file is based on LinuxBIOS implementation of keyboard.c
+static void
+keyboard_init()
+{
+    /* ------------------- Flush buffers ------------------------*/
+    /* Wait until buffer is empty */
+    kbd_flush(0xff);
+
+    /* flush incoming keys */
+    u16 max=0x2000;
+    while (--max > 0) {
+        outb(0x00, PORT_DIAG);
+        if (inb(PORT_PS2_STATUS) & 0x01) {
+            inb(PORT_PS2_DATA);
+            max = 0x2000;
+        }
+    }
+
+    // Due to timer issues, and if the IPS setting is > 15000000,
+    // the incoming keys might not be flushed here. That will
+    // cause a panic a few lines below.  See sourceforge bug report :
+    // [ 642031 ] FATAL: Keyboard RESET error:993
+
+    /* ------------------- controller side ----------------------*/
+    /* send cmd = 0xAA, self test 8042 */
+    outb(0xaa, PORT_PS2_STATUS);
+
+    kbd_flush(0x00);
+    kbd_waitdata(0x01);
+
+    /* read self-test result, 0x55 should be returned from 0x60 */
+    if (inb(PORT_PS2_DATA) != 0x55)
+        keyboard_panic(991);
+
+    /* send cmd = 0xAB, keyboard interface test */
+    outb(0xab, PORT_PS2_STATUS);
+
+    kbd_flush(0x10);
+    kbd_waitdata(0x11);
+
+    /* read keyboard interface test result, */
+    /* 0x00 should be returned form 0x60 */
+    if (inb(PORT_PS2_DATA) != 0x00)
+        keyboard_panic(992);
+
+    /* Enable Keyboard clock */
+    outb(0xae, PORT_PS2_STATUS);
+    outb(0xa8, PORT_PS2_STATUS);
+
+    /* ------------------- keyboard side ------------------------*/
+    /* reset kerboard and self test  (keyboard side) */
+    outb(0xff, PORT_PS2_DATA);
+
+    kbd_flush(0x20);
+    kbd_waitdata(0x21);
+
+    /* keyboard should return ACK */
+    if (inb(PORT_PS2_DATA) != 0xfa)
+        keyboard_panic(993);
+
+    kbd_waitdata(0x31);
+
+    if (inb(PORT_PS2_DATA) != 0xaa)
+        keyboard_panic(994);
+
+    /* Disable keyboard */
+    outb(0xf5, PORT_PS2_DATA);
+
+    kbd_flush(0x40);
+    kbd_waitdata(0x41);
+
+    /* keyboard should return ACK */
+    if (inb(PORT_PS2_DATA) != 0xfa)
+        keyboard_panic(995);
+
+    /* Write Keyboard Mode */
+    outb(0x60, PORT_PS2_STATUS);
+
+    kbd_flush(0x50);
+
+    /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
+    outb(0x61, PORT_PS2_DATA);
+
+    kbd_flush(0x60);
+
+    /* Enable keyboard */
+    outb(0xf4, PORT_PS2_DATA);
+
+    kbd_flush(0x70);
+    kbd_waitdata(0x71);
+
+    /* keyboard should return ACK */
+    if (inb(PORT_PS2_DATA) != 0xfa)
+        keyboard_panic(996);
+
+    outb(0x77, PORT_DIAG);
+}
+
+void
+kbd_setup()
+{
+    u16 x = offsetof(struct bios_data_area_s, kbd_buf) - 0x400;
+    SET_BDA(kbd_mode, 0x10);
+    SET_BDA(kbd_buf_head, x);
+    SET_BDA(kbd_buf_tail, x);
+    SET_BDA(kbd_buf_start_offset, x);
+
+    SET_BDA(kbd_buf_end_offset
+            , x + FIELD_SIZEOF(struct bios_data_area_s, kbd_buf));
+
+    keyboard_init();
+}
+
 static u8
 enqueue_key(u8 scan_code, u8 ascii_code)
 {
@@ -26,8 +174,8 @@ enqueue_key(u8 scan_code, u8 ascii_code)
     if (buffer_tail == buffer_head)
         return 0;
 
-    SET_FARVAR(0x0000, *(u8*)(temp_tail+0x400+0), ascii_code);
-    SET_FARVAR(0x0000, *(u8*)(temp_tail+0x400+1), scan_code);
+    SET_FARVAR(SEG_BDA, *(u8*)(temp_tail+0x400+0), ascii_code);
+    SET_FARVAR(SEG_BDA, *(u8*)(temp_tail+0x400+1), scan_code);
     SET_BDA(kbd_buf_tail, buffer_tail);
     return 1;
 }
@@ -45,11 +193,11 @@ dequeue_key(u8 *scan_code, u8 *ascii_code, u8 incr)
             break;
         if (!incr)
             return 0;
-        nop();
+        cpu_relax();
     }
 
-    *ascii_code = GET_FARVAR(0x0000, *(u8*)(buffer_head+0x400+0));
-    *scan_code  = GET_FARVAR(0x0000, *(u8*)(buffer_head+0x400+1));
+    *ascii_code = GET_FARVAR(SEG_BDA, *(u8*)(buffer_head+0x400+0));
+    *scan_code  = GET_FARVAR(SEG_BDA, *(u8*)(buffer_head+0x400+1));
 
     if (incr) {
         u16 buffer_start = GET_BDA(kbd_buf_start_offset);
@@ -234,10 +382,10 @@ set_leds()
 }
 
 // INT 16h Keyboard Service Entry Point
-void VISIBLE
+void VISIBLE16
 handle_16(struct bregs *regs)
 {
-//    debug_enter(regs);
+    //debug_enter(regs);
 
     set_leds();
 
@@ -491,7 +639,8 @@ process_key(u8 scancode)
             break; /* toss key releases ... */
         }
         if (scancode > MAX_SCAN_CODE) {
-            BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
+            BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n"
+                    , scancode);
             return;
         }
         u8 asciicode;
@@ -502,7 +651,8 @@ process_key(u8 scancode)
         } else if (shift_flags & 0x04) { /* CONTROL */
             asciicode = GET_VAR(CS, info->control);
             scancode = GET_VAR(CS, info->control) >> 8;
-        } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
+        } else if ((mf2_state & 0x02) > 0
+                   && scancode >= 0x47 && scancode <= 0x53) {
             /* extended keys handling */
             asciicode = 0xe0;
             scancode = GET_VAR(CS, info->normal) >> 8;
@@ -541,10 +691,10 @@ process_key(u8 scancode)
 }
 
 // INT09h : Keyboard Hardware Service Entry Point
-void VISIBLE
-handle_09(struct bregs *regs)
+void VISIBLE16
+handle_09()
 {
-    //debug_isr(regs);
+    //debug_isr();
 
     // disable keyboard
     outb(0xad, PORT_PS2_STATUS);