天天看点

输入系统框架及调试一、框架概述二、编写App需要的知识三、调试技巧

一、框架概述

在应用开发中,只需要知道API使用输入子系统。但是如果了解更多的内核中子系统框架,了解数据流程,有助于解决开发过程中碰到的硬件问题、驱动问题。

输入子系统的框架如下:

核心层把五花八门的硬件数据转换成统一的格式,发送给事件层,事件层会统一分发给App。

输入系统框架及调试一、框架概述二、编写App需要的知识三、调试技巧

假设用户程序直接访问

/dev/input/event0

设备节点,或者使用

tslib

访问设备节点,数据的流程如下:

  1. App发起读操作,若无数据则休眠
  2. 用户操作设备,硬件产生中断
  3. 输入系统驱动层对应的驱动程序处理中断。
    • 读取到数据,转换位标准的输入事件,向核心层汇报
    • 输入事件就是一个

      struct input_event

      结构体
  4. 核心层可以决定把输入事件转发给上面哪个handler来处理
    • 从handler名字来看,它就是用来处理输入操作的。其中有evdev_handler、kdb_handler、joydev_handler等等
    • 最常用的是evdev_handler:它把input_event保存在内核,APP可以读取到原始数据,同时支持多个APP同时访问,获得同一份数据
    • 当APP在等待数据时,evdev_handler会把它唤醒,App就可以返回数据
  5. App对输入事件的处理
    • App获得数据的方法有2中,直接访问设备节点(/dev/input/event0),或者通过tslib、libinput着类库来间接访问设备节点。

二、编写App需要的知识

2.1 内核中输入设备的表示

内核中使用input_dev结构体来表示输入设备,内容如下:

struct input_dev {
    const char *name;
    const char *phys;
    const char *uniq;
    struct input_id id;

    unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
	//各种的事件类型支持表,每个位都表示特定类型中的具体细节
    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)];

		//....
		
};
           

2.2 App可以得到什么数据

每个输入事件,对应一个一个的

struct input_event

,内容如下:

// include/uapi/linux/input.h
struct input_event { 
//每个输入事件input_event中都含有发生时间,"自系统启动以来过了多少时间"
    struct timeval time;		
    __u16 type;				//哪类事件
    __u16 code;				//哪个事件
    __s32 value;			//事件值
};

// include/uapi/linux/time.h
struct timeval {
    __kernel_time_t     tv_sec;     /* seconds */
    __kernel_suseconds_t    tv_usec;    /* microseconds */
};

           

上面的input_event中的三个属性,分别对应不同的内容:

  1. type:表示哪类事件

    比如EV_KEY表示按键类、EV_REL表示相对位移(比如鼠标),EV_ABS表示绝对位置(比如触摸屏)

/*
 * Event types
 */
//include/uapi/linux/input-event-codes.h
#define EV_SYN				0x00
#define EV_KEY				0x01			//按键类
#define EV_REL				0x02			//相对位置(鼠标)
#define EV_ABS				0x03			//绝对位移(触摸屏)
#define EV_MSC				0x04
#define EV_SW			  	0x05
#define EV_LED				0x11
#define EV_SND				0x12
#define EV_REP				0x14
#define EV_FF			  	0x15
#define EV_PWR				0x16
#define EV_FF_STATUS		0x17
#define EV_MAX				0x1f
#define EV_CNT			(EV_MAX+1)
           
  1. code:表示该类事件下的哪一个事件

    比如对于EV_KEY(按键)类事件,它表示键盘。

    键盘上有很多键盘,比如数字键1、2、3,字母键A、B、C里等。

//include/uapi/linux/input-event-codes.h
#define KEY_RESERVED			0			//KEY_开头的各种各样的按键
#define KEY_ESC					1
#define KEY_1					2
#define KEY_2					3
#define KEY_3					4
#define KEY_4					5
#define KEY_5					6
#define KEY_6					7
#define KEY_7					8
#define KEY_8					9
#define KEY_9					10
#define KEY_0					11
#define KEY_MINUS				12
#define KEY_EQUAL				13
#define KEY_BACKSPACE			14
#define KEY_TAB					15
           

然后对于触摸屏,它提供的是绝对位置信息,有X方向、Y方向,还有压力值。

#define ABS_X           0x00		//X方向
#define ABS_Y           0x01		//y方向
#define ABS_Z           0x02		//压力值
           
  1. value:表示事件值

    对于按键:它的value可以是0(表示按键被按下)、1(表示按键被松开)、2(表示长按)

    对于触摸屏:它的value就是坐标值、压力值。

  2. 事件之间的界限

    APP读取数据时,可以得到一个或多个数据,比如一个触摸屏的一个触电会上报X、Y位置信息,也可能会上报压力值。

    APP怎么知道它已经读到了完整的数据?

    驱动程序上报完一些列的数据后,它会上报一个“同步事件”,表示数据上报完毕。

    App读到”同步事件“时,就知道已经读完了当前的数据。

    同步事件也是一个input_event结构体,它的type、code、value三项都是0

2.3 输入子系统支持完成的API操作

输入子系统支持这些机制:

阻塞和非阻塞

POLL/SELECT

异步通知

三、调试技巧

3.1 了解设备信息

输入设备节点名为/dev/input/eventX(也可能时/dev/eventX,X可以是0、1、2…)

查看设备节点,可以执行下面的命令

[[email protected]:~]# ls /dev/input/* -l
crw-rw----    1 root     input      13,  64 Jan  1 00:08 /dev/input/event0
crw-rw----    1 root     input      13,  65 Jan  1 00:08 /dev/input/event1
crw-rw----    1 root     input      13,  63 Jan  1 00:08 /dev/input/mice

/dev/input/by-path:
total 0
lrwxrwxrwx    1 root     root             9 Jan  1 00:08 platform-20cc000.snvs:snvs-powerkey-event -> ../event0
lrwxrwxrwx    1 root     root             9 Jan  1 00:08 platform-gpio-keys-event -> ../event1
[[email protected]:~]#
           

怎么知道这些设备节点对应什么硬件呢?可以在板子上执行以下命令:

cat /proc/bus/input/devices

这条指令的含义就是获取与event对应的相关设备信息,可以看到类似以下的结果:

[[email protected]:~]# cat /proc/bus/input/devices
I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="20cc000.snvs:snvs-powerkey"
P: Phys=snvs-pwrkey/input0
S: Sysfs=/devices/soc0/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-powerkey/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0

I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="sgpio-keys"
P: Phys=gpio-keys/input0
S: Sysfs=/devices/soc0/sgpio-keys/input/input1
U: Uniq=
H: Handlers=kbd event1
B: PROP=0
B: EV=3
B: KEY=800

I: Bus=0018 Vendor=0416 Product=1001 Version=0200
N: Name="Goodix Capacitive TouchScreen"
P: Phys=input/ts
S: Sysfs=/devices/soc0/soc/2100000.aips-bus/21a0000.i2c/i2c-0/0-005d/input/input2
U: Uniq=
H: Handlers=kbd event2
B: PROP=2
B: EV=b
B: KEY=400 0 0 0 0 0 0 20000000 0 0 0
B: ABS=2658000 3
           

这个结果的第一列有I、N、P、S、U、H、B,这些对应的一行的含义是:

  1. I:由结构体struct inputid来进行描述,驱动程序中会定义这样的结构体:
// include/uapi/linux/input.h
struct input_id {
	__u16 bustype;
	__u16 vendor;
	__u16 product;
	__u16 version;
};
           
  1. N:name of the device,设备名称
  2. P:physical path to the device in the system hierarchy,系统层次结构体中设备的物理路径
  3. S:sysfs path,位于sys文件系统的路径
  4. U:unique identification code for the device(if device has it),设备的位移标识码
  5. H:list of input handles associated with the device,与设备关联的输入句柄列表
  6. 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,根据

Event types

的选项,支持的是第0位(EV_SYN)、第1位(EV_KEY),第三位(EV_ABS)

同样的

B: ABS=2658000 3

abs后面的是2个32位的数字,即0x2658000和0x3。组成一个64位的数字“0x2658000 00000003"

对应的位有0、1、47、48、50、53、54,转换成宏是0、1、0x2f、0x30、0x32、0x35、0x36:

#define ABS_X           0x00
#define ABS_Y           0x01
#define ABS_MT_SLOT     0x2f    /* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR  0x30    /* Major axis of touching ellipse */
#define ABS_MT_WIDTH_MAJOR  0x32    /* Major axis of approaching ellipse */
#define ABS_MT_POSITION_X   0x35    /* Center X touch position */
#define ABS_MT_POSITION_Y   0x36    /* Center Y touch position */
           

这个输入设备支持上述的ABS_X、ABS_Y ABS_MT_SLOT、ABS_MT_TOUCH_MAJOR、ABS_MT_WIDTH_MAJOR、ABS_MT_POSITION_X、ABS_MT_POSITION_Y这些绝对位置事件。

3.2 使用命令读取数据

调试输入系统时,直接执行类似下面的命令,然后操作对应的输入设备即可读出数据:

[email protected]:~# hexdump /dev/input/event2

在开发板上执行上述命令之后,点击按键或是点击触摸屏,就会打印下面的信息:

# 序号	秒(占2个) 微秒(占2个) type code  value(占2个)
0000000 46d4 6148 7f73 0004 0003 002f 0000 0000		# type是3,对应EV_ABS, code=0x2f对应ABS_MT_SLOT,
0000010 46d4 6148 7f73 0004 0003 0039 0002 0000		# type=3, code=0x39,对应ABS_MT_TRACKING_ID,值位0x20000
0000020 46d4 6148 7f73 0004 0003 0035 01e0 0000		# type=3, code=0x35,对应ABS_MT_POSITION_X,值为0x1e00000
0000030 46d4 6148 7f73 0004 0003 0036 00fb 0000	
0000040 46d4 6148 7f73 0004 0003 0030 0049 0000
0000050 46d4 6148 7f73 0004 0003 0032 0049 0000
0000060 46d4 6148 7f73 0004 0001 014a 0001 0000
0000070 46d4 6148 7f73 0004 0003 0000 01e0 0000
0000080 46d4 6148 7f73 0004 0003 0001 00fb 0000
0000090 46d4 6148 7f73 0004 0000 0000 0000 0000		# type=0, code=0, value=0,同步事件
00000a0 46d4 6148 70be 0006 0003 0039 ffff ffff
00000b0 46d4 6148 70be 0006 0001 014a 0000 0000
00000c0 46d4 6148 70be 0006 0000 0000 0000 0000		# type=0, code=0, value=0,同步事件
           

继续阅读