Linux 輸入子系統原理理解(原創)


linux    輸入子系統原理理解(原創)

以前學了單獨的按鍵設備驅動以及鼠標驅動,實際上,在linux中實現這些設備驅動,有一種更為推薦的方法,就是input輸入子系統。平常我們的按鍵,觸摸屏,鼠標等輸入型設備都可以利用input接口來簡化驅動程序並實現設備驅動。

輸入子系統原理

linux輸入子系統的體系結構可以分為三個層面,分別為:驅動層、輸入核心層、事件處理層,三個有點類似PHP的MVC模式,意思就是每個層次只是負責單獨的一個功能,無需參與其他的功能,有點類似函數的封裝,好了,廢話不多說,三個層面具體的功能如下:

(1)驅動層:將底層的硬件輸入轉化為統一的事件類型,向輸入核心(input core)匯報0,簡單來說,驅動層就是負責匯報事情。

(2)輸入核心層:為驅動層提供輸入設備的注冊和操作接口。

比如: 1. 用input_register_device函數對設備進行注冊;

2. 通知事件處理層對事件進行處理;

3. 在/proc下產生相應的設備信息。

(3)事件處理層:主要作用就是與用戶空間進行交互。包含提供驅動程序的fops接口,在/dev下生成相應的設備文件節點nod等功能。

 

總的來說,歸納一下上面的一大段內容:

一個事件,如鼠標移動,鍵盤按下事件,首先通過 
 驅動層Driver --> 輸入核心層 InputCore-->事件處理層Event handler-->用戶空間userspace的順序來完成事件的響應。

 

設備描述

Input設備用input_dev結構體來描述。

在input子系統實現設備驅動程序中,驅動的核心工作是向系統報告按鍵,觸摸屏,鼠標等事件,無須關心文件操作接口,因為這些接口是有事件處理層Event handler來實現的。

 

好了,原理講的差不多了,接下來講一下驅動的實現

驅動實現

1.事件支持

首先一個設備驅動,我們應該通過set_bit()函數來告訴輸入子系統它支持哪些事件,哪些按鍵,例如:

Set_bit(EV_KEY,button_dev.evbit);            告訴輸入子系統支持按鍵的時間

Struct input_dev中有兩個成員:

      Evbit: 事件類型

      Keybit: 按鍵類型

 

事件類型:

      EV_RST            reset                   EV_KEY            按鍵

      EV_REL            相對坐標     EV_ABS      絕對坐標

      EV_MSC            其他                  EV_LEC            LED

      EV_SND            聲音                   EV_REP            repeat

      EV_FF            力反饋

但事件類型為EV_KEY時,還需指明按鍵類型:

      BTN_LEFT:      鼠標左鍵            BTN_0:數字0鍵

      BTN_RIGHT:      鼠標右鍵            BTN_1:數字1鍵

      BTN_MIDDLE:      鼠標中鍵

 

2.報告事件

當事件真的發生的時間,我們的驅動層應該向輸入核心層Input Core來報告EV_KEY,EV_REL,EV_ABS等事件,報告函數分別為:

Void input_report_key(struct input_dev *dev,unsigned int code, int value)

Void input_report_rel(struct input_dev *dev,unsigned int code, int value)

Void input_report_abs(struct input_dev *dev,unsigned int code, int value)

 

Code: 事件的代碼:如果事件類型是EV_KEY, 則該代碼則為設備的鍵盤代碼,例如鍵盤上按鍵代碼值為0~127 ,鼠標鍵代碼值為0x110 ~ 0x116 具體請參考include/linux/input.h文件

Value:事件的值。如果事件類型是EV_KEY, 按鍵按下時值為1,松開即為0

 

3.報告結束

Input_sync()用於告訴輸入核心層input core:此次報告已經結束

 

例如,在觸摸屏設備驅動中,一次點擊的整個報告事件過程如下:

Input_report_abs(input_dev, ABS_X, x);      //報告X坐標

Input_report_abs(input_dev, ABS_Y, y);      //報告Y坐標

Input_report_abs(input_dev, ABS_PRESSURE, 1);      //報告事件類型為按下,且value值為1

Input_sync(input_dev);      //報告完畢,同步事件

 

 

一個按鍵驅動程序的局部函數:

//在按鍵中斷中報告事件

Static void buton_interrupt(int irq, void *dummy, struct pt_regs *fp)

{

      //注意,此處所有的按鍵都要報告,無論是0號按鍵還是1號按鍵

      Input_report_key(&button_dev, BTN_0, inb(BUTTON_PORT0));

Input_report_key(&button_dev, BTN_1, inb(BUTTON_PORT1));

      Input_sync(&button_dev);      //報告完畢,同步事件

      //此時,輸入核心層和事件處理層就會將收集的事件類型形成相應的數據,放到file operation 中和 buffer中,以用用戶空間讀取

}

 

//驅動初始化函數

Static int __init  button_init(void){

            //申請中斷,因為按鍵事件報告是在中斷中執行

            If( request_irq(BUTTON_IRQ,button_interrupt, 0, “button”, NULL) )

                      Return –EBUSY;

             Set_bit(EV_KEY, button_dev.evbit);  //告訴輸入子系統支持EV_key事件
             Set_bit(BTN_0, button_dev.keybit);  //告訴輸入子系統支持0號鍵
             Set_bit(BTN_1, button_dev.keybit);  //告訴輸入子系統支持1號鍵

             Input_register_device(&button_dev);      //注冊input設備

}

 

 

應用程序實現

      當相應的時間響應,用戶空間讀取事件是,所讀取到的是 input_event 結構的信息,不再是一個單純的數字,

在input_event 結構中,已經包含 按鍵類型type, 按鍵鍵值code等信息。

用戶需要對input_event 進行相應的解析,獲得相應的信息。
 

Struct   input_event  ev_key;  //聲明結構體 

Button_fd = open(“/dev/event0”,O_RDWR); 

While(1){

      Count = read(button_fd, &ev_key, sizeof( struct input_event ));

      for( i=0; i<(int)count/sizeof( struct input_event ); i++ ){

            if(EV_KEY  ==  ev_key.type)            //確定類型是否相同

                       printf(“type:%d, code:%d, value:%d \n”,ev_key.type,ev_key.code,ev_key.value);

            If( EV_SYN == ev_key.type )  

                       Printf(“syn event \n”);

}

}

Close(button_fd);

 

我們都在路上,有時苦有時甜,為此我送給自己以及相同愛好者們兩個字:堅持 。
       一起加油!!!
       2015年1月16日 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM