本次直播围绕按键驱动项目,依次讲解阻塞IO与等待队列、中断顶底半部、并发资源保护、内核定时器延时,最后学习Input输入子系统,结合Platform总线、设备树完成整套输入设备驱动开发。
一、Platform总线 + 中断 + 等待队列实现阻塞IO
1. Platform驱动与设备树中断配置
platform_driver包含probe与remove函数,借助module_platform_driver宏简化模块注册。在probe函数中使用platform_get_irq从设备树解析中断资源。
设备树节点中配置中断相关属性:interrupt-parent指定中断控制器,interrupts设置中断号和触发方式,按键常用下降沿触发。
2. 阻塞IO原理
阻塞模式下应用调用read时,进程休眠;非阻塞read会直接返回当前按键状态。
等待队列核心接口:wait_event_interruptible,进程休眠后可被Ctrl+C信号唤醒,是驱动开发推荐用法。中断触发后,在中断处理函数修改条件变量,调用wake_up_interruptible唤醒等待队列中的进程,进程恢复后读取GPIO电平,通过copy_to_user把数据拷贝到用户空间。
3. 开发流程与调试
- Probe阶段:初始化等待队列,申请GPIO、注册中断;
- Read函数:进入阻塞等待;
- 按键触发中断,中断服务函数唤醒进程,读取按键状态返回上层。
调试时可查看/proc/interrupts,确认中断是否成功注册;出现DTS not found错误,一般是设备树文件缺失,重新拷贝原版dts即可。
二、中断上下文的资源竞争与锁机制
当全局变量同时在中断处理函数、驱动read函数中访问,会产生数据竞争问题。
- 互斥锁不能在中断上下文使用:获取不到锁时会睡眠调度,中断上下文禁止睡眠,容易造成系统卡死。
- 自旋锁spinlock:单核环境主要关闭抢占;多核环境用于核间资源竞争,拿不到锁时CPU空转忙等待,适合短临界区。
- 通用保护接口
spin_lock_irqsave/spin_unlock_irqrestore,加锁同时保存并关闭中断状态,兼顾单核多核场景。
三、中断顶半部与底半部
遵循中断快进快出原则,拆分顶半部、底半部。顶半部只处理硬件紧急操作、清除中断标志;耗时逻辑放到底半部执行,提升中断响应速度。
底半部三种实现:
- Tasklet:运行在中断上下文,不能睡眠,适合轻量任务;
- Workqueue工作队列:运行在进程上下文,允许睡眠,适合耗时操作,常用于按键软件消抖,延时后再次读取GPIO电平过滤抖动;
- 线程化中断:
request_threaded_irq,用线程处理底半部逻辑。
四、内核定时器、延时与睡眠函数
HZ代表系统节拍频率,HZ=100时节拍周期10ms,jiffies统计系统节拍,用来计算时间。
定时器相关API:DEFINE_TIMER、add_timer、mod_timer、del_timer,定时器回调运行在底半部。
udelay/mdelay:忙等待,CPU空转,延时精度高;msleep:任务挂起,CPU切换执行其他任务,调度带来一定时间误差;msleep_interruptible可被信号打断。
五、Input输入子系统
Input子系统专门管理按键、触摸屏、鼠标这类输入设备,主设备号13,无需手动编写read/write函数和手动创建设备节点。
开发流程
input_allocate_device申请input设备;input_set_capability声明设备能力,例如EV_KEY按键事件;input_register_device完成注册;- 上报事件:
input_report_key上报按键状态,每次上报必须调用input_sync同步事件,代表一次输入事件结束。
应用层读取/dev/input/eventX,获取input_event结构体,包含type事件类型、code事件编码、value按键状态。
按键驱动开发中,中断顶半部仅做快速标记,按键消抖、事件上报放在底半部处理。
六、驱动核心设计思想回顾
- 设备驱动分离:Platform总线匹配设备树硬件描述和驱动逻辑,硬件资源写在DTS,驱动只保留操作代码,一份驱动适配多个硬件;
- 内核分层子系统:Pinctrl管理引脚复用、GPIO管理电平、Input子系统统一输入事件上报,开发者直接调用内核封装好的API,不用操作底层寄存器。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/weixin_48445537/article/details/168452649



