kernel - Fix keyboard probe for chromebooks (2)
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 3 Jan 2014 02:11:26 +0000 (18:11 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 3 Jan 2014 02:18:37 +0000 (18:18 -0800)
* Chromebooks (Acer C720 at least) appear to be using a BIOS emulator
  for the i8042 which gets really unhappy if the keyboard port is disabled,
  even temporarily.

* The original FreeBSD code disables and enables the keyboard port all the
  time, and even sends discrete disable commands (instead of just trusting
  that the command register flags will do the job).

  In addition, the PSM probe code may also disable the keyboard port
  temporarily while enabling and messing with the AUX port.

* Remove these actions.  The keyboard probe will now only disable the
  keyboard port during the probe and init_keyboard() call, which then
  proceeds to issue sufficient commands to the keyboard to wake it up
  again.

  The PSM probe now no longer touches the keyboard port disable/interrupt
  bits.  At all.  Theoetically it should not be necessary to disable the
  keyboard port while probing the aux port.

* The saved command_mask in the softc is no longer used, remove it.

* It is unclear whether the controller properly prioritizes data returned
  from controller commands in-front of the data returned from the keyboard
  or aux ports.  However, as it stands now, we cannot safely disable the
  data from the ports while issuing controller commands and waiting for
  controller command responses so...

sys/dev/misc/kbd/atkbd.c
sys/dev/misc/kbd/atkbdc.c
sys/dev/misc/kbd/atkbdcreg.h
sys/dev/misc/psm/psm.c

index c18d598..fc8fb41 100644 (file)
@@ -1054,8 +1054,8 @@ setup_kbd_port(KBDC kbdc, int port, int intr)
 {
        if (!set_controller_command_byte(kbdc,
                KBD_KBD_CONTROL_BITS,
-               ((port) ? KBD_ENABLE_KBD_PORT : KBD_DISABLE_KBD_PORT)
-                   | ((intr) ? KBD_ENABLE_KBD_INT : KBD_DISABLE_KBD_INT)))
+               ((port) ? KBD_ENABLE_KBD_PORT : KBD_DISABLE_KBD_PORT) |
+               ((intr) ? KBD_ENABLE_KBD_INT : KBD_DISABLE_KBD_INT)))
                return 1;
        return 0;
 }
@@ -1098,25 +1098,26 @@ probe_keyboard(KBDC kbdc, int flags)
         */
        int err;
        int c;
-       int m;
 
        if (!kbdc_lock(kbdc, TRUE)) {
                /* driver error? */
                return ENXIO;
        }
 
-       /* temporarily block data transmission from the keyboard */
+       /*
+        * XXX block data transmission from the keyboard.  This can cause
+        * the keyboard to stop sending keystrokes even when re-enabled
+        * under certain circumstances if not followed by a full reset.
+        */
        write_controller_command(kbdc, KBDC_DISABLE_KBD_PORT);
 
        /* flush any noise in the buffer */
        empty_both_buffers(kbdc, 100);
 
        /* save the current keyboard controller command byte */
-       m = kbdc_get_device_mask(kbdc) & ~KBD_KBD_CONTROL_BITS;
        c = get_controller_command_byte(kbdc);
        if (c == -1) {
                /* CONTROLLER ERROR */
-               kbdc_set_device_mask(kbdc, m);
                kbdc_lock(kbdc, FALSE);
                return ENXIO;
        }
@@ -1140,15 +1141,11 @@ probe_keyboard(KBDC kbdc, int flags)
         * to the system later.  It is NOT recommended to hot-plug
         * the AT keyboard, but many people do so...
         */
-       kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
        setup_kbd_port(kbdc, TRUE, TRUE);
 #if 0
-       if (err == 0) {
-               kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
-       } else {
+       if (err) {
                /* try to restore the command byte as before */
-               set_controller_command_byte(kbdc, 0xff, c);
-               kbdc_set_device_mask(kbdc, m);
+               set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS, c);
        }
 #endif
 
@@ -1171,7 +1168,11 @@ init_keyboard(KBDC kbdc, int *type, int flags)
                return EIO;
        }
 
-       /* temporarily block data transmission from the keyboard */
+       /*
+        * XXX block data transmission from the keyboard.  This can cause
+        * the keyboard to stop sending keystrokes even when re-enabled
+        * under certain circumstances if not followed by a full reset.
+        */
        write_controller_command(kbdc, KBDC_DISABLE_KBD_PORT);
 
 #if 0
@@ -1234,7 +1235,7 @@ init_keyboard(KBDC kbdc, int *type, int flags)
                 * We could disable the keyboard port and interrupt... but,
                 * the keyboard may still exist (see above).
                 */
-               set_controller_command_byte(kbdc, 0xff, c);
+               set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS, c);
                kbdc_lock(kbdc, FALSE);
                if (bootverbose)
                        kprintf("atkbd: failed to reset the keyboard.\n");
@@ -1304,7 +1305,8 @@ init_keyboard(KBDC kbdc, int *type, int flags)
                         * The XT kbd isn't usable unless the proper scan
                         * code set is selected. 
                         */
-                       set_controller_command_byte(kbdc, 0xff, c);
+                       set_controller_command_byte(kbdc,
+                                                   KBD_KBD_CONTROL_BITS, c);
                        kbdc_lock(kbdc, FALSE);
                        kprintf("atkbd: unable to set the XT keyboard mode.\n");
                        return EIO;
@@ -1381,7 +1383,10 @@ init_keyboard(KBDC kbdc, int *type, int flags)
                 * This is serious; we are left with the disabled
                 * keyboard intr. 
                 */
-               set_controller_command_byte(kbdc, 0xff, c);
+               set_controller_command_byte(kbdc,
+                               KBD_KBD_CONTROL_BITS | KBD_TRANSLATION |
+                               KBD_OVERRIDE_KBD_LOCK | mux_mask,
+                               c);
                kbdc_lock(kbdc, FALSE);
                kprintf("atkbd: unable to enable the keyboard port and intr.\n");
                return EIO;
@@ -1448,13 +1453,18 @@ write_kbd(KBDC kbdc, int command, int data)
 
     /* disable the keyboard and mouse interrupt */
     crit_enter();
+
 #if 0
+    /*
+     * XXX NOTE: We can't just disable the KBD port any more, even
+     *                  temporarily, without blowing up some BIOS emulations
+     *          if not followed by a full reset.
+     */
     c = get_controller_command_byte(kbdc);
-    if ((c == -1) 
-       || !set_controller_command_byte(kbdc, 
-            kbdc_get_device_mask(kbdc),
-            KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
-                | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+    if ((c == -1) ||
+       !set_controller_command_byte(kbdc,
+               KBD_KBD_CONTROL_BITS,
+               KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT)) {
        /* CONTROLLER ERROR */
         kbdc_lock(kbdc, FALSE);
        crit_exit();
@@ -1475,9 +1485,7 @@ write_kbd(KBDC kbdc, int command, int data)
 
 #if 0
     /* restore the interrupts */
-    if (!set_controller_command_byte(kbdc,
-            kbdc_get_device_mask(kbdc),
-           c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { 
+    if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS, c)) {
        /* CONTROLLER ERROR */
     }
 #else
index e8d69fa..81446a9 100644 (file)
@@ -202,7 +202,6 @@ atkbdc_setup(atkbdc_softc_t *sc, bus_space_tag_t tag, bus_space_handle_t h0,
 {
        if (sc->ioh0 == 0) {    /* XXX */
            sc->command_byte = -1;
-           sc->command_mask = 0;
            sc->lock = FALSE;
            sc->kbd.head = sc->kbd.tail = 0;
            sc->aux.head = sc->aux.tail = 0;
@@ -1043,19 +1042,6 @@ test_aux_port(KBDC p)
 }
 
 int
-kbdc_get_device_mask(KBDC p)
-{
-    return kbdcp(p)->command_mask;
-}
-
-void
-kbdc_set_device_mask(KBDC p, int mask)
-{
-    kbdcp(p)->command_mask = 
-       mask & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS);
-}
-
-int
 get_controller_command_byte(KBDC p)
 {
     if (kbdcp(p)->command_byte != -1)
@@ -1074,7 +1060,7 @@ set_controller_command_byte(KBDC p, int mask, int command)
        return FALSE;
 
     command = (kbdcp(p)->command_byte & ~mask) | (command & mask);
-#if 1
+#if 0
     if (mask & KBD_DISABLE_KBD_PORT) {
            if (command & KBD_DISABLE_KBD_PORT) {
                if (!write_controller_command(p, KBDC_DISABLE_KBD_PORT))
index 767171a..b8960d6 100644 (file)
@@ -290,9 +290,6 @@ int test_controller(KBDC kbdc);
 int test_kbd_port(KBDC kbdc);
 int test_aux_port(KBDC kbdc);
 
-int kbdc_get_device_mask(KBDC kbdc);
-void kbdc_set_device_mask(KBDC kbdc, int mask);
-
 int get_controller_command_byte(KBDC kbdc);
 int set_controller_command_byte(KBDC kbdc, int command, int flag);
 
index 90d3a3e..ad256a2 100644 (file)
@@ -960,8 +960,7 @@ doopen(struct psm_softc *sc, int command_byte)
 
        /* enable the aux port and interrupt */
        if (!set_controller_command_byte(sc->kbdc,
-           kbdc_get_device_mask(sc->kbdc),
-           (command_byte & KBD_KBD_CONTROL_BITS) |
+           KBD_AUX_CONTROL_BITS,
            KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) {
                /* CONTROLLER ERROR */
                disable_aux_dev(sc->kbdc);
@@ -1003,10 +1002,10 @@ reinitialize(struct psm_softc *sc, int doinit)
            sc->unit, c));
 
        /* enable the aux port but disable the aux interrupt and the keyboard */
-       if ((c == -1) || !set_controller_command_byte(sc->kbdc,
-           kbdc_get_device_mask(sc->kbdc),
-           KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT |
-           KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+       if ((c == -1) ||
+           !set_controller_command_byte(sc->kbdc,
+                       KBD_AUX_CONTROL_BITS,
+                       KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
                /* CONTROLLER ERROR */
                crit_exit();
                kbdc_lock(sc->kbdc, FALSE);
@@ -1055,8 +1054,7 @@ reinitialize(struct psm_softc *sc, int doinit)
        } else {
                /* restore the keyboard port and disable the aux port */
                if (!set_controller_command_byte(sc->kbdc,
-                   kbdc_get_device_mask(sc->kbdc),
-                   (c & KBD_KBD_CONTROL_BITS) |
+                   KBD_AUX_CONTROL_BITS,
                    KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
                        /* CONTROLLER ERROR */
                        log(LOG_ERR, "psm%d: failed to disable the aux port "
@@ -1109,7 +1107,8 @@ psmidentify(driver_t *driver, device_t parent)
 #define        endprobe(v)     do {                    \
        if (bootverbose)                        \
                --verbose;                      \
-       kbdc_set_device_mask(sc->kbdc, mask);   \
+       set_controller_command_byte(sc->kbdc,   \
+    KBD_AUX_CONTROL_BITS, KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT); \
        kbdc_lock(sc->kbdc, FALSE);             \
        return (v);                             \
 } while (0)
@@ -1123,7 +1122,6 @@ psmprobe(device_t dev)
        uintptr_t flags;
        int stat[3];
        int command_byte;
-       int mask;
        int i;
 
 #if 0
@@ -1168,34 +1166,40 @@ psmprobe(device_t dev)
        empty_both_buffers(sc->kbdc, 10);
 
        /* save the current command byte; it will be used later */
-       mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS;
        command_byte = get_controller_command_byte(sc->kbdc);
        if (verbose)
                kprintf("psm%d: current command byte:%04x\n", unit,
                    command_byte);
        if (command_byte == -1) {
                /* CONTROLLER ERROR */
-               kprintf("psm%d: unable to get the current command byte value.\n",
+               kprintf("psm%d: unable to get the current "
+                       "command byte value.\n",
                        unit);
                endprobe(ENXIO);
        }
 
        /*
-        * disable the keyboard port while probing the aux port, which must be
-        * enabled during this routine
+        * NOTE: We cannot mess with the keyboard port, do NOT disable it
+        *       while we are probing the aux port during this routine.
+        *       Disabling the keyboard port will break some things
+        *       (Acer c720)... probably related to BIOS emulation of the
+        *       i8042.
         */
        if (!set_controller_command_byte(sc->kbdc,
-           KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
-           KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT |
-           KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+           KBD_AUX_CONTROL_BITS, KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
                /*
                 * this is CONTROLLER ERROR; I don't know how to recover
                 * from this error...
                 */
-               restore_controller(sc->kbdc, command_byte);
                kprintf("psm%d: unable to set the command byte.\n", unit);
                endprobe(ENXIO);
        }
+
+       /*
+        * NOTE: Linux doesn't send discrete aux port enablement commands,
+        *       it is unclear whether this is needed or helps or hinders
+        *       bios emulators.
+        */
        write_controller_command(sc->kbdc, KBDC_ENABLE_AUX_PORT);
 
        /*
@@ -1231,10 +1235,10 @@ psmprobe(device_t dev)
                recover_from_error(sc->kbdc);
                if (sc->config & PSM_CONFIG_IGNPORTERROR)
                        break;
-               restore_controller(sc->kbdc, command_byte);
                if (verbose)
-                       kprintf("psm%d: the aux port is not functioning (%d).\n",
-                           unit, i);
+                       kprintf("psm%d: the aux port is not "
+                               "functioning (%d).\n",
+                               unit, i);
                endprobe(ENXIO);
        }
 
@@ -1254,7 +1258,6 @@ psmprobe(device_t dev)
                 */
                if (!reset_aux_dev(sc->kbdc)) {
                        recover_from_error(sc->kbdc);
-                       restore_controller(sc->kbdc, command_byte);
                        if (verbose)
                                kprintf("psm%d: failed to reset the aux "
                                    "device.\n", unit);
@@ -1276,7 +1279,6 @@ psmprobe(device_t dev)
        if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) {
                /* MOUSE ERROR */
                recover_from_error(sc->kbdc);
-               restore_controller(sc->kbdc, command_byte);
                if (verbose)
                        kprintf("psm%d: failed to enable the aux device.\n",
                            unit);
@@ -1298,7 +1300,6 @@ psmprobe(device_t dev)
        /* verify the device is a mouse */
        sc->hw.hwid = get_aux_id(sc->kbdc);
        if (!is_a_mouse(sc->hw.hwid)) {
-               restore_controller(sc->kbdc, command_byte);
                if (verbose)
                        kprintf("psm%d: unknown device type (%d).\n", unit,
                            sc->hw.hwid);
@@ -1397,20 +1398,16 @@ psmprobe(device_t dev)
 
        /* disable the aux port for now... */
        if (!set_controller_command_byte(sc->kbdc,
-           KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
-           (command_byte & KBD_KBD_CONTROL_BITS) |
-           KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+           KBD_AUX_CONTROL_BITS, KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
                /*
                 * this is CONTROLLER ERROR; I don't know the proper way to
                 * recover from this error...
                 */
-               restore_controller(sc->kbdc, command_byte);
                kprintf("psm%d: unable to set the command byte.\n", unit);
                endprobe(ENXIO);
        }
 
        /* done */
-       kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS);
        kbdc_lock(sc->kbdc, FALSE);
        return (0);
 }
@@ -1544,10 +1541,10 @@ psmopen(struct dev_open_args *ap)
        command_byte = get_controller_command_byte(sc->kbdc);
 
        /* enable the aux port and temporalily disable the keyboard */
-       if (command_byte == -1 || !set_controller_command_byte(sc->kbdc,
-           kbdc_get_device_mask(sc->kbdc),
-           KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT |
-           KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+       if (command_byte == -1 ||
+           !set_controller_command_byte(sc->kbdc,
+                   KBD_AUX_CONTROL_BITS,
+                   KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
                /* CONTROLLER ERROR; do you know how to get out of this? */
                kbdc_lock(sc->kbdc, FALSE);
                crit_exit();
@@ -1599,8 +1596,7 @@ psmclose(struct dev_close_args *ap)
 
        /* disable the aux interrupt and temporalily disable the keyboard */
        if (!set_controller_command_byte(sc->kbdc,
-           kbdc_get_device_mask(sc->kbdc),
-           KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT |
+           KBD_AUX_CONTROL_BITS,
            KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
                log(LOG_ERR,
                    "psm%d: failed to disable the aux int (psmclose).\n",
@@ -1641,9 +1637,7 @@ psmclose(struct dev_close_args *ap)
        }
 
        if (!set_controller_command_byte(sc->kbdc,
-           kbdc_get_device_mask(sc->kbdc),
-           (command_byte & KBD_KBD_CONTROL_BITS) |
-           KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+           KBD_AUX_CONTROL_BITS, KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
                /*
                 * CONTROLLER ERROR;
                 * we shall ignore this error; see the above comment.
@@ -1795,10 +1789,10 @@ block_mouse_data(struct psm_softc *sc, int *c)
 
        crit_enter();
        *c = get_controller_command_byte(sc->kbdc);
-       if ((*c == -1) || !set_controller_command_byte(sc->kbdc,
-           kbdc_get_device_mask(sc->kbdc),
-           KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT |
-           KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
+       if ((*c == -1) ||
+           !set_controller_command_byte(sc->kbdc,
+                   KBD_AUX_CONTROL_BITS,
+                   KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
                /* this is CONTROLLER ERROR */
                crit_exit();
                kbdc_lock(sc->kbdc, FALSE);
@@ -1860,8 +1854,7 @@ unblock_mouse_data(struct psm_softc *sc, int c)
 
        /* restore ports and interrupt */
        if (!set_controller_command_byte(sc->kbdc,
-           kbdc_get_device_mask(sc->kbdc),
-           c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) {
+           KBD_AUX_CONTROL_BITS, c & (KBD_AUX_CONTROL_BITS))) {
                /*
                 * CONTROLLER ERROR; this is serious, we may have
                 * been left with the inaccessible keyboard and