linux input输入子系统应用分析
输入设备(如按键、键盘、触摸屏、鼠标等)是典型的字符设备,其一般的工作机理是底层在按键、触摸等动作发送时产生一个中断(或驱动通过timer定时查询),然后CPU通过SPI、I2 C或外部存储器总线读取键值、坐标等数据,放入1个缓冲区,字符设备驱动管理该缓冲区,而驱动的read()接口让用户可以读取键值、坐标等数据。
显然,在这些工作中,只是中断、读值是设备相关的,而输入事件的缓冲区管理以及字符设备驱动的file_operations接口则对输入设备是通用的。基于此,内核设计了输入子系统,由核心层处理公共的工作。硬件驱动层处理中断、读值相关(中断中上报读值)。
input子系统分层设计,共三层:硬件驱动层、子系统核心层、事件处理层。
驱动层由驱动程序员完成,主要处理中断(上报事件-读值)。
子系统核心层是链接其他两个层之间的纽带和桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。
设备驱动并不创建文件节点,它只负责将采集到数据通过input.c中的函数input_event() 向上一层汇报;而各个事件驱动则分别将他们各自感兴趣的事件信息提取出来,通过文件节点,提供给用户。在这个过程中,input 子系统的核心负责这两层的交互工作,并管理和维护着记录了他们各自信息的链表。
驱动程序中上报的事件,input子系统已规定好,在linux/input.h中。支持的事件类型(Event types)有EV_CNT个(EV_SYN同步事件,EV_KEY按键事件,EV_REL相对坐标事件,EV_ABS绝对坐标事件,EV_MSC零散事件,EV_SW开关事件,EV_LED LED事件,EV_SND,EV_REP重复事件,EV_FF,EV_PWR,EV_FF_STATUS,EV_MAX,EV_CNE)(后几项用于服务事件类型,不代表实际事件),在代码中用“type”表示。每种事件类型都有多种属性(或选项,或编码),代码中用“code”表示,每种属性所对应的值就是要上报的数据(上面所说的“读值”)。“事件-属性-属性值”(type-code-value)唯一表征一个上报事件,“type-code”输入子系统有完整的规定,代码中通过“位掩码”表示支持该事件,该属性。举个简单例子,触摸设备支持绝对坐标事件EV_ABS,EV_ABS有ABS_CNT个code,其中ABS_X代表X坐标,ABS_Y代表Y坐标。
input_dev结构体自带事件相关数组,如下:
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)];
所有输入事件,内核都用统一的数据结构来描述,这个数据结构是input_event。
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
----------------------------------------------------------------------------------------------------
设备支持的事件在驱动模块加载函数中设置,一般设置方法为:
button_dev->evbit[0] = BIT_MASK(EV_KEY); //直接位赋值(兼容性差)
static inline void __set_bit(int nr, volatile unsigned long *addr); // 函数设置,非原子
static inline void set_bit(int nr, volatile unsigned long *addr); //函数设置,原子,asm/bitops/atomic.h
中断上报事件用input_event(),此外还有3个变体函数分别用于报告EV_KEY、EV_REL、EV_ABS事件。
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value);
input_sync()用于事件同步,告知事件接收者驱动已发出一个完整的报告。
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
在宋宝华的《linux设备驱动开发详解》中,介绍了通过input子系统增加输入设备的驱动工作。
>>在模块加载函数中告知input子系统设备可以报告的事情。(set_bit())
>>在模块加载函数中注册输入设备。(int input_register_device(struct input_dev *dev);)
>>在键被按下/抬起、触摸屏被触摸/抬起/移动、鼠标被移动/单击/抬起时通过input_report_xxx()报告发生的事情及对应的键值/坐标等状态。
>>在模块卸载函数中注销输入设备。(void input_unregister_device(struct input_dev *dev);)
---------------------------------------------------------------------------------------------------
事件处理层与用户程序和输入子系统核心打交道,是两层的桥梁。一般内核有好几个事件处理器,像evdev,mousedev,joydev。evdev事件处理器可以处理所有的事情。
evdev用户端结构:
. struct evdev_client {
. struct input_event buffer[EVDEV_BUFFER_SIZE];
. //这个是一个input_event数据结构的数组,input_event代表一个事件,基本成员:类型(type),编码(code),值(value)
. int head; //针对buffer数组的索引
. int tail; //针对buffer数组的索引,当head与tail相等的时候,说明没有事件
. spinlock_t buffer_lock; /* protects access to buffer, head and tail */
. struct fasync_struct *fasync; //异步通知函数
. struct evdev *evdev; //evdev设备
. struct list_head node; // evdev_client 链表项
. };
这个结构在进程打开event0设备的时候调用evdev的open方法,在open中创建这个结构,并初始化。在关闭设备文件的时候释放这个结构。在read时复制到用户空间。
用户空间的应用程序如何访问设备呢,设备文件是?
. struct evdev {
. int exist;
. int open; //打开标志
. int minor; //次设备号
. struct input_handle handle; //关联的input_handle
. wait_queue_head_t wait; //等待队列,当进程读取设备,而没有事件产生的时候,进程就会睡在其上面
. struct evdev_client *grab; //强制绑定的evdev_client结构,这个结构后面再分析
. struct list_head client_list; //evdev_client 链表,这说明一个evdev设备可以处理多个evdev_client,可以有多个进程访问evdev设备
. spinlock_t client_lock; /* protects client_list */
. struct mutex mutex;
. struct device dev; //device结构,说明这是一个设备结构
. };
evdev结构体在配对成功的时候生成,由handler->connect生成,对应设备文件为/dev/input/event(n),如触摸屏驱动的event0,这个设备是用户空间要访问的设备,可以理解它是一个虚拟设备,因为没有对应的硬件,但是通过handle->dev 就可以找到input_dev结构,而它对应着触摸屏,设备文件为/dev/input/input0。这个设备结构生成之后保存在 evdev_table中,索引值是minor。设备文件通过设备号关联。
在evdev_connect()中,有设备文件初始化过程:
…
dev_set_name(&evdev->dev, "event%d", minor);
…
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);
…
也可通过/proc/bus/input/devices查看设备文件,PC虚拟机下测试如下:
cat /proc/bus/input/devices
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0
…
I: Bus=0011 Vendor=0002 Product=0005 Version=0000
N: Name="ImPS/2 Generic Wheel Mouse"
P: Phys=isa0060/serio1/input0
S: Sysfs=/devices/platform/i8042/serio1/input/input3
U: Uniq=
H: Handlers=mouse1 event3
B: PROP=0
B: EV=7
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=103
通过比较VID、PID来找到对应的usb mouse设备,然后找到对应的mouse1、event3
其实也可以不写应用程序,直接通过cat /dev/input/mouse1 | hexdump来获取鼠标数据。
一个网上示例如下:
. /*
2. 20150101
3. just a simple input test code
4. lei_wang
5. */
.
. #include <stdio.h>
. #include <stdlib.h>
. #include <unistd.h>
. #include <fcntl.h>
. #include <string.h>
. #include <linux/input.h>
.
. int main()
. {
. int fd;
. int version;
. int ret;
. struct input_event ev;
.
. fd = open("/dev/input/event1", O_RDONLY);
. if (fd < ) {
. printf("open file failed\n");
. exit();
. }
.
. ioctl(fd, EVIOCGVERSION, &version);
. printf("evdev driver version is 0x%x: %d.%d.%d\n",
. version, version>>, (version>>) & 0xff, version & 0xff);
.
. while () {
. ret = read(fd, &ev, sizeof(struct input_event));
. if (ret < ) {
. printf("read event error!\n");
. exit();
. }
.
. if (ev.type == EV_KEY)
. printf("type %d,code %d, value %d\n", ev.type, ev.code, ev.value);
. }
.
. return ;
. }
参考:
- http://www.linuxidc.com/Linux/2011-09/43187.htm input子系统分析
- http://blog.csdn.net/zhangxizhicn/article/details/6642062 linux input子系统分析
- http://blog.csdn.net/21cnbao/article/details/5615493 linux设备驱动的分层设计思想
- linux设备驱动详解 宋宝华
- http://blog.csdn.net/luckywang1103/article/details/42324229 input子系统—架构、驱动、应用程序
- http://www.cnblogs.com/leaven/archive/2011/02/12/1952793.html linux input子系统io控制字段
- https://blog.csdn.net/jxgz_leo/article/details/51149561 driver: Linux设备模型之input子系统详解
linux input输入子系统应用分析的更多相关文章
- linux input输入子系统分析《四》:input子系统整体流程全面分析
1 input输入子系统整体流程 本节分析input子系统在内核中的实现,包括输入子系统(Input Core),事件处理层(Event Handler)和设备驱动层.由于上节代码讲解了设备 ...
- linux内核输入子系统分析
1.为何引入input system? 以前我们写一些输入设备(键盘.鼠标等)的驱动都是采用字符设备.混杂设备处理的.问题由此而来,Linux开源社区的大神们看到了这大量输入设备如此分散不堪,有木有可 ...
- ARM Linux内核Input输入子系统浅解
--以触摸屏驱动为例 第一章.了解linux input子系统 Linux输入设备总类繁杂,常见的包括有按键.键盘.触摸屏.鼠标.摇杆等等,他们本身就是字符设备,而linux内核将这些 ...
- input输入子系统
一.什么是input输入子系统? 1.Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型.不同原理.不同的输入信 ...
- INPUT输入子系统【转】
转自:https://www.cnblogs.com/deng-tao/p/6094049.html 1.Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等 ...
- INPUT输入子系统——按键
一.什么是input输入子系统? 1.1. Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型.不同原理.不同的 ...
- 12.Linux之输入子系统分析(详解)
版权声明:本文为博主原创文章,转载请标注出处: 在此节之前,我们学的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥/非阻塞.定时 ...
- Linux输入子系统框架分析(1)
在Linux下的输入设备键盘.触摸屏.鼠标等都能够用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层.事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备 ...
- Linux/Android——输入子系统input_event传递 (二)【转】
本文转载自:http://blog.csdn.net/jscese/article/details/42099381 在前文Linux/Android——usb触摸屏驱动 - usbtouchscre ...
随机推荐
- gnuplotx轴的logscale显示
假设数据是这样子的: gnuplot脚本如下: set terminal postscript eps color enhanced set log x set log y set format x ...
- PHP+jQuery 注册模块的改进之一:验证码存入SESSION
/* ******* Date:2014-09-28 ******* Author:小dee ******* Blog:http://www.cnblogs.com/dee0912/*/ 对上一篇博文 ...
- HttpStatusCodeResult
HttpStatusCodeResult:让mvc回传特定的http状态代码与消息给客户端,对于一些特殊的http响应,可利用httpStatusCodeResult帮助我们响应适当的状态代码: 1X ...
- varnish中忽略cookie进行缓存
varnish不缓存cookie的页面,如果html页面中带有cookie以下代码为接收到结尾的文件,自动去除掉cookiesub vcl_recv { if (req.request == ” ...
- The world beyond batch: Streaming 101
https://www.oreilly.com/ideas/the-world-beyond-batch-streaming-101 https://www.oreilly.com/ideas/the ...
- iOS开发入门教程
iOS开发入门教程 http://my.oschina.net/mailzwj/blog/133273 摘要 iOS开发入门教程,从创建项目到运行项目,包括OC基础,调试,模拟器设置等相关知识. iO ...
- CSS中position属性 (absolute,relative,static,fixed)
只要position的属性值设置的不是默认的值则定位的元素都将脱离文档流 1.static是position的默认的值,按照正常的文档流进行排版,设置了该属性值得元素的top,left属性均不起作用. ...
- Wordpress更改后台地址
wordpress默认的后台地址是 xx/wp-admin 或xx/wp-login.php ,谁都知道感觉很不安全, 方法一:使用插件 通过插件在地址上加上只有你知道的参数才能访问 1.后台搜索插 ...
- Jquery调用webService的四种方法
1.编写4种WebService方法 [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(Conf ...
- Spark Programming--Actions II
saveAsTextFile saveAsTextFile(path, compressionCodecClass=None) aveAsTextFile用于将RDD以文本文件的格式存储到文件系统中, ...