天天看點

輸入系統架構及調試一、架構概述二、編寫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,同步事件
           

繼續閱讀