一、input 子系统框架
1. 概念
内核提供统一的输入子系统框架,键盘、鼠标、触摸屏等所有输入类设备都接入 input 子系统。对所有输入类设备做统一规范,统一设备节点,读写数据遵循统一规范。
- 作用:屏蔽不同输入硬件的底层差异,向上层应用提供标准化接口;应用程序统一读取
/dev/input/eventX设备节点获取输入事件。 - 架构层次:硬件层 → 驱动层(上报事件)→ input 核心层 → 事件处理层 → 用户空间。
2. 核心函数接口
input 子系统为驱动开发提供了丰富的核心函数接口,下面按「设备分配与注册」「事件上报」「多点触摸」「位图操作」分类整理常用接口:
| 函数名 | 功能 |
|---|---|
| devm_input_allocate_device | 输入设备分配函数,自动分配 input_dev 结构体,设备卸载自动释放资源 |
| input_allocate_device | 手动分配 input_dev 结构体 |
| input_register_device | 注册输入设备,将设备接入 Linux 输入子系统 |
| input_unregister_device | 注销输入设备,从输入子系统移除设备 |
| input_event | 向输入子系统核心上报原始输入事件 |
| input_report_key | 封装 input_event,专门上报按键事件:按下(value=1)/ 释放(value=0)/ 长按(value=2) |
| input_report_abs | 上报绝对坐标事件,如触摸屏 XY 坐标 |
| input_sync | 上报同步事件,标记一组事件上报结束 |
| input_set_capability | 声明输入设备支持的事件类型与事件码(按键、坐标等) |
| input_set_abs_params | 配置坐标轴参数,范围、精度、最小 / 最大值、模糊值 |
| input_mt_init_slots | 初始化多点协议类型,设置触摸点数量 |
| input_mt_slot | 指定上报的触摸槽位 ID |
| input_mt_report_slot_state | 报告 slot 状态(触摸抬起 / 按下)和触点类型 |
| input_mt_report_pointer_emulation | 处理多点触摸,用于单点兼容模拟 |
| input_mt_sync | 上报多点触摸同步事件,标记一次多点触摸数据上报结束 |
| __set_bit | 位图置位,非原子操作,用于设置设备支持的事件类型 |
| __clear_bit | 位图清位 |
3. 驱动端开发流程
(1)分配struct input_dev结构体(推荐 devm 版本,自动管理资源)
(2)配置 input_dev 成员:name、支持的事件类型(__set_bit)
(3)input_set_capability声明设备能力,例如按键类型、键值
(4)input_register_device把 input 设备注册进内核
(5)of 函数读取设备树节点信息,获取 GPIO
(6)devm_request_irq注册 GPIO 中断,上升下降双边沿触发
(7)中断回调函数中使用 timer 做消抖处理;定时器回调内调用input_event上报按键按下 / 松开事件,调用input_sync提交同步事件
(8)资源释放:del_timer销毁定时器、gpio_free释放 GPIO,input_unregister_device注销设备
注意:使用 input 子系统后,不需要 misc 设备、不需要 file_operations,input 子系统本身属于字符设备。
4. 应用端(APP)读取事件
头文件:#include <linux/input.h>
struct input_event {
struct timeval time; //事件时间戳
__u16 type; //事件大类
__u16 code; //事件编号
__s32 value; //状态数值
};
- 操作:open 打开
/dev/input/eventX,read 读取struct input_event事件。
input_event 成员说明
(1)type 事件大类
EV_KEY:按键事件;EV_ABS:绝对坐标事件(触摸屏);EV_SYN:同步包,一组按键动作(按下 / 松开)结束,必须上报 EV_SYN 同步事件,type=0,code=0,value=0。
(2)code 事件编号 :对应硬件键值,例KEY_0 = 11。
(3)value 状态数值(仅 EV_KEY 有效)
- 1:按键按下
- 0:按键松开
二、platform 驱动框架
1. 概念
Linux 驱动的通用框架是总线 + 设备驱动 ,设备驱动挂载在对应的物理总线上,像 USB、I2C、SPI、UART 都属于真实硬件总线。但嵌入式里很多外设(LED、按键、ADC),并没有真实物理总线,只是直接连在 MPU 内存地址空间。这类外设如果不使用总线框架写驱动,代码会很杂乱,内核也不方便统一管理。所以 Linux 内核从 2.6 版本开始,新增了platform 虚拟总线。就算硬件没有真实总线,这类外设驱动也能套用标准的「总线 + 设备 + 驱动」模型。
platform 总线包含三部分:platform_bus (虚拟总线,内核已经写好,我们不用开发)、platform_device (设备)、platform_driver(驱动)。我们做驱动开发,主要就是编写、适配 platform_device 和 platform_driver。
platform 平台驱动,用于片上集成外设(I2C 控制器、UART、GPIO 控制器等,无物理总线),解决驱动与硬件信息耦合问题。
- 核心思想:设备信息和驱动代码分离。设备信息(寄存器地址、中断号等)放在设备树,驱动只写硬件操作逻辑,同一驱动可适配多个同类型硬件。
- platform 虚拟总线:内核虚拟总线,用来挂载 platform_device(设备)与 platform_driver(驱动),负责匹配。
- 匹配成功:内核执行 probe 函数;卸载执行 remove 函数。
- 好处:统一标准接口,内核方便管理;降低硬件和驱动之间的耦合,驱动代码移植性更好,资源访问更安全。

2. 核心函数接口
| 函数名 | 功能 |
|---|---|
| platform_driver_register | 向内核注册 platform 驱动,挂载到 platform 虚拟总线 |
| platform_driver_unregister | 从内核注销 platform 驱动,解除和设备关联,触发 remove |
| module_platform_driver | 简化注册 / 注销宏,自动生成驱动注册代码,简化平台驱动模块化 |
| platform_get_resource | 从设备获取资源信息,寄存器、内存 |
| platform_get_irq | 从平台设备获取中断资源 |
| platform_set_drvdata | 在平台设备中保存私有数据 |
| platform_get_drvdata | 读取平台设备保存的私有数据 |
3. 匹配过程
(1)内核启动,解析 DTS 设备树,自动生成 platform_device 设备;DTS 节点 reg、gpio、interrupt 等信息存入设备资源列表。
(2)总线维护两条链表:platform 设备链表、platform 驱动链表。
(3)注册 platform_driver 驱动模块,内核触发匹配。 匹配优先级(从高到低): ① of 设备树 compatible 匹配;② ACPI 匹配;③ 设备名匹配;④ id_table 匹配。
(4)匹配成功 → 执行 probe 函数(驱动业务入口)
probe 内部工作:
- 获取设备树资源:寄存器、中断、GPIO;
- 资源申请:request_mem_region、request_irq、gpio_request;
- 硬件初始化、注册子设备(input、misc 等);
- 初始化定时器、创建设备节点;
(5)匹配失败:驱动保持等待状态,后续新增设备节点会再次触发匹配。
4. 代码编写步骤
- DTS 编写设备节点,添加 compatible 属性、reg、interrupt、pinctrl、status="okay";
- 编写 platform_driver 结构体,包含 driver 成员、of_match_table 设备匹配表,定义 probe、remove 回调函数;
- 在 probe 函数中:of 函数解析设备树、获取资源、硬件初始化、注册子驱动;
- remove 函数:资源释放、注销子设备;
- 使用
module_platform_driver注册平台驱动模块。
5. 平台驱动特点
- 标准化,符合内核驱动模型;
- 支持热插拔、资源自动管理;
- 代码可移植性强,硬件信息放在设备树,修改硬件无需改动驱动源码。