input 设备 - kernel 和 应用数据 交互

📅 2026/6/26 7:19:13
input 设备 - kernel 和 应用数据 交互
我们先看些一个sensor report input 的 数据流程[用户程序] ──ioctl(START)── [内核态 驱动逻辑 sensor_enable]│▼[内核态 驱动逻辑 schedule_delayed_work] ──每隔30ms── [report]│┌───────────┬───────────┬───────────┬───────┘▼ ▼ ▼ ▼[ABS_X] [ABS_Y] [ABS_Z] [SYN]└───────────┴───────────┴───────────┘│▼[内核态 驱动逻辑------输入子系统队列]│▼[用户态程序 read()] ── 循环读取每个 event┌──────────────────────────────────────────────────────┐│ 1. 用户调用 ioctl(GSENSOR_IOCTL_START) │└────────────────────┬───────────────────────────────┘│▼┌──────────────────────────────────────────────────────┐│ 2. sensor_enable() → 启动定时任务 ││ schedule_delayed_work(每 poll_delay_ms 执行) │└────────────────────┬───────────────────────────────┘│▼┌──────────────────────────────────────────────────────┐│ 3. sensor_delaywork_func() → report() ││ └── 读取硬件数据 → input_report_abs() ×3 ││ └── input_sync() → 帧结束 │└────────────────────┬───────────────────────────────┘│▼┌──────────────────────────────────────────────────────┐│ 4. 用户 read() → 获取 input_event ││ └── 解析 ev.type/code/value │└──────────────────────────────────────────────────────┘核心流程一句话用户通过 ioctl 启动传感器 → 内核定时读取硬件数据 → 通过 input_report_abs/input_sync 上报 → 用户 read() 获取数据用户如何 获取数据的你 read eventX 这个设备/* 打开传感器控制设备 */gsensor_fd open(GSENSOR_DEV, O_RDONLY);/* 启动传感器 */ioctl(gsensor_fd, GSENSOR_IOCTL_START)input_fd open(INPUT_DEV, O_RDONLY | O_NONBLOCK);/* 主循环读取数据 */while (running) {ssize_t n read(input_fd, ev, sizeof(ev));if (ev.type EV_ABS) {switch (ev.code) {case ABS_X:x ev.value * SCALE; break;case ABS_Y:y ev.value * SCALE; break;case ABS_Z:z ev.value * SCALE; break;}} else if (ev.type EV_SYN) {// 收到syn 后,同步显示一次结果printf(\r%.2f %.2f %.2f, x, y, z);fflush(stdout);}}------------------------------------------------------------------------------------#include linux/input.hstruct input_event {struct timeval time; // 时间戳秒 微秒__u16 type; // 事件类型如 EV_ABS、EV_SYN__u16 code; // 事件代码如 ABS_X、ABS_Y__s32 value; // 事件值传感器数据};为什么每次读取都是固定大小原因1内核保证数据完整性驱动调用input_report_abs()时内核会将数据封装成完整的input_event结构// 驱动中的操作 input_report_abs(input_dev, ABS_X, 1234); // 生成一个 input_event input_sync(input_dev); // 生成另一个 input_event每个事件都是独立的、完整的 24 字节数据块。原因2输入子系统的队列机制内核维护一个事件队列每个队列元素都是完整的input_event内核事件队列 ────────────── ┌─────────────────────────────┐ │ input_event #1 (24字节) │ ← read() 读取这里 ├─────────────────────────────┤ │ input_event #2 (24字节) │ ├─────────────────────────────┤ │ input_event #3 (24字节) │ └─────────────────────────────┘原因3用户程序的读取方式struct input_event ev; ssize_t n read(input_fd, ev, sizeof(ev)); // 读取 24 字节 if (n sizeof(ev)) { // 读取成功ev 包含完整的事件数据 printf(type%d, code%d, value%d\n, ev.type, ev.code, ev.value); }每次读取恰好是一个完整的input_event结构体。数据读取的时序驱动端 用户端 ─────── ────── │ │ │ input_report_abs(ABS_X, 1234) │ │ ↓ │ │ 写入队列: [time][EV_ABS][ABS_X][1234] │ │ │ input_report_abs(ABS_Y, -56) │ │ ↓ │ │ 写入队列: [time][EV_ABS][ABS_Y][-56] │ │ │ input_sync() │ │ ↓ │ │ 写入队列: [time][EV_SYN][0][0] │ │ │ read(ev, 24) → ev [time][EV_ABS][ABS_X][1234] │ │ │ read(ev, 24) → ev [time][EV_ABS][ABS_Y][-56] │ │ │ read(ev, 24) → ev [time][EV_SYN][0][0]在你的代码中的应用struct input_event ev; // 固定大小 24 字节 while (running) { ssize_t n read(input_fd, ev, sizeof(ev)); // 每次读取 24 字节 if (n ! sizeof(ev)) continue; // 确保读取完整 // 解析固定格式的数据 if (ev.type EV_ABS) { switch (ev.code) { case ABS_X: x ev.value * SCALE; break; case ABS_Y: y ev.value * SCALE; break; case ABS_Z: z ev.value * SCALE; break; } } }总结是的struct input_event是固定大小的结构体24字节每次读取都是按照固定格式。这是 Linux 输入子系统的设计原则统一格式所有输入设备键盘、鼠标、传感器都使用相同的input_event结构固定大小保证数据的完整性和可预测性顺序读取用户程序按顺序读取每个完整的事件这就是为什么你可以直接用read(input_fd, ev, sizeof(ev))来读取传感器数据