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

    在刚开始学习驱动程序的时候,分析过输入子系统 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 ,创建杂项设备节点。


  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值