linux input子系統程式碼閱讀簡單記錄
網上可以找到很多關於linux輸入子系統的分析和程式碼導讀,這些文章看的再多,都只是別人的總結,自己始終都是需要看原始碼的。對程式碼的理解,想長時間的記住,是不現實的,乾脆把閱讀分析時的順序記錄下來,如果以後再次看這部分的程式碼,參照這個閱讀順序,應該回憶的也會快一些。
1. /linux-2.6.38/include/linux/input.h 和 /linux-2.6.38/drivers/input/input.c 檔案
1.1 核心的3個結構,struct input_dev,struct input_handler,struct input_handle,用面向物件的思路來看,這3個是核心的父類。
struct input_handler結構對應字元裝置,換句話說,在使用者空間,當使用 /dev/input/event0 等裝置檔案的時候,對應的核心程式碼入口點,就是由struct input_handler實現。
struct input_dev結構是對具體的硬體裝置的抽象,通常都會處理中斷程式,把使用者的輸入傳遞到struct input_handler。
struct input_handle結構的功能,是把struct input_dev連線到struct input_handler上,可以在看程式碼的過程中逐步體會。
1.2 這3個核心父類的主要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_reset_device(struct input_dev *);
int __must_check input_register_handler(struct input_handler *);
void input_unregister_handler(struct input_handler *);
int input_handler_for_each_handle(struct input_handler *, void *data,
int (*fn)(struct input_handle *, void *));
int input_register_handle(struct input_handle *);
void input_unregister_handle(struct input_handle *);
int input_grab_device(struct input_handle *);
void input_release_device(struct input_handle *);
int input_open_device(struct input_handle *);
void input_close_device(struct input_handle *);
int input_flush_device(struct input_handle *handle, struct file *file);
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value);
1.3 在input子系統中,有兩個全域性性的連結串列,static LIST_HEAD(input_dev_list); 和 static LIST_HEAD(input_handler_list);
1.3.1 void input_reset_device(struct input_dev *)函式就是把一個input_dev新增到input_dev_list連結串列上,同時,還會在input_handler_list連結串列中找到和這個input_dev相匹配的struct input_handler,並且把相匹配的input_dev和input_handler 連線(connect)起來(通過input_handle建立連線關係)。當連線上之後,input_dev上發生的中斷事件,就可以傳遞到input_handler,進而傳遞到使用者空間。
1.3.2 int __must_check input_register_handler(struct input_handler *)函式就是把一個struct input_handler新增到input_handler_list連結串列上,同時,會從input_dev_list中找出所有的可以和它匹配的input_dev,並且把相匹配的input_dev和input_handler 連線(connect)起來(通過input_handle建立連線關係)。當連線上之後,input_dev上發生的中斷事件,就可以傳遞到input_handler,進而傳遞到使用者空間。
1.3.3 前面提到的“並且把他們connect起來",是由struct input_handler的一個api函式來實現的,函式原型為int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);用面向物件的思路來看,這是一個虛擬函式,需要由struct input_handler的每一個子類來具體實現。
2. /linux-2.6.38/drivers/input/evdev.c 檔案 (或者是/linux-2.6.38/drivers/input/mousedev.c,滑鼠類裝置對應的input_handler)
evdev.c中,實現了一個struct input_handler的子類,程式碼片段如下:
static struct input_handler evdev_handler = {
.event= evdev_event,
.connect= evdev_connect,
.disconnect= evdev_disconnect,
.fops= &evdev_fops,
.minor= EVDEV_MINOR_BASE,
.name= "evdev",
.id_table= evdev_ids,
};
還實現了struct input_handle的子類,程式碼片段如下:
struct evdev {
int open;
int minor;
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client __rcu *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
bool exist;
};
每當有一個struct input_dev 被connect到evdev_handler上的時候,都會構造一個evdev,將這個input_dev和evdev_handler連線上。
每當有一個使用者空間程式使用到這個struct input_dev的時候,都會構造一個struct evdev_client並且新增到evdev->client_list連結串列上,這樣做的目的是讓多個使用者程序共享同一個輸入裝置,每個程序都會得到同樣的一份資料。
3. /linux-2.6.38/drivers/input/keyboard/gpio_keys.c 和 /linux-2.6.38/arch/x86/platform/mrst/mrst.c 檔案。(或者/linux-2.6.38/drivers/input/touchscreen/intel-mid-touch.c檔案)
在gpio_keys.c中,實現了struct input_dev的子類,程式碼片段如下:
struct gpio_keys_drvdata {
struct input_dev *input;
struct mutex disable_lock;
unsigned int n_buttons;
int (*enable)(struct device *dev);
void (*disable)(struct device *dev);
struct gpio_button_data data[0];
};
mrst.c檔案中則是定義了相關的硬體資源。
在gpio_keys.c的gpio_keys_probe(struct platform_device *pdev)函式中,會呼叫input_register_device函式進行註冊。
4. 開發驅動程式的時候,針對一個具體的輸入裝置,通常都僅僅是繼承struct input_dev,並用input_register_device進行註冊。核心程式中,已經實現了好幾個struct input_handler的子類(類似前面2中的介紹),一般都不需要修改,也不需要擴充。