Linux驱动学习(七)平台总线框架

平台总线模型介绍

无论要完成何种需求, 我们都编写了一个独立的驱动程序, 但这样编

写出来的驱动程序在重用性和可移植性上是很低的, 无论之后要编写一个同类型的驱动还是将该驱动更换一个平台, 都要花费时间重新修改驱动代码。

而驱动的分离和分层这一软件思路的提出(即本章节要讲解的平台总线模型) , 就是为了解决这个问题。

平台总线

平台总线(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");
相关推荐
zhangxiaomm30 分钟前
pytorch 学习笔记(2)-实现一个线性回归模型
pytorch·笔记·学习
失因39 分钟前
Linux 权限管理与 ACL 访问控制
linux·运维·服务器·数据库·centos
玖剹1 小时前
Linux文件操作:从C接口到系统调用
linux·服务器·c语言·c++·笔记·ubuntu
Vdeilae1 小时前
IIS 让asp.net core 项目一直运行
java·服务器·asp.net
Lovyk2 小时前
Linux 系统启动原理
linux·服务器·windows
Jewel Q2 小时前
TCP为什么采用三次握手而不是二次握手
服务器·网络·tcp/ip
藏在歌词里2 小时前
Linux-Shell脚本基础用法
linux·运维·服务器
NfN-sh3 小时前
计数组合学7.12( RSK算法的一些推论)
笔记·学习·算法
Sadsvit3 小时前
Linux 服务器性能监控、分析与优化全指南
java·linux·服务器