概述
在刚开始学习驱动程序的时候,分析过输入子系统 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 devicen");
}
/* 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 devicesn");
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 ,创建杂项设备节点。
最后
以上就是谨慎美女为你收集整理的输入子系统 input_match_device 匹配过程剖析的全部内容,希望文章能够帮你解决输入子系统 input_match_device 匹配过程剖析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复