事件处理层文件主要是用来支持输入设备并与用户空间交互,,这部分代码一般不需要我们自己去编写,因为Linux内核已经自带有一些事件处理器,可以支持大部分输入设备,比如evdev.c、mousedev.c、joydev.c等。一个事件处理器用struct input_handler结构体来表示,在evdev.c文件中的定义如下。
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE, //#define EVDEV_MINOR_BASE 64
.name = "evdev",
.id_table = evdev_ids,
};
int input_register_handler(struct input_handler *handler) @input.c
{
struct input_dev *dev;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
input_table[handler->minor >> 5] = handler;
}
list_add_tail(&handler->node, &input_handler_list);
// 对于每个input_dev,调用input_attach_handler
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
}
当向系统注册一个事件处理器时,系统会将该处理器加入input_handler_list链表中,并从input_dev_list链表中取出每一个dev,调用input_attach_handler看看是否支持该设备。上面蓝色粗体语句表明,当注册一个handler时,它会去设置input_table数组的某一项,input_table的定义在input.c中(static struct input_handler *input_table[8]; ),从这个数组中我们也可以看出该系统最多支持8个事件处理器。
对于输入子系统无论是注册设备input_register_device还是注册事件处理器input_register_handler最终都会调用到input_attach_handler进行匹配,当正确匹配的时候就会调用handler的connect方法,接下来我们就分析evdev.c中evdev_connect到底做了什么。
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) @evdev.c
{
struct evdev *evdev;
int minor;
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
INIT_LIST_HEAD(&evdev->client_list);
dev_set_name(&evdev->dev, "event%d", minor);
evdev->handle.dev = input_get_device(dev);
evdev->handle.handler = handler;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle);
error = evdev_install_chrdev(evdev);
error = device_add(&evdev->dev);
}
申请一个struct evdev结构体,对于evdev_handler事件处理器,它把所有自己可以支持的输入设备都定义为一个struct evdev设备,并且可以支持最多32该设备。在struct evdev中有一个很重要的成员struct input_handle handle,它是连接dev和handler的桥梁,下面给出它的定义:
struct input_handle {
void *private;
int open;
const char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
在evdev_connect()中,handle的两个成员指针分别指向匹配的dev和handler,最后调用input_register_handle()。
int input_register_handle(struct input_handle *handle) @input.c
{
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_dev、input_handler和input_handle三个结构体,不难看出,这三个结构体都维护了两个链表。对于input_dev是名为h_list和node的成员,一个系统中可能会有很多输入设备,这些输入设备就是通过node这个成员连成一个双向链表,其中链表头指向input_dev_list。另一个成员h_list维护了一个handle的链表,一个输入设备可能同时会被几个事件处理器支持,而每一次匹配过程都会申请一个input_handle结构体,将输入设备dev和事件处理器handler关联在一起,对于这些handle有一个共同点是它的成员指针handle->dev都指向该输入设备,并且这些input_handle结构体中的成员d_node连接成一个链表并且链表头指向该输入设备的h_list。对于input_handler也一样,有兴趣的可以自己分析,下面给出一个简单的匹配图。 在上面的evdev_connect()函数分析中我们略去了下面几条语句。
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
device_initialize(&evdev->dev);
error = device_add(&evdev->dev);
这些语句是用来创建设备节点,关于Linux下如何自动创建设备节点的详细资料可以参考博文:
连载中……
上一篇:Linux输入子系统框架
下一篇:Linux输入子系统input.c文件分析
——忠于梦想 勇于实践 linux_xpj@opencores.org
阅读(719) | 评论(0) | 转发(0) |