blob: e032b0f29878865e2fba51a2994228f774c9f131 [file] [log] [blame]
From 2b68920c417303e3adb90631ed9a9cbf864c0a94 Mon Sep 17 00:00:00 2001
From: Dirk Hohndel <dirk@hohndel.org>
Date: Wed, 1 May 2013 11:23:28 -0700
Subject: [PATCH 1/1] Simulate fake Fn key on PS/2 keyboards w/o one (eg. Chromebook Pixel)
This establishes a somewhat generic way to do this and implements a
specific solution for the Pixel where the right ALT key is redefined
to be an Fn key.
Press/release events for the fake Fn key are no longer reported up,
but if the fake Fn key is pressed, then other keys are potentially
translated.
Implemented in this patch are the following mappings:
BS -> Delete
Up -> PgUp
Down -> PgDn
Left -> Home
Right -> End
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
---
drivers/input/keyboard/atkbd.c | 80 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -161,6 +161,19 @@ static const unsigned short atkbd_unxlat
#define ATKBD_KEY_UNKNOWN 0
#define ATKBD_KEY_NULL 255
+#define ATKBD_KEY_IGNORE 0x8000
+#define ATKBD_KEY_BS 0x0e
+#define ATKBD_KEY_DEL 0x53 /* actually E0 53 - same for the rest here */
+#define ATKBD_KEY_ALT_R 0x38
+#define ATKBD_KEY_HOME 0x47
+#define ATKBD_KEY_UP 0x48
+#define ATKBD_KEY_PGUP 0x49
+#define ATKBD_KEY_LEFT 0x4b
+#define ATKBD_KEY_RIGHT 0x4d
+#define ATKBD_KEY_END 0x4f
+#define ATKBD_KEY_DOWN 0x50
+#define ATKBD_KEY_PGDN 0x51
+
#define ATKBD_SCR_1 0xfffe
#define ATKBD_SCR_2 0xfffd
#define ATKBD_SCR_4 0xfffc
@@ -218,6 +231,7 @@ struct atkbd {
bool softraw;
bool scroll;
bool enabled;
+ bool fake_fn;
/* Accessed only from interrupt */
unsigned char emul;
@@ -242,6 +256,7 @@ struct atkbd {
static void (*atkbd_platform_fixup)(struct atkbd *, const void *data);
static void *atkbd_platform_fixup_data;
static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned int);
+static unsigned int (*atkbd_fake_fn_fixup)(struct atkbd *, unsigned int);
/*
* Certain keyboards to not like ATKBD_CMD_RESET_DIS and stop responding
@@ -404,6 +419,13 @@ static irqreturn_t atkbd_interrupt(struc
if (!atkbd->enabled)
goto out;
+ if (atkbd_fake_fn_fixup) {
+ code = atkbd_fake_fn_fixup(atkbd, code);
+ if (code == ATKBD_KEY_IGNORE)
+ /* fake Fn key pressed - ignore */
+ goto out;
+ }
+
input_event(dev, EV_MSC, MSC_RAW, code);
if (atkbd_platform_scancode_fixup)
@@ -997,6 +1019,48 @@ static unsigned int atkbd_oqo_01plus_sca
}
/*
+ * Google Chromebook Pixel is lacking an Fn key. In order to use as
+ * a regular Linux laptop we steal the left Alt key and turn it into
+ * an Fn key
+ */
+static unsigned int atkbd_pixel_fake_fn_fixup(struct atkbd *atkbd, unsigned int code)
+{
+ if (atkbd->emul != 1) {
+ /* handle backspace here as it's the only one w/o
+ * a leading E0/E1 (i.e., emul == 0) */
+ if (atkbd->emul == 0 && atkbd->fake_fn && (code & 0x7f) == ATKBD_KEY_BS) {
+ /* when pretending that Delete was pressed we need
+ * to set emul as well as Delete is E0 53 */
+ atkbd->emul = 1;
+ code = (code & 0x80) | ATKBD_KEY_DEL;
+ }
+ } else if ((code & 0x7f) == ATKBD_KEY_ALT_R) {
+ atkbd->fake_fn = (code & 0x80) ? 0 : 1;
+ atkbd->emul = 0;
+ code = ATKBD_KEY_IGNORE;
+ } else if (atkbd->fake_fn) {
+ unsigned int oldcode = code;
+ switch(code & 0x7f) {
+ case ATKBD_KEY_UP:
+ code = ATKBD_KEY_PGUP;
+ break;
+ case ATKBD_KEY_DOWN:
+ code = ATKBD_KEY_PGDN;
+ break;
+ case ATKBD_KEY_LEFT:
+ code = ATKBD_KEY_HOME;
+ break;
+ case ATKBD_KEY_RIGHT:
+ code = ATKBD_KEY_END;
+ break;
+ }
+ code |= oldcode & 0x80;
+ }
+ return code;
+}
+
+
+/*
* atkbd_set_keycode_table() initializes keyboard's keycode table
* according to the selected scancode set
*/
@@ -1653,6 +1717,13 @@ static int __init atkbd_deactivate_fixup
return 1;
}
+static int __init atkbd_setup_fake_fn_fixup(const struct dmi_system_id *id)
+{
+ atkbd_fake_fn_fixup = id->driver_data;
+
+ return 1;
+}
+
/*
* NOTE: do not add any more "force release" quirks to this table. The
* task of adjusting list of keys that should be "released" automatically
@@ -1802,6 +1873,15 @@ static const struct dmi_system_id atkbd_
},
.callback = atkbd_deactivate_fixup,
},
+ {
+ /* Google Chromebook Pixel */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
+ },
+ .callback = atkbd_setup_fake_fn_fixup,
+ .driver_data = atkbd_pixel_fake_fn_fixup,
+ },
{ }
};