aboutsummaryrefslogtreecommitdiffstats
path: root/simulate-fake-fn-key-on-ps-2-keyboards-w-o-one-eg.-chromebook-pixel.patch
blob: 50e12e96e797a2f2c075ecb062321c4fcadd952b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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;
+}
+
 static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
 	{
 		.matches = {
@@ -1796,6 +1867,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,
+	},
 	{ }
 };