输入子系统 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 ,创建杂项设备节点。
- 输入子系统 input_match_device 匹配过程剖析
- 输入子系统设备名称匹配过程以及probe的调用
- 输入子系统匹配过程之list_for_each_entry()函数分析
- Android输入子系统之启动过程分析
- Linux输入子系统过程分析笔记
- 网络子系统在链路层的收发过程剖析
- 网络子系统在链路层的收发过程剖析(一)
- 网络子系统在链路层的收发过程剖析(二)
- 网络子系统在链路层的收发过程剖析(三)
- 网络子系统在链路层的收发过程剖析(四)
- 网络子系统在链路层的收发过程剖析(一)
- 网络子系统在链路层的收发过程剖析
- 网络子系统在链路层的收发过程剖析
- 输入子系统
- 输入子系统
- 输入子系统
- 输入子系统
- 输入子系统
- Android 四种启动模式和 Activity 的 Flag
- 最长上升子序列
- Ajax请求下载文件
- unity5 测试导出场景和光照贴图丢失的问题
- (三)5阻塞型IO实现
- 输入子系统 input_match_device 匹配过程剖析
- blockhouses
- Activity过度动画应用
- 下拉菜单的实现,纯CSS实现下拉菜单 与 使用JS实现下拉菜单
- 小工具
- java方法提取身份证信息中的生日
- 有关于C指针的一些知识点(又名C指针编程之道读书笔记)
- 使用xshell一类工具时可以使用的vi命令
- (三)6 poll和sellct