linux驱动由浅入深系列:输入子系统之一(input子系统概述、应用层读取event)

时间:2022-08-29 17:56:44

本系列导航:

linux驱动由浅入深系列:输入子系统之一(input子系统概述、应用层读取event)

linux驱动由浅入深系列:输入子系统之二(编写一个gpio_key驱动)

linux驱动由浅入深系列:输入子系统之三(应用层模拟input_event)


本文系列文章先从宏观上了解linux中输入子系统的作用,再看看其在应用层是如何表现出来的,然后在一层层深入到linux内核代码中。

  1,输入子系统引入的好处:

(1)统一了物理形态各异的相似的输入设备的处理功能。例如,各种鼠标,不论PS/2、USB、还是蓝牙,都被同样处理。在移动设备上的触摸屏、按键、各类传感器也都是基于输入子系统的。

(2)提供了用于分发输入报告给用户应用程序的简单的事件(event)接口。你的驱动不必创建、管理/dev节点以及相关的访问方法。因此它能够很方便的调用输入API以发送鼠标移动、键盘按键,或触摸事件给用户空间。X windows这样的应用程序能够无缝地运行于输入子系统提供的event接口之上。

(3)抽取出了输入驱动的通用部分,简化了驱动,并提供了一致性。例如,输入子系统提供了一个底层驱动(成为serio)的集合,支持对串口和键盘控制器等硬件输入的访问。 

2,/dev/input目录

   /dev/input目录下的事件都是在驱动中调用input_register_device(struct input_dev *dev)产生的。如我的开发板的/dev/input目录的内容如下:

root@yzm7332p0:/dev/input # ll
crw-rw---- root input 13, 64 2016-01-03 15:58 event0
crw-rw---- root input 13, 65 2016-01-03 15:58 event1
crw-rw---- root input 13, 66 2016-01-03 15:58 event2
crw-rw---- root input 13, 67 2016-01-03 15:58 event3
crw-rw---- root input 13, 68 2016-01-03 15:58 event4
crw-rw---- root input 13, 69 2016-01-03 15:58 event5
crw-rw---- root input 13, 70 2016-01-03 15:58 event6
crw-rw---- root input 13, 71 2016-01-03 15:58 event7
crw-rw---- root input 13, 72 2016-01-03 15:58 event8

每个event将上报指定的事件,如G-Sensor、触摸屏、气压传感器、按键等。 

3,从上面的event文件节点上无法判断出其对应的设备,那么怎么查看呢?利用proc文件系统。cat /proc/bus/input/devices 

root@yzm7332p0:/ # cat/proc/bus/input/devices
I: Bus=0019 Vendor=0001 Product=0001Version=0100
N: Name="comip-gpio-keys"
P: Phys=gpio-keys/input0
S:Sysfs=/devices/platform/comip-gpio-keys/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=100000 c0000 0 0 0

I: Bus=0018 Vendor=0712 Product=3402Version=0102
N: Name="S3402_TouchScreen"
P: Phys=input/ts
S: Sysfs=/devices/virtual/input/input1
U: Uniq=
H: Handlers=event1
B: PROP=0
B: EV=b
B: KEY=400 0 0 0 0 0 0 0 0 0 0
B: ABS=6640000 0

I: Bus=0018 Vendor=0000 Product=0000Version=0000
N: Name="bma2x2"
P: Phys=
S: Sysfs=/devices/virtual/input/input2
U: Uniq=
H: Handlers=event2
B: PROP=0
B: EV=9
B: ABS=100 7

I: Bus=0018 Vendor=0000 Product=0000Version=0000
N: Name="bma_interrupt"
P: Phys=
S: Sysfs=/devices/virtual/input/input3
U: Uniq=
H: Handlers=event3
B: PROP=0
B: EV=d
B: REL=3c6
B: ABS=3000000

I: Bus=0018 Vendor=0000 Product=0000Version=0000
N: Name="bmg160"
P: Phys=
S: Sysfs=/devices/virtual/input/input4
U: Uniq=
H: Handlers=event4
B: PROP=0
B: EV=9
B: ABS=100 7

I: Bus=0018 Vendor=0000 Product=0000 Version=0000
N: Name="bmm050"
P: Phys=
S: Sysfs=/devices/virtual/input/input5
U: Uniq=
H: Handlers=event5
B: PROP=0
B: EV=9
B: ABS=100 7

I: Bus=0018 Vendor=0000 Product=0000Version=0000
N: Name="bmp280"
P: Phys=
S: Sysfs=/devices/virtual/input/input6
U: Uniq=
H: Handlers=event6
B: PROP=0
B: EV=11
B: MSC=8

I: Bus=0019 Vendor=0000 Product=0000Version=0000
N: Name="comip-powerkey"
P: Phys=
S: Sysfs=/devices/virtual/input/input7
U: Uniq=
H: Handlers=kbd event7
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0

I: Bus=0000 Vendor=0000 Product=0000Version=0000
N: Name="comip_snd_soc Headset"
P: Phys=ALSA
S:Sysfs=/devices/platform/soc-audio/sound/card0/input8
U: Uniq=
H: Handlers=kbd event8
B: PROP=0
B: EV=23
B: KEY=4 0 0 0 c0000 0 0 0
B: SW=14

可以看到上面展示的信息中Handlers和Name就可以让我们了解某个eventX的基本归属。 

4,应用层查看event消息的方法

我们使用hexdump工具查看某个event节点(android平台下可以直接使用getevent命令得到所有输入设备的消息,参考文章“android系列:第三篇android调试常用工具:模拟按键输入,修改分辨率,获得按键消息”,hexdump是通用的做法),如/dev/input/event7。从上面获取的信息中可知event7是单独的powerkey节点(其它设备可能不是这样设计的注意区分哦)。

我们键入busybox hexdump /dev/input /event7,shell阻塞没有任何输出,此刻按下电源按键两次输出如下内容:

root@yzm7332p0:/dev/input # busybox hexdumpevent7
0000000 e6a4 5688 9be1 0003 0001 0074 00010000
0000010 e6a4 5688 9be1 0003 0000 0000 00000000
0000020 e6a4 5688 27e5 0006 0001 0074 00000000
0000030 e6a4 5688 27e5 0006 0000 0000 00000000
0000040 e6a9 5688 7cfb 0008 0001 0074 00010000
0000050 e6a9 5688 7cfb 0008 0000 0000 00000000
0000060 e6a9 5688 109e 000a 0001 0074 00000000
0000070 e6a9 5688 109e 000a 0000 0000 00000000

分析:

linux驱动由浅入深系列:输入子系统之一(input子系统概述、应用层读取event) 

第一列的行号是hexdump输出的

第二列到最后其实为input_event结构体的十六进制数据,看到下面两个结构体就一目了然了。

struct input_event { 
struct timeval time; //时间
__u16 type; //类
__u16 code; //类下事件的值
__s32 value; //0-松开, 1-按下,2-重复
};

struct timeval {
__kernel_time_t tv_sec; //秒
__kernel_suseconds_t tv_usec; //微秒
};

时间为event生成时的系统时间,包括秒、微秒两个字段。

Type是event类型。

linux 驱动程序开发中, 输入子系统总共能产生哪些事件类型?,以及分别是什么意思?详见如下:

Linux中输入设备的事件类型有

EV_SYN 0x00 同步事件
EV_KEY 0x01 按键事件,如KEY_VOLUMEDOWN
EV_REL 0x02 相对坐标, 如鼠标上报的坐标
EV_ABS 0x03 绝对坐标,如触摸屏上报的坐标
EV_MSC 0x04 其它
EV_LED 0x11 LED
EV_SND 0x12 声音
EV_REP 0x14 Repeat
EV_FF 0x15 力反馈

Code就相当于键值,之后在分析代码中可以看到其定义(详见本系列文章后文,暂不展开)。

Value代表按键按下与否。(设备不为按键时,可能表示坐标位置、传感器数值等) 

经过解释,上图中的两次按键事件应该能够看出来了把。

0000030 e6a4 5688 27e5 0006 0000 0000 00000000如此类型的为同步事件,必须跟在每次按键事件的结尾。