平台总线模型介绍
无论要完成何种需求, 我们都编写了一个独立的驱动程序, 但这样编
写出来的驱动程序在重用性和可移植性上是很低的, 无论之后要编写一个同类型的驱动还是将该驱动更换一个平台, 都要花费时间重新修改驱动代码。
而驱动的分离和分层这一软件思路的提出(即本章节要讲解的平台总线模型) , 就是为了解决这个问题。
平台总线
平台总线(Platform bus) 是 Linux 内核中提供的一种虚拟总线, 用于管理和组织与特定硬件平台相关的设备和驱动。 它充当了平台设备(platform device) 和平台驱动(platform driver)之间的桥梁, 负责将它们进行匹配和绑定。
平台总线(Platform bus) 是 Linux 内核中提供的一种虚拟总线, 用于管理和组织与特定硬件平台相关的设备和驱动。 它充当了平台设备(platform device) 和平台驱动(platform driver)之间的桥梁, 负责将它们进行匹配和绑定。
当系统注册一个平台设备(平台驱动时),平台总线会寻找与之匹配的得平台驱动(平台设备)他会遍历已注册得平台驱动列表(平台设备列表),直到找到匹配的驱动(设备)为止。
平台总线优势
(1) 设备与驱动的分离: 传统的设备驱动模型将设备和驱动代码合并在同一个文件中,导致代码冗余和可维护性差。 而平台总线模型将设备代码和驱动代码分离, 设备代码放在device.c 文件中, 驱动代码放在 driver.c 文件中。 这种分离使得设备和驱动的职责更加清晰, 提高了代码的可读性和可维护性。
(2) 提高代码的重用性: 平台总线模型使得相同类型的设备可以共享相同的驱动代码。例如, 在一个硬件平台上存在多个相同类型的设备, 传统的驱动模型需要为每个设备编写独立的驱动代码。 而使用平台总线模型, 只需编写一个通用的驱动代码, 然后为每个设备创建相应的 device.c 文件, 将设备特定的代码放在其中。 这样可以减少代码的重复性, 提高了代码的重用性和可维护性。
(3) 减少重复性代码: 在传统的设备驱动模型中, 如果有多个相同类型的设备存在, 就需要为每个设备编写独立的驱动代码。 而使用平台总线模型, 只需编写一个通用的驱动代码,然后为每个设备创建相应的 device.c 文件, 将设备特定的代码放在其中。 这样可以避免大量的重复性代码, 简化了驱动开发过程。
(4) 提高可移植性: 平台总线模型可以提高驱动的可移植性。 开发者可以编写适应平台总线的平台驱动程序, 从而支持特定的外设, 而无需依赖于特定的标准总线。 这使得驱动可以更容易地在不同的硬件平台之间进行移植和重用。
注册 platform 设备
platform_device_register
函数用于将 platform_device 结构体描述的平台设备注册到内核中。 platform_device_register函数详情如下:
cpp
//函数原型
int platform_device_register(struct platform_device *pdev);
//头文件
#include <linux/platform_device.h>
//函数作用:platform_device_register函数用于将 platform_device 结构体描述的平台设备注册到内核中,使其能够参与设备的资源分配和驱动的匹配。
//参数含义:pdev: 指向 platform_device 结构体的指针, 描述要注册的平台设备的信息。
//成功返回0,失败返回负数
platform_device_unregister
platform_device_unregister 函数用于取消注册已经注册的平台设备, 即从内核中移除设备。在设备不再需要时, 调用该函数可以进行设备的清理和释放操作。
javascript
//函数原型
void platform_device_unregister(struct platform_device *pdev);
//头文件
#include <linux/platform_device.h>
//函数作用platform_device_unregister 函数用于取消注册已经注册的平台设备, 从内核中移除设备。
//pdev: 指向要取消注册的平台设备的 platform_device 结构体指针。
//返回值:无返回值
platform_device结构体
platform_device结构体是用于描述平台设备的数据结构。它包含了平台设备的各种属性和信息,用于在内核中表示和管理平台设备。
cpp
struct platform_device {
const char *name; // 设备的名称,用于唯一标识设备
int id; // 设备的ID,可以用于区分同一种设备的不同实例
bool id_auto; // 表示设备的ID是否自动生成
struct device dev; // 表示平台设备对应的 struct device 结构体,用于设备的基本管理和操作
u32 num_resources; // 设备资源的数量
struct resource *resource; // 指向设备资源的指针
const struct platform_device_id *id_entry; // 指向设备的ID表项的指针,用于匹配设备和驱动
char *driver_override; // 强制设备与指定驱动匹配的驱动名称
/* MFD cell pointer */
struct mfd_cell *mfd_cell; // 指向多功能设备(MFD)单元的指针,用于多功能设备的描述
/* arch specific additions */
struct pdev_archdata archdata; // 用于存储特定于架构的设备数据
};
resource结构体
cpp
struct resource {
resource_size_t start; /* 资源的起始地址 */
resource_size_t end; /* 资源的结束地址 */
const char *name; /* 资源的名称 */
unsigned long flags; /* 资源的标志位 */
unsigned long desc; /* 资源的描述信息 */
struct resource *parent; /* 指向父资源的指针 */
struct resource *sibling; /* 指向同级兄弟资源的指针 */
struct resource *child; /* 指向子资源的指针 */
/* 以下宏定义用于保留未使用的字段 */
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
ANDROID_KABI_RESERVE(3);
ANDROID_KABI_RESERVE(4);
};
完整代码
cpp
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#define MEM_START_ADDR 0xFDD60000
#define MEM_END_ADDR 0xFDD60004
#define IRQ_NUMBER 101
static struct resource my_resources[] = {
{
.start = MEM_START_ADDR, // 内存资源起始地址
.end = MEM_END_ADDR, // 内存资源结束地址
.flags = IORESOURCE_MEM, // 标记为内存资源
},
{
.start = IRQ_NUMBER, // 中断资源号
.end = IRQ_NUMBER, // 中断资源号
.flags = IORESOURCE_IRQ, // 标记为中断资源
},
};
static void my_platform_device_release(struct device *dev)
{
// 释放资源的回调函数
}
static struct platform_device my_platform_device = {
.name = "my_platform_device", // 设备名称
.id = -1, // 设备ID
.num_resources = ARRAY_SIZE(my_resources), // 资源数量
.resource = my_resources, // 资源数组
.dev.release = my_platform_device_release, // 释放资源的回调函数
};
static int __init my_platform_device_init(void)
{
int ret;
ret = platform_device_register(&my_platform_device); // 注册平台设备
if (ret) {
printk(KERN_ERR "Failed to register platform device\n");
return ret;
}
printk(KERN_INFO "Platform device registered\n");
return 0;
}
static void __exit my_platform_device_exit(void)
{
platform_device_unregister(&my_platform_device); // 注销平台设备
printk(KERN_INFO "Platform device unregistered\n");
}
module_init(my_platform_device_init);
module_exit(my_platform_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("topeet");
注册platform驱动
platform_driver_register 函数
cpp
// 函数原型
int platform_driver_register(struct platform_driver *driver);
//头文件
#include <linux/platform_device.h>
//函数租用:platform_driver_register 函数用于将一个平台驱动程序注册到内核中。通过注册平台驱动程序,内核可以识别并与特定的平台设备进行匹配,并在需要时调用相应的回调函数
//参数含义:driver:指向 struct platform_driver 结构体的指针,描述了要注册的平台驱动程序的属性和回调函数
//返回值:返回一个整数值,表示函数的执行状态。如果注册成功,返回 0;如果注册失败,返回一个负数错误码
platform_device_unregister 函数
cpp
//函数原型
void platform_device_unregister(struct platform_device *pdev);
//头文件
#include <linux/platform_device.h>
//函数作用:platform_device_unregister 函数用于从内核中注销平台设备。通过调用该函数,可以将指定的平台设备从系统中移除。
//参数:pdev:指向要注销的平台设备的指针。
//返回值:无返回值
platform_driver结构体
cpp
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; /* 是否阻止延迟探测 */
};
代码
javascript
#include <linux/module.h>
#include <linux/platform_device.h>
// 平台设备的探测函数
static int my_platform_probe(struct platform_device *pdev)
{
printk(KERN_INFO "my_platform_probe: Probing platform device\n");
// 添加设备特定的操作
// ...
return 0;
}
// 平台设备的移除函数
static int my_platform_remove(struct platform_device *pdev)
{
printk(KERN_INFO "my_platform_remove: Removing platform device\n");
// 清理设备特定的操作
// ...
return 0;
}
// 定义平台驱动结构体
static struct platform_driver my_platform_driver = {
.probe = my_platform_probe,
.remove = my_platform_remove,
.driver = {
.name = "my_platform_device",
.owner = THIS_MODULE,
},
};
// 模块初始化函数
static int __init my_platform_driver_init(void)
{
int ret;
// 注册平台驱动
ret = platform_driver_register(&my_platform_driver);
if (ret) {
printk(KERN_ERR "Failed to register platform driver\n");
return ret;
}
printk(KERN_INFO "my_platform_driver: Platform driver initialized\n");
return 0;
}
// 模块退出函数
static void __exit my_platform_driver_exit(void)
{
// 注销平台驱动
platform_driver_unregister(&my_platform_driver);
printk(KERN_INFO "my_platform_driver: Platform driver exited\n");
}
module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("topeet");
probe函数
上面的两个章节中分别注册了platform设备和platform驱动,匹配成功之后会进入在注册platform驱动程序中编写的probe函数,在上个章节只是为了验证是否匹配成功,所以只是在probe中加入了一句相关打印,而驱动是要控制硬件的。
获取device资源
1、直接访问 platform_device 结构体的资源数组
struct platform_driver 结构体继承了 struct device_driver 结构体,因此可以直接访问 struct device_driver 中定义的成员。实例代码如下所示:
cpp
if (pdev->num_resources >= 2) {
struct resource *res_mem = &pdev->resource[0];
struct resource *res_irq = &pdev->resource[1];
// 使用获取到的硬件资源进行处理
printk("Method 1: Memory Resource: start = 0x%lld, end = 0x%lld\n",
res_mem->start, res_mem->end);
printk("Method 1: IRQ Resource: number = %lld\n", res_irq->start);
}
2、使用 platform_get_resource() 获取硬件资源
platform_get_resource()函数用于获取设备的资源信息。它的声明位于<linux/platform_device.h>头文件中,与平台设备(platform_device)相关。
cpp
// 函数原型
struct resource *platform_get_resource(struct platform_device *pdev, unsigned int type, unsigned int num);
//参数说明
/*
pdev:指向要获取资源的平台设备(platform_device)结构体的指针。
type:指定资源的类型,可以是以下值之一:
IORESOURCE_MEM:表示内存资源。
IORESOURCE_IO:表示I/O资源。
IORESOURCE_IRQ:表示中断资源。
示例代码:
cpp
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
static int my_platform_driver_probe(struct platform_device *pdev)
{
struct resource *res_mem, *res_irq;
// 方法1:直接访问 platform_device 结构体的资源数组
if (pdev->num_resources >= 2) {
struct resource *res_mem = &pdev->resource[0];
struct resource *res_irq = &pdev->resource[1];
// 使用获取到的硬件资源进行处理
printk("Method 1: Memory Resource: start = 0x%llx, end = 0x%llx\n",
res_mem->start, res_mem->end);
printk("Method 1: IRQ Resource: number = %lld\n", res_irq->start);
}
// 方法2:使用 platform_get_resource() 获取硬件资源
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res_mem) {
dev_err(&pdev->dev, "Failed to get memory resource\n");
return -ENODEV;
}
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res_irq) {
dev_err(&pdev->dev, "Failed to get IRQ resource\n");
return -ENODEV;
}
// 使用获取到的硬件资源进行处理
printk("Method 2: Memory Resource: start = 0x%llx, end = 0x%llx\n",
res_mem->start, res_mem->end);
printk("Method 2: IRQ Resource: number = %lld\n", res_irq->start);
return 0;
}
static int my_platform_driver_remove(struct platform_device *pdev)
{
// 设备移除操作
return 0;
}
static struct platform_driver my_platform_driver = {
.driver = {
.name = "my_platform_device", // 与 platform_device.c 中的设备名称匹配
.owner = THIS_MODULE,
},
.probe = my_platform_driver_probe,
.remove = my_platform_driver_remove,
};
static int __init my_platform_driver_init(void)
{
int ret;
ret = platform_driver_register(&my_platform_driver); // 注册平台驱动
if (ret) {
printk("Failed to register platform driver\n");
return ret;
}
printk("Platform driver registered\n");
return 0;
}
static void __exit my_platform_driver_exit(void)
{
platform_driver_unregister(&my_platform_driver); // 注销平台驱动
printk("Platform driver unregistered\n");
}
module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("topeet");