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
This commit is contained in:
Michael Gmelin 2015-02-14 22:12:17 +00:00
parent a76d4388e1
commit 6c176113bb
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=278787
4 changed files with 123 additions and 35 deletions

View file

@ -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;

View file

@ -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

View file

@ -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 {

View file

@ -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);
}