[PATCH 2/2] Input: Make lm8323 sticky key to work with console

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This allows using N810 keyboard from console by adding optional
Fn and shift sticky key handling by passing them from platform_data.

Currently some not so obvious mappings are:

- Pipe is Fn + Enter
- Delete is Fn + Backspace
- Escape is Fn + Ctrl

Works also with xserver-xorg-video-fbdev.

Signed-off-by: Tony Lindgren <tony@xxxxxxxxxxx>
---
 arch/arm/mach-omap2/board-n800.c |   87 ++++++++++++++++++++++++++++++++++----
 drivers/input/keyboard/lm8323.c  |   84 +++++++++++++++++++++++++++++++++----
 include/linux/i2c/lm8323.h       |   12 ++----
 3 files changed, 159 insertions(+), 24 deletions(-)

diff --git a/arch/arm/mach-omap2/board-n800.c b/arch/arm/mach-omap2/board-n800.c
index ae85c2c..a654886 100644
--- a/arch/arm/mach-omap2/board-n800.c
+++ b/arch/arm/mach-omap2/board-n800.c
@@ -51,7 +51,16 @@
 #define N800_TSC2301_RESET_GPIO		118
 
 #ifdef CONFIG_MACH_NOKIA_N810
-static s16 rx44_keymap[LM8323_KEYMAP_SIZE] = {
+
+/*
+ * Largest keycode that the chip can send, plus one,
+ * so keys can be mapped directly at the index of the
+ * LM8323 keycode instead of subtracting one.
+ */
+#define N810_HWKEY_SZ			(0x7f + 1)
+
+/* Keymap for lm8323. Negative value means Shift + KEY */
+static s16 rx44_keymap[N810_HWKEY_SZ * 2] = {
 	[0x01] = KEY_Q,
 	[0x02] = KEY_K,
 	[0x03] = KEY_O,
@@ -81,7 +90,7 @@ static s16 rx44_keymap[LM8323_KEYMAP_SIZE] = {
 	[0x21] = KEY_E,
 	[0x22] = KEY_SEMICOLON,
 	[0x23] = KEY_MINUS,
-	[0x24] = KEY_EQUAL,
+	[0x24] = -KEY_EQUAL,
 	[0x2b] = KEY_FN,
 	[0x2c] = KEY_M,
 	[0x2f] = KEY_F8,
@@ -108,15 +117,77 @@ static s16 rx44_keymap[LM8323_KEYMAP_SIZE] = {
 
 	[0x71] = KEY_I,
 	[0x75] = KEY_KPENTER,
+
+	/* End of hardware key map, Fn + key values below */
+
+	[0x01 + N810_HWKEY_SZ] = KEY_1,		/* Fn + KEY_Q */
+	[0x02 + N810_HWKEY_SZ] = -KEY_0,		/* Fn + KEY_K */
+	[0x03 + N810_HWKEY_SZ] = KEY_9,		/* Fn + KEY_O */
+	[0x04 + N810_HWKEY_SZ] = KEY_0,		/* Fn + KEY_P */
+	[0x05 + N810_HWKEY_SZ] = KEY_DELETE,	/* Fn + KEY_BACKSPACE */
+	[0x06 + N810_HWKEY_SZ] = -KEY_1,		/* Fn + KEY_A */
+	[0x07 + N810_HWKEY_SZ] = -KEY_APOSTROPHE,	/* Fn + KEY_S */
+	[0x08 + N810_HWKEY_SZ] = -KEY_2,		/* Fn + KEY_D */
+	[0x09 + N810_HWKEY_SZ] = -KEY_3,		/* Fn + KEY_F */
+	[0x0a + N810_HWKEY_SZ] = KEY_BACKSLASH,	/* Fn + KEY_G */
+	[0x0b + N810_HWKEY_SZ] = KEY_SLASH,	/* Fn + KEY_H */
+	[0x0c + N810_HWKEY_SZ] = -KEY_9,		/* Fn + KEY_J */
+
+	[0x11 + N810_HWKEY_SZ] = KEY_2,		/* Fn + KEY_W */
+	[0x12 + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_F4 */
+	[0x13 + N810_HWKEY_SZ] = -KEY_8,		/* Fn + KEY_L */
+	[0x14 + N810_HWKEY_SZ] = -KEY_SLASH,	/* Fn + KEY_APOSTROPHE */
+	[0x16 + N810_HWKEY_SZ] = KEY_YEN,		/* Fn + KEY_Z */
+	[0x17 + N810_HWKEY_SZ] = -KEY_6,		/* Fn + KEY_X */
+	[0x18 + N810_HWKEY_SZ] = -KEY_GRAVE,	/* Fn + KEY_C */
+	[0x19 + N810_HWKEY_SZ] = -KEY_5,		/* Fn + KEY_V */
+	[0x1a + N810_HWKEY_SZ] = -KEY_7,		/* Fn + KEY_B */
+	[0x1b + N810_HWKEY_SZ] = -KEY_4,		/* Fn + KEY_N */
+	[0x1c + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_LEFTSHIFT */
+	[0x1f + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_F7 */
+
+	[0x21 + N810_HWKEY_SZ] = KEY_3,		/* Fn + KEY_E */
+	[0x22 + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_SEMICOLON */
+	[0x23 + N810_HWKEY_SZ] = -KEY_MINUS,	/* Fn + KEY_MINUS */
+	[0x24 + N810_HWKEY_SZ] = KEY_EQUAL,	/* Fn + -KEY_EQUAL */
+	[0x2b + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_FN */
+	[0x2c + N810_HWKEY_SZ] = KEY_EURO,		/* Fn + KEY_M */
+	[0x2f + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_F8 */
+
+	[0x31 + N810_HWKEY_SZ] = KEY_4,		/* Fn + KEY_R */
+	[0x32 + N810_HWKEY_SZ] = KEY_ESC,		/* Fn + KEY_RIGHTCTRL */
+	[0x34 + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_SPACE */
+	[0x35 + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_COMMA */
+	[0x37 + N810_HWKEY_SZ] = KEY_PAGEUP,	/* Fn + KEY_UP */
+	[0x3c + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_COMPOSE */
+	[0x3f + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_F6 */
+
+	[0x41 + N810_HWKEY_SZ] = KEY_5,		/* Fn + KEY_T */
+	[0x44 + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_DOT */
+	[0x46 + N810_HWKEY_SZ] = KEY_TAB,		/* Fn + KEY_RIGHT */
+	[0x4f + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_F5 */
+	[0x51 + N810_HWKEY_SZ] = KEY_6,		/* Fn + KEY_Y */
+	[0x53 + N810_HWKEY_SZ] = KEY_PAGEDOWN,	/* Fn + KEY_DOWN */
+	[0x55 + N810_HWKEY_SZ] = -KEY_BACKSLASH,	/* Fn + KEY_ENTER */
+	[0x5f + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_ESC */
+
+	[0x61 + N810_HWKEY_SZ] = KEY_7,		/* Fn + KEY_U */
+	[0x64 + N810_HWKEY_SZ] = KEY_ESC,		/* Fn + KEY_LEFT */
+
+	[0x71 + N810_HWKEY_SZ] = KEY_8,		/* Fn + KEY_I */
+	[0x75 + N810_HWKEY_SZ] = KEY_RESERVED,	/* Fn + KEY_KPENTER */
 };
 
 static struct lm8323_platform_data lm8323_pdata = {
-	.repeat = 0, /* Repeat is handled in userspace for now. */
-	.keymap = rx44_keymap,
-
-	.name = "Internal keyboard",
-	.pwm1_name = "keyboard",
-	.pwm2_name = "cover",
+	.repeat		= 1, /* Change to 0 if handled in userspace */
+	.keymap		= rx44_keymap,
+	.keymap_sz	= ARRAY_SIZE(rx44_keymap),
+	.fn_key		= KEY_FN,
+	.shift_key	= KEY_LEFTSHIFT,
+
+	.name		= "Internal keyboard",
+	.pwm1_name	= "keyboard",
+	.pwm2_name	= "cover",
 };
 #endif
 
diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c
index d472da0..485c19f 100644
--- a/drivers/input/keyboard/lm8323.c
+++ b/drivers/input/keyboard/lm8323.c
@@ -155,7 +155,19 @@ struct lm8323_chip {
 	unsigned		pm_suspend : 1;
 	unsigned		keys_down;
 	char			phys[32];
-	s16			keymap[LM8323_KEYMAP_SIZE];
+
+#define SHIFT_LOCKED		(1 << 5)
+#define SHIFT_HELD		(1 << 4)
+#define SHIFT_ONE		(1 << 3)
+#define FN_LOCKED		(1 << 2)
+#define FN_HELD			(1 << 1)
+#define FN_ONE			(1 << 0)
+	u8			sticky;
+
+	s16			*keymap;
+	u16			keymap_sz;
+	u16			fn_key;
+	u16			shift_key;
 	int			size_x;
 	int			size_y;
 	int			debounce_time;
@@ -279,6 +291,41 @@ static inline int lm8323_ispress(u8 event)
 	return (event & 0x80) ? 1 : 0;
 }
 
+static inline u8 is_sticky(struct lm8323_chip *lm, s16 keycode, int isdown)
+{
+	if (isdown) {
+		if (lm->fn_key && keycode == lm->fn_key) {
+			if (lm->sticky & FN_LOCKED)
+				lm->sticky = 0;
+			else if (lm->sticky & FN_ONE)
+				lm->sticky = FN_LOCKED;
+			else
+				lm->sticky = FN_HELD | FN_ONE;
+			return 1;
+		}
+		if (lm->shift_key && keycode == lm->shift_key) {
+			if (lm->sticky & SHIFT_LOCKED)
+				lm->sticky = 0;
+			else if (lm->sticky & SHIFT_ONE)
+				lm->sticky = SHIFT_LOCKED;
+			else
+				lm->sticky = SHIFT_HELD | SHIFT_ONE;
+			return 1;
+		}
+	} else {
+		if (lm->fn_key && keycode == lm->fn_key) {
+			lm->sticky &= ~FN_HELD;
+			return 1;
+		}
+		if (lm->shift_key && keycode == lm->shift_key) {
+			lm->sticky &= ~SHIFT_HELD;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
 static void process_keys(struct lm8323_chip *lm)
 {
 	u8 event;
@@ -300,10 +347,29 @@ static void process_keys(struct lm8323_chip *lm)
 	key_fifo[ret] = 0;
 
 	while ((event = key_fifo[i])) {
-		u8 key = lm8323_whichkey(event);
+		u16 key = lm8323_whichkey(event);
 		int isdown = lm8323_ispress(event);
 		s16 keycode = lm->keymap[key];
 
+		if (is_sticky(lm, keycode, isdown)) {
+			i++;
+			continue;
+		}
+
+		if (lm->sticky & (FN_LOCKED | FN_HELD | FN_ONE)) {
+			keycode = lm->keymap[key + (lm->keymap_sz / 2)];
+			if (keycode < 0) {
+				lm->sticky |= SHIFT_ONE;
+				keycode = abs(keycode);
+			}
+		}
+
+		if (lm->sticky & (SHIFT_LOCKED | SHIFT_HELD | SHIFT_ONE))
+			input_report_key(lm->idev, KEY_LEFTSHIFT, isdown);
+
+		if (lm->sticky && !isdown)
+			lm->sticky &= ~(SHIFT_ONE | FN_ONE);
+
 		if (likely(keycode > 0)) {
 			debug(&lm->client->dev, "key 0x%02x %s\n", key,
 			      isdown ? "down" : "up");
@@ -707,8 +773,12 @@ static int lm8323_probe(struct i2c_client *client,
 				lm->size_y);
 		lm->size_x = 12;
 	}
+	debug(&client->dev, "Keypad size: %d x %d\n", lm->size_x, lm->size_y);
 
-	debug(&c->dev, "Keypad size: %d x %d\n", lm->size_x, lm->size_y);
+	lm->keymap = lm8323_pdata->keymap;
+	lm->keymap_sz = lm8323_pdata->keymap_sz;
+	lm->fn_key = lm8323_pdata->fn_key;
+	lm->shift_key = lm8323_pdata->shift_key;
 
 	lm->debounce_time = lm8323_pdata->debounce_time;
 	if (lm->debounce_time == 0) /* Default. */
@@ -789,11 +859,9 @@ static int lm8323_probe(struct i2c_client *client,
 
 	lm->keys_down = 0;
 	idev->evbit[0] = BIT(EV_KEY);
-	for (i = 0; i < LM8323_KEYMAP_SIZE; i++) {
-		if (lm8323_pdata->keymap[i] > 0)
-			set_bit(lm8323_pdata->keymap[i], idev->keybit);
-
-		lm->keymap[i] = lm8323_pdata->keymap[i];
+	for (i = 0; i < lm->keymap_sz; i++) {
+		if (lm->keymap[i] != 0)
+			set_bit(abs(lm->keymap[i]), idev->keybit);
 	}
 
 	if (lm8323_pdata->repeat)
diff --git a/include/linux/i2c/lm8323.h b/include/linux/i2c/lm8323.h
index 17d6b33..5ea6cef 100644
--- a/include/linux/i2c/lm8323.h
+++ b/include/linux/i2c/lm8323.h
@@ -9,13 +9,6 @@
 
 #include <linux/types.h>
 
-/*
- * Largest keycode that the chip can send, plus one,
- * so keys can be mapped directly at the index of the
- * LM8323 keycode instead of subtracting one.
- */
-#define LM8323_KEYMAP_SIZE (0x7f + 1)
-
 struct lm8323_platform_data {
 	int debounce_time; /* Time to watch for key bouncing, in ms. */
 	int active_time; /* Idle time until sleep, in ms. */
@@ -23,7 +16,10 @@ struct lm8323_platform_data {
 	int size_x;
 	int size_y;
 	int repeat : 1;
-	const s16 *keymap;
+	s16 *keymap;
+	u16 keymap_sz;
+	u16 fn_key;
+	u16 shift_key;
 
 	char *pwm1_name; /* Device name for PWM1. */
 	char *pwm2_name; /* Device name for PWM2. */
-- 
1.5.3.6

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux