在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架、自动创建设备节点、linux中断、poll机制、异步通知、同步互斥、非阻塞、定时器去抖动。

上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/17589311

在这一节里,我们要引入linux的分离分层的概念,linux输入子系统是一个很好的代表,在讲解如何编写input子系统的驱动之前,我们理所当然的要先好好认识一下input子系统的框架。

一、linux输入子系统的框架(摘自作者:刘洪涛,华清远见嵌入式学院讲师。)

下图是input输入子系统框架,输入子系统由输入子系统核心层(Input Core),驱动层和事件处理层(Event Handler)

三部份组成。一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过

input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。

二、drivers/input/input.c:

入口函数input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

[cpp] view
plain
?
  1. 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. }

只有一个open函数,其他read,write函数呢?

[cpp] view
plain
?
  1. static const struct file_operations input_fops = {
  2. .owner = THIS_MODULE,
  3. .open = input_open_file,
  4. };

input_open_file函数

[cpp] view
plain
?
  1. 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. /* 通过handler找到一个新的fops */
  10. new_fops = fops_get(handler->fops);
  11. ...
  12. old_fops = file->f_op;
  13. /* 从此file->f_op = new_fops */
  14. file->f_op = new_fops;
  15. ...
  16. /* 用新的new_fops的打开函数 */
  17. err = new_fops->open(inode, file);
  18. ...
  19. return err;
  20. }

input_handlerj结构体成员

[cpp] view
plain
?
  1. struct input_handler {
  2. void *private;
  3. void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
  4. int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
  5. void (*disconnect)(struct input_handle *handle);
  6. void (*start)(struct input_handle *handle);
  7. const struct file_operations *fops;
  8. int minor;
  9. const char *name;
  10. const struct input_device_id *id_table;
  11. const struct input_device_id *blacklist;
  12. struct list_head    h_list;
  13. struct list_head    node;
  14. };

问:怎么读按键?

APP:read > ... > file->f_op->read

问:input_table数组由谁构造?

答:input_register_handler

三、input_register_handler函数(注册input_handler)

[cpp] view
plain
?
  1. 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. }

四、input_register_device函数(注册input_dev)

[cpp] view
plain
?
  1. 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. }

五、input_attach_handler函数

[cpp] view
plain
?
  1. 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. }

小总结:

注册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
?
  1. 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. /* 分配一个input_handle */
  7. evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
  8. ...
  9. snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
  10. evdev->exist = 1;
  11. evdev->minor = minor;
  12. evdev->handle.dev = input_get_device(dev); // 指向左边的input_dev
  13. evdev->handle.name = evdev->name;
  14. evdev->handle.handler = handler; // 指向右边的input_handler
  15. evdev->handle.private = evdev;
  16. /* 设置dev结构体成员 */
  17. dev_set_name(&evdev->dev, evdev->name);
  18. evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
  19. evdev->dev.class = &input_class;
  20. evdev->dev.parent = &dev->dev;
  21. evdev->dev.release = evdev_free;
  22. device_initialize(&evdev->dev);
  23. /* 注册 */
  24. input_register_handle(&evdev->handle);
  25. ...
  26. }

input_handle结构体成员

[cpp] view
plain
?
  1. struct input_handle {
  2. void *private;
  3. int open;
  4. const char *name;
  5. struct input_dev *dev;
  6. struct input_handler *handler;
  7. struct list_head    d_node;
  8. struct list_head    h_node;
  9. };

问:input_register_handle如何注册?

[cpp] view
plain
?
  1. 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. /* 把handle->d_node添加到dev->h_list
  7. * 这样,就可以从dev->h_list找到handle,进而找到handler
  8. */
  9. list_add_tail_rcu(&handle->d_node, &dev->h_list);
  10. ...
  11. /* 把handle->h_node添加到handler->h_list
  12. * 这样,就可以从handler->h_list找到handle,进而找到dev
  13. */
  14. list_add_tail(&handle->h_node, &handler->h_list);
  15. ...
  16. return 0;
  17. }

小总结:

怎么建立连接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
?
  1. 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. if (client->head == client->tail && evdev->exist &&
  10. (file->f_flags & O_NONBLOCK))
  11. return -EAGAIN;
  12. /* 否则休眠 */
  13. retval = wait_event_interruptible(evdev->wait,
  14. client->head != client->tail || !evdev->exist);
  15. ...
  16. }

问:谁来唤醒?

搜索evdev->wait发现是evdev_event唤醒的

[cpp] view
plain
?
  1. 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. }

问:evdev_event被谁调用?

答:应该是硬件相关的代码,input_dev那层调用的在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数。

举例,在drivers/input/keyboard/gpio_keys.c里的gpio_keys_isr函数

[cpp] view
plain
?
  1. 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
?
  1. 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. input_event(input, type, button->code, !!state);
  9. input_sync(input);
  10. }</span>

问:input_event函数如何上报事件

答:

[cpp] view
plain
?
  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. 硬件相关的代码,比如在中断服务程序里上报事件

linux输入子系统概念介绍的更多相关文章

  1. linux输入子系统

    linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(InputCore)和输入子系统设备驱 ...

  2. linux输入子系统(input subsystem)之evdev.c事件处理过程

    1.代码 input_subsys.drv.c 在linux输入子系统(input subsystem)之按键输入和LED控制的基础上有小改动,input_subsys_test.c不变. input ...

  3. Linux输入子系统(转)

    Linux输入子系统(Input Subsystem) 1.1.input子系统概述 输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中 ...

  4. Linux输入子系统(Input Subsystem)

    Linux输入子系统(Input Subsystem) http://blog.csdn.net/lbmygf/article/details/7360084 input子系统分析  http://b ...

  5. Linux输入子系统框架分析(1)

    在Linux下的输入设备键盘.触摸屏.鼠标等都能够用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层.事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备 ...

  6. Linux输入子系统详解

    input输入子系统框架  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...

  7. linux输入子系统简述【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/7678035 1,linux输入子系统简述 其实驱动这部分大多还是转载别人的,linux ...

  8. 7.Linux 输入子系统分析

    为什么要引入输入子系统? 在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢? 一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons .... ...

  9. Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值

    Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是没用的,甚至是错误的. 重点在最后,前边不过一些假想猜測. ht ...

随机推荐

  1. selenium学习笔记(xpath和css定位)

    简单的介绍下xpath和css的定位 理论知识就不罗列了 还是利用博客园的首页.直接附上代码: 这个是xpath #!/usr/bin/env python # -*- coding: utf_8 - ...

  2. asp.net core mvc中如何把二级域名绑定到特定的控制器上

    由于公司的工作安排,一直在研究其他技术,所以一直没时间更新博客,今天终于可以停下手头的事情,写一些新内容了. 应用场景:企业门户网站会根据内容不同,设置不同的板块,如新浪有体育,娱乐频道,等等.有的情 ...

  3. za2

      程序集?生成后  一个exe,一个dll. 也可以是一个项目. vs 快速生成字段的代码段快捷键,快速生成构造函数,生成普通方法结构的快捷键   ************************* ...

  4. Appium 自动化测试(2)--环境安装:安装Android模拟器

    一.安装java 环境-JDK 略,自行百度安装. 二.安装Android SDK Android SDK提供给我们API库和开发工具构建,测试和调试应用程序,Android.简单来讲,Android ...

  5. 常见CSS浏览器兼容性问题与解决方案【转载自http://blog.csdn.net/chuyuqing/article/details/37561313/】

    所谓的浏览器兼容性问题,是指因为不同的浏览器对同一段代码有不同的解析,造成页面显示效果不统一的情况.在大多数情况下,我们的需求是,无论用户用什么浏览器来查看我们的网站或者登陆我们的系统,都应该是统一的 ...

  6. LeetCode OJ:Palindrome Linked List(回文链表判断)

    Given a singly linked list, determine if it is a palindrome. Follow up:Could you do it in O(n) time ...

  7. Four-operations: 使用node.js实现四则运算程序

    一. 项目基本信息 项目成员: 陈旭钦, 郭鹏燕 项目仓库: https://github.com/Yanzery/Four-operations 二. PSP2.1表格 PSP2.1 Persona ...

  8. react: navigator

    1.page js import React from "react"; import {Link} from "react-router-dom"; impo ...

  9. PHP生成UTF-8编码的CSV文件用Excel打开乱码的问题

    在你要输出的内容前先输出"\xEF\xBB\xBF",例如:你要输出的内容保存在$content里$content = "\xEF\xBB\xBF".$cont ...

  10. React Native组件(一)组件的生命周期

    相关文章 React Native探索系列 前言 React Native有很多组件比如Image.ListView等等,想要合理的使用组件,首先要先了解组件的生命周期. 1.概述 无论你是开发And ...