20251202 - Linux输入子系统

Linux 输入子系统(Input Subsystem)是 Linux 内核中专门负责处理输入设备(按键、触摸屏、鼠标、键盘、游戏手柄等)的框架。


1. 为什么需要输入子系统?

如果没有这个子系统,每个硬件厂家都会发明自己的协议。

  • A 厂家的鼠标发出的数据是:"MOUSE: LEFT CLICK"
  • B 厂家的鼠标发出的数据是:0x01 0x00
  • 触摸屏发出的数据是:X=100, Y=200

作为应用开发者(写 APP 的人),你会疯掉,因为你需要适配成千上万种硬件。

Linux 输入子系统的作用: "统一语言"。

无论底层是鼠标、键盘还是触摸屏,到了应用层,Linux 都把它们统一封装成 struct input_event 结构体。应用程序只需要读取这个结构体,就能知道发生了什么。


2. Linux 输入系统的框架

整个框架可以想象成一个 "三层汉堡包"

第一层:输入设备驱动层 (Input Device Drivers)
  • 位置:最底层,直接和硬件打交道。
  • 职责
    1. 初始化硬件(GPIO、I2C、SPI 等)。
    2. 读取寄存器或电平状态。
    3. 关键点:它不知道这些数据给谁用,它只负责把硬件产生的原始数据(比如"GPIO变低了")转化为核心层能听懂的"事件"(比如"按键被按下了"),然后汇报给核心层。
  • 开发者工作:这是嵌入式驱动工程师主要工作的地方。
第二层:输入核心层 (Input Core)
  • 位置drivers/input/input.c
  • 职责
    1. 它是中间的"大管家"。
    2. 向下提供接口给驱动层注册设备 (input_register_device)。
    3. 向上提供接口给事件处理层注册处理器 (input_register_handler)。
    4. 匹配 (Match):当新的驱动加载时,它会查找有没有合适的处理程序;反之亦然。
第三层:事件处理层 (Input Event Handlers)
  • 位置:最上层,直接和应用程序(用户空间)打交道。
  • 职责
    1. 接收核心层转送过来的事件。
    2. 创建设备节点(例如 /dev/input/event0)。
    3. 把内核事件缓冲起来,当 APP 来 read() 时,发给 APP。
  • 最著名的 Handlerevdev (Event Device)。它通吃所有输入设备,我们在开发中 99% 都是和 /dev/input/eventX 打交道。
架构图解

Plaintext

复制代码
   +-------------------------------+
   |   用户空间应用程序 (APP)      |  (也就是你写的 read/scanf/Qt)
   +-------------------------------+
               ^  | read()
               |  v
   +-------------------------------+
   |   设备节点 /dev/input/event0  |
   +-------------------------------+
               ^
               |  (evdev.c)
   +-------------------------------+
   |   事件处理层 (Event Handler)  |  <-- 标准化接口
   +-------------------------------+
               ^
               |
   +-------------------------------+
   |   输入核心层 (Input Core)     |  <-- 负责撮合匹配
   +-------------------------------+
               ^
               | input_report_key()
   +-------------------------------+
   |   设备驱动层 (Device Driver)  |  <-- 你需要写代码的地方
   +-------------------------------+
               ^
               | (中断/轮询)
   +-------------------------------+
   |      硬件 (按键/触摸屏)       |
   +-------------------------------+

3. 核心数据结构:struct input_event

这是整个子系统的"通用货币"。无论什么设备,最终你在 APP 里读到的都是这个结构体(定义在 <linux/input.h>):

C

复制代码
struct input_event {
    struct timeval time;  // 事件发生的时间戳
    __u16 type;           // 事件类型 (按键? 相对位移? 绝对位移?)
    __u16 code;           // 事件代码 (具体哪个键? X轴还是Y轴?)
    __s32 value;          // 事件值   (按下了还是松开了? 坐标是多少?)
};
  • type (类型) :
    • EV_KEY: 按键(键盘、鼠标左键、触摸屏点击)。
    • EV_REL: 相对位移(鼠标移动)。
    • EV_ABS: 绝对位移(触摸屏坐标)。
    • EV_SYN: 同步事件(告诉系统这一组信号发完了)。
  • code (代码) :
    • 如果 type 是 EV_KEY,code 可能是 BTN_LEFT (鼠标左键) 或 KEY_A (键盘A)。
    • 如果 type 是 EV_ABS,code 可能是 ABS_X (X轴) 或 ABS_Y (Y轴)。
  • value (值) :
    • 对于按键:1 (按下), 0 (松开), 2 (长按重复)。
    • 对于坐标:具体的像素坐标值。

4. 如何调试输入系统?

在嵌入式 Linux (如你的 I.MX6ULL) 上,调试输入设备非常方便。

第一步:查看当前有哪些输入设备

使用 ls 命令查看设备节点:

Bash

复制代码
ls -l /dev/input/

你会看到 event0, event1 等。

查看更详细的信息(看看 event0 到底是谁):

Bash

复制代码
cat /proc/bus/input/devices

或者

Bash

复制代码
ls -l /sys/class/input/event*

这能帮你确定:触摸屏是 event0 还是 event1

第二步:使用 hexdump 查看原始数据

假设触摸屏是 /dev/input/event1。

你可以直接读它:

Bash

复制代码
hexdump /dev/input/event1

然后在屏幕上点一下。你会看到一堆十六进制数据哗啦啦地打印出来。

  • 现象 :有数据打印,说明驱动层硬件是好的。
  • 缺点:看不懂,只能看到原始二进制。
第三步:使用 evtest 工具(神器,强烈推荐)

大多数嵌入式文件系统(Buildroot)都自带这个工具。

  1. 运行 evtest
  2. 它会列出所有输入设备,让你选 ID。
  3. 选择你的触摸屏或按键。
  4. 操作硬件。

它会把二进制结构体翻译成人话:

Plaintext

复制代码
Event: time 16843.22, type 3 (EV_ABS), code 0 (ABS_X), value 500
Event: time 16843.22, type 3 (EV_ABS), code 1 (ABS_Y), value 300
Event: time 16843.22, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 1

这表示:在坐标 (500, 300) 的位置,按下了触摸屏。

第四步:编写简单的测试代码 (APP)

你需要像操作文件一样操作它:

  1. open("/dev/input/event1", O_RDONLY)
  2. read(fd, &my_event, sizeof(struct input_event))
  3. printf 打印结构体里的内容。

总结

  1. 输入子系统是为了统一各类输入设备的接口。
  2. 核心结构体struct input_event(时间、类型、代码、值)。
  3. 核心流程:硬件 -> 驱动 -> Core -> Handler (evdev) -> APP。
  4. 调试 :先用 cat /proc/bus/input/devices 找设备,再用 evtest 抓数据。
相关推荐
boneStudent4 小时前
Day39:智能家居环境监测系统
stm32·单片机·嵌入式硬件·智能家居
Zeku5 小时前
20251202 - Linux输入系统的基础知识 - tslib
stm32·freertos·linux驱动开发·linux应用开发
polarislove02145 小时前
5.8W25Q64 实验(下)-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件
xingzhemengyou16 小时前
STM32 Cortex-M4内核时钟系统
stm32·单片机·嵌入式硬件
Zeku6 小时前
20251130 - 详细解析Framebuffer应用编程中涉及到的API函数
linux·驱动开发·嵌入式软件·linux应用开发
一个平凡而乐于分享的小比特8 小时前
STM32 GPIO 8种工作模式深入详解
stm32·单片机·嵌入式硬件·gpio
x县豆瓣酱9 小时前
STM32F1新建工程(基于STMCubeMX)
stm32·单片机·嵌入式硬件
Sean_woo19989 小时前
Zephyr rtos ESP32系列BSP提交流程指南
stm32·单片机·esp32·wsl·zephyr·立创开发板
polarislove02149 小时前
8.2 时钟树编程-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件