以前,看过国嵌关于input子系统的视频课程,说实话,我看完后脑子里很乱,给我的印象好像是input子系统驱动是一个全新的驱动架构,疑惑相当多。前几天在网上,看到有很多人介绍韦东山老师的linux驱动课程很不错,于是,我就买了第二期的视频,看了韦老师讲解的input子系统视频课程后,我完全明白了整个input子系统的工作机制。为了方便以后查阅,对input子系统的整体框架总结如下:
典型的输入设备(如键盘、鼠标)的工作机制都是差不多的,都是在设备有动作时,向CPU产生一个中断,通知它读取相应的数据。Linux为了方便开发这一类驱动,它实现了这类驱动的通用部分,只留下与设备相关的部分,这样使得开发这一类驱动更加方便。
在Linux中,Input子系统由三大部分组成,它们是Input子系统核心层、Input子系统事件处理层和Input子系统设备驱动层。在通常情况下,Input子系统核心层和Input子系统事件处理层都已经实现了,而作为驱动开发者,我们仅仅只需要完成Input子系统设备驱动层。
对于一个完整的驱动程序,我们首先需要确定设备的主设备号,次设备号,然后向系统注册该设备,最后实现file_operations结构体中的函数。在Input子系统中,这些步骤会分布到不同的层中,最后三个层通过一些联系构成了一个完整的驱动程序。
在input子系统中有三个比较中要的结构体,它们分别是input_handler结构体、input_dev结构体、input_handle结构体。它们的具体代码如下:
struct input_handler {
void *private; //driver相关数据
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); //input_event函数会调用此函数
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; //file_operation结构体,会替换input.c里的file_operation结构体
int minor;
const char *name; //input_handler名称
const struct input_device_id *id_table;
const struct input_device_id *blacklist;
struct list_head h_list; //与该handler相关的handle
struct list_head node;
};
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id 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)];
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 absres[ABS_MAX + 1];
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 input_handle *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
struct device dev;
struct list_head h_list; //与该input_dev相关的handle
struct list_head node;
};
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev; //指向input_dev
struct input_handler *handler; //指向input_handler
struct list_head d_node;
struct list_head h_node;
};
在Input.c(drivers/input)的input_init函数中,有:
err=register_chrdev(INPUT_MAJOR, "input", &input_fops)
它向系统中注册了一个主设备号为INPUT_MAJOR(13),名为input的设备。而它的file_operations结构体,如下所示:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
它仅仅实现了open函数,为没有实现其他函数。其实其他函数会在其open函数中实现。
在input_open_file函数中,会把Input子系统的事件处理层相关结构体handler的file_operations赋值给input_fops,从而完整的实现了file_operations结构体。具体实现的代码:
new_fops = fops_get(handler->fops)
file->f_op = new_fops;
从这里我们可以看出一个Input子系统的驱动的file_operations结构体是在handler结构体中实现的。
对于input_handler结构体,它是input子系统中事件处理层相关的结构体,对于一个具体的事件处理,需要向事件处理层注册这样一个结构体,如在鼠标的事件处理程序mousedev.c的mousedev_init函数中,会调用input_register_handler函数,注册一个具体的handler。而在input_register_handler函数中,会将handler添加到input_handler_list中,然后在input_dev_list中寻找相匹配的dev。具体代码:
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
因而,要形成一个完成的驱动,则必须有相应的dev。
对于input_dev结构体,它是input子系统中设备驱动层相关的结构体,对于一个具体的设备,需要向设备驱动层注册这样一个结构体,如在鼠标的设备驱动程序usbmouse.c的usb_mouse_probe函数中,会调用input_register_device函数,注册一个具体的dev。而在input_register_device函数中,会将dev添加到input_dev_list中,然后再input_handler_list寻找相匹配的handler。具体代码:
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
在input_attach_handler函数中,会调用input_match_device函数,将input_dev_list中的dev与handler相匹配或将input_handler_list中的handler与dev相匹配,匹配成功后就会调用handler中的connect函数。而在connect函数中,会通过handle结构体将匹配好的handler和dev联系起来。如mousedev.c中,connect函数中调用了mousedev_create函数,而在mousedev_create函数中,将handle结构中的handler指向匹配好的handler,而dev指向匹配好的dev,然后会调用input_register_handle函数。
mousedev->handle.dev = input_get_device(dev);
mousedev->handle.name = dev_name(&mousedev->dev);
error = input_register_handle(&mousedev->handle);
在 input_register_handle函数中,将input_handle结构体中的d_node和h_node成员分别添加到了匹配好的dev结构体的h_list中和匹配好的handler结构体的h_list中。
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
list_add_tail_rcu(&handle->d_node, &dev->h_list);
list_add_tail(&handle->h_node, &handler->h_list);
这样,Input驱动程序中的设备驱动和事件处理就这样无缝连接起来了,无论是我们先有了handler还是先有了dev,input子系统都会找了与之匹配的dev或handler,使得三部分之间更加整体化。