Linux输入子系统框架-JZ2440
一、linux输入子系统的框架
下图是input输入子系统框架,输入子系统由输入子系统核心层(Input Core),驱动层和事件处理层(Event Handler)
三部份组成。一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过
input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。
怎么写符合输入子系统框架的驱动程序?
1. 分配一个input_dev结构体
2. 设置
3. 注册
4. 硬件相关的代码,比如在中断服务程序里上报事件
二、drivers/input/input.c:
入口函数input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
1. <span style="font-size:14px;">static int __init input_init(void)
2. {
3. int err;
4. ...
5. /* 创建类 */
6. err = class_register(&input_class);
7. ...
8. /* 注册一个字符驱动,主设备号为13 */
9. err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
10. ...
11. return 0;
12. }</span>
只有一个open函数,其他read,write函数呢?
[cpp] view plain copy print?
1. <span style="font-size:14px;">static const struct file_operations input_fops = {
2. .owner = THIS_MODULE,
3. .open = input_open_file,
4. };</span>
input_open_file函数
[cpp] view plain copy print?
1. <span style="font-size:14px;">static int input_open_file(struct inode *inode, struct file *file)
2. {
3. struct input_handler *handler;
4. const struct file_operations *old_fops, *new_fops = NULL;
5. int err;
6. ...
7. /* 以次设备号为下标,在input_table数组找到一项handler */
8. handler = input_table[iminor(inode) >> 5];
9.
10. /* 通过handler找到一个新的fops */
11. new_fops = fops_get(handler->fops);
12. ...
13. old_fops = file->f_op;
14. /* 从此file->f_op = new_fops */
15. file->f_op = new_fops;
16. ...
17. /* 用新的new_fops的打开函数 */
18. err = new_fops->open(inode, file);
19. ...
20. return err;
21. }</span>
input_handlerj结构体成员
[cpp] view plain copy print?
1. <span style="font-size:14px;">struct input_handler {
2.
3. void *private;
4.
5. void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
6. int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
7. void (*disconnect)(struct input_handle *handle);
8. void (*start)(struct input_handle *handle);
9.
10. const struct file_operations *fops;
11. int minor;
12. const char *name;
13.
14. const struct input_device_id *id_table;
15. const struct input_device_id *blacklist;
16.
17. struct list_head h_list;
18. struct list_head node;
19. };</span>
问:怎么读按键?
APP:read > ... > file->f_op->read
问:input_table数组由谁构造?
答:input_register_handler
三、input_register_handler函数(注册input_handler)
[cpp] view plain copy print?
1. <span style="font-size:14px;">int input_register_handler(struct input_handler *handler)
2. {
3. struct input_dev *dev;
4. ...
5. INIT_LIST_HEAD(&handler->h_list);
6. ...
7. /* 将handler放入input_table数组 */
8. input_table[handler->minor >> 5] = handler;
9. ...
10. /* 将handler放入input_handler_list链表 */
11. list_add_tail(&handler->node, &input_handler_list);
12. ...
13. /* 对于每个input_dev,调用input_attach_handler
14. * 根据input_handler的id_table判断能否支持这个input_dev
15. */
16. list_for_each_entry(dev, &input_dev_list, node)
17. input_attach_handler(dev, handler);
18. ...
19. }</span>
四、input_register_device函数(注册inout_dev)
[cpp] view plain copy print?
1. <span style="font-size:14px;">int input_register_device(struct input_dev *dev)
2. {
3. ...
4. struct input_handler *handler;
5. ...
6. device_add(&dev->dev);
7. ...
8. /* 把input_dev放入input_dev_list链表 */
9. list_add_tail(&dev->node, &input_dev_list);
10. ...
11. /* 对于每一个input_handler,都调用input_attach_handler
12. * 根据input_handler的id_table判断能否支持这个input_dev
13. */
14. list_for_each_entry(handler, &input_handler_list, node)
15. input_attach_handler(dev, handler);
16. ...
17. }</span>
五、input_attach_handler函数
[cpp] view plain copy print?
1. <span style="font-size:14px;">static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
2. {
3. const struct input_device_id *id;
4. ...
5. /* 根据input_handler的id_table判断能否支持这个input_dev */
6. input_match_device(handler->id_table, dev);
7. ...
8. /* 若支持,则调用handler的connect函数,建立连接 */
9. handler->connect(handler, dev, id);
10. ...
11. }</span>
小总结:
注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"。
问:如何建立连接connect?
答:举例,evdev_connect函数
[cpp] view plain copy print?
1. <span style="font-size:14px;">static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
2. const struct input_device_id *id)
3. {
4. struct evdev *evdev;
5. ...
6.
7. /* 分配一个input_handle */
8. evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
9. ...
10. snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
11. evdev->exist = 1;
12. evdev->minor = minor;
13.
14. evdev->handle.dev = input_get_device(dev); // 指向左边的input_dev
15. evdev->handle.name = evdev->name;
16. evdev->handle.handler = handler; // 指向右边的input_handler
17. evdev->handle.private = evdev;
18.
19. /* 设置dev结构体成员 */
20. dev_set_name(&evdev->dev, evdev->name);
21. evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
22. evdev->dev.class = &input_class;
23. evdev->dev.parent = &dev->dev;
24. evdev->dev.release = evdev_free;
25. device_initialize(&evdev->dev);
26.
27. /* 注册 */
28. input_register_handle(&evdev->handle);
29. ...
30. }</span>
input_handle结构体成员
[cpp] view plain copy print?
1. <span style="font-size:14px;">struct input_handle {
2.
3. void *private;
4.
5. int open;
6. const char *name;
7.
8. struct input_dev *dev;
9. struct input_handler *handler;
10.
11. struct list_head d_node;
12. struct list_head h_node;
13. };</span>
问:input_register_handle如何注册?
[cpp] view plain copy print?
1. <span style="font-size:14px;">int input_register_handle(struct input_handle *handle)
2. {
3. struct input_handler *handler = handle->handler;
4. struct input_dev *dev = handle->dev;
5. ...
6.
7. /* 把handle->d_node添加到dev->h_list
8. * 这样,就可以从dev->h_list找到handle,进而找到handler
9. */
10. list_add_tail_rcu(&handle->d_node, &dev->h_list);
11. ...
12.
13. /* 把handle->h_node添加到handler->h_list
14. * 这样,就可以从handler->h_list找到handle,进而找到dev
15. */
16. list_add_tail(&handle->h_node, &handler->h_list);
17. ...
18. return 0;
19. }</span>
小总结:
怎么建立连接connect?
1. 分配一个input_handle结构体
2.
input_handle.dev = input_dev; //
指向左边的input_dev
input_handle.handler = input_handler; //指向右边的input_handler
3. 注册:
input_handler->h_list = &input_handle;
inpu_dev->h_list = &input_handle;
六、怎么读按键?
答:举例,evdev_read
[cpp] view plain copy print?
1. <span style="font-size:14px;">static ssize_t evdev_read(struct file *file, char __user *buffer,
2. size_t count, loff_t *ppos)
3. {
4. struct evdev_client *client = file->private_data;
5. struct evdev *evdev = client->evdev;
6. struct input_event event;
7. ...
8.
9. /* 无数据并且是非阻塞方式打开,则立刻返回 */
10. if (client->head == client->tail && evdev->exist &&
11. (file->f_flags & O_NONBLOCK))
12. return -EAGAIN;
13.
14. /* 否则休眠 */
15. retval = wait_event_interruptible(evdev->wait,
16. client->head != client->tail || !evdev->exist);
17. ...
18. }</span>
问:谁来唤醒?
搜索evdev->wait发现是evdev_event唤醒的
[cpp] view plain copy print?
1. <span style="font-size:14px;">static void evdev_event(struct input_handle *handle,
2. unsigned int type, unsigned int code, int value)
3. {
4. struct evdev *evdev = handle->private;
5. struct evdev_client *client;
6. struct input_event event;
7. ...
8. /* 唤醒 */
9. wake_up_interruptible(&evdev->wait);
10. }</span>
问:evdev_event被谁调用?
答:应该是硬件相关的代码,input_dev那层调用的在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数。
举例,在drivers/input/keyboard/gpio_keys.c里的gpio_keys_isr函数
[cpp] view plain copy print?
1. <span style="font-size:14px;">static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
2. {
3. struct gpio_button_data *bdata = dev_id;
4. struct gpio_keys_button *button = bdata->button;
5. ...
6. /* 上报事件 */
7. gpio_keys_report_event(bdata);
8. return IRQ_HANDLED;
9. }</span>
gpio_keys_report_event函数
[cpp] view plain copy print?
1. <span style="font-size:14px;">static void gpio_keys_report_event(struct gpio_button_data *bdata)
2. {
3. struct gpio_keys_button *button = bdata->button;
4. struct input_dev *input = bdata->input;
5. unsigned int type = button->type ?: EV_KEY;
6. int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
7.
8. /* 上报事件 */
9. input_event(input, type, button->code, !!state);
10. input_sync(input);
11. }</span>
问:input_event函数如何上报事件
答:
[cpp] view plain copy print?
1. input_event-->input_handle_event-->input_pass_event
2. list_for_each_entry_rcu(handle, &dev->h_list, d_node)
3. if (handle->open)
4. handle->handler->event(handle,
5. type, code, value);
怎么写符合输入子系统框架的驱动程序?
1. 分配一个input_dev结构体
2. 设置
3. 注册
4. 硬件相关的代码,比如在中断服务程序里上报事件