当前位置: 首页 > news >正文

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.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 输入子系统的设计原则:

  1. 统一格式:所有输入设备(键盘、鼠标、传感器)都使用相同的input_event结构
  2. 固定大小:保证数据的完整性和可预测性
  3. 顺序读取:用户程序按顺序读取每个完整的事件

这就是为什么你可以直接用read(input_fd, &ev, sizeof(ev))来读取传感器数据!

http://www.jsqmd.com/news/1079977/

相关文章:

  • 3步重塑Windows桌面:NoFences分区工具从零到精通的实战指南
  • ArcObjects SDK 10.8技术栈:解决复杂地理数据可视化的专业级方案
  • 【大二那年我C盘又红了,然后有了这篇操作系统笔记】
  • 图论中的完美匹配重配置:从2-switch到k-switch的连通性探索
  • 从对话框到工作流:我用开源工具把 AI Agent 工程化落地的踩坑实录
  • Etcd 3.6.11 官方版下载(夸克网盘+百度网盘,SHA256校验)
  • 从 UI 渲染者到 AI 组织者:2026 年前端工程师转型 AI 应用开发全指南
  • 爬虫转大模型:新人上手的关键步骤
  • 2026数字化农业:水溶肥科学选配指南,助力高产优质
  • 如何从卫星瓦片拼接出一张高清区域影像?
  • Faster-Whisper-GUI技术适配方案:Kotoba-Whisper日语语音识别优化实践
  • Cahn-Hilliard-Keller-Segel模型:弱解存在性与弱强唯一性证明
  • 从入门到精通:JavaWeb开发全流程详解与实战演练
  • 从Del Pezzo曲面到有理六次曲线:Bertini对合与Coble曲面的构造
  • ISO 13355:2016是啥测试,何为 ISO 13355:2016 标准
  • Buzz语音转录工具完整指南:5步实现离线音频转文字,保护隐私的同时提升10倍效率
  • Appium与Mobile MCP实战对比:零配置工具能否撼动自动化测试王者?
  • 轨迹受限优化:基于局部几何的线性收敛新框架解析
  • 别只盯着计算机!未来10年的金饭碗,全在这8大类新工科里了
  • 电磁流量计选型指南:精准匹配工况需求,保障工业测量可靠性
  • 后端转AI应用开发必看:2026年机会与避坑指南(收藏版)
  • Web音视频SDK技术解析:浏览器端实时通信的实现与优化
  • BilibiliDown:3分钟快速上手的跨平台B站视频下载器终极指南
  • 监控费蛋糕盒戏哦格凸河日哦
  • IT爱学堂-Vibe Coding AI全栈开发实战实战分享
  • 私域电商系统架构深度拆解:微三云云平台的技术选型与数据闭环设计
  • 227个实战案例!ArcObjects SDK 10.8终极开发指南:从零掌握GIS核心技术
  • uni-app 零基础入门精讲:从环境搭建到多端发布
  • Java基础:String、StringBuilder 和 StringBufferr对比
  • 主流操作系统大盘点:从桌面到移动