08 在设备树里描述platform_device

本文介绍如何在设备树的dts文件中定义platform_device及其子节点,并通过平台驱动进行匹配和驱动。文章详细展示了如何在dts文件中添加设备节点、编译设备树以及在驱动中使用接口函数读取设备树中的属性。

在设备树的dts文件里,带有compatible属性的节点就是表示一个platform_device.

在设备树里增加一个设备,则在内核里的dts文件里描述设备节点即可. 在H5方案里,则在arch/arm64/boot/dts/allwinner/sun50i-h5-nanopi-neo2.dts文件里。
如在dts文件里加入以下内容:

    mynodes@77885566 {  /* 则创建出来的platform_device的名为mynodes@77885566  */
        compatible = "mynodes";  /* 设备节点的属性 */
        autorepeat = <1>;

        btn1 {  /* 设备子节点 */
            label = "btn1";   /* 设备子节点的属性 */ 
            code  = <0x11>;
        };    
        btn2 {
            label = "btn2";
            code  = <0x22>;
        };
    };

增加内容后,则重编设备树:

make dtbs ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-

再把编译出来的sun50i-h5-nanopi-neo2.dtb替换板上所用的dtb文件,重启系统后,可以查看到内容:

^_^ / # ls /sys/bus/platform/devices/mynodes@77885566/
driver_override  of_node/         subsystem/
modalias         power/           uevent

^_^ / # ls /sys/bus/platform/devices/mynodes@77885566/of_node/
autorepeat  btn1/       btn2/       compatible  name


在dst设备树文件描述设备后就需要与platform_driver进行匹配和驱动了.
在设备驱动里获取设备树中的设备资源需要一套接口函数来实现:

#include <linux/property.h>

//用于获取设备节点的属性成员值函数, propname用于指定要获取值的属性名
bool device_property_present(struct device *dev, const char *propname);
int device_property_read_u8_array(struct device *dev, const char *propname,
                  u8 *val, size_t nval);
int device_property_read_u16_array(struct device *dev, const char *propname,
                   u16 *val, size_t nval);
int device_property_read_u32_array(struct device *dev, const char *propname,
                   u32 *val, size_t nval);
int device_property_read_u64_array(struct device *dev, const char *propname,
                   u64 *val, size_t nval);
int device_property_read_string_array(struct device *dev, const char *propname,
                      const char **val, size_t nval);
int device_property_read_string(struct device *dev, const char *propname,
                const char **val);
int device_property_match_string(struct device *dev,
                 const char *propname, const char *string);

//用于获取设备子节点的属性值函数. fwnode是表示子节点的对象地址
bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname);
int fwnode_property_read_u8_array(struct fwnode_handle *fwnode,
                  const char *propname, u8 *val,
                  size_t nval);
int fwnode_property_read_u16_array(struct fwnode_handle *fwnode,
                   const char *propname, u16 *val,
                   size_t nval);
int fwnode_property_read_u32_array(struct fwnode_handle *fwnode,
                   const char *propname, u32 *val,
                   size_t nval);
int fwnode_property_read_u64_array(struct fwnode_handle *fwnode,
                   const char *propname, u64 *val,
                   size_t nval);
int fwnode_property_read_string_array(struct fwnode_handle *fwnode,
                      const char *propname, const char **val,
                      size_t nval);
int fwnode_property_read_string(struct fwnode_handle *fwnode,
                const char *propname, const char **val);
int fwnode_property_match_string(struct fwnode_handle *fwnode,
                 const char *propname, const char *string);

struct fwnode_handle *device_get_next_child_node(struct device *dev,
                         struct fwnode_handle *child);


----------
unsigned int device_get_child_node_count(struct device *dev); //获取设备的子节点个数

//产生一个for循环用于检查所有的子节点
#define device_for_each_child_node(dev, child)              \
     for (child = device_get_next_child_node(dev, NULL); child;  \
          child = device_get_next_child_node(dev, child))

//注意函数以"device"开头表示读取设备的属性, 以"fwnode"开头表示读取子节点的属性.

用于获取mynodes设备资源的驱动源码:

/* mydrv.c */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>

int myprobe(struct platform_device *pdev)
{
    struct fwnode_handle *fwhandle;
    const char *str;
    u32 val;

    //获取设备子节点的个数
    printk("child node count : %d\n", device_get_child_node_count(&pdev->dev));
    //获取设备属性autorepeat的值
    printk("%d\n", device_property_read_bool(&pdev->dev, "autorepeat"));

    //遍历设备的每个子节点
    device_for_each_child_node(&pdev->dev, fwhandle) {
        //获取设备子节点的label属性值
        fwnode_property_read_string(fwhandle, "label", &str);
        printk("label = %s\n",  str);
        //获取设备子节点的code属性值
        fwnode_property_read_u32(fwhandle, "code", &val);
        printk("code = %x\n", val);
    };

    return 0;
}

int myremove(struct platform_device *pdev)
{
    printk("in myremove ...\n");
    return 0;
}

struct of_device_id ids[] = {
    {.compatible = "mynodes"},
    {},
};

struct platform_driver mydrv = {
    .probe = myprobe,
    .remove = myremove,

    .driver = {
        .owner = THIS_MODULE,
        .name = "mydrv" ,

        .of_match_table = ids,
    },
};

module_platform_driver(mydrv);
MODULE_LICENSE("GPL");

编译驱动模块加载后的输出结果:

[  419.424065] child node count : 2
[  419.427429] 1
[  419.429054] label = btn1
[  419.431690] code = 11
[  419.434000] label = btn2
[  419.436623] code = 22
<think> 首先,用户的问题是:“我想了解设备树platform_device结构体的相关信息 请问设备树platform_device结构体的介绍、关系及使用”。这基于之前的引用内容,我需要参考那些引用来回答。 回顾引用: - 引用[1]: 描述platform_device的作用:定义硬件资源的基本属性,注册到Platform总线与驱动匹配。提及了结构体定义。 - 引用[2]: 强调了platform_device在嵌入式系统中的重要性,用于硬件抽象、驱动匹配和资源管理。 - 引用[3]: 详细说明了platform_device的核心作用(硬件抽象、驱动匹配、资源管理),并给出了结构体定义的简化版。 - 引用[4]: 解释了如何将设备树中的device node结构体转换成platform_device结构体,包括函数调用关系。 - 引用[5]: 讨论了如何匹配platform_deviceplatform_driver,通过Platform总线。 用户的问题是询问设备树Device Tree)和platform_device结构体的介绍、关系及使用。设备树在这很关键,因为引用[4]提到了device node结构体转换成platform_device的过程。 关键点: - **设备树Device Tree)**:在嵌入式Linux系统中,设备树是一种描述硬件配置的数据结构,用于避免硬编码硬件信息在内核中。它允许在启动时动态加载硬件描述。 - **platform_device结构体**:是Linux内核中的一个结构体,用于表示平台设备(如SoC上的外设),包含设备名称、资源(如内存地址、中断号)等信息。 关系: - 设备树描述了硬件,包括设备的属性、资源等。 - 在内核启动时,设备树的节点(device node)会被解析并转换为platform_device结构体(如引用[4]所述)。 - 这些platform_device结构体注册到Platform总线上。 - 然后,Platform总线匹配对应的platform_driver结构体(如引用[5]所述),实现驱动加载和设备初始化。 使用: - 开发者通过设备树源文件(.dts)描述硬件。 - 编译为设备树二进制(.dtb),由引导程序加载。 - 内核解析dtb,创建device node,再转换为platform_device。 - 驱动开发者实现platform_driver,使用platform_device的资源(如ioremap内存、注册中断)来操作硬件。 根据系统级指令: - 所有行内数学表达式用$...$格式。但这个问题涉及内核结构体,可能不需要数学公式。如果需要,如内存地址或中断号,可以用$。 - 独立公式用$$...$$,但这可能不需要。 - LaTeX语法正确:我将使用LaTeX for code or structure if needed. - 使用中文回答:整个回答用中文。 - 生成相关问题:结束时生成相关问题。 - 引用标识:在回答中引用的段落末尾添加引用标识,如[^1]。 结构回答: - 清晰结构:逐步解决问题。 - 介绍设备树。 - 介绍platform_device结构体。 - 解释它们的关系。 - 描述使用场景和步骤。 - 结束时生成相关问题。 大纲: 1. **介绍设备树Device Tree)**: - 什么是设备树?它用于嵌入式系统,描述硬件配置。 - 避免硬编码硬件信息,提高可移植性。 - 引用[4]中提到device node结构体。 2. **介绍platform_device结构体**: - 根据引用[1]、[2]、[3],描述其作用:硬件抽象、资源管理、驱动匹配。 - 核心字段:name, id, resource, dev等。 - 注册到Platform总线。 3. **关系**: - 设备树中的节点被解析为device node结构体。 - 然后,通过函数如of_platform_device_create_pdata()转换为platform_device结构体(引用[4])。 - platform_device注册后,与platform_driver匹配(引用[5])。 4. **使用**: - 开发步骤: - 编写设备树源文件(.dts)。 - 编译为.dtb。 - 内核启动时加载dtb,解析并创建platform_device。 - 实现platform_driver驱动。 - 示例:如何在驱动中使用platform_device的资源。 结束时生成相关问题: - 基于主题,如设备树编译、驱动开发相关问题。 确保回答真实可靠:我已基于提供的引用构建知识;引用来自用户输入,所以正确引用。 </think> ### 设备树platform_device结构体的介绍、关系及使用 在Linux内核开发中,设备树Device Tree)和`platform_device`结构体是描述和管理硬件设备的核心机制,尤其在嵌入式SoC系统中广泛应用。下面我将逐步介绍它们的概念、相互关系及典型使用场景,内容基于您提供的引用信息[^1][^2][^3][^4][^5]。 --- #### 1. **设备树Device Tree)的介绍** 设备树是一种硬件描述语言(DTS),用于**动态传递硬件配置信息**给Linux内核。它取代了传统的内核硬编码硬件参数方式,提高了系统的可移植性和灵活性。 - **作用**:描述硬件拓扑结构,包括CPU、内存、外设(如UART、GPIO)的资源属性(如寄存器地址、中断号)。例如,设备树定义的内存地址可能表示为`reg = <0x10000000 0x1000>`,表示起始地址为$0x10000000$、大小为$0x1000$字节。 - **结构**:设备树以节点(node)组织,每个节点对应一个硬件设备,包含属性(properties)如`compatible`(用于驱动匹配)、`reg`(资源地址)等。内核启动时,引导程序(如U-Boot)加载设备树二进制文件(.dtb),内核解析后转换为`device_node`结构体(引用[4])。 - **优点**:支持热插拔、减少内核重新编译需求,适用于嵌入式系统(如ARM、RISC-V平台)。 #### 2. **platform_device结构体的介绍** `platform_device`是Linux内核中定义平台设备(Platform Device)的关键结构体,代表SoC(片上系统)内部的硬件外设,如I2C控制器或定时器。 - **核心作用**(引用[1][^1][^2][^3]): - **硬件抽象**:将物理硬件(如寄存器、中断)抽象为软件可操作的对象。 - **资源管理**:封装设备资源信息,例如内存区域(`struct resource`)或中断号。 - **驱动匹配**:通过`name`或`id`字段与对应的`platform_driver`驱动匹配,实现自动绑定。 - **结构体定义(简化版)**(引用[3][^3]): ```c struct platform_device { const char *name; // 设备名称,用于驱动匹配(如 "uart0") int id; // 设备ID(用于区分同名设备) struct device dev; // 继承自基础device结构体 struct resource *resource; // 资源数组(内存、中断等) u32 num_resources; // 资源数量 void *platform_data; // 设备私有数据 }; ``` - 其中,`struct resource`定义资源,如: ```c struct resource { resource_size_t start; // 资源起始地址 ($0x10000000$) resource_size_t end; // 资源结束地址 ($0x1000FFFF$) unsigned long flags; // 资源类型(如 IORESOURCE_MEM) }; ``` #### 3. **设备树platform_device的关系** 设备树和`platform_device`通过内核的Platform总线机制紧密关联,实现硬件描述到驱动加载的自动转换: - **转换过程**(引用[4][^4]): 1. **设备树解析**:内核启动时,解析设备树二进制(.dtb),为每个设备节点创建`device_node`结构体(包含属性如`compatible`)。 2. **节点转换为platform_device**:内核调用函数链(如`of_platform_populate()`)遍历`device_node`节点: - 对兼容节点(如匹配`simple-bus`),执行`of_platform_device_create_pdata()`。 - 该函数分配`platform_device`内存,并将节点属性(如`reg`、`interrupts`)转换为`resource`数组。 - 最终,`of_device_add()`注册`platform_device`到Platform总线。 3. **驱动匹配**:注册的`platform_device`存储在Platform总线的`devices_kset`中(引用[5][^5]),当匹配的`platform_driver`(存储在`drivers_kset`中)注册时,基于`name`或`compatible`属性进行绑定。 - **关键关系总结**: - 设备树是硬件**描述层**(静态数据)。 - `device_node`是内核内部**中间表示**。 - `platform_device`是硬件**操作层**,提供资源给驱动使用。 - 该模型提高了代码可移植性:修改设备树即可适配新硬件,无需改动驱动代码(引用[2][^2])。 #### 4. **使用场景与步骤** 在嵌入式驱动开发中,设备树和`platform_device`的典型使用流程如下: - **步骤1: 编写设备树源文件(.dts)** 定义硬件属性。例如,描述一个UART设备: ```dts uart0: serial@10000000 { compatible = "vendor,uart"; // 驱动匹配标识 reg = <0x10000000 0x1000>; // 寄存器地址范围(起始 $0x10000000$, 大小 $0x1000$) interrupts = <10>; // 中断号 }; ``` 编译为二进制文件(.dtb)并加载到内核。 - **步骤2: 驱动开发中使用platform_device** 在驱动代码中: - 实现`platform_driver`驱动结构体,定义`probe()`函数(设备初始化)。 - 在`probe()`中,通过`platform_get_resource()`获取资源(如内存地址): ```c struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); void __iomem *base = ioremap(res->start, resource_size(res)); // 映射物理地址到虚拟地址 ``` - 匹配基于`compatible`属性:驱动声明`of_match_table`(引用[5][^5])。 - **常见使用场景**: - **嵌入式启动**:设备树传递硬件配置,内核自动创建`platform_device`。 - **驱动开发**:简化资源访问,驱动仅需处理`platform_device`提供的资源。 - **调试**:通过`/sys/devices/platform/`查看注册的设备。 - **优点与局限性**: - **优点**:硬件与驱动解耦,支持动态配置(适用于不同开发板)。 - **局限性**:仅适用于Platform总线设备;非热插拔设备(如PCI)使用其他机制。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值