歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux input子系統分析

Linux input子系統分析

日期:2017/2/28 16:06:39   编辑:Linux教程
Input子系統處理輸入事務,任何輸入設備的驅動程序都可以通過Input輸入子系統提供的接口注冊到內核,利用子系統提供的功能來與用戶空間交互。輸入設備一般包括鍵盤,鼠標,觸摸屏等,在內核中都是以輸入設備出現的。下面分析input輸入子系統的結構,以及功能實現。
一. Input子系統結構與功能實現
1. Input子系統是分層結構的,總共分為三層: 硬件驅動層,子系統核心層,事件處理層。
(1)其中硬件驅動層負責操作具體的硬件設備,這層的代碼是針對具體的驅動程序的,需要驅動程序的作者來編寫。
(2)子系統核心層是鏈接其他兩個層之間的紐帶與橋梁,向下提供驅動層的接口,向上提供事件處理層的接口。
(3)事件處理層負責與用戶程序打交道,將硬件驅動層傳來的事件報告給用戶程序。
2. 各層之間通信的基本單位就是事件,任何一個輸入設備的動作都可以抽象成一種事件,如鍵盤的按下,觸摸屏的按下,鼠標的移動等。事件有三種屬性:類型(type),編碼(code),值(value),Input子系統支持的所有事件都定義在input.h中,包括所有支持的類型,所屬類型支持的編碼等。事件傳送的方向是 硬件驅動層-->子系統核心-->事件處理層-->用戶空間
3. 以觸摸屏為例說明輸入子系統的工作流程:
注:mini2440的觸摸屏驅動所用驅動層對應的模塊文件為:s3c2410_ts.c,事件處理層對應的模塊文件為 evdev.c
(1)s3c2410_ts模塊初始化函數中將觸摸屏注冊到了輸入子系統中,於此同時,注冊函數在事件處理層鏈表中尋找事件處理器,這裡找到的是evdev,並且將驅動與事件處理器掛載。並且在/dev/input中生成設備文件event0,以後我們訪問這個文件就會找的我們的觸摸屏驅動程序。
(2)應用程序打開設備文件/dev/input/event0,讀取設備文件,調用evdev模塊中read,如果沒有事件進程就會睡眠。
(3)當觸摸屏按下,驅動層通過子系統核心將事件(就是X,Y坐標),傳給事件處理層也就是evdev,evdev喚醒睡眠的進程,將事件傳給進程處理。

二.主要input通用數據結構
1.input_dev 這是input設備基本的設備結構,每個input驅動程序中都必須分配初始化這樣一個結構,成員比較多
(1)有以下幾個數組:

  1. unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //事件支持的類型
  2. // 下面是每種類型支持的編碼
  3. unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //按鍵
  4. unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
  5. unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //絕對坐標,其中觸摸屏驅動使用的就是這個
  6. unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
  7. unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
  8. unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
  9. unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
  10. unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
evbit[BITS_TO_LONGS(EV_CNT)]; 這個數組以位掩碼的形式,代表了這個設備支持的事件的類型。設置方式如:
dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS)
absbit[BITS_TO_LONGS(ABS_CNT)]; 這個數組也是以位掩碼的形式,代表這個類型的事件支持的編碼
觸摸屏驅動支持EV_ABS,所以要設置這個數組, 有一個專門設置這個數組的函數input_set_abs_params,代碼如下:
  1. static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)
  2. {
  3. dev->absmin[axis] = min;
  4. dev->absmax[axis] = max;
  5. dev->absfuzz[axis] = fuzz;
  6. dev->absflat[axis] = flat;
  7. dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis); //填充了absbit這個數組
  8. }
觸摸屏驅動中是這樣調用的
input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0); //這個是設置ad轉換的x坐標
input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0); //這個是設置ad轉換的y坐標
input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0); //這個是設置觸摸屏是否按下的標志
設置ABS_X編碼值范圍為0-0x3ff,因為mini2440的AD轉換出的數據最大為10位,所以不會超過0x3ff。

(2) struct input_id id 成員
這個是標識設備驅動特征的

  1. struct input_id {
  2. __u16 bustype; //總線類型
  3. __u16 vendor; //生產廠商
  4. __u16 product; //產品類型
  5. __u16 version; //版本
  6. };
如果需要特定的事件處理器來處理這個設備的話,這幾個就非常重要,因為子系統核心是通過他們,將設備驅動與事件處理層聯系起來的。但是因為觸摸屏驅動所用的事件處理器為evdev,匹配所有,所有這個初始化
也無關緊要。
(3) 還有其他一些成員,也比較重要,但是驅動程序可以不用管,都是由子系統核心來處理的。
(4) 可以看出input_dev 結構所屬層為硬件驅動層,以後就用input_dev來表示輸入設備。
2. input_handler 這是事件處理器的數據結構,代表一個事件處理器
(1)幾個操作函數
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
event 函數是當事件處理器接收到了來自input設備傳來的事件時調用的處理函數,負責處理事件,非常重要,在事件傳遞過程中會詳細分析。
connect 函數是當一個input設備模塊注冊到內核的時候調用的,將事件處理器與輸入設備聯系起來的函數,也就是將input_dev和input_handler配對的函數。
disconnect 函數實現connect相反的功能。
start 暫時沒有發現有什麼作用。
(2) 兩個id
const struct input_device_id *id_table; //這個是事件處理器所支持的input設備
const struct input_device_id *blacklist; //這個是事件處理器應該忽略的input設備
這兩個數組都會用在connect函數中,input_device_id結構與input_id結構類似,但是input_device_id有一個flag,用來讓程序選擇比較哪項,如:busytype,vendor還是其他。
(3) 兩個鏈表
struct list_headh_list; //這個鏈表用來鏈接他所支持的input_handle結構,input_dev與input_handler配對之後就會生成一個input_handle結構
struct list_headnode; //鏈接到input_handler_list,這個鏈表鏈接了所有注冊到內核的事件處理器
(4) 其他的成員一看代碼就知道是什麼意思,這裡就不說明了。
3. input_handle 結構體代表一個成功配對的input_dev和input_handler
  1. struct input_handle {
  2. void *private; //每個配對的事件處理器都會分配一個對應的設備結構,如evdev事件處理器的evdev結構,注意這個結構與設備驅動層的input_dev不同,初始化handle時,保存到這裡。
  3. int open; //打開標志,每個input_handle 打開後才能操作,這個一般通過事件處理器的open方法間接設置
  4. const char *name;
  5. struct input_dev *dev; //關聯的input_dev結構
  6. struct input_handler *handler; //關聯的input_handler結構
  7. struct list_head d_node; //input_handle通過d_node連接到了input_dev上的h_list鏈表上
  8. struct list_head h_node; //input_handle通過h_node連接到了input_handler的h_list鏈表上
  9. };
4. 三個數據結構之間的關系
input_dev 是硬件驅動層,代表一個input設備
input_handler 是事件處理層,代表一個事件處理器
input_handle 個人認為屬於核心層,代表一個配對的input設備與input事件處理器
input_dev 通過全局的input_dev_list鏈接在一起。設備注冊的時候實現這個操作。
input_handler 通過全局的input_handler_list鏈接在一起。事件處理器注冊的時候實現這個操作(事件處理器一般內核自帶,一般不需要我們來寫)

input_hande 沒有一個全局的鏈表,它注冊的時候將自己分別掛在了input_dev 和 input_handler 的h_list上了。通過input_dev 和input_handler就可以找到input_handle 在設備注冊和事件處理器, 注冊的時候都要進行配對工作,配對後就會實現鏈接。通過input_handle也可以找到input_dev和input_handler。

Copyright © Linux教程網 All Rights Reserved