Platform平台总线架构:原理、实践与应用

Platform平台总线架构:原理、实践与应用

引言

在 Linux 内核的设备驱动模型中,总线(Bus)扮演着至关重要的角色。它是连接 CPU 和各种设备的桥梁,负责数据传输和设备管理。Platform 总线作为 Linux 内核中的一种特殊总线,主要用于管理那些直接连接到 CPU 上的设备,也就是所谓的“平台设备”。本文将深入探讨 Platform 总线架构的原理、工作机制以及如何编写基于 Platform 总线的设备驱动程序。

Platform 总线架构概述

什么是 Platform 总线

Platform 总线是 Linux 内核为那些没有明确总线连接的设备而设计的一种虚拟总线。这些设备通常直接集成在主板上,如嵌入式系统中的 GPIO 控制器、定时器、看门狗等。由于它们没有像 PCI、USB 那样的物理总线,因此需要一种特殊的机制来管理和驱动它们,Platform 总线应运而生。

为什么需要 Platform 总线

在 Linux 内核中,设备驱动模型遵循分离原则,即设备(Device)和驱动(Driver)是分离的。设备描述了硬件的属性和资源,而驱动则负责实现对硬件的操作。Platform 总线提供了一个统一的框架,使得设备和驱动可以独立开发和注册,提高了代码的可维护性和可移植性。

Platform 总线的工作机制

设备和驱动的注册

在 Platform 总线架构中,设备和驱动需要分别向内核注册。设备注册时会提供设备的资源信息,如内存地址、中断号等;驱动注册时会提供对设备的操作方法,如初始化、读写等。

以下是一个简单的 Platform 设备注册示例:

1
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
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

// 定义设备资源
static struct resource my_device_resources[] = {
[0] = {
.start = 0x1000,
.end = 0x10ff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 10,
.end = 10,
.flags = IORESOURCE_IRQ,
},
};

// 定义 Platform 设备
static struct platform_device my_platform_device = {
.name = "my_device",
.id = -1,
.num_resources = ARRAY_SIZE(my_device_resources),
.resource = my_device_resources,
};

// 模块初始化函数
static int __init my_device_init(void)
{
return platform_device_register(&my_platform_device);
}

// 模块退出函数
static void __exit my_device_exit(void)
{
platform_device_unregister(&my_platform_device);
}

module_init(my_device_init);
module_exit(my_device_exit);

MODULE_LICENSE("GPL");

上述代码定义了一个简单的 Platform 设备,并在模块初始化时向内核注册该设备。设备资源包括一段内存地址和一个中断号。

以下是一个简单的 Platform 驱动注册示例:

1
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

// 驱动的 probe 函数,当设备和驱动匹配时调用
static int my_driver_probe(struct platform_device *pdev)
{
printk(KERN_INFO "My driver probed!\n");
return 0;
}

// 驱动的 remove 函数,当设备移除时调用
static int my_driver_remove(struct platform_device *pdev)
{
printk(KERN_INFO "My driver removed!\n");
return 0;
}

// 定义 Platform 驱动
static struct platform_driver my_platform_driver = {
.probe = my_driver_probe,
.remove = my_driver_remove,
.driver = {
.name = "my_device",
.owner = THIS_MODULE,
},
};

// 模块初始化函数
static int __init my_driver_init(void)
{
return platform_driver_register(&my_platform_driver);
}

// 模块退出函数
static void __exit my_driver_exit(void)
{
platform_driver_unregister(&my_platform_driver);
}

module_init(my_driver_init);
module_exit(my_driver_exit);

MODULE_LICENSE("GPL");

上述代码定义了一个简单的 Platform 驱动,并在模块初始化时向内核注册该驱动。驱动提供了 proberemove 两个函数,分别在设备和驱动匹配以及设备移除时调用。

设备和驱动的匹配

当设备和驱动都注册到 Platform 总线后,内核会根据设备和驱动的 name 属性进行匹配。如果两者的 name 相同,则认为设备和驱动匹配成功,内核会调用驱动的 probe 函数进行设备的初始化。

设备资源的管理

Platform 设备注册时会提供设备的资源信息,驱动可以通过 platform_get_resource 函数来获取这些资源。例如,获取内存资源的代码如下:

1
2
3
4
5
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) {
// 处理内存资源
}

上述代码通过 platform_get_resource 函数获取设备的第一个内存资源。

编写基于 Platform 总线的设备驱动程序

步骤概述

编写基于 Platform 总线的设备驱动程序通常需要以下步骤:

  1. 定义 Platform 驱动结构体,包含 proberemove 等函数。
  2. 实现 probe 函数,在该函数中进行设备的初始化和资源分配。
  3. 实现 remove 函数,在该函数中进行设备的清理和资源释放。
  4. 注册和注销 Platform 驱动。

示例代码

以下是一个完整的基于 Platform 总线的设备驱动程序示例:

1
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>

// 驱动的 probe 函数,当设备和驱动匹配时调用
static int my_driver_probe(struct platform_device *pdev)
{
struct resource *res;
void __iomem *base_addr;

// 获取设备的内存资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Failed to get memory resource\n");
return -ENODEV;
}

// 映射内存资源
base_addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base_addr)) {
dev_err(&pdev->dev, "Failed to map memory resource\n");
return PTR_ERR(base_addr);
}

printk(KERN_INFO "My driver probed, memory mapped to %p\n", base_addr);

return 0;
}

// 驱动的 remove 函数,当设备移除时调用
static int my_driver_remove(struct platform_device *pdev)
{
printk(KERN_INFO "My driver removed\n");
return 0;
}

// 定义 Platform 驱动
static struct platform_driver my_platform_driver = {
.probe = my_driver_probe,
.remove = my_driver_remove,
.driver = {
.name = "my_device",
.owner = THIS_MODULE,
},
};

// 模块初始化函数
static int __init my_driver_init(void)
{
return platform_driver_register(&my_platform_driver);
}

// 模块退出函数
static void __exit my_driver_exit(void)
{
platform_driver_unregister(&my_platform_driver);
}

module_init(my_driver_init);
module_exit(my_driver_exit);

MODULE_LICENSE("GPL");

上述代码实现了一个简单的基于 Platform 总线的设备驱动程序。在 probe 函数中,驱动获取设备的内存资源并进行映射;在 remove 函数中,驱动进行简单的清理工作。

总结

Platform 总线架构为 Linux 内核中没有明确总线连接的设备提供了一种有效的管理和驱动机制。通过设备和驱动的分离,提高了代码的可维护性和可移植性。本文详细介绍了 Platform 总线的原理、工作机制以及如何编写基于 Platform 总线的设备驱动程序。希望通过本文的介绍,读者能够对 Platform 总线架构有更深入的理解。