| 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, |
| + }, |
| { } |
| }; |
| |