最近在学习Linux内核驱动开发时,研究了设备树(Device Tree)的解析流程和设备匹配机制。走读代码,记录下代码流程,也方便自己回顾。如果大家也在学习类似内容,或许也可以有所参考,如果有理解不到位地方,欢迎指正交流。设备树解析过程全解析
Linux内核启动时,设备树的解析是一个关键步骤,它决定了系统如何识别和管理硬件资源。setup_arch ==> setup_machine_fdt(__fdt_pointer); // 将设备树地址映射到虚拟内存中 unflatten_device_tree(); ==> __unflatten_device_tree // 把设备树解析成树形结构 ==> unflatten_dt_nodes // 执行2次,第一次计算内存大小,第二次展开设备树
解析过程解释:
内核获取设备树的物理地址(__fdt_pointer)
将设备树映射到虚拟内存空间
分两次遍历设备树节点:第一次计算所需内存大小,第二次实际构建数据结构
设备树解析后,每个节点都会转换为device_node结构体:struct device_node { const char *name; // 节点名 phandle phandle; // 节点句柄 const char *full_name; // 节点全路径名 struct fwnode_handle fwnode; // 固件节点句柄 struct property *properties; // 属性链表 struct property *deadprops; // 已移除的属性 struct device_node *parent; // 父节点 struct device_node *child; // 子节点 struct device_node *sibling; // 兄弟节点#if defined(CONFIG_OF_KOBJ) struct kobject kobj; // kobject对象#endif unsigned long _flags; // 标志位 void *data; // 私有数据#if defined(CONFIG_SPARC) unsigned int unique_id; // 唯一ID struct of_irq_controller *irq_trans; // 中断控制器#endif};
设备匹配机制
pci_register_driver ==> __pci_register_driver ==> driver_register ==> bus_add_driver ==> driver_attach ==> __driver_attach ==> driver_match_device // 调用总线的match函数,大多都是直接匹配设备树,name,id_table,ACPI drv->bus->match ? drv->bus->match(dev, drv) : 1;
of_driver_match_device ==> of_match_device(drv->of_match_table, dev) ==> of_match_node(matches, dev->of_node) ==> __of_match_node(matches, node) // node类型为device_node ==> __of_device_is_compatible(node, matches->compatible, matches->type, matches->name) // 直接匹配设备的compatible属性
匹配关键:驱动通过of_match_table指定兼容的设备树节点,内核通过比较节点的compatible属性来实现匹配。