Author: grembo (ports committer)
Date: Sat Feb 14 22:12:17 2015
New Revision: 278787
URL: https://svnweb.freebsd.org/changeset/base/278787

Log:
  Quirk based support of Chromebook keyboard found in Acer C720
  
  This probably supports other devices based on SeaBIOS, which need
  to be added to the smbios based quirks table.
  
  The functionality has been ported from DragonFlyBSD and adapted
  to FreeBSD's more general purpose environment.
  
  Devices not covered by a quirk shouldn't be affected at all. Thanks
  to jhb and kostikbel for reviewing the code.
  
  Reviewed by:  kostikbel, jhb
  Approved by:  jhb, kostikbel
  Differential Revision: https://reviews.freebsd.org/D1802

Modified:
  head/sys/dev/atkbdc/atkbd.c
  head/sys/dev/atkbdc/atkbdc.c
  head/sys/dev/atkbdc/atkbdcreg.h
  head/sys/dev/atkbdc/psm.c

Modified: head/sys/dev/atkbdc/atkbd.c
==============================================================================
--- head/sys/dev/atkbdc/atkbd.c Sat Feb 14 21:16:19 2015        (r278786)
+++ head/sys/dev/atkbdc/atkbd.c Sat Feb 14 22:12:17 2015        (r278787)
@@ -77,6 +77,10 @@ typedef struct atkbd_state {
 
 static void            atkbd_timeout(void *arg);
 static void            atkbd_shutdown_final(void *v);
+static int             atkbd_reset(KBDC kbdc, int flags, int c);
+
+#define HAS_QUIRK(p, q)                (((atkbdc_softc_t *)(p))->quirks & q)
+#define ALLOW_DISABLE_KBD(kbdc)        !HAS_QUIRK(kbdc, 
KBDC_QUIRK_KEEP_ACTIVATED)
 
 int
 atkbd_probe_unit(device_t dev, int irq, int flags)
@@ -1095,6 +1099,39 @@ atkbd_shutdown_final(void *v)
 #endif
 }
 
+static int
+atkbd_reset(KBDC kbdc, int flags, int c)
+{
+       /* reset keyboard hardware */
+       if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) {
+               /*
+                * KEYBOARD ERROR
+                * Keyboard reset may fail either because the keyboard
+                * doen't exist, or because the keyboard doesn't pass
+                * the self-test, or the keyboard controller on the
+                * motherboard and the keyboard somehow fail to shake hands.
+                * It is just possible, particularly in the last case,
+                * that the keyboard controller may be left in a hung state.
+                * test_controller() and test_kbd_port() appear to bring
+                * the keyboard controller back (I don't know why and how,
+                * though.)
+                */
+               empty_both_buffers(kbdc, 10);
+               test_controller(kbdc);
+               test_kbd_port(kbdc);
+               /*
+                * We could disable the keyboard port and interrupt... but, 
+                * the keyboard may still exist (see above). 
+                */
+               set_controller_command_byte(kbdc,
+                   ALLOW_DISABLE_KBD(kbdc) ? 0xff : KBD_KBD_CONTROL_BITS, c);
+               if (bootverbose)
+                       printf("atkbd: failed to reset the keyboard.\n");
+               return (EIO);
+       }
+       return (0);
+}
+
 /* local functions */
 
 static int
@@ -1250,13 +1287,14 @@ probe_keyboard(KBDC kbdc, int flags)
                kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
        } else {
                /* try to restore the command byte as before */
-               set_controller_command_byte(kbdc, 0xff, c);
+               set_controller_command_byte(kbdc,
+                   ALLOW_DISABLE_KBD(kbdc) ? 0xff : KBD_KBD_CONTROL_BITS, c);
                kbdc_set_device_mask(kbdc, m);
        }
 #endif
 
        kbdc_lock(kbdc, FALSE);
-       return err;
+       return (HAS_QUIRK(kbdc, KBDC_QUIRK_IGNORE_PROBE_RESULT) ? 0 : err);
 }
 
 static int
@@ -1299,6 +1337,12 @@ init_keyboard(KBDC kbdc, int *type, int 
                return EIO;
        }
 
+       if (HAS_QUIRK(kbdc, KBDC_QUIRK_RESET_AFTER_PROBE) &&
+           atkbd_reset(kbdc, flags, c)) {
+               kbdc_lock(kbdc, FALSE);
+               return EIO;
+       }
+
        /* 
         * Check if we have an XT keyboard before we attempt to reset it. 
         * The procedure assumes that the keyboard and the controller have 
@@ -1343,31 +1387,9 @@ init_keyboard(KBDC kbdc, int *type, int 
        if (bootverbose)
                printf("atkbd: keyboard ID 0x%x (%d)\n", id, *type);
 
-       /* reset keyboard hardware */
-       if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) {
-               /*
-                * KEYBOARD ERROR
-                * Keyboard reset may fail either because the keyboard
-                * doen't exist, or because the keyboard doesn't pass
-                * the self-test, or the keyboard controller on the
-                * motherboard and the keyboard somehow fail to shake hands.
-                * It is just possible, particularly in the last case,
-                * that the keyboard controller may be left in a hung state.
-                * test_controller() and test_kbd_port() appear to bring
-                * the keyboard controller back (I don't know why and how,
-                * though.)
-                */
-               empty_both_buffers(kbdc, 10);
-               test_controller(kbdc);
-               test_kbd_port(kbdc);
-               /*
-                * We could disable the keyboard port and interrupt... but, 
-                * the keyboard may still exist (see above). 
-                */
-               set_controller_command_byte(kbdc, 0xff, c);
+       if (!HAS_QUIRK(kbdc, KBDC_QUIRK_RESET_AFTER_PROBE) &&
+           atkbd_reset(kbdc, flags, c)) {
                kbdc_lock(kbdc, FALSE);
-               if (bootverbose)
-                       printf("atkbd: failed to reset the keyboard.\n");
                return EIO;
        }
 
@@ -1387,7 +1409,8 @@ init_keyboard(KBDC kbdc, int *type, int 
                         * 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, 
ALLOW_DISABLE_KBD(kbdc)
+                           ? 0xff : KBD_KBD_CONTROL_BITS, c);
                        kbdc_lock(kbdc, FALSE);
                        printf("atkbd: unable to set the XT keyboard mode.\n");
                        return EIO;
@@ -1402,6 +1425,17 @@ init_keyboard(KBDC kbdc, int *type, int 
        c |= KBD_TRANSLATION;
 #endif
 
+       /*
+        * Some keyboards require a SETLEDS command to be sent after
+        * the reset command before they will send keystrokes to us
+        */
+       if (HAS_QUIRK(kbdc, KBDC_QUIRK_SETLEDS_ON_INIT) &&
+           send_kbd_command_and_data(kbdc, KBDC_SET_LEDS, 0) != KBD_ACK) {
+               printf("atkbd: setleds failed\n");
+       }
+       if (!ALLOW_DISABLE_KBD(kbdc))
+           send_kbd_command(kbdc, KBDC_ENABLE_KBD);
+
        /* enable the keyboard port and intr. */
        if (!set_controller_command_byte(kbdc, 
                KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK,
@@ -1412,7 +1446,9 @@ init_keyboard(KBDC kbdc, int *type, int 
                 * This is serious; we are left with the disabled
                 * keyboard intr. 
                 */
-               set_controller_command_byte(kbdc, 0xff, c);
+               set_controller_command_byte(kbdc, ALLOW_DISABLE_KBD(kbdc)
+                   ? 0xff : (KBD_KBD_CONTROL_BITS | KBD_TRANSLATION |
+                       KBD_OVERRIDE_KBD_LOCK), c);
                kbdc_lock(kbdc, FALSE);
                printf("atkbd: unable to enable the keyboard port and intr.\n");
                return EIO;

Modified: head/sys/dev/atkbdc/atkbdc.c
==============================================================================
--- head/sys/dev/atkbdc/atkbdc.c        Sat Feb 14 21:16:19 2015        
(r278786)
+++ head/sys/dev/atkbdc/atkbdc.c        Sat Feb 14 22:12:17 2015        
(r278787)
@@ -114,6 +114,41 @@ static int wait_for_kbd_ack(atkbdc_softc
 static int wait_for_aux_data(atkbdc_softc_t *kbdc);
 static int wait_for_aux_ack(atkbdc_softc_t *kbdc);
 
+struct atkbdc_quirks {
+    const char* bios_vendor;
+    const char*        maker;
+    const char*        product;
+    int                quirk;
+};
+
+static struct atkbdc_quirks quirks[] = {
+    {"coreboot", "Acer", "Peppy",
+       KBDC_QUIRK_KEEP_ACTIVATED | KBDC_QUIRK_IGNORE_PROBE_RESULT |
+       KBDC_QUIRK_RESET_AFTER_PROBE | KBDC_QUIRK_SETLEDS_ON_INIT},
+
+    {NULL, NULL, NULL, 0}
+};
+
+#define QUIRK_STR_MATCH(s1, s2) (s1 == NULL || \
+    (s2 != NULL && !strcmp(s1, s2)))
+
+static int
+atkbdc_getquirks(void)
+{
+    int i;
+    char* bios_vendor = kern_getenv("smbios.bios.vendor");
+    char* maker = kern_getenv("smbios.system.maker");
+    char* product = kern_getenv("smbios.system.product");
+
+    for (i=0; quirks[i].quirk != 0; ++i)
+       if (QUIRK_STR_MATCH(quirks[i].bios_vendor, bios_vendor) &&
+           QUIRK_STR_MATCH(quirks[i].maker, maker) &&
+           QUIRK_STR_MATCH(quirks[i].product, product))
+               return (quirks[i].quirk);
+
+    return (0);
+}
+
 atkbdc_softc_t
 *atkbdc_get_softc(int unit)
 {
@@ -295,6 +330,7 @@ atkbdc_setup(atkbdc_softc_t *sc, bus_spa
 #else
        sc->retry = 5000;
 #endif
+       sc->quirks = atkbdc_getquirks();
 
        return 0;
 }
@@ -1124,7 +1160,8 @@ void
 kbdc_set_device_mask(KBDC p, int mask)
 {
     kbdcp(p)->command_mask = 
-       mask & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS);
+       mask & (((kbdcp(p)->quirks & KBDC_QUIRK_KEEP_ACTIVATED)
+           ? 0 : KBD_KBD_CONTROL_BITS) | KBD_AUX_CONTROL_BITS);
 }
 
 int

Modified: head/sys/dev/atkbdc/atkbdcreg.h
==============================================================================
--- head/sys/dev/atkbdc/atkbdcreg.h     Sat Feb 14 21:16:19 2015        
(r278786)
+++ head/sys/dev/atkbdc/atkbdcreg.h     Sat Feb 14 22:12:17 2015        
(r278787)
@@ -202,6 +202,11 @@ typedef struct atkbdc_softc {
     kqueue kbd;                        /* keyboard data queue */
     kqueue aux;                        /* auxiliary data queue */
     int retry;
+    int quirks;                        /* controller doesn't like deactivate */
+#define KBDC_QUIRK_KEEP_ACTIVATED      (1 << 0)
+#define KBDC_QUIRK_IGNORE_PROBE_RESULT (1 << 1)
+#define KBDC_QUIRK_RESET_AFTER_PROBE   (1 << 2)
+#define KBDC_QUIRK_SETLEDS_ON_INIT     (1 << 3)
 } atkbdc_softc_t; 
 
 enum kbdc_device_ivar {

Modified: head/sys/dev/atkbdc/psm.c
==============================================================================
--- head/sys/dev/atkbdc/psm.c   Sat Feb 14 21:16:19 2015        (r278786)
+++ head/sys/dev/atkbdc/psm.c   Sat Feb 14 22:12:17 2015        (r278787)
@@ -371,6 +371,10 @@ static devclass_t psm_devclass;
 /* other flags (flags) */
 #define        PSM_FLAGS_FINGERDOWN    0x0001  /* VersaPad finger down */
 
+#define kbdcp(p)                       ((atkbdc_softc_t *)(p))
+#define ALWAYS_RESTORE_CONTROLLER(kbdc)        !(kbdcp(kbdc)->quirks \
+    & KBDC_QUIRK_KEEP_ACTIVATED)
+
 /* Tunables */
 static int tap_enabled = -1;
 TUNABLE_INT("hw.psm.tap_enabled", &tap_enabled);
@@ -1231,7 +1235,8 @@ psmprobe(device_t dev)
                 * this is CONTROLLER ERROR; I don't know how to recover
                 * from this error...
                 */
-               restore_controller(sc->kbdc, command_byte);
+               if (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+                       restore_controller(sc->kbdc, command_byte);
                printf("psm%d: unable to set the command byte.\n", unit);
                endprobe(ENXIO);
        }
@@ -1270,7 +1275,8 @@ psmprobe(device_t dev)
                recover_from_error(sc->kbdc);
                if (sc->config & PSM_CONFIG_IGNPORTERROR)
                        break;
-               restore_controller(sc->kbdc, command_byte);
+               if (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+                       restore_controller(sc->kbdc, command_byte);
                if (verbose)
                        printf("psm%d: the aux port is not functioning (%d).\n",
                            unit, i);
@@ -1293,7 +1299,8 @@ psmprobe(device_t dev)
                 */
                if (!reset_aux_dev(sc->kbdc)) {
                        recover_from_error(sc->kbdc);
-                       restore_controller(sc->kbdc, command_byte);
+                       if (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+                               restore_controller(sc->kbdc, command_byte);
                        if (verbose)
                                printf("psm%d: failed to reset the aux "
                                    "device.\n", unit);
@@ -1315,7 +1322,8 @@ 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 (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+                       restore_controller(sc->kbdc, command_byte);
                if (verbose)
                        printf("psm%d: failed to enable the aux device.\n",
                            unit);
@@ -1337,7 +1345,8 @@ 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 (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+                       restore_controller(sc->kbdc, command_byte);
                if (verbose)
                        printf("psm%d: unknown device type (%d).\n", unit,
                            sc->hw.hwid);
@@ -1443,7 +1452,8 @@ psmprobe(device_t dev)
                 * this is CONTROLLER ERROR; I don't know the proper way to
                 * recover from this error...
                 */
-               restore_controller(sc->kbdc, command_byte);
+               if (ALWAYS_RESTORE_CONTROLLER(sc->kbdc))
+                       restore_controller(sc->kbdc, command_byte);
                printf("psm%d: unable to set the command byte.\n", unit);
                endprobe(ENXIO);
        }
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to