linux输入子系统概念介绍
在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架、自动创建设备节点、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);
plain?
- static int __init input_init(void)
- {
- int err;
- ...
- /* 创建类 */
- err = class_register(&input_class);
- ...
- /* 注册一个字符驱动,主设备号为13 */
- err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
- ...
- return 0;
- }
只有一个open函数,其他read,write函数呢?
plain?
- static const struct file_operations input_fops = {
- .owner = THIS_MODULE,
- .open = input_open_file,
- };
input_open_file函数
plain?
- 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;
- ...
- /* 以次设备号为下标,在input_table数组找到一项handler */
- handler = input_table[iminor(inode) >> 5];
- /* 通过handler找到一个新的fops */
- new_fops = fops_get(handler->fops);
- ...
- old_fops = file->f_op;
- /* 从此file->f_op = new_fops */
- file->f_op = new_fops;
- ...
- /* 用新的new_fops的打开函数 */
- err = new_fops->open(inode, file);
- ...
- return err;
- }
input_handlerj结构体成员
plain?
- struct input_handler {
- void *private;
- void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
- 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;
- const struct input_device_id *blacklist;
- struct list_head h_list;
- struct list_head node;
- };
问:怎么读按键?
APP:read > ... > file->f_op->read
问:input_table数组由谁构造?
答:input_register_handler
三、input_register_handler函数(注册input_handler)
plain?
- int input_register_handler(struct input_handler *handler)
- {
- struct input_dev *dev;
- ...
- INIT_LIST_HEAD(&handler->h_list);
- ...
- /* 将handler放入input_table数组 */
- input_table[handler->minor >> 5] = handler;
- ...
- /* 将handler放入input_handler_list链表 */
- list_add_tail(&handler->node, &input_handler_list);
- ...
- /* 对于每个input_dev,调用input_attach_handler
- * 根据input_handler的id_table判断能否支持这个input_dev
- */
- list_for_each_entry(dev, &input_dev_list, node)
- input_attach_handler(dev, handler);
- ...
- }
四、input_register_device函数(注册input_dev)
plain?
- int input_register_device(struct input_dev *dev)
- {
- ...
- struct input_handler *handler;
- ...
- device_add(&dev->dev);
- ...
- /* 把input_dev放入input_dev_list链表 */
- list_add_tail(&dev->node, &input_dev_list);
- ...
- /* 对于每一个input_handler,都调用input_attach_handler
- * 根据input_handler的id_table判断能否支持这个input_dev
- */
- list_for_each_entry(handler, &input_handler_list, node)
- input_attach_handler(dev, handler);
- ...
- }
五、input_attach_handler函数
plain?
- static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
- {
- const struct input_device_id *id;
- ...
- /* 根据input_handler的id_table判断能否支持这个input_dev */
- input_match_device(handler->id_table, dev);
- ...
- /* 若支持,则调用handler的connect函数,建立连接 */
- handler->connect(handler, dev, id);
- ...
- }
小总结:
注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"。
问:如何建立连接connect?
答:举例,evdev_connect函数
plain?
- static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
- const struct input_device_id *id)
- {
- struct evdev *evdev;
- ...
- /* 分配一个input_handle */
- evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
- ...
- snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
- evdev->exist = 1;
- evdev->minor = minor;
- evdev->handle.dev = input_get_device(dev); // 指向左边的input_dev
- evdev->handle.name = evdev->name;
- evdev->handle.handler = handler; // 指向右边的input_handler
- evdev->handle.private = evdev;
- /* 设置dev结构体成员 */
- dev_set_name(&evdev->dev, evdev->name);
- 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);
- /* 注册 */
- input_register_handle(&evdev->handle);
- ...
- }
input_handle结构体成员
plain?
- struct input_handle {
- void *private;
- int open;
- const char *name;
- struct input_dev *dev;
- struct input_handler *handler;
- struct list_head d_node;
- struct list_head h_node;
- };
问:input_register_handle如何注册?
plain?
- int input_register_handle(struct input_handle *handle)
- {
- struct input_handler *handler = handle->handler;
- struct input_dev *dev = handle->dev;
- ...
- /* 把handle->d_node添加到dev->h_list
- * 这样,就可以从dev->h_list找到handle,进而找到handler
- */
- list_add_tail_rcu(&handle->d_node, &dev->h_list);
- ...
- /* 把handle->h_node添加到handler->h_list
- * 这样,就可以从handler->h_list找到handle,进而找到dev
- */
- list_add_tail(&handle->h_node, &handler->h_list);
- ...
- return 0;
- }
小总结:
怎么建立连接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
plain?
- static ssize_t evdev_read(struct file *file, char __user *buffer,
- size_t count, loff_t *ppos)
- {
- struct evdev_client *client = file->private_data;
- struct evdev *evdev = client->evdev;
- struct input_event event;
- ...
- /* 无数据并且是非阻塞方式打开,则立刻返回 */
- if (client->head == client->tail && evdev->exist &&
- (file->f_flags & O_NONBLOCK))
- return -EAGAIN;
- /* 否则休眠 */
- retval = wait_event_interruptible(evdev->wait,
- client->head != client->tail || !evdev->exist);
- ...
- }
问:谁来唤醒?
搜索evdev->wait发现是evdev_event唤醒的
plain?
- 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;
- ...
- /* 唤醒 */
- wake_up_interruptible(&evdev->wait);
- }
问:evdev_event被谁调用?
答:应该是硬件相关的代码,input_dev那层调用的在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数。
举例,在drivers/input/keyboard/gpio_keys.c里的gpio_keys_isr函数
plain?
- static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
- {
- struct gpio_button_data *bdata = dev_id;
- struct gpio_keys_button *button = bdata->button;
- ...
- /* 上报事件 */
- gpio_keys_report_event(bdata);
- return IRQ_HANDLED;
- }</span>
gpio_keys_report_event函数
plain?
- static void gpio_keys_report_event(struct gpio_button_data *bdata)
- {
- struct gpio_keys_button *button = bdata->button;
- struct input_dev *input = bdata->input;
- unsigned int type = button->type ?: EV_KEY;
- int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
- /* 上报事件 */
- input_event(input, type, button->code, !!state);
- input_sync(input);
- }</span>
问:input_event函数如何上报事件
答:
plain?
- input_event-->input_handle_event-->input_pass_event
- list_for_each_entry_rcu(handle, &dev->h_list, d_node)
- if (handle->open)
- handle->handler->event(handle,
- type, code, value);
怎么写符合输入子系统框架的驱动程序?
1. 分配一个input_dev结构体
2. 设置
3. 注册
4. 硬件相关的代码,比如在中断服务程序里上报事件
linux输入子系统概念介绍的更多相关文章
- linux输入子系统
linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(InputCore)和输入子系统设备驱 ...
- linux输入子系统(input subsystem)之evdev.c事件处理过程
1.代码 input_subsys.drv.c 在linux输入子系统(input subsystem)之按键输入和LED控制的基础上有小改动,input_subsys_test.c不变. input ...
- Linux输入子系统(转)
Linux输入子系统(Input Subsystem) 1.1.input子系统概述 输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中 ...
- Linux输入子系统(Input Subsystem)
Linux输入子系统(Input Subsystem) http://blog.csdn.net/lbmygf/article/details/7360084 input子系统分析 http://b ...
- Linux输入子系统框架分析(1)
在Linux下的输入设备键盘.触摸屏.鼠标等都能够用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层.事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备 ...
- Linux输入子系统详解
input输入子系统框架 linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...
- linux输入子系统简述【转】
本文转载自:http://blog.csdn.net/xubin341719/article/details/7678035 1,linux输入子系统简述 其实驱动这部分大多还是转载别人的,linux ...
- 7.Linux 输入子系统分析
为什么要引入输入子系统? 在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢? 一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons .... ...
- Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值
Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是没用的,甚至是错误的. 重点在最后,前边不过一些假想猜測. ht ...
随机推荐
- python 正则表达式使用
正则表达式总结: python支持的字符和语法:. 匹配任意除换行符\n 之外的所有字符\ 转义字符 比如说要匹配的字符中含有 \ . * 等可以使用\转义 例如 \* \. \\[.....] 直接 ...
- 分分享知识-快乐自己: @Component注解的使用
@controller 控制器(注入服务) 用于标注控制层,相当于struts中的action层 @service 服务(注入dao) 用于标注服务层,主要用来进行业务的逻辑处理 @repositor ...
- 【Python】函数的参数对应
我们已经接触过函数(function)的参数(arguments)传递.当时我们根据位置,传递对应的参数.我们将接触更多的参数传递方式. 回忆一下位置传递: def f(a,b,c): return ...
- HBase Cassandra比较
转自:http://itindex.net/detail/22338-cassandra-hbase-%E8%AE%BE%E8%AE%A1 Cassandra HBase 一致性 Quorum ...
- 条款32:确定public继承中塑造出来的是IS-A关系而不是其他的关系
首先考虑的是一个很典型的关系,就是矩形与正方形的关系: class Recantagle{ virtual void setHeight(int); virtual void setWidth(int ...
- jQuery中this与$(this)的区别
起初以为this和$(this)就是一模子刻出来.但是我在阅读时,和coding时发现,总不是一回事,这里就谈谈this与$(this)的区别. jQuery中this与$(this)的区别 $(&q ...
- 项目工程结构说明(Internal)
注意:想要彻底把Internal关键字搞清楚,就耐着性子把她读完.当然了这篇文章只是对其他文章的总结.也算是引用吧.主要还是为了把知识点搞清楚 进入主题之前先来了解一下,项目.解决方案.程序集.命名空 ...
- Java与数据库数据类型对应表
Java中的数据类型和SQL中的数据类型有很多不一样,需要仔细区分,不然易在开发中造成莫名的错误. Java数据类型 hibernate数据类型 标准SQL数据类型(PS:对于不同的DB可能有所差异) ...
- HAWQ取代传统数仓实践(十)——维度表技术之杂项维度
一.什么是杂项维度 简单地说,杂项维度就是一种包含的数据具有很少可能值的维度.事务型商业过程通常产生一系列混杂的.低基数的标志位或状态信息.与其为每个标志或属性定义不同的维度,不如建立单独的将不同维度 ...
- 11.求二元查找树的镜像[MirrorOfBST]
[题目] 输入一颗二元查找树,将该树转换为它的镜像,即在转换后的二元查找树中,左子树的结点都大于右子树的结点.用递归和循环两种方法完成树的镜像转换. 例如输入: 8 / \ 6 1 ...