我们先看些一个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.h>
struct 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))来读取传感器数据!