概述
1 input设备初始化
我们以一个遥控器的设备为例,分析input设备的注册流程:
1.1 申请input设备
remote_dev = input_allocate_device();
1.2 初始化设备结构体
remote_dev->name = XXX_NAME;
remote_dev->phys = XXX_PHYS;
remote_dev->id.vendor = XXX_VENDOR_ID;
remote_dev->id.product = XXX_PRODUCT_ID;
remote_dev->id.version = XXX_CTL_VERSION;
1.3 配置设备支持的事件类型
set_bit(EV_KEY, remote_dev->evbit);
set_bit(EV_REP, remote_dev->evbit);
1.4 配置设备支持的按键值
remote_dev->keycode = xxx_key_tab;
remote_dev->keycodesize = sizeof(int);
remote_dev->keycodemax = ARRAY_SIZE(xxx_key_tab);
1.5 注册input设备
input_register_device(remote_dev);
2 input设备的注册流程
2.1 input_register_device函数
根据代码中注释来分析注册流程:
int input_register_device(struct input_dev *dev)
{
struct input_devres *devres = NULL;
struct input_handler *handler;
unsigned int packet_size;
const char *path;
int error;
if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) { // 绝对设备,却没有坐标信息,拒绝注册
return -EINVAL;
}
__set_bit(EV_SYN, dev->evbit); // 每个输入设备都会生成EV_SYN/SYN_REPORT事件
error = device_add(&dev->dev);
if (error)
goto err_free_vals;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%s as %sn",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error)
goto err_device_del;
list_add_tail(&dev->node, &input_dev_list); // 将设备插入input_dev_list链尾
list_for_each_entry(handler, &input_handler_list, node) // 遍历所有input事件驱动,匹配设备
input_attach_handler(dev, handler); // 下面细说
input_wakeup_procfs_readers(); // poll事件,这里不细说
mutex_unlock(&input_mutex);
}
2.2 input设备匹配input handler的过程
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
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: %dn",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
好,划重点,我们来好好分析下输入子系统的匹配过程!
-
事实上,当每一个 dev 和 handler 注册到内核进来,都会调用到 input_attach_handler 函数进行对比匹配。
当Input dev注册时,遍历所有handler,调用input_attach_handler匹配。
当handler注册时,遍历所有Input dev,调用input_attach_handler匹配。
-
在遍历的过程中,即使有一个匹配成功了,也会继续匹配下去。
所以Input dev和handler是多对多的关系。
举例:学习时用的按键输入子系统驱动实验,会发现按键上报的数据可以从tty1 中 cat 到,也可以从 eventx 中 cat 到。
-
真正进行匹配的是handler->id_table 和 dev。
下面我们分析下内核中常见的几种handler和我们的remote_dev匹配的过程:
2.2.1 remote_dev与evdev handler匹配
evdev.c -> handler -> id_table
static const struct input_device_id evbug_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
remote_dev->evbit
set_bit(EV_KEY, remote_dev->evbit); // 按键事件
set_bit(EV_REP, remote_dev->evbit); // 重复事件
匹配函数input_match_device_id
bool input_match_device_id(const struct input_dev *dev,
const struct input_device_id *id)
{
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)
return false;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
return false;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
return false;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
return false;
/* __bitmap_subset(A, B): 判断A位图是否是B位图的子集*/
if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
!bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
!bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
!bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
!bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
return false;
}
return true;
}
evdev.c -> handler -> id_table除了driver_info都为空,所以以上if判断中都进不去,程序直接跳到28行返回真。
这也说明了"evdev事件驱动可以适用于任何的 input_dev."
2.2.2 remote_dev与keyboard handler匹配
我们继续来看keyboard handler
keyboard -> handler -> id_table
bool input_match_device_id(const struct input_dev *dev,
const struct input_device_id *id)
{
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)
return false;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
return false;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
return false;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
return false;
/* __bitmap_subset(A, B): 判断A位图是否是B位图的子集*/
if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
!bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
!bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
!bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
!bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
return false;
}
return true;
}
remote_dev->evbit
set_bit(EV_KEY, remote_dev->evbit); // 按键事件
set_bit(EV_REP, remote_dev->evbit); // 重复事件
keyboard handler的flag=INPUT_DEVICE_ID_MATCH_EVBIT,所以input_match_device_id中前4条if条件也不成立。然后evbit位图中置位了EV_KEY和EV_SND. 而remote_dev的evbit位图置位了EV_KEY和EV_REP.
显然,keyboard handler->evbit是remote_dev->evbit的子集,说明keyboard驱动支持我们的remote_dev
2.2.3 remote_dev与mousedev_handler handler匹配
mousedev -> handler -> id_table
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事件驱动匹配,这里不再细说。
2.3 Input设备与handler建立连接
dev和handler匹配成功后,会调用handler->connect(handler, dev, id);
2.3.1 evdev事件驱动
如果是evdev事件驱动,会调用evdev_connect接口:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error;
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %dn", error);
return error;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
evdev->exist = true;
dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
dev_no -= EVDEV_MINOR_BASE;
dev_set_name(&evdev->dev, "event%d", dev_no);
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
cdev_init(&evdev->cdev, &evdev_fops);
error = cdev_device_add(&evdev->cdev, &evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
err_free_minor:
input_free_minor(minor);
return error;
}
我们来重点分析下evdev_connect做了哪些事情:
step1:创建一个evdev设备,分配一个次设备号,该设备号决定了/dev/input/eventX中的X的值。
step2:初始化evdev->handle,这个handle不是evdev->handler,它们两有完全不同的用途。
evdev->handle用来绑定匹配成功的Input_dev和evdev->handler
step3:将evdev->handle注册进内核. 见44行。
事实上就是将evdev->handle挂在了input_dev_list和input_handler_list的h_list上。从此,建立好了三者 的铁三角关系,通过input_handler_list和input_dev_list以及input_hande中任何一方,都可以找到彼此。
借用大神的一张图,直观的感受下这个三角关系:
step4:将step1创建的evdev设备注册到内核,见48-50行。app访问我们的input_dev设备就是通过这个新建的evdev设备节点来访问的。
3 总结
回到我们的remote_dev,至此remote_dev的注册,匹配,连接过程就完成了。设备/dev/input/eventX被创建,app在访问remote_dev时,我们最终就能够通过基于evdev->fops的open、read、write等方式在应用层获取数据.
-
evdev事件驱动可以支持任何类型的dev,并且会在/dev/input/eventX下创建eventX设备节点。
-
如果dev支持按键类事件,会匹配keyboard事件驱动,会将数据上送到tty层。
-
如果 dev 支持鼠标的一些事件,会匹配 mousedev的事件驱动,具体还会要分析id_table,并且会创建杂项设备节点。
4 遗留问题
至此,我们已经分析完了遥控器设备的注册流程,回过头想想,其实还有很多问题没有解决,比如:
- 遥控器数据是如何上报的?app怎样高效得到遥控器上报的数据?app怎么辨别用户按下的是什么键?
- app open一个input节点,最终怎么操作到我们remote设备?read和write又是如何访问设备的?
- evdev匹配这很多input_dev,它是如何区分这些设备的?如何管理这个设备的数据?
至于app如何访问input_dev的这些细节,这个也比较复杂,下回剖析~
最后
以上就是眼睛大铅笔为你收集整理的input子系统分析一(input设备注册)1 input设备初始化的全部内容,希望文章能够帮你解决input子系统分析一(input设备注册)1 input设备初始化所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复