driver: Linux设备模型之input子系统具体解释
本节从总体上解说了输入子系统的框架结构。有助于读者从总体上认识linux的输入子系统。在陷入代码分析的过程中,通过本节的知识可以找准方向,明确原理。
本节重点:
- 输入子系统的框架结构
- 各层相应内核中的文件位置
- 输入子系统的事件处理机制
- 输入子系统的驱动层基本操作流程
- 输入子系统的驱动层经常使用函数
本节难点:
- 输入子系统的事件处理机制
- 输入子系统的驱动工作流程
1 初识linux输入子系统
linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。
对于输入子系统设备驱动层而言,主要实现对硬件设备的读写訪问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。
对于核心层而言,为设备驱动层提供了规范和接口。设备驱动层仅仅要关心怎样驱动硬件并获得硬件数据(比如按下的按键数据),然后调用核心层提供的接口,核心层会自己主动把数据提交给事件处理层。
对于事件处理层而言,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。
对于linux输入子系统的框架结构例如以下图1所看到的:
图1 linux输入子系统框架结构
由上图所展现的内容就是linux输入子系统的分层结构。
/dev/input文件夹下显示的是已经注冊在内核中的设备编程接口。用户通过open这些设备文件来打开不同的输入设备进行硬件操作。
事件处理层为不同硬件类型提供了用户訪问及处理接口。
比如当我们打开设备/dev/input/mice时。会调用到事件处理层的Mouse Handler来处理输入事件。这也使得设备驱动层无需关心设备文件的操作,由于Mouse Handler已经有了相应事件处理的方法。
输入子系统由内核代码drivers/input/input.c构成。它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。
下图2简单描写叙述了linux输入子系统的事件处理机制:
图2 linux输入子系统事件处理机制
由上图可知输入子系统核心层提供的支持以及怎样上报事件到input event drivers。
作为输入设备的驱动开发人员,须要做下面几步:
? 在驱动载入模块中,设置你的input设备支持的事件类型。类型參见表1设置
?
注冊中断处理函数。比如键盘设备须要编写按键的抬起、放下,触摸屏设备须要编写按下、抬起、绝对移动。鼠标设备须要编写单击、抬起、相对移动。而且须要在必要的时候提交硬件数据(键值/坐标/状态等等)
? 将输入设备注冊到输入子系统中
表1 Linux输入子系统支持的数据类型
EV_SYN 0x00 同步事件
EV_KEY 0x01 按键事件
EV_REL 0x02 相对坐标(如:鼠标移动。报告相对最后一次位置的偏移)
EV_ABS 0x03 绝对坐标(如:触摸屏或操作杆。报告绝对的坐标位置)
EV_MSC 0x04 其他
EV_SW 0x05 开关
EV_LED 0x11 按键/设备灯
EV_SND 0x12 声音/警报
EV_REP 0x14 反复
EV_FF 0x15 力反馈
EV_PWR 0x16 电源
EV_FF_STATUS 0x17 力反馈状态
EV_MAX 0x1f 事件类型最大个数和提供位掩码支持
由表1可知,设备所能表示的事件种类,一个设备可以选择一个或多个事件类型上报给输入子系统。
Linux输入子系统提供了设备驱动层上报输入事件的函数,在include/linux/input.h中:
voidinput_report_key(struct input_dev *dev, unsigned int code, int value); //上报按键事件
voidinput_report_rel(struct input_dev *dev, unsigned int code, int value); //上报相对坐标事件
voidinput_report_abs(struct input_dev *dev, unsigned int code, int value); //上报绝对坐标事件
……
当提交输入设备产生的输入事件之后。须要调用下面的函数来通知输入子系统,以处理设备产生的完整事件:
- void input_sync(struct input_dev *dev);
2 输入设备驱动的简单案例
在Linux内核文档的documentation/input下,有一个input-programming.txt文件,解说了编写输入设备驱动程序的核心步骤。
提供的案例代码描写叙述了一个button设备,产生的事件通过BUTTON_PORT引脚获取。当有按下/释放发生时,BUTTON_IRQ被触发,下面是驱动的源码:
- #include
- #include
- #include
- #include
- #include
- static struct input_dev *button_dev;
- static void button_interrupt(int irq, void*dummy, struct pt_regs *fp)
- {
- input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1);
- input_sync(button_dev);
- }
- static int __init button_init(void)
- {
- int error;
- if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button",NULL)) {
- printk(KERN_ERR"button.c: Can't allocate irq %d\n", button_irq);
- return -EBUSY;
- }
- button_dev = input_allocate_device();
- if (!button_dev) {
- printk(KERN_ERR"button.c: Not enough memory\n");
- error = -ENOMEM;
- goto err_free_irq;
- }
- button_dev->evbit[0] = BIT(EV_KEY);
- button_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);
- error = input_register_device(button_dev);
- if (error) {
- printk(KERN_ERR"button.c: Failed to register device\n");
- goto err_free_dev;
- }
- return 0;
- err_free_dev:
- input_free_device(button_dev);
- err_free_irq:
- free_irq(BUTTON_IRQ, button_interrupt);
- return error;
- }
- static void __exit button_exit(void)
- {
- input_unregister_device(button_dev);
- free_irq(BUTTON_IRQ, button_interrupt);
- }
- module_init(button_init);
- module_exit(button_exit);
编写基于输入子系统的设备驱动程序须要包括,由于它包括了输入子系统的接口和全部的宏定义,这些内容在编写输入设备驱动程序时须要用到。
button_init函数说明:
当模块载入(insmod)或内核引导过程中,button_init函数会被调用。
首先做的工作是获取可以正确控制硬件设备的硬件资源(比如内存、IO内存、中断和DMA),在代码中BUTTON_IRQ作为BUTTON设备的中断资源。通过request_irq()函数被申请注冊。
当有按键按下/释放时,调用button_interrupt()中断处理函数获取按键值BUTTON_PORT(BUTTON设备的I/O资源)。
那么输入子系统怎么可以知道这个设备为输入设备呢?通过第8行为设备定义一个用于描写叙述一个输入设备对象。
- static struct input_dev *button_dev;
定义了button_dev之后,怎样通知输入子系统有新的输入设备了呢?或者说怎样把一个新的输入设备增加到输入子系统中呢?可以通过输入子系统核心层input.c中提供的函数分配一个输入设备,在代码的第25行。
- button_dev= input_allocate_device();
有了输入设备的描写叙述,当事件产生时,输入子系统怎么可以知道设备产生的事件类型呢?通过32和33行的代码。
- button_dev->evbit[0]= BIT(EV_KEY);
- button_dev->keybit[LONG(BTN_0)]= BIT(BTN_0);
当中evbit和keybit成员分别代表设备产生的事件类型和上报的按键值。
当中输入子系统的一些位操作NBITS、BIT、LONG经常被用到:
- #defineNBITS(x) (((x)/BITS_PER_LONG)+1) //通过位x获取数组的长度
- #defineBIT(x) (1UL<<((x)%BITS_PER_LONG)) //返回位x在数组中的位域
- #defineLONG(x) ((x)/BITS_PER_LONG) //返回位x的索引
以上的工作做完之后。就可以注冊为输入设备了,代码的35行。
- input_register_device(button_dev);
这个函数把button_dev输入设备挂入输入设备链表中,而且通知事件处理层调用connect函数完毕设备和事件处理的绑定,当用户打开设备时,便可以调用到相应的事件处理接口获得硬件上报的数据了。
input_register_device()函数是会睡眠的函数。因此不可以在中断上下文和持有自旋锁的代码中调用。
当我们把上面的工作做完之后。设备驱动中唯一值得关注的就是button_interrupt()中断处理函数了。当按键动作发生,button_interrupt()函数被调用。完毕事件的上报由当中的两条语句完毕。
- input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1);
- input_sync(button_dev);
当中input_report_key上报了这是一个按键事件,且它的值为inb(BUTTON_PORT) & 1。由于案例代码仅仅产生一个按键的值,因此input_sync()在这里不起关键作用。但假设是一个触摸屏。即有x坐标和y坐标,则须要通过input_sync()函数把x和y坐标完整地传递给输入子系统。
driver: Linux设备模型之input子系统具体解释的更多相关文章
- driver: Linux设备模型之input子系统详解
本节从整体上讲解了输入子系统的框架结构.有助于读者从整体上认识linux的输入子系统.在陷入代码分析的过程中,通过本节的知识能够找准方向,明白原理. 本节重点: 输入子系统的框架结构 各层对应内核中的 ...
- linux设备模型:扩展篇
Linux设备模型组件:总线 一.定义:总线是不同IC器件之间相互通讯的通道;在计算机中,一个总线就是处理器与一个或多个不同外设之间的通讯通道;为了设备模型的目的,所有的设备都通过总线相互连接,甚至 ...
- Linux设备模型(总线、设备、驱动程序和类)
Linux设备驱动程序学习(13) -Linux设备模型(总线.设备.驱动程序和类)[转] 文章的例子和实验使用<LDD3>所配的lddbus模块(稍作修改). 提示:在学习这部分内容是一 ...
- Linux设备模型(热插拔、mdev 与 firmware)【转】
转自:http://www.cnblogs.com/hnrainll/archive/2011/06/10/2077469.html 转自:http://blog.chinaunix.net/spac ...
- linux设备模型_转
建议原博文查看,效果更佳. 转自:http://www.cnblogs.com/wwang/category/269350.html Linux设备模型 (1) 随着计算机的周边外设越来越丰富,设备管 ...
- Linux设备模型(9)_device resource management ---devm申请空间【转】
转自:http://www.wowotech.net/linux_kenrel/device_resource_management.html . 前言 蜗蜗建议,每一个Linux驱动工程师,都能瞄一 ...
- Linux设备模型 学习总结
看LDD3中设备模型一章,觉得思维有些混乱.这里从整体的角度来理理思路.本文从四个方面来总结一些内容: 1.底层数据结构:kobject,kset.2.linux设备模型层次关系:bus_type,d ...
- Linux设备模型——设备驱动模型和sysfs文件系统解读
本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的.在分析过程中,本文将以platform总线和spi主控制器的 ...
- Linux 设备模型浅析之 uevent 篇(2)
Linux 设备模型浅析之 uevent 篇 本文属本人原创,欢迎转载,转载请注明出处.由于个人的见识和能力有限,不可能面 面俱到,也可能存在谬误,敬请网友指出,本人的邮箱是 yzq.seen@gma ...
随机推荐
- ionic3 cordova ionic-native插件
ionic-native插件 cordova安装插件 以及 ionic-native插件使用过程以及步骤 cordova plugin add cordova-plugin-插件名称. //安装插件 ...
- Unable to set localhost. This prevents creation of a GUID
原因:tomcat无法解析hostname 解决方案:解决方案:在/etc/hosts文件中添加hostname解析
- magento 报错及解决方法
在后台安装主题包时安装出错,重新进入后台进不去,前台也进不去,提示“Service Temporarily Unavailable” 删除根目录下的maintenance.flag文件即可.
- CF 990B. Micro-World【数组操作/贪心/STL/二分搜索】
[链接]:CF [题意]:对任意一个数a[i] ,可以对任意 满足 i != j 且 a[i] > a[j] && a[i] <= a[j] +k 的 a[j] 可以被删掉 ...
- n2n搭建手记-1-V1
搭建环境 supernode :阿里云主机一台 aly1(Centos 6.5) edg2node:美团云机器两台 mty1,mty2(Centos 7.0) Step-1 各机器安装subviers ...
- SendMessage 和 PostMessage
1.首先是返回值意义的区别,我们先看一下 MSDN 里的声明: LRESULT SendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lPar ...
- mysql对表的操作
创建表 简单的方式 CREATE TABLE person ( number INT(11), name VARCHAR(255), birthday DATE ); 或者是 CREATE TABLE ...
- Ubuntu 终端常用命令
文件目录类 1.建立目录:mkdir 目录名 2.删除空目录:rmdir 目录名 3.无条件删除子目录: rm -rf 目录名 4.改变当前目录:cd 目录名 (进入用户home目录:cd ~;进入上 ...
- RobotFramework操作浏览器滚动条
RobotFramework操作浏览器滚动条 (2016-12-21 11:52:43) 转载▼ 标签: selenium it 分类: 自动化测试 其实只要是用多了selenium+webdrive ...
- 【Treap模板详细注释】BZOJ3224-普通平衡树
模板题:D错因见注释 #include<iostream> #include<cstdio> #include<cstring> #include<algor ...