5.7 input子系统
1. input子系统介绍
input子系统是用于管理输入设备(按键、键盘、鼠标、触摸屏等设备)的系统。
2. input子系统框架
input子系统分为三层,从上到下分别为输入事件处理层、输入核心层、输入设备驱动层。
2.1 输入事件处理层
(1)源码路径:
① /drivers/input/evdev.c
② /drivers/input/joydev.c
(2)分析evdev.c
① 模块注册:
struct input_handler {void *private;void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);bool (*match)(struct input_handler *handler, struct input_dev *dev);int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);void (*disconnect)(struct input_handle *handle);void (*start)(struct input_handle *handle);const struct file_operations *fops;int minor;const char *name;const struct input_device_id *id_table;struct list_head h_list;struct list_head node;
};static const struct input_device_id evdev_ids[] = {{ .driver_info = 1 }, /* Matches all devices */{ }, /* Terminating zero entry */
};MODULE_DEVICE_TABLE(input, evdev_ids);static struct input_handler evdev_handler = {.event = evdev_event,.connect = evdev_connect,.disconnect = evdev_disconnect,.fops = &evdev_fops,.minor = EVDEV_MINOR_BASE,.name = "evdev",.id_table = evdev_ids,
};static int __init evdev_init(void)
{return input_register_handler(&evdev_handler);
}static void __exit evdev_exit(void)
{input_unregister_handler(&evdev_handler);
}module_init(evdev_init);
module_exit(evdev_exit);
② evdev_connect:
/** Create new evdev device. Note that input core serializes calls* to connect and disconnect so we don't need to lock evdev_table here.*/
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{struct evdev *evdev;int minor;int error;for (minor = 0; minor < EVDEV_MINORS; minor++)if (!evdev_table[minor])break;if (minor == EVDEV_MINORS) {printk(KERN_ERR "evdev: no more free evdev devices\n");return -ENFILE;}evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);if (!evdev)return -ENOMEM;INIT_LIST_HEAD(&evdev->client_list);spin_lock_init(&evdev->client_lock);mutex_init(&evdev->mutex);init_waitqueue_head(&evdev->wait);dev_set_name(&evdev->dev, "event%d", minor);evdev->exist = 1;evdev->minor = minor;evdev->handle.dev = input_get_device(dev);evdev->handle.name = dev_name(&evdev->dev);evdev->handle.handler = handler;evdev->handle.private = evdev;evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);evdev->dev.class = &input_class;evdev->dev.parent = &dev->dev;evdev->dev.release = evdev_free;device_initialize(&evdev->dev);error = input_register_handle(&evdev->handle);if (error)goto err_free_evdev;error = evdev_install_chrdev(evdev);if (error)goto err_unregister_handle;error = device_add(&evdev->dev);if (error)goto err_cleanup_evdev;return 0;err_cleanup_evdev:evdev_cleanup(evdev);err_unregister_handle:input_unregister_handle(&evdev->handle);err_free_evdev:put_device(&evdev->dev);return error;
}static void evdev_disconnect(struct input_handle *handle)
{struct evdev *evdev = handle->private;device_del(&evdev->dev);evdev_cleanup(evdev);input_unregister_handle(handle);put_device(&evdev->dev);
}
③ evdev_event:
static void evdev_pass_event(struct evdev_client *client,struct input_event *event)
{/** Interrupts are disabled, just acquire the lock*/spin_lock(&client->buffer_lock);wake_lock_timeout(&client->wake_lock, 5 * HZ);client->buffer[client->head++] = *event;client->head &= EVDEV_BUFFER_SIZE - 1;spin_unlock(&client->buffer_lock);if (event->type == EV_SYN)kill_fasync(&client->fasync, SIGIO, POLL_IN);
}/** Pass incoming event to all connected clients.*/
static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
{struct evdev *evdev = handle->private;struct evdev_client *client;struct input_event event;struct timespec ts;ktime_get_ts(&ts);event.time.tv_sec = ts.tv_sec;event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;event.type = type;event.code = code;event.value = value;rcu_read_lock();client = rcu_dereference(evdev->grab);if (client)evdev_pass_event(client, &event);elselist_for_each_entry_rcu(client, &evdev->client_list, node)evdev_pass_event(client, &event);rcu_read_unlock();wake_up_interruptible(&evdev->wait);
}
2.2 输入核心层
输入核心层源码位于/drivers/input/input.c中
2.2.1 模块注册
(1)input_init:
static int __init input_init(void)
{int err;input_init_abs_bypass();err = class_register(&input_class); // 类注册if (err) {printk(KERN_ERR "input: unable to register input_dev class\n");return err;}err = input_proc_init(); // input子系统的proc文件系统初始化if (err)goto fail1;err = register_chrdev(INPUT_MAJOR, "input", &input_fops); // 字符设备驱动注册if (err) {printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);goto fail2;}return 0;fail2: input_proc_exit();fail1: class_unregister(&input_class);return err;
}static void __exit input_exit(void)
{input_proc_exit();unregister_chrdev(INPUT_MAJOR, "input");class_unregister(&input_class);
}subsys_initcall(input_init);
module_exit(input_exit);
(2)input_proc_init:
static int __init input_proc_init(void)
{struct proc_dir_entry *entry;proc_bus_input_dir = proc_mkdir("bus/input", NULL);if (!proc_bus_input_dir)return -ENOMEM;entry = proc_create("devices", 0, proc_bus_input_dir,&input_devices_fileops);if (!entry)goto fail1;entry = proc_create("handlers", 0, proc_bus_input_dir,&input_handlers_fileops);if (!entry)goto fail2;return 0;fail2: remove_proc_entry("devices", proc_bus_input_dir);fail1: remove_proc_entry("bus/input", NULL);return -ENOMEM;
}static void input_proc_exit(void)
{remove_proc_entry("devices", proc_bus_input_dir);remove_proc_entry("handlers", proc_bus_input_dir);remove_proc_entry("bus/input", NULL);
}
(3)input_fops:
static int input_open_file(struct inode *inode, struct file *file)
{struct input_handler *handler;const struct file_operations *old_fops, *new_fops = NULL;int err;err = mutex_lock_interruptible(&input_mutex);if (err)return err;/* No load-on-demand here? */handler = input_table[iminor(inode) >> 5];if (handler)new_fops = fops_get(handler->fops);mutex_unlock(&input_mutex);/** That's _really_ odd. Usually NULL ->open means "nothing special",* not "no device". Oh, well...*/if (!new_fops || !new_fops->open) {fops_put(new_fops);err = -ENODEV;goto out;}old_fops = file->f_op;file->f_op = new_fops;err = new_fops->open(inode, file);if (err) {fops_put(file->f_op);file->f_op = fops_get(old_fops);}fops_put(old_fops);
out:return err;
}static const struct file_operations input_fops = {.owner = THIS_MODULE,.open = input_open_file,
};
2.2.2 设备驱动层接口函数
(1)input_allocate_device:
/*** input_allocate_device - allocate memory for new input device** Returns prepared struct input_dev or NULL.** NOTE: Use input_free_device() to free devices that have not been* registered; input_unregister_device() should be used for already* registered devices.*/
struct input_dev *input_allocate_device(void)
{struct input_dev *dev;dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);if (dev) {dev->dev.type = &input_dev_type;dev->dev.class = &input_class;device_initialize(&dev->dev);mutex_init(&dev->mutex);spin_lock_init(&dev->event_lock);INIT_LIST_HEAD(&dev->h_list);INIT_LIST_HEAD(&dev->node);__module_get(THIS_MODULE);}return dev;
}
EXPORT_SYMBOL(input_allocate_device);/*** input_free_device - free memory occupied by input_dev structure* @dev: input device to free** This function should only be used if input_register_device()* was not called yet or if it failed. Once device was registered* use input_unregister_device() and memory will be freed once last* reference to the device is dropped.** Device should be allocated by input_allocate_device().** NOTE: If there are references to the input device then memory* will not be freed until last reference is dropped.*/
void input_free_device(struct input_dev *dev)
{if (dev)input_put_device(dev);
}
EXPORT_SYMBOL(input_free_device);
(2)input_set_capability:
/*** input_set_capability - mark device as capable of a certain event* @dev: device that is capable of emitting or accepting event* @type: type of the event (EV_KEY, EV_REL, etc...)* @code: event code** In addition to setting up corresponding bit in appropriate capability* bitmap the function also adjusts dev->evbit.*/
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{switch (type) {case EV_KEY:__set_bit(code, dev->keybit);break;case EV_REL:__set_bit(code, dev->relbit);break;case EV_ABS:__set_bit(code, dev->absbit);break;case EV_MSC:__set_bit(code, dev->mscbit);break;case EV_SW:__set_bit(code, dev->swbit);break;case EV_LED:__set_bit(code, dev->ledbit);break;case EV_SND:__set_bit(code, dev->sndbit);break;case EV_FF:__set_bit(code, dev->ffbit);break;case EV_PWR:/* do nothing */break;default:printk(KERN_ERR"input_set_capability: unknown type %u (code %u)\n",type, code);dump_stack();return;}__set_bit(type, dev->evbit);
}
EXPORT_SYMBOL(input_set_capability);
(3)input_register_device:
/*** input_register_device - register device with input core* @dev: device to be registered** This function registers device with input core. The device must be* allocated with input_allocate_device() and all it's capabilities* set up before registering.* If function fails the device must be freed with input_free_device().* Once device has been successfully registered it can be unregistered* with input_unregister_device(); input_free_device() should not be* called in this case.*/
int input_register_device(struct input_dev *dev)
{static atomic_t input_no = ATOMIC_INIT(0);struct input_handler *handler;const char *path;int error;/* Every input device generates EV_SYN/SYN_REPORT events. */__set_bit(EV_SYN, dev->evbit);/* KEY_RESERVED is not supposed to be transmitted to userspace. */__clear_bit(KEY_RESERVED, dev->keybit);/* Make sure that bitmasks not mentioned in dev->evbit are clean. */input_cleanse_bitmasks(dev);/** If delay and period are pre-set by the driver, then autorepeating* is handled by the driver itself and we don't do it in input.c.*/init_timer(&dev->timer);if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {dev->timer.data = (long) dev;dev->timer.function = input_repeat_key;dev->rep[REP_DELAY] = 250;dev->rep[REP_PERIOD] = 33;}if (!dev->getkeycode)dev->getkeycode = input_default_getkeycode;if (!dev->setkeycode)dev->setkeycode = input_default_setkeycode;dev_set_name(&dev->dev, "input%ld",(unsigned long) atomic_inc_return(&input_no) - 1);error = device_add(&dev->dev);if (error)return error;path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);printk(KERN_INFO "input: %s as %s\n",dev->name ? dev->name : "Unspecified device", path ? path : "N/A");kfree(path);error = mutex_lock_interruptible(&input_mutex);if (error) {device_del(&dev->dev);return error;}list_add_tail(&dev->node, &input_dev_list);list_for_each_entry(handler, &input_handler_list, node)input_attach_handler(dev, handler); // hander和device匹配input_wakeup_procfs_readers();mutex_unlock(&input_mutex);return 0;
}
EXPORT_SYMBOL(input_register_device);/*** input_unregister_device - unregister previously registered device* @dev: device to be unregistered** This function unregisters an input device. Once device is unregistered* the caller should not try to access it as it may get freed at any moment.*/
void input_unregister_device(struct input_dev *dev)
{struct input_handle *handle, *next;input_disconnect_device(dev);mutex_lock(&input_mutex);list_for_each_entry_safe(handle, next, &dev->h_list, d_node)handle->handler->disconnect(handle);WARN_ON(!list_empty(&dev->h_list));del_timer_sync(&dev->timer);list_del_init(&dev->node);input_wakeup_procfs_readers();mutex_unlock(&input_mutex);device_unregister(&dev->dev);
}
EXPORT_SYMBOL(input_unregister_device);
2.2.3 input_register_device中handler和device的匹配
#define MATCH_BIT(bit, max) \for (i = 0; i < BITS_TO_LONGS(max); i++) \if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \break; \if (i != BITS_TO_LONGS(max)) \continue;static const struct input_device_id *input_match_device(struct input_handler *handler,struct input_dev *dev)
{const struct input_device_id *id;int i;for (id = handler->id_table; id->flags || id->driver_info; id++) {if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)if (id->bustype != dev->id.bustype)continue;if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)if (id->vendor != dev->id.vendor)continue;if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)if (id->product != dev->id.product)continue;if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)if (id->version != dev->id.version)continue;MATCH_BIT(evbit, EV_MAX);MATCH_BIT(keybit, KEY_MAX);MATCH_BIT(relbit, REL_MAX);MATCH_BIT(absbit, ABS_MAX);MATCH_BIT(mscbit, MSC_MAX);MATCH_BIT(ledbit, LED_MAX);MATCH_BIT(sndbit, SND_MAX);MATCH_BIT(ffbit, FF_MAX);MATCH_BIT(swbit, SW_MAX);if (!handler->match || handler->match(handler, dev))return id;}return NULL;
}static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{const struct input_device_id *id;int error;id = input_match_device(handler, dev);if (!id)return -ENODEV;error = handler->connect(handler, dev, id);if (error && error != -ENODEV)printk(KERN_ERR"input: failed to attach handler %s to device %s, ""error: %d\n",handler->name, kobject_name(&dev->dev.kobj), error);return error;
}
2.2.4 事件处理层驱动注册接口
/*** input_register_handle - register a new input handle* @handle: handle to register** This function puts a new input handle onto device's* and handler's lists so that events can flow through* it once it is opened using input_open_device().** This function is supposed to be called from handler's* connect() method.*/
int input_register_handle(struct input_handle *handle)
{struct input_handler *handler = handle->handler;struct input_dev *dev = handle->dev;int error;/** We take dev->mutex here to prevent race with* input_release_device().*/error = mutex_lock_interruptible(&dev->mutex);if (error)return error;/** Filters go to the head of the list, normal handlers* to the tail.*/if (handler->filter)list_add_rcu(&handle->d_node, &dev->h_list);elselist_add_tail_rcu(&handle->d_node, &dev->h_list);mutex_unlock(&dev->mutex);/** Since we are supposed to be called from ->connect()* which is mutually exclusive with ->disconnect()* we can't be racing with input_unregister_handle()* and so separate lock is not needed here.*/list_add_tail_rcu(&handle->h_node, &handler->h_list);if (handler->start)handler->start(handle);return 0;
}
EXPORT_SYMBOL(input_register_handle);/*** input_unregister_handle - unregister an input handle* @handle: handle to unregister** This function removes input handle from device's* and handler's lists.** This function is supposed to be called from handler's* disconnect() method.*/
void input_unregister_handle(struct input_handle *handle)
{struct input_dev *dev = handle->dev;list_del_rcu(&handle->h_node);/** Take dev->mutex to prevent race with input_release_device().*/mutex_lock(&dev->mutex);list_del_rcu(&handle->d_node);mutex_unlock(&dev->mutex);synchronize_rcu();
}
EXPORT_SYMBOL(input_unregister_handle);
2.3 输入设备驱动层
2.3.1 分析按键驱动button-x210.c
(1)模块装载:
static struct platform_driver s3c_button_device_driver = {.probe = s3c_button_probe,.remove = s3c_button_remove,.suspend = s3c_button_suspend,.resume = s3c_button_resume,.driver = {.name = "s3c-button",.owner = THIS_MODULE,}
};static struct platform_device s3c_device_button = {.name = "s3c-button",.id = -1,
};static int __init s3c_button_init(void)
{ platform_device_register(&s3c_device_button); return platform_driver_register(&s3c_button_device_driver);
}static void __exit s3c_button_exit(void)
{platform_driver_unregister(&s3c_button_device_driver);platform_device_unregister(&s3c_device_button);
}module_init(s3c_button_init);
module_exit(s3c_button_exit);
(2)s3c_button_probe
static int s3c_button_probe(struct platform_device *pdev)
{int i;int ret;#if defined(CONFIG_X210) && (CONFIG_X210 > 0)/* gph0_1 (power) */ret = gpio_request(S5PV210_GPH0(1), "GPH0");if(ret)printk("button-x210: request gpio GPH0(1) fail");s3c_gpio_setpull(S5PV210_GPH0(1), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH0(1), S3C_GPIO_SFN(0));s3c_button_history[0] = gpio_get_value(S5PV210_GPH0(1));/* gph2_0 (up) */ret = gpio_request(S5PV210_GPH2(0), "GPH2");if(ret)printk("button-x210: request gpio GPH2(0) fail");s3c_gpio_setpull(S5PV210_GPH2(0), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH2(0), S3C_GPIO_SFN(0));s3c_button_history[1] = gpio_get_value(S5PV210_GPH2(0));/* gph0_3 (down) */ret = gpio_request(S5PV210_GPH0(3), "GPH0");if(ret)printk("button-x210: request gpio GPH0(3) fail");s3c_gpio_setpull(S5PV210_GPH0(3), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH0(3), S3C_GPIO_SFN(0));s3c_button_history[2] = gpio_get_value(S5PV210_GPH0(3));/* gph0_2 (left) */ret = gpio_request(S5PV210_GPH0(2), "GPH0");if(ret)printk("button-x210: request gpio GPH0(2) fail");s3c_gpio_setpull(S5PV210_GPH0(2), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0));s3c_button_history[3] = gpio_get_value(S5PV210_GPH0(2));/* gph2_1 (right) */ret = gpio_request(S5PV210_GPH2(1), "GPH1");if(ret)printk("button-x210: request gpio GPH2(1) fail");s3c_gpio_setpull(S5PV210_GPH2(1), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH2(1), S3C_GPIO_SFN(0));s3c_button_history[4] = gpio_get_value(S5PV210_GPH2(1));/* gph2_3 (menu) */ret = gpio_request(S5PV210_GPH2(3), "GPH2");if(ret)printk("button-x210: request gpio GPH2(3) fail");s3c_gpio_setpull(S5PV210_GPH2(3), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH2(3), S3C_GPIO_SFN(0));s3c_button_history[5] = gpio_get_value(S5PV210_GPH2(3));/* gph2_2 (back) */ret = gpio_request(S5PV210_GPH2(2), "GPH2");if(ret)printk("button-x210: request gpio GPH2(2) fail");s3c_gpio_setpull(S5PV210_GPH2(2), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH2(2), S3C_GPIO_SFN(0));s3c_button_history[6] = gpio_get_value(S5PV210_GPH2(2));
#elif defined(CONFIG_I210) && (CONFIG_I210 > 0)/* gph0_1 (power) */ret = gpio_request(S5PV210_GPH0(1), "GPH0");if(ret)printk("button-x210: request gpio GPH0(1) fail");s3c_gpio_setpull(S5PV210_GPH0(1), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH0(1), S3C_GPIO_SFN(0));s3c_button_history[0] = gpio_get_value(S5PV210_GPH0(1));/* gph0_4 (up) */ret = gpio_request(S5PV210_GPH0(4), "GPH0");if(ret)printk("button-x210: request gpio GPH0(4) fail");s3c_gpio_setpull(S5PV210_GPH0(4), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH0(4), S3C_GPIO_SFN(0));s3c_button_history[1] = gpio_get_value(S5PV210_GPH0(4));/* gph0_3 (down) */ret = gpio_request(S5PV210_GPH0(3), "GPH0");if(ret)printk("button-x210: request gpio GPH0(3) fail");s3c_gpio_setpull(S5PV210_GPH0(3), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH0(3), S3C_GPIO_SFN(0));s3c_button_history[2] = gpio_get_value(S5PV210_GPH0(3));/* gph0_2 (left) */ret = gpio_request(S5PV210_GPH0(2), "GPH0");if(ret)printk("button-x210: request gpio GPH0(2) fail");s3c_gpio_setpull(S5PV210_GPH0(2), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0));s3c_button_history[3] = gpio_get_value(S5PV210_GPH0(2));/* gph1_0 (right) */ret = gpio_request(S5PV210_GPH1(0), "GPH1");if(ret)printk("button-x210: request gpio GPH1(0) fail");s3c_gpio_setpull(S5PV210_GPH1(0), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH1(0), S3C_GPIO_SFN(0));s3c_button_history[4] = gpio_get_value(S5PV210_GPH1(0));/* gph1_3 (menu) */ret = gpio_request(S5PV210_GPH1(3), "GPH1");if(ret)printk("button-x210: request gpio GPH1(3) fail");s3c_gpio_setpull(S5PV210_GPH1(3), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH1(3), S3C_GPIO_SFN(0));s3c_button_history[5] = gpio_get_value(S5PV210_GPH1(3));/* gph1_1 (back) */ret = gpio_request(S5PV210_GPH1(1), "GPH1");if(ret)printk("button-x210: request gpio GPH1(1) fail");s3c_gpio_setpull(S5PV210_GPH1(1), S3C_GPIO_PULL_UP);s3c_gpio_cfgpin(S5PV210_GPH1(1), S3C_GPIO_SFN(0));s3c_button_history[6] = gpio_get_value(S5PV210_GPH1(1));
#endifinput = input_allocate_device();if(!input) return -ENOMEM;set_bit(EV_KEY, input->evbit);for(i = 0; i < MAX_BUTTON_CNT; i++)set_bit(s3c_Keycode[i], input->keybit);input->name = "s3c-button";input->phys = "s3c-button/input0";input->id.bustype = BUS_HOST;input->id.vendor = 0x0001;input->id.product = 0x0001;input->id.version = 0x0100;input->keycode = s3c_Keycode;if(input_register_device(input) != 0){printk("s3c-button input register device fail!!\n");input_free_device(input);return -ENODEV;}/* Scan timer init */init_timer(&timer);timer.function = s3cbutton_timer_handler;timer.expires = jiffies + (HZ/100);add_timer(&timer);printk("s3c button Initialized!!\n");return 0;
}
(3)input_dev
struct input_dev {const char *name;const char *phys;const char *uniq;struct input_id id;unsigned long evbit[BITS_TO_LONGS(EV_CNT)];unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];unsigned long relbit[BITS_TO_LONGS(REL_CNT)];unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];unsigned long swbit[BITS_TO_LONGS(SW_CNT)];unsigned int keycodemax;unsigned int keycodesize;void *keycode;int (*setkeycode)(struct input_dev *dev,unsigned int scancode, unsigned int keycode);int (*getkeycode)(struct input_dev *dev,unsigned int scancode, unsigned int *keycode);struct ff_device *ff;unsigned int repeat_key;struct timer_list timer;int sync;int abs[ABS_CNT];int rep[REP_MAX + 1];unsigned long key[BITS_TO_LONGS(KEY_CNT)];unsigned long led[BITS_TO_LONGS(LED_CNT)];unsigned long snd[BITS_TO_LONGS(SND_CNT)];unsigned long sw[BITS_TO_LONGS(SW_CNT)];int absmax[ABS_CNT];int absmin[ABS_CNT];int absfuzz[ABS_CNT];int absflat[ABS_CNT];int absres[ABS_CNT];int (*open)(struct input_dev *dev);void (*close)(struct input_dev *dev);int (*flush)(struct input_dev *dev, struct file *file);int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);struct input_handle *grab;spinlock_t event_lock;struct mutex mutex;unsigned int users;bool going_away;struct device dev;struct list_head h_list;struct list_head node;
};
#define to_input_dev(d) container_of(d, struct input_dev, dev)
(4)定时器
static void s3cbutton_timer_handler(unsigned long data)
{int flag;/* power */flag = gpio_get_value(S5PV210_GPH0(1));if(flag != s3c_button_history[0]){if(flag){input_report_key(input, s3c_Keycode[0], 0);}else{input_report_key(input, s3c_Keycode[0], 1);}s3c_button_history[0] = flag;}/* up */flag = gpio_get_value(S5PV210_GPH2(0));if(flag != s3c_button_history[1]){if(flag){ input_report_key(input, s3c_Keycode[1], 0);}else{input_report_key(input, s3c_Keycode[1], 1);}s3c_button_history[1] = flag;}/* down */flag = gpio_get_value(S5PV210_GPH0(3));if(flag != s3c_button_history[2]){if(flag){input_report_key(input, s3c_Keycode[2], 0);}else{input_report_key(input, s3c_Keycode[2], 1);}s3c_button_history[2] = flag;}/* left */flag = gpio_get_value(S5PV210_GPH0(2));if(flag != s3c_button_history[3]){if(flag){input_report_key(input, s3c_Keycode[3], 0);}else{input_report_key(input, s3c_Keycode[3], 1);}s3c_button_history[3] = flag;}/* right */flag = gpio_get_value(S5PV210_GPH2(1));if(flag != s3c_button_history[4]){if(flag){input_report_key(input, s3c_Keycode[4], 0);}else{input_report_key(input, s3c_Keycode[4], 1);}s3c_button_history[4] = flag;}/* menu */flag = gpio_get_value(S5PV210_GPH2(3));if(flag != s3c_button_history[5]){if(flag){input_report_key(input, s3c_Keycode[5], 0);}else{input_report_key(input, s3c_Keycode[5], 1);}s3c_button_history[5] = flag;}/* back */flag = gpio_get_value(S5PV210_GPH2(2));if(flag != s3c_button_history[6]){if(flag){input_report_key(input, s3c_Keycode[6], 0);}else{input_report_key(input, s3c_Keycode[6], 1);}s3c_button_history[6] = flag;}/* Kernel Timer restart */mod_timer(&timer,jiffies + HZ/100);
}