diff --git a/sys/dev/atkbdc/atkbd.c b/sys/dev/atkbdc/atkbd.c index 0d2b44ba5182..d93c1c6da01b 100644 --- a/sys/dev/atkbdc/atkbd.c +++ b/sys/dev/atkbdc/atkbd.c @@ -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 flags) 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 flags) 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 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, 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 flags) 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 flags) * 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; diff --git a/sys/dev/atkbdc/atkbdc.c b/sys/dev/atkbdc/atkbdc.c index 9368dbeb1018..69ffa63f338e 100644 --- a/sys/dev/atkbdc/atkbdc.c +++ b/sys/dev/atkbdc/atkbdc.c @@ -114,6 +114,41 @@ static int wait_for_kbd_ack(atkbdc_softc_t *kbdc); 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_space_tag_t tag, bus_space_handle_t h0, #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 diff --git a/sys/dev/atkbdc/atkbdcreg.h b/sys/dev/atkbdc/atkbdcreg.h index 44a9801cf8df..db590b96cc9f 100644 --- a/sys/dev/atkbdc/atkbdcreg.h +++ b/sys/dev/atkbdc/atkbdcreg.h @@ -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 { diff --git a/sys/dev/atkbdc/psm.c b/sys/dev/atkbdc/psm.c index 9a6ae72ae415..94cf880f14e8 100644 --- a/sys/dev/atkbdc/psm.c +++ b/sys/dev/atkbdc/psm.c @@ -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); }