一、多模输入子系统设备热插拔事件来源
当前多模输入子系统处理设备热插拔事件的过程如图所示
现有多模输入子系统框架中,设备原始输入事件要经由 libinput 处理,封装成 libinput 的事件类型。多模输入子系统接收并处理 libinput 封装的事件。特别地,libinput 依赖 udev 获得输入设备热插拔事件的通知。udev 将监听热插拔事件、读取热插拔的设备信息,连同枚举已加载设备等接口实现封装成 libudev 共享库。libinput 通过 libudev 封装的这些功能接口与 udev 通信,因而在编译时多模输入模块需要链接到 libudev。
libinput 依赖的两个关键路径是设备节点“/dev/input/”和设备系统路径“/sys/class/input/”。系统路径可以通过设备节点关联得到。通过设备节点可获得设备支持的事件和属性,以及接收和发送输入事件。通过系统路径可以获得设备的详细信息。
libinput 依赖设备能力标记来分设备类型进行事件处理。这些标记是由 udevd 通过应用配置的规则计算得到的。
总结下来有几个关键点:
(1)libudev 是在 eudev 项目中实现的。
(2)多模输入子系统从 udevd 获知输入设备热插拔,而设备节点由 ueventd 创建。
(3)多模输入子系统依赖 udev 提供的信息包括:输入设备热插拔通知;输入设备节点路径;输入设备的能力标记。udev 支持广泛的设备类型,为此定义了一整套复杂的规则来处理设备热插拔事件。多模输入子系统只支持输入设备,我们可以通过简单的逻辑来获取这些信息。
二、判断设备类型
不同的输入设备支持不同的输入事件和属性的组合。
2.1 如触摸屏支持的事件
Discriminator::DeviceType::TOUCHSCREEN, {
.eventTypes {
EV_KEY,
EV_ABS,
},
.keys {
BTN_TOUCH,
},
.abs {
ABS_MT_TOUCH_MAJOR,
ABS_MT_TOUCH_MINOR,
ABS_MT_POSITION_X,
ABS_MT_POSITION_Y,
ABS_MT_TRACKING_ID,
ABS_MT_PRESSURE,
......
},
.properties {
INPUT_PROP_DIRECT,
},
}
2.2 鼠标支持的输入事件和属性
Discriminator::DeviceType::MOUSE, {
.eventTypes {
EV_KEY,
EV_REL,
},
.keys {
BTN_LEFT,
BTN_RIGHT,
BTN_MIDDLE
},
.rels {
REL_X,
REL_Y,
REL_WHEEL
}
},
2.3 键盘支持的输入事件和属性
Discriminator::DeviceType::KEYBOARD,
{
.eventTypes {
EV_KEY,
},
.keys {
KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9,
KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_TAB, KEY_Q,
KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I,
KEY_O, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE,
KEY_ENTER, KEY_LEFTCTRL, KEY_A, KEY_S, KEY_D, KEY_F,
KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON,
KEY_LEFTSHIFT, KEY_BACKSLASH, KEY_Z, KEY_X,
KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, KEY_DOT,
KEY_SLASH, KY_RIGHTSHIFT, KEY_LEFTALT, KEY_SPACE,
KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6,
KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_NUMLOCK,
KEY_SCROLLLOCK, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KPMINUS,
KEY_KP4, KEY_KP5, KEY_KP6, KEY_KPPLUS, KEY_KP1, KEY_KP2,
KEY_KP3, KEY_KP0, KEY_KPDOT, KEY_F11, KEY_F12, ......
},
},
三、主要程序处理逻辑
3.1 libinput 初始化
3.2 热插拔事件处理
3.3 模块结构
3.4 接口列表 struct udev;
四、udevd 进程
4.1 udevd 是一个守护进程
udevd 启动时通过 netlink scoket 监听内核通知的设备热插拔事件:
src/udev/udevd.c:main()
当接收到内核发出的设备热插拔事件时,udevd 将事件加入事件队列,启动工作进程处理事件:
src/udev/udevd.c:main()
工作进程处理热插拔事件,应用配置的规则,执行处理例程,然后将处理后的事件转发给 libudev 事件监听者(libinput):
src/udev/udevd.c:worker_spawn()
libinput 依赖 udevd 处理过的事件进行设备识别。如果去掉规则匹配部分的逻辑,测试时 libinput 不能正常识别设备。参考日志:
4.2 udevd 规则处理逻辑
udevd 启动时会遍历预定的目录,寻找规则配置文件。udevd 逐个解析每个规则配置文件,形成一个规则项的数组。单个配置文件解析得到的规则项是按序连续排列的,其中第一项(如下图的蓝色项)记录了一些从相应规则配置文件解析得到的规则项的信息,包括规则项的个数等。从该配置文件解析得到的规则项紧接着按规则的类型顺序排列。这些规则项可分为两类,一类用来做匹配,另一类指示执行的动作。如下图所示:
应用规则时,单个规则配置文件中的所有规则项是作为一个整体考虑的。设备的属性应匹配所有的匹配项,才能应用后面的动作。
4.3 UDEV 鼠标标记
五、ueventd 进程
5.1 ueventd 是一个守护进程
它通过 netlink scoket 监听内核生成的 uevent 消息。当 ueventd 接收到 uevent 消息时,采取适当的动作来处理。典型的场景是当有设备热插/拔时,内核发送 uevent 事件通知用户端;ueventd 接收到此 uevent 事件,执行如创建/移除设备节点的动作。
系统正常启动后,ueventd 会超时退出。当有设备热插拔事件上报时,如果 udevd 进程未运行,则系统会拉起 ueventd 进程处理事件,创建设备节点。
OpenHarmony 的 ueventd 进程的代码在“/base/startup/init_lite/ueventd/”目录中。系统启动后,ueventd 通过 netlink scoket 监听内核发送的输入设备热插拔事件。
5.2 当前 ueventd 的实现支持“input”子系统事件的处理
参考日志:
5.3 ueventd 可接收并处理设备热插拔事件
测试时加载了两个虚拟触摸屏设备,uevent 响应并创建了设备节点,ueventd 输出日志:
5.4 测试插入键盘,uevent 创建了设备节点
参考日志:
5.5 测试插入鼠标,uevent 创建了设备节点
5.6 ueventd 的日志输出
5.7 一个问题是 ueventd 有监听超时时间设置
设备启动后 uevent 进程因超时而结束运行。如果去除 ueventd 的超时设置,ueventd 则保持运行:
5.8 另一个问题是 ueventd 的功能简单
未提供事件通知的接口和设备信息获取的接口,如果多模依赖 ueventd 实现设备热插拔,则需要增加实现这些接口。