Chinaunix首页 | 论坛 | 博客

分类: LINUX

2012-10-12 08:49:41

    事件处理层文件主要是用来支持输入设备并与用户空间交互,,这部分代码一般不需要我们自己去编写,因为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

阅读(3975) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~