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_axis.c b/drivers/input/misc/gpio_axis.c
index 6178220..0acf4a5 100644
--- a/drivers/input/misc/gpio_axis.c
+++ b/drivers/input/misc/gpio_axis.c
@@ -20,7 +20,7 @@
 #include <linux/slab.h>
 
 struct gpio_axis_state {
-	struct input_dev *input_dev;
+	struct gpio_event_input_devs *input_devs;
 	struct gpio_event_axis_info *info;
 	uint32_t pos;
 };
@@ -88,14 +88,16 @@
 			if (ai->flags & GPIOEAF_PRINT_EVENT)
 				pr_info("axis %d-%d change %d\n",
 					ai->type, ai->code, change);
-			input_report_rel(as->input_dev, ai->code, change);
+			input_report_rel(as->input_devs->dev[ai->dev],
+						ai->code, change);
 		} else {
 			if (ai->flags & GPIOEAF_PRINT_EVENT)
 				pr_info("axis %d-%d now %d\n",
 					ai->type, ai->code, pos);
-			input_event(as->input_dev, ai->type, ai->code, pos);
+			input_event(as->input_devs->dev[ai->dev],
+					ai->type, ai->code, pos);
 		}
-		input_sync(as->input_dev);
+		input_sync(as->input_devs->dev[ai->dev]);
 	}
 	as->pos = pos;
 }
@@ -107,7 +109,7 @@
 	return IRQ_HANDLED;
 }
 
-int gpio_event_axis_func(struct input_dev *input_dev,
+int gpio_event_axis_func(struct gpio_event_input_devs *input_devs,
 			 struct gpio_event_info *info, void **data, int func)
 {
 	int ret;
@@ -134,13 +136,21 @@
 			ret = -ENOMEM;
 			goto err_alloc_axis_state_failed;
 		}
-		as->input_dev = input_dev;
+		as->input_devs = input_devs;
 		as->info = ai;
+		if (ai->dev >= input_devs->count) {
+			pr_err("gpio_event_axis: bad device index %d >= %d "
+				"for %d:%d\n", ai->dev, input_devs->count,
+				ai->type, ai->code);
+			ret = -EINVAL;
+			goto err_bad_device_index;
+		}
 
-		input_set_capability(input_dev, ai->type, ai->code);
+		input_set_capability(input_devs->dev[ai->dev],
+				     ai->type, ai->code);
 		if (ai->type == EV_ABS) {
-			input_set_abs_params(input_dev, ai->code, 0,
-					     ai->decoded_size - 1, 0, 0);
+			input_set_abs_params(input_devs->dev[ai->dev], ai->code,
+					     0, ai->decoded_size - 1, 0, 0);
 		}
 		for (i = 0; i < ai->count; i++) {
 			ret = gpio_request(ai->gpio[i], "gpio_event_axis");
@@ -174,6 +184,7 @@
 err_request_gpio_failed:
 		;
 	}
+err_bad_device_index:
 	kfree(as);
 	*data = NULL;
 err_alloc_axis_state_failed: