驱动-平台总线-probe

平台总线-probe函数 的了解和使用

文章目录

  • 前言
  • 一、参考资料
  • 二、程序测试
    • 总线设备编码
    • 总线驱动编码
    • ko 驱动调试
  • 三、知识点-platform_get_resource 方法
    • 函数原型
    • 基本使用方法
      • 获取内存资源
      • 获取中断资源
    • 现代替代方法
      • 对于内存资源-devm_platform_ioremap_resource
      • 对于中断资源-platform_get_irq
    • 资源定义来源
      • 传统方式(C代码定义)
      • 设备树方式(现代推荐)
  • 总结


前言

  • 驱动-平台总线-platform设备注册platform驱动注册篇 的文章中,我们理解了platform 总线,了解了platform driver和platform device 的注册,配对 知识。

  • 匹配成功之后会进入在注册 platform 驱动程序中编写的 probe 函数,在上个章节只是为了验证是否匹配成功,所以只是在 probe 中加入了一句相关打印,而驱动是要控制硬件的,但是平台总线模型对硬件的描述写在了 platform_device.c 中,platform 设备和 platform 驱动匹配成功之后,那我们如何在驱动platform_driver.c 的 probe 函数中,得到 platform_device.c 中编写的硬件资源呢?

也就是 平台总线体系中,device 负责描述设备相关,但是驱动需要把设备用起来,那就需要再驱动里面初始化设备、控制设备、获取设备相关信息的。 这里就是probe 方法里面要做的。

  • 我们这里简单看看再probe 函数里面如何获取设备资源、描述信息等。

一、参考资料

驱动-平台总线-platform设备注册platform驱动注册篇
平台总线-probe函数编写
手把手教Linux驱动-platform总线详解
platform总线设备驱动模型

二、程序测试

总线设备编码

驱动-平台总线-platform设备注册platform驱动注册篇 里面的程序我们直接拿来用,编码如下:

#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 可以用于区分同一种设备的不同实例。这个参数是可选的,如果不需要使用 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("fangchen");

源码解读:这里前面的基本知识,

  • 定义一个platform device 基本流程。 定义 platform_device。包含基本属性 name 、id、num_resources 、resource 、dev.release
  • 对资源数组的定义,是接下来我们在driver 里面要获取的内容
  • 注册平台设备 platform_device_register;驱动卸载的时候,注销平台设备 platform_device_unregister

总线驱动编码

#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("fangchen");

ko 驱动调试

我们分别加载 device 和 driver 驱动,如下:
在这里插入图片描述
我们直接看看结果:

驱动-平台总线-probe_第1张图片

这里看看结果,

  • 是不是 driver 和 device 匹配后,my_platform_driver_probe 调用
  • probe = my_platform_driver_probe 回调函数中,通过 platform_device 的指针就是platform_device 设备的引用,进而直接获取 platform_device 相关硬件描述,比如资源 pdev->resource
  • 通过platform_get_resource api 函数获取硬件资源信息。

三、知识点-platform_get_resource 方法

  • 上面代码调试地方,使用 platform_get_resource() 获取硬件资源。是 Linux 平台总线(platform bus)驱动中获取硬件资源的核心函数,主要用于从 platform_device 中提取内存区域、I/O端口、中断等资源信息。
  • latform_get_resource 是平台驱动获取硬件资源的基础接口,理解其工作原理对于开发稳定的设备驱动至关重要。在现代内核开发中,虽然推荐使用更高级的封装函数,但了解这个底层接口仍然很有价值。

函数原型

struct resource *platform_get_resource(struct platform_device *dev,
                                      unsigned int type, unsigned int num);

参数说明:

  • dev:指向 platform_device 结构的指针

  • type:资源类型,常用值:IORESOURCE_MEM:内存区域资源;IORESOURCE_IO:I/O端口资源;IORESOURCE_IRQ:中断资源

  • num:资源的索引号(从0开始)

成功:返回指向 resource 结构的指针

基本使用方法

获取内存资源

struct resource *mem_res;

mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
    dev_err(&pdev->dev, "Failed to get memory resource\n");
    return -ENXIO;
}

// 通常接着进行内存映射
void __iomem *base = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(base)) {
    return PTR_ERR(base);
}

获取中断资源

struct resource *irq_res;

irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res) {
    dev_err(&pdev->dev, "Failed to get IRQ resource\n");
    return -ENXIO;
}

int irq_num = irq_res->start;  // 获取实际中断号

现代替代方法

虽然 platform_get_resource 仍然可用,但内核推荐使用更高级的接口:

对于内存资源-devm_platform_ioremap_resource

void __iomem *base;

base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
    return PTR_ERR(base);
}

这个函数一次性完成了资源获取和内存映射,并自动管理资源释放。

对于中断资源-platform_get_irq

int irq;

irq = platform_get_irq(pdev, 0);
if (irq < 0) {
    return irq;  // irq contains error code
}

资源定义来源

传统方式(C代码定义)

static struct resource my_device_resources[] = {
    [0] = {
        .start = 0xFE000000,
        .end = 0xFE000FFF,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = 42,
        .end = 42,
        .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
    }
};

设备树方式(现代推荐)

my_device: my-device@fe000000 {
    compatible = "vendor,my-device";
    reg = <0xFE000000 0x1000>;
    interrupts = <0 42 IRQ_TYPE_EDGE_RISING>;
};

总结

  • 了解platform_driver 中 probe属性对应回调方法
  • 这里我们通过probe 属性,衍生到driver 层如何获取device 硬件资源等

你可能感兴趣的:(系统-驱动,驱动开发,平台总线probe,probe,驱动模型-平台总线-probe)