什麼是INPUT
Input子系統處理輸入事務,任何輸入設備的驅動程序都可以通過Input輸入子系統提供的接口註冊到內核,利用子系統提供的功能來與用戶空間交互。輸入設備一般包括鍵盤,鼠標,觸摸屏等,在內核中都是以輸入設備出現的。下面分析input輸入子系統的結構,以及功能實現。
Linux中input系統主設備號是13
次設備號:
0-31 joystick(遊戲杆)
32-62 mouse(鼠標)
63 mice(鼠標)
64-95 事件(Event)設備
1. Input子系統是分層結構的,總共分爲三層: 硬件驅動層,子系統核心層,事件處理層。
(1)其中硬件驅動層負責操作具體的硬件設備,這層的代碼是針對具體的驅動程序的,需要驅動程序的作者來編寫。
(2)子系統核心層是鏈接其他兩個層之間的紐帶與橋樑,向下提供驅動層的接口,向上提供事件處理層的接口。
(3)事件處理層負責與用戶程序打交道,將硬件驅動層傳來的事件報告給用戶程序。
2.Input子系統的三個重要結構體:
input_dev 是硬件驅動層,代表一個input設備
input_handler 是事件處理層,代表一個事件處理器
input_handle 個人認爲屬於核心層,代表一個配對的input設備與input事件處理器
- struct input_handle {
- void *private; //每個配對的事件處理器都會分配一個對應的設備結構,如evdev事件處理器的evdev結構,注意這個結構與設備驅動層的input_dev不同,初始化handle時,保存到這裏。
- int open; //打開標誌,每個input_handle 打開後才能操作,這個一般通過事件處理器的open方法間接設置
- const char *name;
- struct input_dev *dev; //關聯的input_dev結構
- struct input_handler *handler; //關聯的input_handler結構
- struct list_head d_node; //input_handle通過d_node連接到了input_dev上的h_list鏈表上
- struct list_head h_node; //input_handle通過h_node連接到了input_handler的h_list鏈表上
- };
- struct input_handler {
- void *private;
- 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);
- const struct file_operations *fops;
- int minor;
- const char *name;
- const struct input_device_id *id_table;
- const struct input_device_id *blacklist;
- struct list_head h_list;
- struct list_head node;
- };
- struct input_dev {
- void *private;
- const char *name;
- const char *phys;
- const char *uniq;
- struct input_id id;
- <span style="WHITE-SPACE: pre"> </span>........
- int (*open)(struct input_dev *dev);
- void (*close)(struct input_dev *dev);
- int (*flush)(struct input_dev *dev, struct file *file);
- int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
- struct class_device cdev;
- union { /* temporarily so while we switching to struct device */
- struct device *parent;
- } dev;
- struct list_head h_list;
- struct list_head node;
- }
- 在內核中,input_dev 表示一個 input設備;input_handler 來表示input設備的 interface。 所有的input_dev 用雙向鏈表 input_dev_list 連起來。
- 在調用 int input_register_device(struct input_dev *dev) 的時候,會將新的 input_dev 加入到這個鏈表中。所有的input_handler 用雙向鏈表 input_handler_list 連起來。
- 在調用 int input_register_handler(struct input_handler *handler) 的時候,會將新的 input_handler 加入到這個鏈表中。每 個input_dev 和 input_handler 是要關聯上才能工作的,在註冊 input_dev 或者 input_handler的時候,就遍歷上面的列表,找到相匹配的,然後調用 input_handler 的 connect函數來將它們聯繫到一起。
- 通常在input_handler 的 connect函數中,就會創建 input_handle, input_handle就是負責將 input_dev 和input_handler 聯繫在一起的.
輸入事件
各層之間通信的基本單位就是事件,任何一個輸入設備的動作都可以抽象成一種事件,如鍵盤的按下,觸摸屏的按下,鼠標的移動等。事件有三種屬性:類型(type),編碼(code),值(value),Input子系統支持的所有事件都定義在input.h中,包括所有支持的類型,所屬類型支持的編碼等。事件傳送的方向是 硬件驅動層-->子系統核心-->事件處理層-->用戶空間
設備有着自己特殊的按鍵鍵碼,我需要將一些標準的按鍵,比如0-9,X-Z等模擬成標準按鍵,比如KEY_0,KEY-Z等,所以需要用到按鍵模擬,具體 方法就是操作/dev/input/event1文件,向它寫入個input_event結構體就可以模擬按鍵的輸入了。
linux/input.h中有定義,這個文件還定義了標準按鍵的編碼等
struct input_event {
struct timeval time; //按鍵時間
__u16 type; //類型,在下面有定義
__u16 code; //要模擬成什麼按鍵
__s32 value;//是按下還是釋放
};
code:
事件的代碼.如果事件的類型代碼是EV_KEY,該代碼code爲設備鍵盤代碼.代碼植0~127爲鍵盤上的按鍵代碼,0x110~0x116 爲鼠標上按鍵代碼,其中0x110(BTN_ LEFT)爲鼠標左鍵,0x111(BTN_RIGHT)爲鼠標右鍵,0x112(BTN_ MIDDLE)爲鼠標中鍵.其它代碼含義請參看include/linux/input.h文件. 如果事件的類型代碼是EV_REL,code值表示軌跡的類型.如指示鼠標的X軸方向REL_X(代碼爲0x00),指示鼠標的Y軸方向REL_Y(代碼
爲0x01),指示鼠標中輪子方向REL_WHEEL(代碼爲0x08).
value:
事件的值.如果事件的類型代碼是EV_KEY,當按鍵按下時值爲1,鬆開時值爲0;如果事件的類型代碼是EV_ REL,value的正數值和負數值分別代表兩個不同方向的值.
type:
/*
* Event types功能實現
*/
#define EV_SYN 0x00
// 同步事件
#define EV_KEY 0x01 // 鍵盤或者按鍵,表示一個鍵碼
#define EV_REL 0x02 // 鼠標設備,表示一個相對的光標位置結果(相對座標)
#define EV_ABS 0x03 // 手寫板產生的值,其是一個絕對整數值
#define EV_MSC 0x04 // 其他類型
#define EV_SW 0x05 //事件切換事件
#define EV_LED 0x11 // LED燈設備
#define EV_SND 0x12 // 輸入聲音
#define EV_REP 0x14 // 允許重複按鍵類型
#define EV_FF 0x15 //力反饋
#define EV_PWR 0x16 // 電源管理事件
#define EV_FF_STATUS 0x17 //力反饋狀態
#define EV_MAX 0x1f //事件類型最大個數
有哪些API
分配/釋放一個輸入設備:
struct input_dev *input_allocate_device(void);
void input_free_device(struct input_dev *dev);
註冊/註銷輸入設備:
int __must_check input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *);
報告輸入事件:
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);/* 報告指定type、code的輸入事件 */
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);/* 報告絕對座標 */
void input_sync(struct input_dev *dev);/* 報告同步事件 */
Input驅動編寫步驟
1.分配一個輸入設備;Set_bit(EV_KEY,button_dev.evbit)
Struct input_dev中有兩個成員,一個是evbit;一個是keybit.分別用來表示設備所支持的事件類型和按鍵類型。