Input: gpio_event: Allow multiple input devices per gpio_event device

This is needed to support devices that put non-keyboard buttons in
the keyboard matrix. For instance several devices put the trackball
button in the keyboard matrix. In this case BTN_MOUSE should be
reported from the same input device as REL_X/Y.

It is also useful for devices that have multiple logical keyboard in
the same matrix. The HTC dream has a menu key on the external keyboard
and another menu key on the slide-out keyboard. With a single input
device only one of these menu keys can be mapped to KEY_MENU.

Signed-off-by: Arve Hjønnevåg <arve@android.com>
diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c
index 10b4411..adb00ab 100644
--- a/drivers/input/misc/gpio_matrix.c
+++ b/drivers/input/misc/gpio_matrix.c
@@ -22,7 +22,7 @@
 #include <linux/wakelock.h>
 
 struct gpio_kp {
-	struct input_dev *input_dev;
+	struct gpio_event_input_devs *input_devs;
 	struct gpio_event_matrix_info *keypad_info;
 	struct hrtimer timer;
 	struct wake_lock wake_lock;
@@ -38,9 +38,11 @@
 {
 	struct gpio_event_matrix_info *mi = kp->keypad_info;
 	int key_index = out * mi->ninputs + in;
-	unsigned short keycode = mi->keymap[key_index];;
+	unsigned short keyentry = mi->keymap[key_index];
+	unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+	unsigned short dev = keyentry >> MATRIX_CODE_BITS;
 
-	if (!test_bit(keycode, kp->input_dev->key)) {
+	if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) {
 		if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
 			pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
 				"cleared\n", keycode, out, in,
@@ -105,8 +107,11 @@
 {
 	struct gpio_event_matrix_info *mi = kp->keypad_info;
 	int pressed = test_bit(key_index, kp->keys_pressed);
-	unsigned short keycode = mi->keymap[key_index];
-	if (pressed != test_bit(keycode, kp->input_dev->key)) {
+	unsigned short keyentry = mi->keymap[key_index];
+	unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+	unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+
+	if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) {
 		if (keycode == KEY_RESERVED) {
 			if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS)
 				pr_info("gpiomatrix: unmapped key, %d-%d "
@@ -119,7 +124,7 @@
 					"changed to %d\n", keycode,
 					out, in, mi->output_gpios[out],
 					mi->input_gpios[in], pressed);
-			input_report_key(kp->input_dev, keycode, pressed);
+			input_report_key(kp->input_devs->dev[dev], keycode, pressed);
 		}
 	}
 }
@@ -280,7 +285,7 @@
 	return err;
 }
 
-int gpio_event_matrix_func(struct input_dev *input_dev,
+int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs,
 	struct gpio_event_info *info, void **data, int func)
 {
 	int i;
@@ -312,12 +317,22 @@
 			pr_err("gpiomatrix: Failed to allocate private data\n");
 			goto err_kp_alloc_failed;
 		}
-		kp->input_dev = input_dev;
+		kp->input_devs = input_devs;
 		kp->keypad_info = mi;
-		set_bit(EV_KEY, input_dev->evbit);
 		for (i = 0; i < key_count; i++) {
-			if (mi->keymap[i] && mi->keymap[i] <= KEY_MAX)
-				set_bit(mi->keymap[i], input_dev->keybit);
+			unsigned short keyentry = mi->keymap[i];
+			unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+			unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+			if (dev >= input_devs->count) {
+				pr_err("gpiomatrix: bad device index %d >= "
+					"%d for key code %d\n",
+					dev, input_devs->count, keycode);
+				err = -EINVAL;
+				goto err_bad_keymap;
+			}
+			if (keycode && keycode <= KEY_MAX)
+				input_set_capability(input_devs->dev[dev],
+							EV_KEY, keycode);
 		}
 
 		for (i = 0; i < mi->noutputs; i++) {
@@ -367,8 +382,9 @@
 		err = gpio_keypad_request_irqs(kp);
 		kp->use_irq = err == 0;
 
-		pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for %s "
-			"in %s mode\n", input_dev->name,
+		pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for "
+			"%s%s in %s mode\n", input_devs->dev[0]->name,
+			(input_devs->count > 1) ? "..." : "",
 			kp->use_irq ? "interrupt" : "polling");
 
 		if (kp->use_irq)
@@ -399,6 +415,7 @@
 err_request_output_gpio_failed:
 		;
 	}
+err_bad_keymap:
 	kfree(kp);
 err_kp_alloc_failed:
 err_invalid_platform_data: