summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorArve Hjønnevåg <arve@android.com>2009-07-24 15:19:56 -0700
committerArve Hjønnevåg <arve@android.com>2009-07-29 14:29:32 -0700
commitc7b88835d33184838b25b785d31ff4af612fbb6a (patch)
tree73ed925407df06b298cce3aff9898a2491bd0bc3 /drivers/input
parente73bc4a9c2dd2d8f9f8188ebf9a5aeca302e2a23 (diff)
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>
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/misc/gpio_axis.c29
-rw-r--r--drivers/input/misc/gpio_event.c89
-rw-r--r--drivers/input/misc/gpio_input.c29
-rw-r--r--drivers/input/misc/gpio_matrix.c43
-rw-r--r--drivers/input/misc/gpio_output.c27
5 files changed, 152 insertions, 65 deletions
diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c
index c801172aa9ee..30b9f5681ce9 100644
--- a/drivers/input/misc/gpio_axis.c
+++ b/drivers/input/misc/gpio_axis.c
@@ -19,7 +19,7 @@
#include <linux/interrupt.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;
};
@@ -87,14 +87,16 @@ static void gpio_event_update_axis(struct gpio_axis_state *as, int report)
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;
}
@@ -106,7 +108,7 @@ static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id)
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;
@@ -133,13 +135,21 @@ int gpio_event_axis_func(struct input_dev *input_dev,
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");
@@ -173,6 +183,7 @@ err_gpio_direction_input_failed:
err_request_gpio_failed:
;
}
+err_bad_device_index:
kfree(as);
*data = NULL;
err_alloc_axis_state_failed:
diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c
index 8b64c1e579ba..139b3600ed7e 100644
--- a/drivers/input/misc/gpio_event.c
+++ b/drivers/input/misc/gpio_event.c
@@ -21,7 +21,7 @@
#include <linux/platform_device.h>
struct gpio_event {
- struct input_dev *input_dev;
+ struct gpio_event_input_devs *input_devs;
const struct gpio_event_platform_data *info;
struct early_suspend early_suspend;
void *state[0];
@@ -31,15 +31,25 @@ static int gpio_input_event(
struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
int i;
+ int devnr;
int ret = 0;
int tmp_ret;
struct gpio_event_info **ii;
struct gpio_event *ip = input_get_drvdata(dev);
+ for (devnr = 0; devnr < ip->input_devs->count; devnr++)
+ if (ip->input_devs->dev[devnr] == dev)
+ break;
+ if (devnr == ip->input_devs->count) {
+ pr_err("gpio_input_event: unknown device %p\n", dev);
+ return -EIO;
+ }
+
for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) {
if ((*ii)->event) {
- tmp_ret = (*ii)->event(ip->input_dev, *ii,
- &ip->state[i], type, code, value);
+ tmp_ret = (*ii)->event(ip->input_devs, *ii,
+ &ip->state[i],
+ devnr, type, code, value);
if (tmp_ret)
ret = tmp_ret;
}
@@ -62,7 +72,9 @@ static int gpio_event_call_all_func(struct gpio_event *ip, int func)
"no function\n");
goto err_no_func;
}
- ret = (*ii)->func(ip->input_dev, *ii, &ip->state[i],
+ if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend)
+ continue;
+ ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i],
func);
if (ret) {
pr_err("gpio_event_probe: function failed\n");
@@ -78,7 +90,9 @@ static int gpio_event_call_all_func(struct gpio_event *ip, int func)
while (i > 0) {
i--;
ii--;
- (*ii)->func(ip->input_dev, *ii, &ip->state[i], func & ~1);
+ if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend)
+ continue;
+ (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1);
err_func_failed:
err_no_func:
;
@@ -108,37 +122,51 @@ static int __init gpio_event_probe(struct platform_device *pdev)
{
int err;
struct gpio_event *ip;
- struct input_dev *input_dev;
struct gpio_event_platform_data *event_info;
+ int dev_count = 1;
+ int i;
+ int registered = 0;
event_info = pdev->dev.platform_data;
if (event_info == NULL) {
pr_err("gpio_event_probe: No pdata\n");
return -ENODEV;
}
- if (event_info->name == NULL ||
- event_info->info == NULL ||
- event_info->info_count == 0) {
+ if ((!event_info->name && !event_info->names[0]) ||
+ !event_info->info || !event_info->info_count) {
pr_err("gpio_event_probe: Incomplete pdata\n");
return -ENODEV;
}
+ if (!event_info->name)
+ while (event_info->names[dev_count])
+ dev_count++;
ip = kzalloc(sizeof(*ip) +
- sizeof(ip->state[0]) * event_info->info_count, GFP_KERNEL);
+ sizeof(ip->state[0]) * event_info->info_count +
+ sizeof(*ip->input_devs) +
+ sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL);
if (ip == NULL) {
err = -ENOMEM;
pr_err("gpio_event_probe: Failed to allocate private data\n");
goto err_kp_alloc_failed;
}
+ ip->input_devs = (void*)&ip->state[event_info->info_count];
platform_set_drvdata(pdev, ip);
- input_dev = input_allocate_device();
- if (input_dev == NULL) {
- err = -ENOMEM;
- pr_err("gpio_event_probe: Failed to allocate input device\n");
- goto err_input_dev_alloc_failed;
+ for (i = 0; i < dev_count; i++) {
+ struct input_dev *input_dev = input_allocate_device();
+ if (input_dev == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: "
+ "Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ input_set_drvdata(input_dev, ip);
+ input_dev->name = event_info->name ?
+ event_info->name : event_info->names[i];
+ input_dev->event = gpio_input_event;
+ ip->input_devs->dev[i] = input_dev;
}
- input_set_drvdata(input_dev, ip);
- ip->input_dev = input_dev;
+ ip->input_devs->count = dev_count;
ip->info = event_info;
if (event_info->power) {
#ifdef CONFIG_HAS_EARLYSUSPEND
@@ -150,18 +178,18 @@ static int __init gpio_event_probe(struct platform_device *pdev)
ip->info->power(ip->info, 1);
}
- input_dev->name = ip->info->name;
- input_dev->event = gpio_input_event;
-
err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT);
if (err)
goto err_call_all_func_failed;
- err = input_register_device(input_dev);
- if (err) {
- pr_err("gpio_event_probe: Unable to register %s input device\n",
- input_dev->name);
- goto err_input_register_device_failed;
+ for (i = 0; i < dev_count; i++) {
+ err = input_register_device(ip->input_devs->dev[i]);
+ if (err) {
+ pr_err("gpio_event_probe: Unable to register %s "
+ "input device\n", ip->input_devs->dev[i]->name);
+ goto err_input_register_device_failed;
+ }
+ registered++;
}
return 0;
@@ -175,8 +203,13 @@ err_call_all_func_failed:
#endif
ip->info->power(ip->info, 0);
}
- input_free_device(input_dev);
+ for (i = 0; i < registered; i++)
+ input_unregister_device(ip->input_devs->dev[i]);
+ for (i = dev_count - 1; i >= registered; i--) {
+ input_free_device(ip->input_devs->dev[i]);
err_input_dev_alloc_failed:
+ ;
+ }
kfree(ip);
err_kp_alloc_failed:
return err;
@@ -185,6 +218,7 @@ err_kp_alloc_failed:
static int gpio_event_remove(struct platform_device *pdev)
{
struct gpio_event *ip = platform_get_drvdata(pdev);
+ int i;
gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
if (ip->info->power) {
@@ -193,7 +227,8 @@ static int gpio_event_remove(struct platform_device *pdev)
#endif
ip->info->power(ip->info, 0);
}
- input_unregister_device(ip->input_dev);
+ for (i = 0; i < ip->input_devs->count; i++)
+ input_unregister_device(ip->input_devs->dev[i]);
kfree(ip);
return 0;
}
diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c
index 7e307f267a2a..0d5c5bdbe14a 100644
--- a/drivers/input/misc/gpio_input.c
+++ b/drivers/input/misc/gpio_input.c
@@ -38,7 +38,7 @@ struct gpio_key_state {
};
struct gpio_input_state {
- struct input_dev *input_dev;
+ struct gpio_event_input_devs *input_devs;
const struct gpio_event_input_info *info;
struct hrtimer timer;
int use_irq;
@@ -126,7 +126,7 @@ static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer)
pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) "
"changed to %d\n", ds->info->type,
key_entry->code, i, key_entry->gpio, pressed);
- input_event(ds->input_dev, ds->info->type,
+ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
key_entry->code, pressed);
}
@@ -193,7 +193,7 @@ static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id)
"(%d) changed to %d\n",
ds->info->type, key_entry->code, keymap_index,
key_entry->gpio, pressed);
- input_event(ds->input_dev, ds->info->type,
+ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
key_entry->code, pressed);
}
return IRQ_HANDLED;
@@ -232,7 +232,7 @@ err_gpio_get_irq_num_failed:
return err;
}
-int gpio_event_input_func(struct input_dev *input_dev,
+int gpio_event_input_func(struct gpio_event_input_devs *input_devs,
struct gpio_event_info *info, void **data, int func)
{
int ret;
@@ -275,13 +275,22 @@ int gpio_event_input_func(struct input_dev *input_dev,
goto err_ds_alloc_failed;
}
ds->debounce_count = di->keymap_size;
- ds->input_dev = input_dev;
+ ds->input_devs = input_devs;
ds->info = di;
wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input");
spin_lock_init(&ds->irq_lock);
for (i = 0; i < di->keymap_size; i++) {
- input_set_capability(input_dev, di->type,
+ int dev = di->keymap[i].dev;
+ if (dev >= input_devs->count) {
+ pr_err("gpio_event_input_func: bad device "
+ "index %d >= %d for key code %d\n",
+ dev, input_devs->count,
+ di->keymap[i].code);
+ ret = -EINVAL;
+ goto err_bad_keymap;
+ }
+ input_set_capability(input_devs->dev[dev], di->type,
di->keymap[i].code);
ds->key_state[i].ds = ds;
ds->key_state[i].debounce = DEBOUNCE_UNKNOWN;
@@ -308,9 +317,10 @@ int gpio_event_input_func(struct input_dev *input_dev,
spin_lock_irqsave(&ds->irq_lock, irqflags);
ds->use_irq = ret == 0;
- pr_info("GPIO Input Driver: Start gpio inputs for %s in %s "
- "mode\n",
- input_dev->name, ret == 0 ? "interrupt" : "polling");
+ pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s "
+ "mode\n", input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "",
+ ret == 0 ? "interrupt" : "polling");
hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ds->timer.function = gpio_event_input_timer_func;
@@ -336,6 +346,7 @@ err_gpio_configure_failed:
err_gpio_request_failed:
;
}
+err_bad_keymap:
wake_lock_destroy(&ds->wake_lock);
kfree(ds);
err_ds_alloc_failed:
diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c
index 671ebb0ce1c6..6d1c1a7ff67d 100644
--- a/drivers/input/misc/gpio_matrix.c
+++ b/drivers/input/misc/gpio_matrix.c
@@ -21,7 +21,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;
@@ -37,9 +37,11 @@ static void clear_phantom_key(struct gpio_kp *kp, int out, int in)
{
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,
@@ -104,8 +106,11 @@ static void report_key(struct gpio_kp *kp, int key_index, int out, int in)
{
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 "
@@ -118,7 +123,7 @@ static void report_key(struct gpio_kp *kp, int key_index, int out, int in)
"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);
}
}
}
@@ -279,7 +284,7 @@ err_gpio_get_irq_num_failed:
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;
@@ -311,12 +316,22 @@ int gpio_event_matrix_func(struct input_dev *input_dev,
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++) {
@@ -366,8 +381,9 @@ int gpio_event_matrix_func(struct input_dev *input_dev,
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)
@@ -398,6 +414,7 @@ err_output_gpio_configure_failed:
err_request_output_gpio_failed:
;
}
+err_bad_keymap:
kfree(kp);
err_kp_alloc_failed:
err_invalid_platform_data:
diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c
index 6f8453c97bd2..2aac2fad0a17 100644
--- a/drivers/input/misc/gpio_output.c
+++ b/drivers/input/misc/gpio_output.c
@@ -18,8 +18,9 @@
#include <linux/gpio_event.h>
int gpio_event_output_event(
- struct input_dev *input_dev, struct gpio_event_info *info, void **data,
- unsigned int type, unsigned int code, int value)
+ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
+ void **data, unsigned int dev, unsigned int type,
+ unsigned int code, int value)
{
int i;
struct gpio_event_output_info *oi;
@@ -29,14 +30,14 @@ int gpio_event_output_event(
if (!(oi->flags & GPIOEDF_ACTIVE_HIGH))
value = !value;
for (i = 0; i < oi->keymap_size; i++)
- if (code == oi->keymap[i].code)
+ if (dev == oi->keymap[i].dev && code == oi->keymap[i].code)
gpio_set_value(oi->keymap[i].gpio, value);
return 0;
}
int gpio_event_output_func(
- struct input_dev *input_dev, struct gpio_event_info *info, void **data,
- int func)
+ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
+ void **data, int func)
{
int ret;
int i;
@@ -48,9 +49,20 @@ int gpio_event_output_func(
if (func == GPIO_EVENT_FUNC_INIT) {
int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH);
- for (i = 0; i < oi->keymap_size; i++)
- input_set_capability(input_dev, oi->type,
+
+ for (i = 0; i < oi->keymap_size; i++) {
+ int dev = oi->keymap[i].dev;
+ if (dev >= input_devs->count) {
+ pr_err("gpio_event_output_func: bad device "
+ "index %d >= %d for key code %d\n",
+ dev, input_devs->count,
+ oi->keymap[i].code);
+ ret = -EINVAL;
+ goto err_bad_keymap;
+ }
+ input_set_capability(input_devs->dev[dev], oi->type,
oi->keymap[i].code);
+ }
for (i = 0; i < oi->keymap_size; i++) {
ret = gpio_request(oi->keymap[i].gpio,
@@ -79,6 +91,7 @@ err_gpio_direction_output_failed:
err_gpio_request_failed:
;
}
+err_bad_keymap:
return ret;
}