linux驱动-Input输入子系统

2015年05月21日 08:37    发布者:Fundyqds
以前,看过国嵌关于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;
unsigned long keybit;
unsigned long relbit;
unsigned long absbit;
unsigned long mscbit;
unsigned long ledbit;
unsigned long sndbit;
unsigned long ffbit;
unsigned long swbit;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;
int rep;unsigned long key;
unsigned long led;
unsigned long snd;
unsigned long sw;int absmax;
int absmin;
int absfuzz;
int absflat;
int absres;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_handlerstruct 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,使得三部分之间更加整体化。