summaryrefslogtreecommitdiff
path: root/drivers/input/misc/gpio_event.c
diff options
context:
space:
mode:
authorArve Hjønnevåg <arve@android.com>2009-07-24 15:19:56 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:37:56 -0800
commit029dcd0b6f943f31f86b625c9c8e16d88381acb4 (patch)
tree322d08f08e9ca08d6548b8f02788d860210030c6 /drivers/input/misc/gpio_event.c
parent27dea20c8713e6d28c119e6668a0ea50e17d94b3 (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/misc/gpio_event.c')
-rw-r--r--drivers/input/misc/gpio_event.c89
1 files changed, 62 insertions, 27 deletions
diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c
index 7be0cb5214bb..15939e5e2303 100644
--- a/drivers/input/misc/gpio_event.c
+++ b/drivers/input/misc/gpio_event.c
@@ -22,7 +22,7 @@
#include <linux/slab.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];
@@ -32,15 +32,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;
}
@@ -63,7 +73,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");
@@ -79,7 +91,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:
;
@@ -109,37 +123,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
@@ -151,18 +179,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;
@@ -176,8 +204,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;
@@ -186,6 +219,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) {
@@ -194,7 +228,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;
}