Linux 驱动的 platform 架构主要用于嵌入式设备中,它是 Linux 内核设备模型的一部分,旨在简化设备驱动的开发。Platform 设备和 platform 驱动架构提供了一种通用的方法来处理嵌入式系统中没有总线的设备。这种架构的关键在于将硬件和驱动程序分离,通过平台设备模型定义设备和驱动之间的接口。
Platform 设备 (platform_device):
Platform 驱动 (platform_driver):
Platform 设备注册:
Platform 设备通常在设备树文件(DTS 文件)中声明。内核在启动时解析这些文件并注册设备。设备树会描述设备的各种属性,比如内存地址、IRQ 中断等。
示例设备树:
my_device@10000000 {
compatible = "my_vendor,my_device";
reg = <0x10000000 0x1000>; // 内存映射地址和大小
interrupt-parent = <&intc>;
interrupts = <5>;
};
也可以通过内核代码静态注册平台设备:
struct platform_device my_device = {
.name = "my_device",
.id = -1,
.resource = my_device_resources,
.num_resources = ARRAY_SIZE(my_device_resources),
};
platform_device_register(&my_device);
Platform 驱动注册:
驱动程序通过 platform_driver
结构体和其注册函数与设备绑定。该结构体中定义了各种回调函数,如设备探测(probe)和移除(remove)等。
驱动示例:
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_device",
.owner = THIS_MODULE,
},
};
module_platform_driver(my_driver);
Device Tree 和 Platform 驱动的匹配:
compatible
字段是设备树中用于匹配设备和驱动的关键属性,驱动中的 of_match_table
定义了支持的设备类型。
示例:
static const struct of_device_id my_of_match[] = {
{ .compatible = "my_vendor,my_device", },
{},
};
MODULE_DEVICE_TABLE(of, my_of_match);
static struct platform_driver my_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "my_device",
.of_match_table = my_of_match,
.owner = THIS_MODULE,
},
};
结合了设备的注册、驱动的加载和卸载等基本功能。我们将创建一个虚拟的 my_platform_device
设备,并编写一个驱动程序与其匹配,使用设备树进行配置和驱动匹配。
我们将编写两个文件:一个是设备驱动代码,另一个是设备树配置(如果目标设备使用设备树)。
my_platform_driver.c
)#include
#include
#include
#include
// 用于存储设备的虚拟地址
struct my_platform_data {
void __iomem *base_addr;
};
// 设备探测函数 (probe)
static int my_platform_probe(struct platform_device *pdev)
{
struct resource *res;
struct my_platform_data *data;
printk(KERN_INFO "my_platform_driver: probe called\n");
// 分配私有数据结构
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
// 获取设备资源 (内存地址)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
printk(KERN_ERR "my_platform_driver: no memory resource\n");
return -ENODEV;
}
// 映射设备寄存器
data->base_addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->base_addr))
return PTR_ERR(data->base_addr);
platform_set_drvdata(pdev, data);
printk(KERN_INFO "my_platform_driver: device initialized successfully\n");
return 0;
}
// 设备移除函数 (remove)
static int my_platform_remove(struct platform_device *pdev)
{
struct my_platform_data *data = platform_get_drvdata(pdev);
// 释放映射的寄存器
if (data && data->base_addr)
devm_iounmap(&pdev->dev, data->base_addr);
printk(KERN_INFO "my_platform_driver: device removed\n");
return 0;
}
// 设备树匹配表
static const struct of_device_id my_of_match[] = {
{ .compatible = "my_vendor,my_device", },
{},
};
MODULE_DEVICE_TABLE(of, my_of_match);
// 平台驱动结构
static struct platform_driver my_platform_driver = {
.driver = {
.name = "my_platform_driver",
.of_match_table = my_of_match,
.owner = THIS_MODULE,
},
.probe = my_platform_probe,
.remove = my_platform_remove,
};
// 模块初始化
static int __init my_platform_init(void)
{
return platform_driver_register(&my_platform_driver);
}
// 模块退出
static void __exit my_platform_exit(void)
{
platform_driver_unregister(&my_platform_driver);
}
module_init(my_platform_init);
module_exit(my_platform_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux platform driver example");
my_device.dts
)如果目标硬件使用设备树(例如嵌入式设备),在设备树文件中描述设备信息。
/my_device@10000000 {
compatible = "my_vendor,my_device";
reg = <0x10000000 0x1000>; // 内存地址和大小
interrupt-parent = <&intc>;
interrupts = <5>;
};
为了编译驱动,我们需要一个简单的 Makefile
。
obj-m += my_platform_driver.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
my_platform_driver.c
。my_device.dts
并编译为设备树二进制文件(my_device.dtb
)。编写并保存 Makefile
文件。
在终端中运行以下命令来编译驱动程序:
make
这将生成一个名为 my_platform_driver.ko
的内核模块文件。
如果你使用设备树来描述设备,确保设备树已经加载到目标设备中。在嵌入式系统中,通常通过引导加载程序(如 U-Boot)加载设备树文件。
你可以通过以下命令检查设备树是否加载成功:
ls /sys/firmware/devicetree/base/my_device@10000000
如果设备树已成功加载,你将看到设备在 /sys/firmware/devicetree/base/
中的相关信息。
使用以下命令将编译好的驱动加载到内核中:
sudo insmod my_platform_driver.ko
加载驱动后,可以使用 dmesg
命令查看内核日志,确保驱动成功加载并初始化设备:
dmesg | tail
你应该看到类似如下的日志输出:
[ 1234.56789] my_platform_driver: probe called
[ 1234.56790] my_platform_driver: device initialized successfully
你可以检查 /sys/bus/platform/drivers/my_platform_driver/
目录,确认驱动是否正确绑定到设备:
ls /sys/bus/platform/drivers/my_platform_driver/
该目录下应该有匹配到的设备实例。
如果不再需要该驱动,可以使用以下命令卸载模块:
sudo rmmod my_platform_driver
同样,你可以通过 dmesg
检查卸载的日志输出:
dmesg | tail
日志中应显示设备被成功移除的消息:
[ 5678.98765] my_platform_driver: device removed
如果你希望清理生成的文件,可以运行以下命令:
make clean
probe
和 remove
函数来管理设备的初始化和卸载。compatible
字段匹配驱动。Makefile
编译生成 .ko
文件。insmod
加载,rmmod
卸载。dmesg
日志和 sysfs
目录检查设备的绑定和驱动状态。通过这些步骤,你可以编写、编译、加载并测试一个基于 Linux platform 架构的设备驱动。