linux input输入子系统分析(input_dev和handler匹配分析)

内核的输入子系统是相对分散的,对多种不同类别的输入设备(如键盘,鼠标,跟踪球,操纵杆,触摸屏,加速计和手写板)进行统一抽象处理。输入子系统包括两类驱动程序:事件驱动程序和设备驱动程序。事件驱动程序负责和应用程序的接口,而设备驱动程序负责和底层输入设备的通信。鼠标事件生成文件mousedev属于事件驱动程序,而PS/2鼠标驱动程序是设备驱动程序。事件驱动程序是标准的,对所有的输入类都是可用的,所以要实现的是设备驱动程序而不是事件驱动程序。设备驱动程序可以利用一个已经存在的,合适的事件驱动程序通过输入核心和用户应用程序接口。(事件驱动程序不需要我们去实现)
输入子系统带来了如下好处: 1.统一了物理形态各异的相似的输入设备的处理功能 2.提供了用于分发输入报告给用户应用程序的简单的事件接口 3.抽取出了输入驱动程序的通用部分,简化了驱动,并引入了一致性 如下图,input子系统分三层,最上一层是event handler,中间是intput core,底层是input driver。input driver把event report到input core层。input core对event进行分发,传到event handler,相应的event handler层把event放到event buffer中,等待用户进程来取。
这里写图片描述
在上一篇中分析的设备驱动程序的probe函数中,先对input_dev结构体进行了属性的配置,然后调用了input_register_device函数向input_core注册这个input_dev设备。下面看看input_register_device这个函数在匹配中做了什么:
首先看input_register_device源码:

 int input_register_device(struct input_dev *dev)
{
    static atomic_t input_no = ATOMIC_INIT(0);
    struct input_handler *handler;
    const char *path;
    int error;

    /* Every input device generates EV_SYN/SYN_REPORT events. */
    __set_bit(EV_SYN, dev->evbit);

    /* KEY_RESERVED is not supposed to be transmitted to userspace. */
    __clear_bit(KEY_RESERVED, dev->keybit);

    /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
    input_cleanse_bitmasks(dev);

    if (!dev->hint_events_per_packet)
        dev->hint_events_per_packet =
                input_estimate_events_per_packet(dev);

    /*
     * If delay and period are pre-set by the driver, then autorepeating
     * is handled by the driver itself and we don't do it in input.c.
     */

     //rep主要是处理重复按键,如果没有定义dev->rep[REP_DELAY]和dev->rep[REP_PERIOD],  
     //则将其赋值为默认值。dev->rep[REP_DELAY]是指第一次按下多久算一次,这里是250ms,  
     //dev->rep[REP_PERIOD]指如果按键没有被抬起,每33ms算一次。  

    init_timer(&dev->timer);
    if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
        dev->timer.data = (long) dev;
        dev->timer.function = input_repeat_key;
        dev->rep[REP_DELAY] = 250;//这两个设置和按键的灵敏度有关
        dev->rep[REP_PERIOD] = 33;
    }

    if (!dev->getkeycode)
        dev->getkeycode = input_default_getkeycode;

    if (!dev->setkeycode)
        dev->setkeycode = input_default_setkeycode;

    dev_set_name(&dev->dev, "input%ld",
             (unsigned long) atomic_inc_return(&input_no) - 1);

    error = device_add(&dev->dev);//将dev加入到linux的设备模型中
    if (error)
        return error;

    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
    pr_info("%s as %s\n",
        dev->name ? dev->name : "Unspecified device",
        path ? path : "N/A");
    kfree(path);

    error = mutex_lock_interruptible(&input_mutex);
    if (error) {
        device_del(&dev->dev);
        return error;
    }
//这个函数主要功能时将input_dev与input_handler_list中的handler进行匹配,
//匹配过程看input_attach_handler源码
    list_add_tail(&dev->node, &input_dev_list);
    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();

    mutex_unlock(&input_mutex);

    return 0;
}   

下面 input_attach_handler源码:

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
//先执行input_match_device函数对dev和handler进行匹配,
//若匹配成功后,执行handler的connect函数,先看input_match_device函数的匹配
//工程,匹配成功后再看connect函数。
    const struct input_device_id *id;
    int error;

    id = input_match_device(handler, dev);
    if (!id)
        return -ENODEV;

    error = handler->connect(handler, dev, id);
    if (error && error != -ENODEV)
        pr_err("failed to attach handler %s to device %s, error: %d\n",
               handler->name, kobject_name(&dev->dev.kobj), error);

    return error;
}

input_match_device函数匹配过程

static const struct input_device_id *input_match_device(struct input_handler *handler,
                            struct input_dev *dev)
{
    const struct input_device_id *id;
    int i;
//这边的input_dev的各项属性,是在设备的驱动程序中注册的,这里的handler的各项属性是在evdev.c(事件驱动程序)
//中赋值的,关于handler的注册将在下面的文章中分析,这里暂时认为这个handler是可以匹配所有的dev
    for (id = handler->id_table; id->flags || id->driver_info; id++) {

        if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
            if (id->bustype != dev->id.bustype)
                continue;

        if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
            if (id->vendor != dev->id.vendor)
                continue;

        if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
            if (id->product != dev->id.product)
                continue;

        if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
            if (id->version != dev->id.version)
                continue;

        MATCH_BIT(evbit,  EV_MAX);
        MATCH_BIT(keybit, KEY_MAX);
        MATCH_BIT(relbit, REL_MAX);
        MATCH_BIT(absbit, ABS_MAX);
        MATCH_BIT(mscbit, MSC_MAX);
        MATCH_BIT(ledbit, LED_MAX);
        MATCH_BIT(sndbit, SND_MAX);
        MATCH_BIT(ffbit,  FF_MAX);
        MATCH_BIT(swbit,  SW_MAX);

        if (!handler->match || handler->match(handler, dev))
            return id;
    }

    return NULL;
}

匹配成功后,调用handler->connect(handler, dev, id)函数,这个函数的定义也是在evdev.c中进行定义的,先
贴出源码进行分析这个函数:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
             const struct input_device_id *id)
{
    struct evdev *evdev;
    int minor;
    int error;

     首先补充几个知识点:(这些知识点也会在下一篇的handler注册中介绍) 
    // static struct input_handler *input_table[8];  
     //#define INPUT_DEVICES 256  
    // 一共有8个input_handler,对应256个设备,所以一个handler对应32个设备。  
    // 这个问题在我参加的一次linux驱动的面试中被问到,当时真是汗啊!!!  
    // static struct evdev *evdev_table[EVDEV_MINORS];  
    // #define EVDEV_MINORS  32  
    // evdev理论上可对应32个设备,其对应的设备节点一般位于/dev/input/event0~/dev/input/event4  
    // 下边的for循环,在evdev_table数组中找一个未使用的地方   
    // evdev_table用来记录此handler下的所有的evdev 
    for (minor = 0; minor < EVDEV_MINORS; minor++)
        if (!evdev_table[minor])
            break;

    if (minor == EVDEV_MINORS) {
        pr_err("no more free evdev devices\n");
        return -ENFILE;
    }

    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
    if (!evdev)
        return -ENOMEM;

    INIT_LIST_HEAD(&evdev->client_list);
    spin_lock_init(&evdev->client_lock);
    mutex_init(&evdev->mutex);
    init_waitqueue_head(&evdev->wait);
//匹配成功后 还在/dev/input下生成eventX节点
    dev_set_name(&evdev->dev, "event%d", minor);
    evdev->exist = true;
    evdev->minor = minor;
//evdev这个结构体中含有handle这个结构体变量,handle中包含了dev结构体和handler结构体
//dev用来记录input_dev,handler用来记录与dev相匹配的handle,并把evdev作为handle
//的私有变量
    evdev->handle.dev = input_get_device(dev);
    evdev->handle.name = dev_name(&evdev->dev);
    evdev->handle.handler = handler;
    evdev->handle.private = evdev;
//设置evdevX这个设备的主设备号和次设备号
    evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
    evdev->dev.class = &input_class;
    evdev->dev.parent = &dev->dev;
    evdev->dev.release = evdev_free;
    device_initialize(&evdev->dev);

// input_register_handle完成的主要功能是:  
//  list_add_tail_rcu(&handle->d_node, &dev->h_list);将handle->d_node加到dev->h_list中
//  list_add_tail(&handle->h_node, &handler->h_list);将handle->h_node加到handler->h_list中
//这样以来无论是先得到dev还是先得到handler都可以通过handle找到与之匹配的handle,handle将dev和handler
//紧密联合起来     
    error = input_register_handle(&evdev->handle);
    if (error)
        goto err_free_evdev;
//evdev_install_chrdev完成的功能是evdev_table[evdev->minor]=evdev; 
//将evdev加入到evdev_table中
    error = evdev_install_chrdev(evdev);
    if (error)
        goto err_unregister_handle;

    error = device_add(&evdev->dev);//向设备模型中添加这个设备
    if (error)
        goto err_cleanup_evdev;

    return 0;

 err_cleanup_evdev:
    evdev_cleanup(evdev);
 err_unregister_handle:
    input_unregister_handle(&evdev->handle);
 err_free_evdev:
    put_device(&evdev->dev);
    return error;
}

在connect函数中的evdev结构体,其有结构体变量handle,在handle中又有handler和dev这两个结构体
用来描述相匹配的handler和dev。然后向设备模型添加evdev结构体,完成了整个input_dev和handler的匹配过程

猜你喜欢

转载自blog.csdn.net/chihunqi5879/article/details/80803467