精品轉載:linux input 子系統分析 一

linux input 子系統分析 一 2013-01-09 00:37:52

分類:

原文地址:linux input 子系統分析 一 作者:xieyancheng

linux input子系統分析--概述與數據結構

        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.   
  8.     dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);  //填充了absbit這個數組  
  9. }  

   觸摸屏驅動中是這樣調用的
    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。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章