linux輸入子系統(linux input subsystem)從上到下由三層實現,分別爲:輸入子系統事件處理層(EventHandler)、輸入子系統核心層(InputCore)和輸入子系統設備驅動層。html
對於輸入子系統設備驅動層而言,主要實現對硬件設備的讀寫訪問,中斷設置,並把硬件產生的事件轉換爲核心層定義的規範提交給事件處理層。即將底層的硬件輸入轉化爲統一事件形式,想輸入核心(Input Core)彙報。linux
對於核心層而言,爲設備驅動層提供了規範和接口。設備驅動層只要關心如何驅動硬件並得到硬件數據(例如按下的按鍵數據),而後調用核心層提供的接口,核心層會自動把數據提交給事件處理層。承上啓下。爲驅動層提供輸入設備註冊與操做接口,如:input_register_device;通知事件處理層對事件進行處理;在/Proc下產生相應的設備信息編程
對於事件處理層而言,則是用戶編程的接口(設備節點),並處理驅動層提交的數據處理。和用戶空間交互。(Linux中在用戶空間將全部的設備都當初文件來處理,因爲在通常的驅動程序中都有提供fops接口,以及在/dev下生成相應的設備文件nod,這些操做在輸入子系統中由事件處理層完成)框架
對於linux輸入子系統的框架結構以下圖1所示:函數
由上圖所展示的內容就是linux輸入子系統的分層結構。spa
/dev/input目錄下顯示的是已經註冊在內核中的設備編程接口,用戶經過open這些設備文件來打開不一樣的輸入設備進行硬件操做。.net
事件處理層爲不一樣硬件類型提供了用戶訪問及處理接口。例如當咱們打開設備/dev/input/mice時,會調用到事件處理層的Mouse Handler來處理輸入事件,這也使得設備驅動層無需關心設備文件的操做,由於Mouse Handler已經有了對應事件處理的方法。code
輸入子系統由內核代碼drivers/input/input.c構成,它的存在屏蔽了用戶到設備驅動的交互細節,爲設備驅動層和事件處理層提供了相互通訊的統一界面。htm
由上圖可知輸入子系統核心層提供的支持以及如何上報事件到input event drivers。blog
做爲輸入設備的驅動開發者,須要作如下幾步:
在驅動加載模塊中,設置你的input設備支持的事件類型,類型參見表1設置
註冊中斷處理函數,例如鍵盤設備須要編寫按鍵的擡起、放下,觸摸屏設備須要編寫按下、擡起、絕對移動,鼠標設備須要編寫單擊、擡起、相對移動,而且須要在必要的時候提交硬件數據(鍵值/座標/狀態等等)
將輸入設備註冊到輸入子系統中
表1 Linux輸入子系統支持的數據類型
EV_SYN 0x00 同步事件 EV_KEY 0x01 按鍵事件 EV_REL 0x02 相對座標(如:鼠標移動,報告相對最後一次位置的偏移) EV_ABS 0x03 絕對座標(如:觸摸屏或操做杆,報告絕對的座標位置) EV_MSC 0x04 其它 EV_SW 0x05 開關 EV_LED 0x11 按鍵/設備燈 EV_SND 0x12 聲音/警報 EV_REP 0x14 重複 EV_FF 0x15 力反饋 EV_PWR 0x16 電源 EV_FF_STATUS 0x17 力反饋狀態 EV_MAX 0x1f 事件類型最大個數和提供位掩碼支持 |
Linux輸入子系統提供了設備驅動層上報輸入事件的函數,在include/linux/input.h中:
voidinput_report_key(struct input_dev *dev, unsigned int code, int value); //上報按鍵事件
voidinput_report_rel(struct input_dev *dev, unsigned int code, int value); //上報相對座標事件
voidinput_report_abs(struct input_dev *dev, unsigned int code, int value); //上報絕對座標事件
……
當提交輸入設備產生的輸入事件以後,須要調用下面的函數來通知輸入子系統,以處理設備產生的完整事件:
void input_sync(struct input_dev *dev);
設備描述:
input_dev結構
實現設備驅動核心工做是:向系統報告按鍵、觸摸屏等輸入事件(event,經過input_event結構描述),再也不須要關心文件操做接口。驅動報告事件通過inputCore和Eventhandler到達用戶空間。
註冊輸入設備函數:
int input_register_device(struct input_dev *dev)
註銷輸入設備函數:
void input_unregister_device(struct input_dev *dev)
驅動實現——初始化(事件支持):
set_bit()告訴input輸入子系統支持哪些事件,哪些按鍵。例如:
set_bit(EV_KEY,button_dev.evbit) (其中button_dev是struct input_dev類型)
struct input_dev中有兩個成員爲:
evbit:
事件類型(包括
EV_RST,EV_REL,EV_MSC,EV_KEY,EV_ABS,EV_REP等)
keybit:
按鍵類型(當事件類型爲EV_KEY時包括
BTN_LEFT,BTN_0,BTN_1,BTN_MIDDLE等)
驅動實現——報告事件:
用於報告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)
驅動實現——報告結束:
input_sync()同步用於告訴input core子系統報告結束。
實例:觸摸屏設備驅動中,一次點擊的整個報告過程以下:
input_reprot_abs(input_dev,ABS_X,x); //x座標
input_reprot_abs(input_dev,ABS_Y,y); // y座標
input_reprot_abs(input_dev,ABS_PRESSURE,1);
input_sync(input_dev);//同步結束
實例分析(按鍵中斷程序):
//按鍵初始化
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); //支持設備兩個鍵
set_bit(BTN_1,button_dev.keybit); //
input_register_device(&button_dev);//註冊input設備
}
/*在按鍵中斷中報告事件*/
Static void button_interrupt(int irq,void *dummy,struct pt_regs *fp)
{
input_report_key(&button_dev,BTN_0,inb(BUTTON_PORT0));//讀取寄存器BUTTON_PORT0的值
input_report_key(&button_dev,BTN_1,inb(BUTTON_PORT1));
input_sync(&button_dev);
}
總結:input子系統仍然是字符設備驅動程序,可是代碼量減小不少,input子系統只須要完成兩個工做:初始化和事件報告(這裏在linux中是經過中斷來實現的)。讀者不妨用sourceinsignt 輸入input_init去搜關於輸入子系統的實現
http://blog.csdn.net/ielife/article/details/7798952
http://www.cnblogs.com/deng-tao/p/6094049.html
http://www.cnblogs.com/myblesh/articles/2367648.html