platform_driver_probe与platform_driver_register的区别

Platform Bus 驱动模型
本文详细介绍了Linux内核中的Platform Bus驱动模型,包括Platform device和Platform driver的基本概念、结构定义及其绑定过程。Platform Bus作为一种特殊的总线,主要用于集成在SOC上的外设或控制器。
 
2010-01-20 18:51

Platform Device and Drivers
从<linux/platform_device.h>我们可以了解Platform bus上面的驱动模型接口:platform_device,platform_driver。和PCI和USB这些大结构的总线不同,虚拟总线 Platform bus使用最小结构来集成SOC processer上的各种外设,或者各种“legacy”之间的互联。

Platform device
典型的Platform device是系统中的各种自主设备,包括各种桥接在外围总线上的port-based device和host,以及各种集成在SOC platform上的控制器。他们都有一个特点,那就是CPU BUS可以直接寻址,或者特殊的情况platform_device连接在其他总线上,但是它的寄存器是可以被直接寻址的。
Platform device有一个名字,用来进行driver的绑定;还有诸如中断,地址之类的一些资源列表

struct platform_device {
const char *name;
u32 id;
struct device dev;
u32 num_resources;
struct resource *resource;
};


Platform drivers
Platform driver满足标准driver model,对driver的discovery/enumeration是在driver外部进行的,driver提供了probe()和 remove()方法.Platfomr dirvers通过标准模型提供power management和shutdown通知。

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 (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};

Probe()函数必须验证指定设备的硬件是否真的存在,probe()可以使用设备的资源,包括时钟,platform_data等,Platform driver可以通过下面的函数完成对驱动的注册:
int platform_driver_register(struct platform_driver *drv);
一般来说设备是不能被热插拔的,所以可以将probe()函数放在init段里面来节省driver运行时候的内存开销:
int platform_driver_probe(struct platform_driver *drv,
     int (*probe)(struct platform_device *))

Device Enumeration
作为一个规则,平台(一般来说是板级)启动代码会注册所有的Platform device:
int platform_device_register(struct platform_device *pdev);
int platform_add_devices(struct platform_device **pdevs, int ndev);
一般来说只会注册那些实际存在的设备,不过也存在特殊的情况,比如kernel可能需要与一个不在板子上的外部网络适配器工作,或者那些不挂在任何总线上的控制器。
一般情况下,固件启动的过程会输出一个描述板子上所有存在设备的表。如果没有这个表,系统启动代码建立正确的设备的唯一方法就是为一个特定的板子编译一个kernel。这种board-specific kernel广泛用在嵌入式和一般系统的开发上。
在大部分情况下,设备的memory和IRQ资源不足够让驱动正常工作。board setup code会用device的platform_data域来为设备提供一些额外的资源。
嵌入式系统上的设备会频繁地使用一个或者多个时钟,这些时钟因为节电的原因只有在真正使用的时候才会被打开,系统在启动过程中会为设备分配时钟,可以通过clk_get(&pdev->dev, clock_name)来获得需要的时钟。

Legacy Drivers : Device Probing
一些driver并不会完全遵守标准driver model,这些driver会去注册自己的platform device,而不是让系统来完成注册。这是不值得推
荐的,主要用来兼容以前的一些旧设备。可以通过下面的API来支持这些legacy driver,一般这些API使用在不支持热插拔的driver上面:
struct platform_device *platform_device_alloc(
   const char *name, int id);
可以使用platform_device_alloc动态地创建一个设备,一个更好的方法是,通过下面的函数动态创建一个设备,并把这个设备注册到系统中:
struct platform_device *platform_device_register_simple(
   const char *name, int id,
   struct resource *res, unsigned int nres);

Device Naming and Driver Binding
platform_device.dev.bus_id是一个设备在总线上的名字,它包含两部分:
* platform_device.name   设备名字,用来进行driver的匹配
* platform_device.id     设备实例的标号,如果是-1,表示同样名字的设备只有一个
举个简单的例子,name/id是“serial/1”则它的bus_id就是serial.1 如果name/id是“serial/0”则它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”则它的bus_id就是serial。
driver的绑定是通过driver core自动完成的,完成driver和device的匹配后以后会自动执行probe()函数,如果函数执行成功,则driver和device就绑定在一起了,drvier和device匹配的方法有3种:
* 当一个设备注册的时候,他会在总线上寻找匹配的driver,platform device一般在系统启动很早的时候就注册了
* 当一个驱动注册[platform_driver_register()]的时候,他会遍历所有总线上的设备来寻找匹配,在启动的过程驱动的注册一般比较晚,或者在模块载入的时候
* 当一个驱动注册[platform_driver_probe()]的时候, 功能上和使用platform_driver_register()是一样的,唯一的区别是它不能被以后其他的device probe了,也就是说这个driver只能和一个device绑定。

      Platform device 和 Platform driver实际上是cpu总线可以直接寻址的设备和驱动,他们挂载在一个虚拟的总线platform_bus_type上,是一种bus- specific设备和驱动。与其他bus-specific驱动比如pci是一样的。他们都是将device和device_driver加了一个 warpper产生,仔细看看platform_device就可以看到它必然包含一个device dev,而platform_driver也一样,它必然包含一个device_driver driver。
      所有的设备通过bus_id挂在总线上,多个device可以共用一个driver,但是一个device不可以对应多个driver。驱动去注册时候会根据设备名寻找设备,没有设备会注册失败,注册的过程会通过probe来进行相应资源的申请,以及硬件的初始化,如果probe执行成功,则device 和driver的绑定就成功了。设备注册的时候同样会在总线上寻找相应的驱动,如果找到他也会试图绑定,绑定的过程同样是执行probe。

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/yili_xie/archive/2010/01/15/5193609.aspx

<think>我们正在讨论Linux内核驱动开发中的两个函数:platform_driver_register和module_platform_driver。 首先,我们需要明确这两个函数都是用于注册平台设备驱动(platform driver)的,但使用方式和上下文有所不同。 1. platform_driver_register: 这是一个直接用于注册平台驱动的函数。通常,在驱动模块的初始化函数中调用它。 使用platform_driver_register时,需要手动编写模块的init和exit函数。 例如: static int __init my_driver_init(void) { return platform_driver_register(&my_driver); } static void __exit my_driver_exit(void) { platform_driver_unregister(&my_driver); } module_init(my_driver_init); module_exit(my_driver_exit); 2. module_platform_driver: 这是一个宏,用于简化平台驱动的注册。它会自动生成上述的init和exit函数,并调用module_init和module_exit。 使用module_platform_driver,你只需要提供platform_driver结构体即可。 例如: static struct platform_driver my_driver = { .probe = my_probe, .remove = my_remove, .driver = { .name = "my_driver", .owner = THIS_MODULE, }, }; module_platform_driver(my_driver); 区别: - platform_driver_register需要手动编写模块初始化和退出函数,而module_platform_driver则自动生成这些函数。 - module_platform_driver是一个宏,它简化了代码,减少了样板代码的编写。但是,如果需要在注册驱动之前或之后执行一些额外的操作,那么使用platform_driver_register会更灵活。 使用场景: - 如果驱动模块的初始化只需要注册平台驱动,没有其他操作,那么使用module_platform_driver更简洁。 - 如果需要在注册平台驱动之前进行一些初始化(如分配资源、注册其他设备等),或者在退出时需要进行额外的清理,那么应该使用platform_driver_register,并在自定义的init和exit函数中添加这些操作。 总结: module_platform_driverplatform_driver_register的一个简化封装,适用于标准情况。而platform_driver_register提供了更大的灵活性。 下面我们通过代码示例来具体说明: 使用platform_driver_register的示例: ```c #include <linux/module.h> #include <linux/platform_device.h> static int my_probe(struct platform_device *pdev) { // 驱动探测函数 return 0; } static int my_remove(struct platform_device *pdev) { // 驱动移除函数 return 0; } static struct platform_driver my_driver = { .probe = my_probe, .remove = my_remove, .driver = { .name = "my_driver", .owner = THIS_MODULE, }, }; static int __init my_driver_init(void) { // 可以在这里添加其他初始化代码 return platform_driver_register(&my_driver); } static void __exit my_driver_exit(void) { platform_driver_unregister(&my_driver); // 可以在这里添加其他清理代码 } module_init(my_driver_init); module_exit(my_driver_exit); MODULE_LICENSE("GPL"); ``` 使用module_platform_driver的示例: ```c #include <linux/module.h> #include <linux/platform_device.h> static int my_probe(struct platform_device *pdev) { // 驱动探测函数 return 0; } static int my_remove(struct platform_device *pdev) { // 驱动移除函数 return 0; } static struct platform_driver my_driver = { .probe = my_probe, .remove = my_remove, .driver = { .name = "my_driver", .owner = THIS_MODULE, }, }; module_platform_driver(my_driver); MODULE_LICENSE("GPL"); ``` 在第二个示例中,module_platform_driver宏会自动展开成类似于第一个示例中的init和exit函数,并调用module_init和module_exit。 因此,选择使用哪个取决于是否需要额外的初始化和清理步骤。 注意:在Linux内核代码中,module_platform_driver的定义如下(以较新的内核版本为例): ```c #define module_platform_driver(__platform_driver) \ module_driver(__platform_driver, platform_driver_register, \ platform_driver_unregister) ``` 而module_driver宏的定义是: ```c #define module_driver(__driver, __register, __unregister, ...) \ static int __init __driver##_init(void) \ { \ return __register(&(__driver) , ##__VA_ARGS__); \ } \ module_init(__driver##_init); \ static void __exit __driver##_exit(void) \ { \ __unregister(&(__driver) , ##__VA_ARGS__); \ } \ module_exit(__driver##_exit) ``` 因此,使用module_platform_driver(my_driver)会被展开成: ```c static int __init my_driver_init(void) { return platform_driver_register(&my_driver); } module_init(my_driver_init); static void __exit my_driver_exit(void) { platform_driver_unregister(&my_driver); } module_exit(my_driver_exit); ``` 所以,本质上,module_platform_driver就是帮我们写了init和exit函数,然后调用platform_driver_registerplatform_driver_unregister。 这样,我们就清楚地了解了platform_driver_register和module_platform_driver的使用方法和区别。 相关问题: 1. 在Linux设备驱动开发中,平台设备(platform device)和平台驱动(platform driver)是如何匹配的? 2. 除了platform_driver_register,还有哪些常用的驱动注册函数? 3. 在平台驱动的probe函数中通常会进行哪些操作? 4. 如何编写一个平台设备(platform device)并将其注册到内核?</think>在 Linux 内核驱动开发中,`platform_driver_register` 和 `module_platform_driver` 都是用于注册平台设备驱动的接口,但它们在用法和设计目的上有显著区别。以下是详细分析: --- ### 1. `platform_driver_register` #### 作用 手动注册平台驱动到内核,需**显式定义**模块的初始化和退出函数。 #### 典型使用场景 当驱动需要在注册前/后执行额外操作时(如资源分配、调试初始化等)。 #### 代码结构 ```c #include <linux/platform_device.h> static struct platform_driver my_driver = { .probe = my_probe, .remove = my_remove, .driver = { .name = "my_device", .owner = THIS_MODULE, }, }; static int __init my_driver_init(void) { // 可在此添加自定义初始化代码 return platform_driver_register(&my_driver); } static void __exit my_driver_exit(void) { // 可在此添加自定义清理代码 platform_driver_unregister(&my_driver); } module_init(my_driver_init); module_exit(my_driver_exit); ``` #### 特点 - **灵活性高**:允许在 `init/exit` 函数中添加自定义逻辑。 - **代码冗余**:需手动编写初始化和退出函数。 --- ### 2. `module_platform_driver` #### 作用 通过宏**自动生成**模块的初始化和退出函数,简化注册流程。 #### 典型使用场景 标准平台驱动注册,无需额外的初始化/清理操作。 #### 代码结构 ```c #include <linux/platform_device.h> static struct platform_driver my_driver = { .probe = my_probe, .remove = my_remove, .driver = { .name = "my_device", .owner = THIS_MODULE, }, }; module_platform_driver(my_driver); // 自动生成 init/exit 函数 ``` #### 宏展开原理 `module_platform_driver` 宏的定义如下: ```c #define module_platform_driver(__platform_driver) \ module_driver(__platform_driver, platform_driver_register, platform_driver_unregister) ``` 它等价于自动生成: ```c static int __init my_driver_init(void) { return platform_driver_register(&my_driver); } static void __exit my_driver_exit(void) { platform_driver_unregister(&my_driver); } module_init(my_driver_init); module_exit(my_driver_exit); ``` #### 特点 - **简洁性**:一行代码替代手动编写 `init/exit` 函数。 - **局限性**:无法在注册前后插入自定义代码。 --- ### 3. 核心区别总结 | **特性** | `platform_driver_register` | `module_platform_driver` | |------------------------|--------------------------------------|------------------------------------| | **注册方式** | 手动调用注册函数 | 宏自动生成注册代码 | | **自定义初始化/清理** | 支持(可在 `init/exit` 中添加逻辑) | 不支持 | | **代码复杂度** | 较高(需显式定义函数) | 极低(单行宏调用) | | **适用场景** | 需要额外初始化的驱动 | 标准平台驱动 | | **底层实现** | 直接调用内核注册接口 | 宏封装 `platform_driver_register` | --- ### 4. 选择建议 - 使用 `module_platform_driver` 当驱动是**纯平台驱动**且无需特殊初始化。 - 使用 `platform_driver_register` 当需要: - 在注册前分配资源(如内存、中断)。 - 在退出时执行额外清理。 - 调试或日志记录注册过程。 > **示例场景**: > 若驱动需在 `probe` 前初始化硬件寄存器,应选择 `platform_driver_register` 并在 `my_driver_init` 中完成硬件配置。 --- ### 相关问题 1. 平台设备(`platform_device`)和平台驱动(`platform_driver`)如何通过名称匹配? 2. 在 `probe` 函数中通常需要完成哪些关键操作? 3. 如何在内核中注册一个平台设备(`platform_device`)? 4. 使用 `module_platform_driver` 时,如何确保驱动兼容热插拔机制? 5. 平台驱动模型传统字符设备驱动注册(如 `register_chrdev`)有何本质区别
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值