diff options
author | Arve Hjønnevåg <arve@android.com> | 2009-07-24 15:19:56 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:37:56 -0800 |
commit | 029dcd0b6f943f31f86b625c9c8e16d88381acb4 (patch) | |
tree | 322d08f08e9ca08d6548b8f02788d860210030c6 /drivers/input/misc/gpio_event.c | |
parent | 27dea20c8713e6d28c119e6668a0ea50e17d94b3 (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.c | 89 |
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; } |