[内核学习]Platform驱动

/ 0评 / 0赞

platform 总线是嵌入式 Linux 的主力总线:DT 里那些不挂 PCI 不挂 USB 的外设,串口,I2C 控制器,GPIO 控制器,全是 platform 设备.上一篇看了总线三角,这篇站到驱动作者这边,把 probe/remove 生命周期,配对规则,devm 资源管理一次跑通,unbind/bind 手术也现场做一遍.

这篇回答几件事:

先搭台.platform 驱动的骨架非常薄,probe 加 remove 加一个 platform_driver 结构:

static int kblog_probe(struct platform_device *pdev) { ... }
static void kblog_remove(struct platform_device *pdev) { ... }

static struct platform_driver kblog_driver = {
	.probe	= kblog_probe,
	.remove	= kblog_remove,
	.driver	= { .name = "kblog-plat" },
};

设备侧这次不依赖 DT,模块参数控制动态生造一个 platform 设备,顺便把 platform_data 塞进去:

pdev = platform_device_register_data(NULL, "kblog-plat", -1,
	&demo_pdata, sizeof(demo_pdata));

实机把整场戏演完.insmod,只有驱动没有设备:

driver 注册成功, 但还没有设备, probe 不会跑

这句日志本身就是知识点:platform_driver_register 只是把驱动挂上总线,没有配对对象时 probe 静默.写 1 生造设备,配对发生,probe 立即触发:

device 注册: kblog-plat
kblog-plat kblog-plat: probe: pdata label=kblog-demo mode=3

platform_data 原样到达.注意设备名叫 kblog-plat 而不是 kblog-plat.0 — 注册时 id 传 -1 表示"不要编号后缀",我第一轮实验拿 kblog-plat.0 去 unbind,吃了个 No such device,这种小坑踩一次就记住了.

下面是这篇最好玩的部分:不动模块,只做解绑:

$ echo kblog-plat > /sys/bus/platform/drivers/kblog-plat/unbind
kblog-plat kblog-plat: remove: devm 内存即将自动释放(...)

remove 跑了,但模块还加载着,/proc/modules 里一条不少.这时候驱动和设备都在,只是断开了配对.再把设备名写进 bind,probe 重新跑,又打一遍那行日志.unbind/bind 是驱动模型的现场手术刀:换驱动版本,重置设备状态,都靠它,不用 reboot 不用 rmmod.设备树里 status = "okay" 的设备开机自动配对,用这个文件可以事后人为干预.

probe/remove 之外,现代驱动的一半体力活在资源管理上,主角 devm.传统写法 probe 里 kzalloc,ioremap,request_irq,remove 里对着 kfree,iounmap,free_irq,顺序还不能错,漏一个就是泄漏,错一个就是 oops.devm 的思路是把"资源的生命周期"绑定到"设备的生命周期":probe 里用 devm_kzalloc,devm_platform_ioremap_resource,devm_request_irq,这些函数内部把释放回调登记到设备的资源表(devres),unbind 或注销时内核按登记的逆序自动释放.remove 里一句 kfree 都不用写 — 实验里 remove 只打了一行日志,资源照样干干净净.

devm 的边界也要清楚:它绑的是设备,不是模块.手动 platform_device_unregister 的路径同样触发 devres 释放,所以热插拔设备安全;但模块卸载不等于设备注销,直接 rmmod 而设备还在,devm 不会救你 — 我的模块在 exit 里先注销设备再注销驱动,顺序是刻意的.另外中断里不能 sleep,devm 分配是可能睡眠的,上下文要分清.

配对规则最后说透,这是面试和实战双高频.platform 总线的 match 函数在 drivers/base/platform.c:1305,优先级从高到低:

  1. driver_override,sysfs 里手工强制指定,最高优先
  2. of_driver_match_device,DT compatible 对 of_match_table,嵌入式首选
  3. acpi_driver_match_device,x86 世界的对等物
  4. pdrv->id_table,传统表匹配
  5. strcmp(pdev->name, drv->name),兜底的名字比较

板上真实设备走第 2 条(上篇的串口案例),本篇实验设备没有 OF 节点,一路落到第 5 条名字相等.同一条总线同时服务两套世界,靠的就是这个漏斗.另外配对成功不等于万事大吉:probe 返回负数,配对立刻解除,总线还会把设备挂进 deferred 队列等依赖就绪(比如时钟没准备好),这叫 probe deferral,是驱动工程师日常的一部分,排查时 /sys/kernel/debug/devices_deferred 一看便知.

最后留几个容易忽略的细节:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注