輸入設備總類繁雜,包括按鍵,鍵盤,觸摸屏,鼠標,搖桿等等,它們本身都是字符設備,不過內核為了能將這些設備的共性抽象出來,簡化驅動的開發,建立了一個Input子系統。Input子系統分為三層,從下至上分別是輸入設備驅動層,輸入核心層以及輸入事件驅動層。這三層中的輸入核心層和輸入事件驅動層都是內核已經完成了的,因此需要我們完成的只有輸入設備驅動層。考慮輸入設備主要的工作過程都是 動作產生(按鍵,觸屏……)-->產生中斷-->讀取數值(鍵值,坐標……)-->將數值傳遞給應用程序。最後一個步驟就屬於事件的處理,對於同一類設備,他們的處理方式都是相同的,因此內核已在事件驅動層為我們做好了,不需我們操心,而產生中斷-->讀取數值是因設備而異的,需要我們根據具體的設備來編寫驅動。一個大致的工作流程就是,input device向上層報告-->input core接收報告,並根據在注冊input device時建立好的連接選擇哪一類handler來處理事件-->通過handler將數據存放在相應的dev(evdev,mousedev…)實例的緩沖區中,等待應用程序來讀取。當然,有時候也需要從應用層向設備層逆向傳遞,比如控制一些和設備相關的LED,蜂鳴器等。設備驅動層,輸入核心層和事件處理層之間的關系可以用下圖來闡釋:
下面來看看Input子系統的關鍵數據結構
- struct input_dev {
- const char *name;
- const char *phys;
- const char *uniq;
- struct input_id id;//與input_handler匹配用的id,包括
-
- unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //設備支持的事件類型
- unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //按鍵事件支持的子事件類型
- unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //相對坐標事件支持的子事件類型
- unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //絕對坐標事件支持的子事件類型
- unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
- unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
- unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
- unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
- unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
-
- unsigned int keycodemax;
- unsigned int keycodesize;
- void *keycode;
- int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
- int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);
-
- struct ff_device *ff;
-
- unsigned int repeat_key; //最近一次的按鍵值
- struct timer_list timer;
-
- int sync;
-
- int abs[ABS_MAX + 1];
- int rep[REP_MAX + 1];
-
- unsigned long key[BITS_TO_LONGS(KEY_CNT)];//反應設備當前的按鍵狀態
- unsigned long led[BITS_TO_LONGS(LED_CNT)];//反應設備當前的led狀態
- unsigned long snd[BITS_TO_LONGS(SND_CNT)];//反應設備當前的聲音輸入狀態
- unsigned long sw[BITS_TO_LONGS(SW_CNT)]; //反應設備當前的開關狀態
-
- int absmax[ABS_MAX + 1];//來自絕對坐標事件的最大鍵值
- int absmin[ABS_MAX + 1];//來自絕對坐標事件的最小鍵值
- int absfuzz[ABS_MAX + 1];
- int absflat[ABS_MAX + 1];
-
- int (*open)(struct input_dev *dev); //第一次打開設備時調用,初始化設備用
- void (*close)(struct input_dev *dev);//最後一個應用程序釋放設備時用,關閉設備
- int (*flush)(struct input_dev *dev, struct file *file);
- /*用於處理傳遞給設備的事件,如LED事件和聲音事件*/
- int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
-
- struct input_handle *grab;//當前占有該設備的input_handle
-
- spinlock_t event_lock;
- struct mutex mutex;
-
- unsigned int users;//打開該設備的用戶數量(input handlers)
- int going_away;
-
- struct device dev;
-
- struct list_head h_list;//該鏈表頭用於鏈接此設備所關聯的input_handle
- struct list_head node; //用於將此設備鏈接到input_dev_list
- };
- struct input_handler {
-
- void *private;
-
- /*event用於處理事件*/
- void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
- /*connect用於建立handler和device的聯系*/
- int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
- /*disconnect用於解除handler和device的聯系*/
- void (*disconnect)(struct input_handle *handle);
- void (*start)(struct input_handle *handle);
-
- const struct file_operations *fops;//handler的一些處理函數
- int minor;//次設備號
- const char *name;
-
- const struct input_device_id *id_table;//用於和device匹配
- const struct input_device_id *blacklist;//匹配黑名單
-
- struct list_head h_list;//用於鏈接和此handler相關的handle
- struct list_head node; //用於將該handler鏈入input_handler_list
- };
- struct input_handle {
-
- void *private;
-
- int open;//記錄設備的打開次數(有多少個應用程序訪問設備)
- const char *name;
-
- struct input_dev *dev;//指向所屬的device
- struct input_handler *handler;//指向所屬的handler
-
- struct list_head d_node;//用於鏈入所屬device的handle鏈表
- struct list_head h_node;//用於鏈入所屬handler的handle鏈表
- };
我們可以看到,input_device和input_handler中都有一個h_list,而input_handle擁有指向input_dev和input_handler的指針,也就是說input_handle是用來關聯input_dev和input_handler的,那麼為什麼一個input_device和input_handler
中擁有的是h_list而不是一個handle呢?因為一個device可能對應多個handler,而一個handler也不能只處理一個device,比如說一個鼠標,它可以對應even handler,也可以對應mouse handler,因此當其注冊時與系統中的handler進行匹配,就有可能產生兩個實例,一個是evdev,另一個是mousedev,而任何一個實例中都只有一個handle。至於以何種方式來傳遞事件,就由用戶程序打開哪個實例來決定。後面一個情況很容易理解,一個事件驅動不能只為一個甚至一種設備服務,系統中可能有多種設備都能使用這類handler,比如event handler就可以匹配所有的設備。在input子系統中,有8種事件驅動,每種事件驅動最多可以對應32個設備,因此dev實例總數最多可以達到256個。下一節將以even handler為例介紹設備注冊以及打開的過程。