1. 程式人生 > >linux input子系統程式碼閱讀簡單記錄

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中的介紹),一般都不需要修改,也不需要擴充。