赞
踩
linux为管理输入设备而实现的统一管理输入设备的系统框架。
![输入系统框架图](https://img-blog.csdnimg.cn/a9f7fcd28ec64e06a82195f47ed26d35.png)
ps:个人简述一下,硬件动作产生数据,数据传给相关的硬件驱动,硬件驱动上报给输入系统核心层,输入系统核心层传给输入系统事件层,输入系统事件层直接上传给APP或通过tslib和libinput上传给APP。
① APP发起读操作,若无数据则休眠;
② 用户操作设备,硬件上产生中断;
③ 输入系统驱动层对应的驱动程序处理中断:
读取到数据,转换为标准的输入事件,向核心层汇报。
所谓输入事件就是一个“struct input_event”结构体。
④ 核心层可以决定把输入事件转发给上面哪个handler来处理:
⑤ APP对输入事件的处理:
APP获得数据的方法有2种:直接访问设备节点(比如/dev/input/event0,1,2,…),或者通过tslib、libinput这类库来间接访问设备节点。这些库简化了对数据的处理。
使用input_dev结构体来表示输入设备,它的内容如下:
可以得到一系列的输入事件,就是一个一个“struct input_event”,它定义如下:
每个输入事件input_event中都含有发生时间:timeval表示的是“自系统启动以来过了多少时间”,它是一个结构体,含有“tv_sec、tv_usec”两项(即秒、微秒)。
① type:表示哪类事件
比如EV_KEY表示按键类、EV_REL表示相对位移(比如鼠标),EV_ABS表示绝对位置(比如触摸屏)。有这几类事件(参考Linux内核头文件):
② code:表示该类事件下的哪一个事件
比如对于EV_KEY(按键)类事件,它表示键盘。键盘上有很多按键,比如数字键1、2、3,字母键A、B、C里等。所以可以有这些事件:
③ value:表示事件值
对于按键,它的value可以是0(表示按键被按下)、1(表示按键被松开)、2(表示长按);
对于触摸屏,它的value就是坐标值、压力值。
④ 事件之间的界线
驱动程序上报完一系列的数据后,会上报一个“同步事件”,表示数据上报完毕。APP读到“同步事件”时,就知道已经读完了当前的数据。
同步事件也是一个input_event结构体,它的type、code、value三项都是0。
1.确定设备信息
输入设备的设备节点名为/dev/input/eventX(也可能是/dev/eventX,X表示0、1、2等数字)。查看设备节点,可以执行以下命令:
ls /dev/input/* -l
或
ls /dev/event* -l
执行 获取设备节点对应硬件
cat /proc/bus/input/devices
① I:id of the device(设备ID)
该参数由结构体struct input_id来进行描述
② N:name of the device
设备名称
③ P:physical path to the device in the system hierarchy
系统层次结构中设备的物理路径。
④ S:sysfs path
位于sys文件系统的路径
⑤ U:unique identification code for the device(if device has it)
设备的唯一标识码
⑥ H:list of input handles associated with the device.
与设备关联的输入句柄列表。
⑦ B:bitmaps(位图)
PROP:device properties and quirks(设备属性)
EV:types of events supported by the device(设备支持的事件类型)
KEY:keys/buttons this device has(此设备具有的键/按钮)
MSC:miscellaneous events supported by the device(设备支持的其他事件)
LED:leds present on the device(设备上的指示灯)
值得注意的是B位图,比如上图中“B: EV=b”用来表示该设备支持哪类输入事件。b的二进制是1011,bit0、1、3为1,表示该设备支持0、1、3这三类事件,即EV_SYN、EV_KEY、EV_ABS。
再举一个例子,“B: ABS=2658000 3”如何理解?
它表示该设备支持EV_ABS这一类事件中的哪一些事件。这是2个32位的数字:0x2658000、0x3,高位在前低位在后,组成一个64位的数字:“0x2658000,00000003”,数值为1的位有:0、1、47、48、50、53、54,即:0、1、0x2f、0x30、0x32、0x35、0x36,对应以下这些宏:
2.使用命令读取数据
hexdump /dev/input/event0
1.查询方式
2.休眠唤醒
3.poll
4.异步通知
int ioctl(int fd,unsigned long request,...)
有些驱动程序对request的格式有要求,它的格式如下:
比如dir为_IOC_READ(即2)时,表示APP要读数据;为_IOC_WRITE(即4)时,表示APP要写数据。
size表示这个ioctl能传输数据的最大字节数。
type、nr的含义由具体的驱动程序决定。
比如要读取输入设备的evbit时,ioctl的request要写为“EVIOCGBIT(0, size)”,size的大小可以由你决定:你想读多少字节就设置为多少。这个宏的定义如下:
APP调用open函数时,传入“O_NONBLOCK”表示“非阻塞”。
APP调用read函数读取数据时,如果驱动程序中有数据,那么APP的read函数会返回数据,否则也会立刻返回错误。
APP调用open函数时,不要传入“O_NONBLOCK”。
APP调用read函数读取数据时,如果驱动程序中有数据,那么APP的read函数会返回数据;否则APP就会在内核态休眠,当有数据时驱动程序会把APP唤醒,read函数恢复执行并返回数据给APP。
POLL机制、SELECT机制是完全一样的,只是APP接口函数不一样。
简单地说,它们就是“定个闹钟”:在调用poll、select函数时可以传入“超时时间”。在这段时间内,条件合适时(比如有数据可读、有空间可写)就会立刻返回,否则等到“超时时间”结束时返回错误。
用法如下。
APP先调用open函数时。
APP不是直接调用read函数,而是先调用poll或select函数,这2个函数中可以传入“超时时间”。它们的作用是:如果驱动程序中有数据,则立刻返回;否则就休眠。在休眠期间,如果有人操作了硬件,驱动程序获得数据后就会把APP唤醒,导致poll或select立刻返回;如果在“超时时间”内无人操作硬件,则时间到后poll或select函数也会返回。APP可以根据函数的返回值判断返回原因:有数据?无数据超时返回?
APP根据poll或select的返回值判断有数据之后,就调用read函数读取数据时,这时就会立刻获得数据。
poll/select函数可以监测多个文件,可以监测多种事件:
事件类型 说明
POLLIN 有数据可读
POLLRDNORM 等同于POLLIN
POLLRDBAND Priority band data can be read,有优先级较较高的“band data”可读
Linux系统中很少使用这个事件
POLLPRI 高优先级数据可读
POLLOUT 可以写数据
POLLWRNORM 等同于POLLOUT
POLLWRBAND Priority data may be written
POLLERR 发生了错误
POLLHUP 挂起
POLLNVAL 无效的请求,一般是fd未open
在调用poll函数时,要指明:
① 你要监测哪一个文件:哪一个fd
② 你想监测这个文件的哪种事件:是POLLIN、还是POLLOUT
驱动程序通知APP时,它会发出“SIGIO”这个信号,表示有“IO事件”要处理。
就APP而言,你想处理SIGIO信息,那么需要提供信号处理函数,并且要跟SIGIO挂钩。这可以通过一个signal函数来“给某个信号注册处理函数”,用法如下:
除了注册SIGIO的处理函数,APP还要做什么事?想想这几个问题:
① 内核里有那么多驱动,你想让哪一个驱动给你发SIGIO信号?
APP要打开驱动程序的设备节点。
② 驱动程序怎么知道要发信号给你而不是别人?
APP要把自己的进程ID告诉驱动程序。
③ APP有时候想收到信号,有时候又不想收到信号:
应该可以把APP的意愿告诉驱动:设置Flag里面的FASYNC位为1,使能“异步通知”。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。