一、初識linux輸入子系統
linux輸入子系統(linux input subsystem)從上到下由三層實現,分別為:輸入子系統事件處理層(EventHandler)、輸入子系統核心層(InputCore)和輸入子系統設備驅動層。
對於輸入子系統設備驅動層而言,主要實現對硬件設備的讀寫訪問,中斷設置,並把硬件產生的事件轉換為核心層定義的規范提交給事件處理層。即將底層的硬件輸入轉化為統一事件形式,想輸入核心(Input Core)匯報。
對於核心層而言,為設備驅動層提供了規范和接口。設備驅動層只要關心如何驅動硬件並獲得硬件數據(例如按下的按鍵數據),然后調用核心層提供的接口,核心層會自動把數據提交給事件處理層。承上啟下。為驅動層提供輸入設備注冊與操作接口,如:input_register_device;通知事件處理層對事件進行處理;在/Proc下產生相應的設備信息
對於事件處理層而言,則是用戶編程的接口(設備節點),並處理驅動層提交的數據處理。和用戶空間交互。(Linux中在用戶空間將所有的設備都當初文件來處理,由於在一般的驅動程序中都有提供fops接口,以及在/dev下生成相應的設備文件nod,這些操作在輸入子系統中由事件處理層完成)
對於linux輸入子系統的框架結構如下圖1所示:

由上圖所展現的內容就是linux輸入子系統的分層結構。
/dev/input目錄下顯示的是已經注冊在內核中的設備編程接口,用戶通過open這些設備文件來打開不同的輸入設備進行硬件操作。
事件處理層為不同硬件類型提供了用戶訪問及處理接口。例如當我們打開設備/dev/input/mice時,會調用到事件處理層的Mouse Handler來處理輸入事件,這也使得設備驅動層無需關心設備文件的操作,因為Mouse Handler已經有了對應事件處理的方法。
輸入子系統由內核代碼drivers/input/input.c構成,它的存在屏蔽了用戶到設備驅動的交互細節,為設備驅動層和事件處理層提供了相互通信的統一界面。

由上圖可知輸入子系統核心層提供的支持以及如何上報事件到input event drivers。
作為輸入設備的驅動開發者,需要做以下幾步:
在驅動加載模塊中,設置你的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 事件類型最大個數和提供位掩碼支持 |
由表1可知,設備所能表示的事件種類,一個設備可以選擇一個或多個事件類型上報給輸入子系統。
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
