输入子系统 input_match_device 匹配过程剖析

来源:互联网 发布:js调用手机相册插件 编辑:程序博客网 时间:2024/04/30 20:47

    在刚开始学习驱动程序的时候,分析过输入子系统 dev handler 它们之间的关系,现在回过头来复习时,发现当初并没有总结 dev 和 handler 具体的匹配过程,它们是一对一的关系,还是可以多对多?

    为什么会想到这个问题呢,是因为曾经在 2440 平台上做按键输入子系统驱动实验时发现按键上报的数据可以从tty1 中 cat 到,也可以从 eventn 中 cat 到。

    inputn 说到底在之前的文章中就已经分析过了,dev 和Evdev.c 中的 handler 匹配时,调用handler->connect 函数时创建的设备节点,可见如下代码:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
{
        // 不要关心 evdev ,只看 evdev->handle 即可,这里构建了一个 handle ,注意不是handler
        // handle 就是个 中间件,可以理解成胶带,它把 hander 与 dev 连在一起
        evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

        dev_set_name(&evdev->dev, "event%d", minor);
        // 第一次建立联系,在 handle 中记录 dev 与 handle 的信息,这样通过handle就可以找到dev与handler
        // 即是 实现 handle -> dev   handle -> hander 的联系
        evdev->handle.dev = dev;
        evdev->handle.handler = handler;
        // 申请设备号,创建设备节点
        devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor)

       //在input类下创建设备,文件夹的名字是evdev->name(inputn),设备名字dev->cdev.dev(eventn)
        cdev = class_device_create(&input_class, &dev->cdev, devt,
        dev->cdev.dev, evdev->name);
        // 注册 handle
        error = input_register_handle(&evdev->handle);

 }

    那么,tty1中的数据又是从何而来?几番查证,我发现在 keyboard.c 中有另外一个 handler ,它似乎也和 dev 进行了匹配,然后来看一下这个 handler 的 event 函数。

static void kbd_event(struct input_handle *handle, unsigned int event_type,      unsigned int event_code, int value){if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev))kbd_rawcode(value);if (event_type == EV_KEY)kbd_keycode(event_code, value, HW_RAW(handle->dev));tasklet_schedule(&keyboard_tasklet);do_poke_blanked_console = 1;schedule_console_callback();}
    如果是按键类事件的话,就会调用到 kbd_keycode 函数,在这个函数里会调用 put_queue 

static void put_queue(struct vc_data *vc, int ch){struct tty_struct *tty = vc->vc_tty;if (tty) {tty_insert_flip_char(tty, ch, 0);con_schedule_flip(tty);}}
    tty_insert_flip_char 就是 tty 层的东西了,数据就是在这里被送到了我们一开始提到的 tty1。说这些想说明什么呢?显然我们注册的一个 dev 设备和两个 handler 匹配了。

   下面,重点来分析输入子系统的匹配过程!当每一个 dev 和 handler 注册到内核进来,都会调用到 input_attach_handler 函数进行对比匹配。

    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);

    当我们注册一个 dev 进来时,我们可以看到 input_handler_list 链表里的每一个 handler 都会拿出来和 dev 进行匹配,即使有一个匹配成功了,也会继续匹配下去。

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    id = input_match_device(handler->id_table, dev);

    error = handler->connect(handler, dev, id);
}    
    我们可以看到,进行匹配的是 handler->id_table 和 dev ,下面我们以 evdev.c 中的 handler 和 keyboard.c 中的 handler 来分析和我们 dev 的匹配过程。

evdev.c -> handler

static const struct input_device_id evbug_ids[] = {{ .driver_info = 1 },/* Matches all devices */{ },/* Terminating zero entry */};
keyboard.c -> handler

static const struct input_device_id kbd_ids[] = {{.flags = INPUT_DEVICE_ID_MATCH_EVBIT,.evbit = { BIT_MASK(EV_KEY) },    },{.flags = INPUT_DEVICE_ID_MATCH_EVBIT,.evbit = { BIT_MASK(EV_SND) },    },{ },    /* Terminating entry */};
dev
/* 1. 分配一个Input_dev结构体 */buttons_dev = input_allocate_device();if(buttons_dev == NULL){printk(KERN_ERR "Unable to allocate input device\n");}/* 2. 设置 */set_bit(EV_KEY, buttons_dev->evbit); //设置设备支持的事件类型为按键类型set_bit(KEY_L, buttons_dev->keybit);//设置支持哪些 按键类型 set_bit(KEY_S, buttons_dev->keybit);//设置支持哪些 按键类型 set_bit(KEY_ENTER, buttons_dev->keybit);//设置支持哪些 按键类型 set_bit(KEY_1, buttons_dev->keybit);//设置支持哪些 按键类型 set_bit(KEY_2, buttons_dev->keybit);//设置支持哪些 按键类型 set_bit(KEY_3, buttons_dev->keybit);//设置支持哪些 按键类型 /* 3.注册 */error = input_register_device(buttons_dev);
匹配函数
static const struct input_device_id *input_match_device(const struct input_device_id *id,struct input_dev *dev){int i;for (; 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);/*for (i = 0; i < BITS_TO_LONGS(EV_MAX); i++){//如果 id 的支持项 > = dev MATCH_BIT 下一项,或者id==0 进行下一项if ((id->evbit[i] & dev->evbit[i]) != id->evbit[i]) break; }if (i != BITS_TO_LONGS(EV_MAX))continue;*/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);return id;}return NULL;}
    evdev.c 中的 handler 只有一个 driver.info ,没有 flags 所以前面4个if语句都不会成立,直接跳到 MATCH_BIT 函数,首先判断的是支持哪些事件配型。
for (i = 0; i < BITS_TO_LONGS(EV_MAX); i++){//如果 id 的支持项 > = dev MATCH_BIT 下一项,或者id==0 进行下一项if ((id->evbit[i] & dev->evbit[i]) != id->evbit[i]) break; }if (i != BITS_TO_LONGS(EV_MAX))continue;
    BITS_TO_LONGS(EV_MAX) 将宏展开计算一下的话,不难计算出它的值为 1 。重点是下边这条 if 语句。

    以 evbit 为例,如果 dev 支持某一个事件类型,它将在 dev->evbit[0] (看下idtable的定义,evbit数组的成员个数为1,也就是说只有evbit[0])中的某一个 Bit 置1.

    1、evdev.c handler->id = NULL,因此 handler->id->evbit[0]等成员全为0,0&任何值都为0,0!=0不成立,所以不会跳出循环,最后 return id 匹配成功。这也就是为什么说,evdev.c 中的 handler 可以适用于任何的 dev.

    2、keyboard.c handler->id 中的 flag == INPUT_DEVICE_ID_MATCH_EVBIT ,前面4个if条件同样不成立。然后是MATCH_BIT 函数。handler->id.evdev = BIT_MASK(EV_KEY),首先是第一条,MATCH_BIT(evbit, EV_MAX);我们之前 set_bit(EV_KEY, buttons_dev->evbit); 所以他俩相等都为 EV_KEY ,此条匹配成功,进行下一条时,因为 handler->id.keybit 等都为 0 ,因此也都会匹配成功。也就是说,keyboard.c 中的 handler 会匹配支持任何按键类事件的 dev . 

    内核中的 handler 并不多,都分析到这一步了,我们来顺便分一下关于鼠标的 handler .mousedev.c

static struct input_handler mousedev_handler = {.event =mousedev_event,.connect =mousedev_connect,.disconnect =mousedev_disconnect,.fops =&mousedev_fops,.minor =MOUSEDEV_MINOR_BASE,.name ="mousedev",.id_table =mousedev_ids,};
    看一下 mousedev_ids
static const struct input_device_id mousedev_ids[] = {{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_KEYBIT |INPUT_DEVICE_ID_MATCH_RELBIT,.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },.relbit = { BIT_MASK(REL_X) | BIT_MASK(REL_Y) },},/* A mouse like device, at least one button,   two relative axes */{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_RELBIT,.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },.relbit = { BIT_MASK(REL_WHEEL) },},/* A separate scrollwheel */{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_KEYBIT |INPUT_DEVICE_ID_MATCH_ABSBIT,.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },},/* A tablet like device, at least touch detection,   two absolute axes */{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_KEYBIT |INPUT_DEVICE_ID_MATCH_ABSBIT,.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },.keybit = { [BIT_WORD(BTN_TOOL_FINGER)] =BIT_MASK(BTN_TOOL_FINGER) },.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |BIT_MASK(ABS_PRESSURE) |BIT_MASK(ABS_TOOL_WIDTH) },},/* A touchpad */{.flags = INPUT_DEVICE_ID_MATCH_EVBIT |INPUT_DEVICE_ID_MATCH_KEYBIT |INPUT_DEVICE_ID_MATCH_ABSBIT,.evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },.keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },.absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },},/* Mouse-like device with absolute X and Y but ordinary   clicks, like hp ILO2 High Performance mouse */{ },/* Terminating entry */};
    支持以上事件的就会和 mousedev.c 中的 handler 匹配,然后调用 handler->connect

static int mousedev_connect(struct input_handler *handler,    struct input_dev *dev,    const struct input_device_id *id){struct mousedev *mousedev;int minor;int error;for (minor = 0; minor < MOUSEDEV_MINORS; minor++)if (!mousedev_table[minor])break;if (minor == MOUSEDEV_MINORS) {printk(KERN_ERR "mousedev: no more free mousedev devices\n");return -ENFILE;}mousedev = mousedev_create(dev, handler, minor);if (IS_ERR(mousedev))return PTR_ERR(mousedev);error = mixdev_add_device(mousedev);if (error) {mousedev_destroy(mousedev);return error;}return 0;}
   会创建一个杂项设备节点!

下面是结论:

   1、evdev.c 中的 handler 可以匹配任何类型的 dev ,并且在 input 类下的 inputn 文件夹下创建 eventn 设备节点。

   2、如果 dev 支持按键类事件,还会匹配  keyboard.c 中的 handler ,并且将数据上送到 tty 层。

   3、如果 dev 支持鼠标的一些事件,还会匹配 mousedev.c 中的 handler ,创建杂项设备节点。


1 0
原创粉丝点击