设备树是什么

设备树是什么
设备树是什么device tree 设备树dts device tree source 设备树源文件dtc device tree compiler 设备树编译/反编译/调试工具dtb device tree blob 设备树二进制文件uboot只会加载一个设备树文件即dtb文件设备树语法节点根节点 子节点![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_urlassets%2Fimage-20260305144540580.pngpos_idimg-D5CYTguq-1782301774293)在节点的{}里面是描述该节点的属性property即设备的特性。它的值是多样化的1.它可以是字符串string如①也可能是字符串数组string-list如②2.它也可以是32 bit unsigned integers如cell⑧整形用表示3.它也可以是binary data如③十六进制用[]表示4.它也可能是空如⑦典型节点chosen nodechosen { bootargs tegraid40.0.0.00.00 vmalloc256M videotegrafb consolettyS0,115200n8 earlyprintk; };chosen node 主要用来描述由系统指定的runtime parameter它并没有描述任何硬件设备节点信息。原先通过tag list传递的一些linux kernel运行的参数可以通过chosen节点来传递。如command line可以通过bootargs这个property来传递。如果存在chosen node它的parent节点必须为“/”根节点。本示例都以imx6ull的硬件环境下讲解aliases nodealiases { i2c6 pca9546_i2c0; i2c7 pca9546_i2c1; i2c8 pca9546_i2c2; i2c9 pca9546_i2c3; };aliases node用来定义别名类似C中引用。上面是一个在.dtsi中的典型应用当使用i2c6时也即使用pca9546_i2c0使得引用节点变得简单方便。例当.dts include 该.dtsi时将i2c6的status属性赋值为okay则表明该主板上的pca9546_i2c0处于enable状态反之status赋值为disabled则表明该主板上的pca9546_i2c0处于disenable状态。memory nodememory { device_type memory; reg 0x00000000 0x20000000; /* 512 MB */ };对于memory nodedevice_type必须为memory由之前的描述可以知道该memory node是以0x00000000为起始地址以0x20000000为结束地址的512MB的空间。一般而言在.dts中不对memory进行描述而是通过bootargs中类似521M0x00000000的方式传递给内核。设备树中的引用示例/ { uart1: serial02020000 { compatible fsl,imx6ul-uart; reg 0x02020000 0x4000; status disabled; }; }; uart1 { status okay; };标签格式标签: 节点名地址 { // 节点内容 };编译后uart1节点最终会包含status okay但标签uart1:和引用uart1都会消失。在板子的/proc/device-tree/下你会看到目录/serial02020000/或者/soc/serial02020000/取决于具体路径里面有一个status文件内容为okay。没有任何名为uart1或uart1的文件或目录存在。xxxx是设备树源文件.dts中的语法它用于引用一个预先定义的标签label目的是将后续的属性或子节点合并到那个标签所代表的节点中。这相当于一种“粘合剂”方便在多个地方修改同一个节点。如何使用使用步骤在你的内核环境下编译dts为dtb文件在你的开发板或其他环境下替换原来的dtb文件重启内核查看设备节点是否存在在板子上/proc/device-tree目录是内核提供的设备树运行时视图它的一级目录和文件直接对应设备树根节点/下的子节点和属性。编写驱动代码和应用代码驱动中如何使用设备树获取设备节点dtsled.nd of_find_node_by_path(/alphaled);eg/* 获取设备树中的属性数据 */ /* 1、获取设备节点alphaled */ dtsled.nd of_find_node_by_path(/alphaled); if(dtsled.nd NULL) { printk(alphaled node can not found!\r\n); return -EINVAL; } else { printk(alphaled node has been found!\r\n); }驱动与设备树节点绑定的“信物”是compatible属性。获取资源——核心 OF 操作函数一旦进入probe函数驱动就可以通过设备节点指针通常是struct device_node *np pdev-dev.of_node;来提取所需的硬件信息了。以下是一些最常用的操作函数资源类型常用函数作用寄存器地址of_iomap(np, 0)将reg属性中第0段地址直接映射为虚拟地址省去了ioremap的步骤。of_address_to_resource(np, 0, res)获取reg属性并填充到一个struct resource结构体中之后可以用devm_ioremap_resource进行映射。中断号irq_of_parse_and_map(np, 0)解析interrupts属性第0个中断并映射为Linux中断号。of_irq_get(np, 0)功能类似推荐使用。GPIOof_get_named_gpio(np, enable-gpios, 0)获取名为enable-gpios的属性中定义的GPIO编号之后可以用gpio_request等函数操作。整型属性of_property_read_u32(np, clock-frequency, val)读取一个32位整型属性值。属性长度of_property_count_elems_of_size(np, reg, sizeof(u32))获取reg属性中包含了多少个地址/长度对。字符串of_property_read_string(np, status, str)读取字符串属性。设备树如何工作进阶编译阶段dtc :用于编译dts文件为dtb文件dtb :用于kernel去使用设备树.dts(源文件)↓ dtc编译 .dtb(二进制Blob)↓ 嵌入内核或单独加载 启动时加载到内存引导阶段目的加载到内存Bootloader如U-Boot加载设备树 //1. 从存储设备读取dtb //2. 修正地址relocation //3. 传递给内核 //4. 跳转到内核入口内核初始化阶段加载到内核完后需要将设备树进行解析// arch/arm/kernel/setup.cvoid__initsetup_arch(char**cmdline_p){// 1. 早期设备树扫描early_init_dt_scan_nodes();// 2. 解析内存信息early_init_dt_scan_memory();// 3. 解析chosen节点bootargs等of_scan_flat_dt(early_init_dt_scan_chosen,boot_command_line);}展开设备树结构// drivers/of/fdt.cvoid__initunflatten_device_tree(void){// 将平面设备树转换为树形结构__unflatten_device_tree(initial_boot_params,of_root,of_allnodes);// 最终生成的结构// of_root - 根节点// of_allnodes - 所有节点的链表}这样就得到了设备树数据后续就需要驱动去匹配了。驱动匹配设备树进阶这里需要介绍几个核心的结构体相关数据结构设备树相关结构// 设备树节点内核表示structdevice_node{constchar*name;// 节点名称constchar*type;// 设备类型phandle phandle;// 句柄constchar*full_name;// 完整路径名structproperty*properties;// 属性链表structdevice_node*parent;// 父节点structdevice_node*child;// 子节点structdevice_node*sibling;// 兄弟节点constvoid*data;// 设备特定数据};// 设备树属性structproperty{char*name;// 属性名intlength;// 值长度void*value;// 属性值structproperty*next;// 下一个属性unsignedlong_flags;// 内部标志};平台设备结构// 由设备树节点创建的platform_devicestructplatform_device{constchar*name;// 设备名称intid;// 设备IDstructdevicedev;// 基础设备结构u32 num_resources;// 资源数量structresource*resource;// 资源数组conststructplatform_device_id*id_entry;// 设备树相关structdevice_node*of_node;// 对应的设备树节点structirq_domain*irq_domain;};驱动匹配结构// 设备树匹配表structof_device_id{charname[32];// 设备名称chartype[32];// 设备类型charcompatible[128];// 兼容字符串constvoid*data;// 私有数据};// 平台驱动结构structplatform_driver{int(*probe)(structplatform_device*);int(*remove)(structplatform_device*);void(*shutdown)(structplatform_device*);structdevice_driverdriver;// 基础驱动结构conststructplatform_device_id*id_table;// 设备树匹配表conststructof_device_id*of_match_table;};驱动匹配过程阶段一设备树节点转换为设备// drivers/of/platform.c/** * of_platform_populate() - 从设备树创建设备 */intof_platform_populate(structdevice_node*root,conststructof_device_id*matches,structdevice*parent){structdevice_node*child;// 遍历设备树节点for_each_child_of_node(root,child){// 检查节点是否可用if(!of_device_is_available(child))continue;// 创建设备of_platform_device_create_pdata(child,matches,parent,NULL);}return0;}/** * of_platform_device_create_pdata() - 创建platform_device */staticstructplatform_device*of_platform_device_create_pdata(structdevice_node*np,constchar*bus_id,void*platform_data,structdevice*parent){structplatform_device*pdev;// 1. 分配platform_devicepdevplatform_device_alloc(,PLATFORM_DEVID_NONE);if(!pdev)returnNULL;// 2. 设置设备树节点pdev-dev.of_nodeof_node_get(np);pdev-dev.parentparent;// 3. 设置设备名称if(bus_id)pdev-namebus_id;elsepdev-namenp-name;// 4. 解析设备资源内存、IRQ等of_device_add_resource(pdev,np);// 5. 添加到系统if(platform_device_add(pdev)!0){platform_device_put(pdev);returnNULL;}returnpdev;}阶段二驱动注册// 驱动注册示例staticstructplatform_drivermy_driver{.driver{.namemy-device,.ownerTHIS_MODULE,.of_match_tablemy_of_match,// 关键设备树匹配表},.probemy_probe,.removemy_remove,};// 设备树匹配表staticconststructof_device_idmy_of_match[]{{.compatiblevendor,device-v1.0},{.compatiblevendor,device-v2.0},{.compatiblevendor,generic-device},{}// 结束标记};MODULE_DEVICE_TABLE(of,my_of_match);// 驱动注册module_platform_driver(my_driver);// 展开后的注册函数staticint__initmy_driver_init(void){returnplatform_driver_register(my_driver);}module_init(my_driver_init);阶段三匹配执行过程此处是内核在做的事情具体不展开分析了。后续持续更新感谢观看