Process event on ps2 keyboard irq even if event already read.
authorKevin O'Connor <kevin@koconnor.net>
Sun, 14 Mar 2010 02:05:12 +0000 (21:05 -0500)
committerKevin O'Connor <kevin@koconnor.net>
Sun, 14 Mar 2010 02:05:12 +0000 (21:05 -0500)
Some old DOS programs will hook the keyboard irq, read the keyboard
data on irq, and then call the BIOS handler expecting it to continue
process the event.  So, the BIOS can't assume it is the first to read
the data from the ps2 port.

Also, reduce window where a ps2 command could conflict with incoming
data.  Disable all data during interrupt flushing, and only re-enable
the desired port just prior to sending the command.

Also, discard data from any interrupts if init hasn't completed.

src/ps2port.c

index 59541315fd7f8a6f8968bdc6a226894b3167ef06..47dfd49bad846fd79cbf1b6c5047a2a63a54d960 100644 (file)
@@ -157,8 +157,8 @@ ps2_recvbyte(int aux, int needack, int timeout)
                 }
             }
 
-            // This data not for us - XXX - just discard it for now.
-            dprintf(1, "Discarding ps2 data %x (status=%x)\n", data, status);
+            // This data not part of command - just discard it.
+            dprintf(1, "Discarding ps2 data %02x (status=%02x)\n", data, status);
         }
 
         if (check_time(end)) {
@@ -202,12 +202,8 @@ ps2_command(int aux, int command, u8 *param)
 
     // Disable interrupts and keyboard/mouse.
     u8 ps2ctr = GET_EBDA(ps2ctr);
-    u8 newctr = ps2ctr;
-    if (aux)
-        newctr |= I8042_CTR_KBDDIS;
-    else
-        newctr |= I8042_CTR_AUXDIS;
-    newctr &= ~(I8042_CTR_KBDINT|I8042_CTR_AUXINT);
+    u8 newctr = ((ps2ctr | I8042_CTR_AUXDIS | I8042_CTR_KBDDIS)
+                 & ~(I8042_CTR_KBDINT|I8042_CTR_AUXINT));
     dprintf(6, "i8042 ctr old=%x new=%x\n", ps2ctr, newctr);
     int ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr);
     if (ret)
@@ -216,6 +212,15 @@ ps2_command(int aux, int command, u8 *param)
     // Flush any interrupts already pending.
     yield();
 
+    // Enable port command is being sent to.
+    if (aux)
+        newctr &= ~I8042_CTR_AUXDIS;
+    else
+        newctr &= ~I8042_CTR_KBDDIS;
+    ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr);
+    if (ret)
+        goto fail;
+
     if (command == ATKBD_CMD_RESET_BAT) {
         // Reset is special wrt timeouts and bytes received.
 
@@ -328,11 +333,15 @@ handle_74(void)
     u8 v = inb(PORT_PS2_STATUS);
     if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA))
         != (I8042_STR_OBF|I8042_STR_AUXDATA)) {
-        dprintf(1, "mouse irq but no mouse data.\n");
+        dprintf(1, "ps2 mouse irq but no mouse data.\n");
         goto done;
     }
     v = inb(PORT_PS2_DATA);
 
+    if (!(GET_EBDA(ps2ctr) & I8042_CTR_AUXINT))
+        // Interrupts not enabled.
+        goto done;
+
     process_mouse(v);
 
 done:
@@ -350,12 +359,16 @@ handle_09(void)
 
     // read key from keyboard controller
     u8 v = inb(PORT_PS2_STATUS);
-    if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA)) != I8042_STR_OBF) {
-        dprintf(1, "keyboard irq but no keyboard data.\n");
+    if (v & I8042_STR_AUXDATA) {
+        dprintf(1, "ps2 keyboard irq but found mouse data?!\n");
         goto done;
     }
     v = inb(PORT_PS2_DATA);
 
+    if (!(GET_EBDA(ps2ctr) & I8042_CTR_KBDINT))
+        // Interrupts not enabled.
+        goto done;
+
     process_key(v);
 
 done:
@@ -394,13 +407,8 @@ keyboard_init(void *data)
         return;
     }
 
-    // Enable keyboard and mouse ports.
-    ret = i8042_command(I8042_CMD_KBD_ENABLE, NULL);
-    if (ret)
-        return;
-    ret = i8042_command(I8042_CMD_AUX_ENABLE, NULL);
-    if (ret)
-        return;
+    // Disable keyboard and mouse events.
+    SET_EBDA(ps2ctr, I8042_CTR_KBDDIS | I8042_CTR_AUXDIS);
 
 
     /* ------------------- keyboard side ------------------------*/
@@ -424,7 +432,7 @@ keyboard_init(void *data)
     if (ret)
         return;
 
-    // Keyboard Mode: scan code convert, disable mouse, enable IRQ 1
+    // Keyboard Mode: disable mouse, scan code convert, enable kbd IRQ
     SET_EBDA(ps2ctr, I8042_CTR_AUXDIS | I8042_CTR_XLATE | I8042_CTR_KBDINT);
 
     /* Enable keyboard */