diff options
author | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 09:48:20 +0200 |
---|---|---|
committer | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 14:02:16 +0200 |
commit | 2489007e7d740ccbc3e0a202914e243ad5178787 (patch) | |
tree | b8e6380ea7b1da63474ad68a5dba997e01146043 /drivers/input/sec-input-bridge.c | |
parent | 5f67568eb31e3a813c7c52461dcf66ade15fc2e7 (diff) | |
download | kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.zip kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.gz kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.bz2 |
merge opensource jb u5
Change-Id: I1aaec157aa196f3448eff8636134fce89a814cf2
Diffstat (limited to 'drivers/input/sec-input-bridge.c')
-rw-r--r-- | drivers/input/sec-input-bridge.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/drivers/input/sec-input-bridge.c b/drivers/input/sec-input-bridge.c new file mode 100644 index 0000000..efb2187 --- /dev/null +++ b/drivers/input/sec-input-bridge.c @@ -0,0 +1,388 @@ +/* + * sec-input-bridge.c - Specific control input event bridge + * for Samsung Electronics + * + * Copyright (C) 2010 Samsung Electronics + * Yongsul Oh <yongsul96.oh@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <linux/workqueue.h> +#include <linux/mutex.h> + +#include <linux/input/sec-input-bridge.h> + +struct sec_input_bridge { + struct sec_input_bridge_platform_data *pdata; + struct work_struct work; + struct mutex lock; + struct platform_device *dev; + + /* + * Because this flag size is 32 byte, Max map table number is 32. + */ + u32 send_uevent_flag; + u8 *check_index; +}; + +static void input_bridge_set_ids(struct input_device_id *ids, unsigned int type, + unsigned int code) +{ + switch (type) { + case EV_KEY: + ids->flags = INPUT_DEVICE_ID_MATCH_KEYBIT; + __set_bit(code, ids->keybit); + break; + + case EV_REL: + ids->flags = INPUT_DEVICE_ID_MATCH_RELBIT; + __set_bit(code, ids->relbit); + break; + + case EV_ABS: + ids->flags = INPUT_DEVICE_ID_MATCH_ABSBIT; + __set_bit(code, ids->absbit); + break; + + case EV_MSC: + ids->flags = INPUT_DEVICE_ID_MATCH_MSCIT; + __set_bit(code, ids->mscbit); + break; + + case EV_SW: + ids->flags = INPUT_DEVICE_ID_MATCH_SWBIT; + __set_bit(code, ids->swbit); + break; + + case EV_LED: + ids->flags = INPUT_DEVICE_ID_MATCH_LEDBIT; + __set_bit(code, ids->ledbit); + break; + + case EV_SND: + ids->flags = INPUT_DEVICE_ID_MATCH_SNDBIT; + __set_bit(code, ids->sndbit); + break; + + case EV_FF: + ids->flags = INPUT_DEVICE_ID_MATCH_FFBIT; + __set_bit(code, ids->ffbit); + break; + + case EV_PWR: + /* do nothing */ + break; + + default: + printk(KERN_ERR + "input_bridge_set_ids: unknown type %u (code %u)\n", + type, code); + return; + } + + ids->flags |= INPUT_DEVICE_ID_MATCH_EVBIT; + __set_bit(type, ids->evbit); +} + +static void input_bridge_work(struct work_struct *work) +{ + struct sec_input_bridge *bridge = container_of(work, + struct sec_input_bridge, + work); + int state, i; + char env_str[16]; + char *envp[] = { env_str, NULL }; + + mutex_lock(&bridge->lock); + + for (i = 0; i < bridge->pdata->num_map; i++) { + if (bridge->send_uevent_flag & (1 << i)) { + if (bridge->pdata->mmap[i].enable_uevent) { + printk(KERN_ERR + "!!!!sec-input-bridge: OK!!, KEY input matched , now send uevent!!!!\n"); + sprintf(env_str, "%s=%s", + bridge->pdata->mmap[i].uevent_env_str, + bridge->pdata->mmap[i]. + uevent_env_value); + printk(KERN_ERR + "<kobject_uevent_env for sec-input-bridge>, event: %s\n", + env_str); + state = + kobject_uevent_env(&bridge->dev->dev.kobj, + bridge->pdata->mmap[i]. + uevent_action, envp); + if (state != 0) + printk(KERN_ERR + "<error, kobject_uevent_env fail> with action : %d\n", + bridge->pdata->mmap[i]. + uevent_action); + } + if (bridge->pdata->mmap[i].pre_event_func) { + bridge->pdata->mmap[i]. + pre_event_func(bridge->pdata->event_data); + } + + bridge->send_uevent_flag &= ~(1 << i); + } + } + + if (bridge->pdata->lcd_warning_func) + bridge->pdata->lcd_warning_func(); + + mutex_unlock(&bridge->lock); + + printk(KERN_INFO "<sec-input-bridge> all process done !!!!\n"); +} + +static void input_bridge_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + int rep_check; + int i; + + struct input_handler *sec_bridge_handler = handle->handler; + struct sec_input_bridge *sec_bridge = sec_bridge_handler->private; + + rep_check = test_bit(EV_REP, sec_bridge_handler->id_table->evbit); + rep_check = (rep_check << 1) | 1; + + switch (type) { + case EV_KEY: + if (value & rep_check) { + printk(KERN_INFO + "sec-input-bridge: KEY input intercepted, type : %d , code : %d , value %d ", + type, code, value); + + for (i = 0; i < sec_bridge->pdata->num_map; i++) { + if (sec_bridge->pdata->mmap[i]. + mkey_map[(sec_bridge->check_index[i])]. + code == code) { + sec_bridge->check_index[i]++; + if ((sec_bridge->check_index[i]) >= + (sec_bridge->pdata->mmap[i]. + num_mkey)) { + sec_bridge->send_uevent_flag |= + 1 << i; + schedule_work(&sec_bridge-> + work); + sec_bridge->check_index[i] = 0; + } + } else if (sec_bridge->pdata->mmap[i]. + mkey_map[0].code == code) { + sec_bridge->check_index[i] = 1; + } else { + sec_bridge->check_index[i] = 0; + } + } + } + break; + + default: + break; + } + +} + +static int input_bridge_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "sec-input-bridge"; + + error = input_register_handle(handle); + if (error) { + printk(KERN_ERR + "sec-input-bridge: Failed to register input bridge handler, " + "error %d\n", error); + kfree(handle); + return error; + } + + error = input_open_device(handle); + if (error) { + printk(KERN_ERR + "sec-input-bridge: Failed to open input bridge device, " + "error %d\n", error); + input_unregister_handle(handle); + kfree(handle); + return error; + } + + return 0; +} + +static void input_bridge_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static struct input_handler input_bridge_handler = { + .event = input_bridge_event, + .connect = input_bridge_connect, + .disconnect = input_bridge_disconnect, + .name = "sec-input-bridge", +}; + +static int __devinit sec_input_bridge_probe(struct platform_device *dev) +{ + struct sec_input_bridge_platform_data *pdata; + struct sec_input_bridge *bridge; + struct input_device_id *input_bridge_ids; + + int state; + int i, j; + int k = 0; + int total_num_key = 0; + + pdata = dev->dev.platform_data; + if (!pdata) { + dev_err(&dev->dev, "No samsung input bridge platform data.\n"); + return -EINVAL; + } + + if (pdata->num_map == 0) { + dev_err(&dev->dev, + "No samsung input bridge platform data. num_mkey == 0\n"); + return -EINVAL; + } + + bridge = kzalloc(sizeof(struct sec_input_bridge), GFP_KERNEL); + if (!bridge) + return -ENOMEM; + + bridge->check_index = kzalloc(sizeof(u8) * pdata->num_map, GFP_KERNEL); + if (!bridge->check_index) { + kfree(bridge); + return -ENOMEM; + } + + bridge->send_uevent_flag = 0; + + for (i = 0; i < pdata->num_map; i++) + total_num_key += pdata->mmap[i].num_mkey; + + input_bridge_ids = + kzalloc(sizeof(struct input_device_id[(total_num_key + 1)]), + GFP_KERNEL); + if (!input_bridge_ids) { + kfree(bridge->check_index); + kfree(bridge); + return -ENOMEM; + } + memset(input_bridge_ids, 0x00, sizeof(input_bridge_ids)); + + for (i = 0; i < pdata->num_map; i++) { + for (j = 0; j < pdata->mmap[i].num_mkey; j++) { + input_bridge_set_ids(&input_bridge_ids[k++], + pdata->mmap[i].mkey_map[j].type, + pdata->mmap[i].mkey_map[j].code); + } + } + + input_bridge_handler.private = bridge; + input_bridge_handler.id_table = input_bridge_ids; + + state = input_register_handler(&input_bridge_handler); + if (state) + goto input_register_fail; + + bridge->dev = dev; + bridge->pdata = pdata; + + INIT_WORK(&bridge->work, input_bridge_work); + mutex_init(&bridge->lock); + + platform_set_drvdata(dev, bridge); + + return 0; + + input_register_fail: + kfree(bridge->check_index); + kfree(bridge); + kfree(input_bridge_ids); + + return state; + +} + +static int __devexit sec_input_bridge_remove(struct platform_device *dev) +{ + struct sec_input_bridge *bridge = platform_get_drvdata(dev); + + cancel_work_sync(&bridge->work); + mutex_destroy(&bridge->lock); + kfree(input_bridge_handler.id_table); + input_unregister_handler(&input_bridge_handler); + kfree(bridge->check_index); + kfree(bridge); + platform_set_drvdata(dev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int sec_input_bridge_suspend(struct platform_device *dev, + pm_message_t state) +{ + return 0; +} + +static int sec_input_bridge_resume(struct platform_device *dev) +{ + return 0; +} +#else +#define sec_input_bridge_suspend NULL +#define sec_input_bridge_resume NULL +#endif + +static struct platform_driver sec_input_bridge_driver = { + .probe = sec_input_bridge_probe, + .remove = __devexit_p(sec_input_bridge_remove), + .suspend = sec_input_bridge_suspend, + .resume = sec_input_bridge_resume, + .driver = { + .name = "samsung_input_bridge", + }, +}; + +static int __init sec_input_bridge_init(void) +{ + return platform_driver_register(&sec_input_bridge_driver); +} + +static void __exit sec_input_bridge_exit(void) +{ + platform_driver_unregister(&sec_input_bridge_driver); +} + +module_init(sec_input_bridge_init); +module_exit(sec_input_bridge_exit); + +MODULE_AUTHOR("Yongsul Oh <yongsul96.oh@samsung.com>"); +MODULE_DESCRIPTION("Input Event -> Specific Control Bridge"); +MODULE_LICENSE("GPL"); |