6-Platform总线

发布时间:2026/10/30 4:23:22
6-Platform总线

什么是平台总线:

平台总线(Platformbus)是Linux内核中提供的一种虚拟总线,用于管理和组织与特定硬 件平台相关的设备和驱动。它充当了平台设备(platformdevice)和平台驱动(platformdriver) 之间的桥梁,负责将它们进行匹配和绑定。

但Linux 驱动开发里,几乎所有 “嵌入式硬件设备” 最终都会挂在某个总线上(平台总线只是其中最常用、最简单的一种)。IIC,SPI等等

平台总线模型的使用:

平台总线模型将一个驱动分成了两个部分,分别是device.c和driver.c:这样方便移植

  • device.c:用来描述硬件(比如硬件资源、寄存器地址、中断号等)
  • driver.c:用来控制硬件(比如初始化、读写、业务逻辑等)

通过名字匹配成功进入probe函数,deiver.c可以拿到device.c里面的硬件资源

platform_device:

两个重要结构体

struct platform_device { const char *name; int id; bool id_auto; struct device dev; u32 num_resources; //这个非常重要 struct resource *resource; };

start,end是起始,终止资源,flag是具体的资源类型,描述硬件

static struct resource my_device_resources[] = { [0] = { .start = 0xFDD60000, .end = 0xFDD60004, .flags = IORESOURCE_MEM, }, //中断资源 [1] = { .start = 13, .end = 13, .flags = IORESOURCE_IRQ, }, };

注册platform设备device

#include <linux/module.h> #include <linux/platform_device.h> #include <linux/ioport.h> #define MEM_START_ADDR 0xFEC30004 #define MEM_END_ADDR 0xFEC30008 #define IRQ_NUMBER 112 /* 硬件资源定义 */ 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, }, }; /* 必须实现的 release 函数 */ static void my_platform_device_release(struct device *dev) { } /* 定义平台设备 */ static struct platform_device my_platform_device = { //用于driver匹配 .name = "my_platform_device", //id没啥用,就是显示的名字后面带一个序号比如a.1,-1不显示 .id = -1, .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_driver:

#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); printk(KERN_INFO "Platform driver initialized\n"); return 0; } /* 驱动注销 */ static void __exit my_platform_driver_exit(void) { platform_driver_unregister(&my_platform_driver); printk(KERN_INFO "Platform driver exited\n"); } module_init(my_platform_driver_init); module_exit(my_platform_driver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("topeet");

probe函数编写:

注意参数为struct platform_device *,说明可以拿到硬件资源了,匹配成功的话 static int my_platform_driver_probe(struct platform_device *pdev) { }

获取资源类型API

/* pdev:指向要获取资源的平台设备(platform_device)结构体的指针。 type:指定资源的类型,可选值包括: IORESOURCE_MEM:表示内存资源 IORESOURCE_IO:表示 I/O 资源 IORESOURCE_IRQ:表示中断资源 其他资源类型的宏定义可在 <linux/ioport.h> 和 <linux/irq.h> 头文件中找到。 num:指定要获取的资源的索引。当一个设备中存在多个相同类型的资源时,通过该索引选择特定资源。 */ struct resource *platform_get_resource(struct platform_device *pdev, unsigned int type, unsigned int num);

比如下面,就可以拿到资源了

// 方法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; }

编写LED驱动:只写了主要的

unsigned int* vir_gpio_dr; res_mem=platform_get_resource(pdev,IORESOURCE_MEM,0); vir_gpio_dr=ioremap(res_mem->start,4); //将物理地址转化为虚拟地址

接下来就可以操作vir_gpio进行点灯了