07、注册 platform 驱动
一、注册 platform 驱动
以下是更直白的改写版本:
platform_driver_register 函数详解
作用 这个函数用于把平台驱动程序注册到 Linux 内核中。注册后,内核能自动匹配对应的硬件设备,并在需要时调用驱动中的功能函数。
参数和返回值
参数
driver
:指向平台驱动程序描述结构体的指针。这个结构体里包含驱动的名字、功能函数(如初始化设备的probe
函数)等信息。返回值
- 0:注册成功。
- 负数:注册失败,返回具体的错误码(比如内存不足或设备已存在)。
底层实现原理
- 宏简化调用
platform_driver_register
其实是一个宏,它调用底层函数__platform_driver_register
,并自动关联当前驱动模块:
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE)
2
THIS_MODULE
是当前驱动模块的指针,确保驱动和模块生命周期绑定。- 核心注册函数 实际工作由
__platform_driver_register
完成,步骤如下:
int __platform_driver_register(struct platform_driver *drv, struct module *owner)
{
// 1. 设置驱动的所有者为当前模块
drv->driver.owner = owner;
// 2. 指定驱动所属的总线类型为"平台总线"
drv->driver.bus = &platform_bus_type;
// 3. 绑定内核默认的设备匹配函数
drv->driver.probe = platform_drv_probe; // 设备匹配时调用
drv->driver.remove = platform_drv_remove; // 移除设备时调用
drv->driver.shutdown = platform_drv_shutdown;// 系统关机时调用
// 4. 最终将驱动注册到内核总线系统
return driver_register(&drv->driver);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
关键步骤解释
设置所有者
drv->driver.owner = owner;
将驱动和当前模块绑定,避免内核找不到驱动来源。指定总线类型
drv->driver.bus = &platform_bus_type;
告知内核这是平台总线上的驱动,方便内核在平台设备出现时自动匹配。绑定回调函数 内核需要知道驱动在什么时机做什么事:
probe
:当设备被发现时,调用驱动的初始化函数。remove
:当设备被移除时,调用清理函数。shutdown
:系统关机时调用的收尾函数。
注册到内核
driver_register()
是最终注册动作,将驱动添加到内核的总线系统中,完成注册流程。
:::
总结
调用 platform_driver_register
后,内核会:
记录这个驱动的信息;
自动扫描已存在的设备,尝试匹配;
当设备出现时,触发驱动的 probe
函数完成初始化。
这样开发者只需要写好驱动的 probe
和 remove
等核心函数,内核会自动管理注册和匹配过程。
通过这个流程,驱动就能被系统识别并正常工作了。 :::
二、probe函数
重点看看 platform 总线的 probe 函数是如何执行的:
static int platform_drv_probe(struct device *_dev)
{
// 将传递给驱动程序的设备指针转换为 platform_driver 结构体指针
struct platform_driver *drv = to_platform_driver(_dev->driver);
// 将传递给驱动程序的设备指针转换为 platform_device 结构体指针
struct platform_device *dev = to_platform_device(_dev);
int ret;
// 设置设备节点的默认时钟属性
ret = of_clk_set_defaults(_dev->of_node, false);
if (ret < 0)
return ret;
// 将设备附加到电源域
ret = dev_pm_domain_attach(_dev, true);
if (ret)
goto out;
// 调用驱动程序的探测函数(probe)
if (drv->probe) {
ret = drv->probe(dev);
if (ret)
dev_pm_domain_detach(_dev, true);
}
out:
// 处理探测延迟和错误情况
if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
dev_warn(_dev, "probe deferral not supported\n");
ret = -ENXIO;
}
return ret;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
该函数主要负责平台设备的初始化过程,具体流程如下:
将设备信息转换为驱动程序能识别的格式
- 将设备指针转换成驱动结构体(drv)
- 将设备指针转换成设备结构体(dev)
设置设备时钟参数
- 自动配置设备的默认时钟设置,确保设备时钟正常工作
连接电源管理系统
- 将设备关联到对应的电源控制模块,管理设备的电源状态
初始化设备检测
- 调用驱动的初始化函数(probe)进行设备检测
- 如果初始化失败: • 断开电源关联 • 返回错误码
处理延迟初始化情况
- 如果驱动不支持延迟初始化: • 当检测被延迟时(返回特定错误码) • 直接报错并返回"设备不存在"错误码
这个流程就像给新设备安装驱动程序:先做好设备信息转换,设置好基本参数,连接电源管理,然后执行核心初始化检测。如果检测遇到问题,会先清理已做的配置,再根据具体情况返回对应的错误信息。
三、platform_driver 结构体
platform_driver 结构体是 Linux 内核中用于编写平台设备驱动程序的重要数据结构。 它提供了与平台设备驱动相关的函数和数据成员, 以便与平台设备进行交互和管理。 该结构体定义在内核的“ /include/linux/platform_device.h” 文件中, 具体内容如下所示:
struct platform_driver {
int (*probe)(struct platform_device *); /* 平台设备的探测函数指针 */
int (*remove)(struct platform_device *); /* 平台设备的移除函数指针 */
void (*shutdown)(struct platform_device *);/* 平台设备的关闭函数指针 */
int (*suspend)(struct platform_device *, pm_message_t state);/* 平台设备的挂起函数指针 */
int (*resume)(struct platform_device *);/* 平台设备的恢复函数指针 */
struct device_driver driver;/* 设备驱动程序的通用数据 */
const struct platform_device_id *id_table;/* 平台设备与驱动程序的关联关系表 */
bool prevent_deferred_probe; /* 是否阻止延迟探测 */
};
2
3
4
5
6
7
8
9
10
平台驱动结构体(struct platform_driver)是开发人员用来编写平台设备驱动的核心工具。它包含几个关键功能:
- probe函数 当系统发现一个设备与驱动匹配时,这个函数会自动运行。它的主要任务是初始化设备,配置硬件,让设备开始工作。
- remove函数 当设备被移除或驱动被卸载时,这个函数会执行清理工作,比如释放内存、关闭硬件资源等。
- shutdown函数 关机时调用,用来安全关闭设备,确保数据不会丢失。
- suspend和resume函数
- suspend:系统进入休眠(如睡眠模式)时,保存设备状态并暂停工作。
- resume:系统恢复时,重新激活设备并恢复之前的状态。
- driver(驱动信息) 这部分记录驱动的基本信息:
- 必须设置
name
字段,且这个名字要和设备的名称(platform_device的.name)完全一致,这样才能让驱动和设备正确匹配。 - 还包含驱动所属的总线类型、模块信息等通用数据。
- id_table(设备ID表) 这是一个设备ID列表,用于更精确地匹配设备。如果设备的ID在表中,就会优先使用这个驱动,比仅靠
driver.name
匹配更优先。 - prevent_deferred_probe 一个开关:如果设为"true",系统会立即加载驱动,而不是延迟探测设备。
使用方法 开发人员填写这个结构体里的各个函数和参数,然后将驱动注册到系统中。当设备被系统检测到时:
- 如果驱动的
driver.name
或id_table
与设备匹配,就会自动调用probe
函数初始化设备。 - 后续设备的移除、休眠、恢复等操作,都会对应调用其他函数处理。
这个结构体还包含了设备驱动的基础功能(继承自struct device_driver
),能和其他设备驱动共享通用代码,减少重复开发。