网上可以找到很多关于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中的介绍),一般都不需要修改,也不需要扩充。