• DTB:设备树学习:内核对设备树的处理


    内核版本:linux-4.19

    之前系统的学习了有关设备树的一些知识,时间长了总会有忘记的时候,所以现在把所学到的知识记录下来。

    系统启动后,内核会执行一段汇编代码,汇编代码暂不分析,我们从 start_kernel 开始。

    一、优先被初始化的信息

    调用流程:

    start_kernel
    	-->setup_arch
    		-->setup_machine_fdt
    			-->early_init_dt_verify			/* 验证设备树文件 */
    			-->of_flat_dt_match_machine		/* 与内核中注册的 machine_desc 进行比较, 最终获取到与之匹配的 machine_desc */
    				-->arch_get_next_mach		/* 获取到 machine_desc 的 dt_compat 属性 */
    			-->early_init_dt_scan_nodes		/* 获取到设备树中 chosen、{size,address}-cells、memory 信息 */
    

    early_init_dt_verify 代码:

    bool __init early_init_dt_verify(void *params)
    {
    	if (!params)
    		return false;
    
    	/* 验证设备树的 magic */
    	if (fdt_check_header(params))
    		return false;
    
    	/* 设置 device-tree 指针 */
    	initial_boot_params = params;
    	of_fdt_crc32 = crc32_be(~0, initial_boot_params,
    				fdt_totalsize(initial_boot_params));
    	return true;
    }
    

    of_flat_dt_match_machine 代码:

    获取到最为匹配的 machine_desc。

    const void * __init of_flat_dt_match_machine(const void *default_match,
    		const void * (*get_next_compat)(const char * const**))
    {
        ...
        
        while ((data = get_next_compat(&compat))) {
        		score = of_flat_dt_match(dt_root, compat);
        		if (score > 0 && score < best_score) {
        			best_data = data;
        			best_score = score;
        		}
        	}
    	...
    	
    	return best_data;
    }
    

    early_init_dt_scan_nodes 代码:

    /* 扫描 /chosen 节点,处理 bootargs 并保存至 boot_command_line */
    of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
    
    /* 获取 {size,address}-cells 信息 */
    of_scan_flat_dt(early_init_dt_scan_root, NULL);
    
    /* 设置 memeory */
    of_scan_flat_dt(early_init_dt_scan_memory, NULL);
    

    通过 early_init_dt_scan_memory 函数,最后调用 memblock_add 来完成 memory 的设置。

    二、设备树展开

    接下来,内核会展开设备树,并将节点构建为 device_node。便于系统管理、使用。

    调用流程:

    start_kernel
    	-->setup_arch
    		-->unflatten_device_tree
    			-->__unflatten_device_tree
    				-->unflatten_dt_nodes(blob, NULL, dad, NULL);		/* First pass, scan for size */
    					-->fdt_next_node								/* 获取每个 node 的 offsize, 并统计整体大小 */
    				-->unflatten_dt_nodes(blob, mem, dad, mynodes);		/* Second pass, do actual unflattening */
    					-->populate_node
    

    device_node 结构:

    struct device_node {
    	const char *name;   /* 节点的 name 属性 */
    	const char *type;   /* 节点的 device_type 属性 */
    	phandle phandle;
    	const char *full_name;
    	struct fwnode_handle fwnode;
    
    	struct	property *properties;   /* 节点的属性 */
    	struct	property *deadprops;	/* removed properties */
    	struct	device_node *parent;
    	struct	device_node *child;
    	struct	device_node *sibling;
    #if defined(CONFIG_OF_KOBJ)
    	struct	kobject kobj;
    #endif
    	unsigned long _flags;
    	void	*data;
    #if defined(CONFIG_SPARC)
    	const char *path_component_name;
    	unsigned int unique_id;
    	struct of_irq_controller *irq_trans;
    #endif
    };
    

    property 结构:

    struct property {
    	char	*name;  /* 属性名字 */
    	int	length;     /* 属性值长度 */
    	void	*value; /* 属性值指针 */
    	struct property *next;
    #if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
    	unsigned long _flags;
    #endif
    #if defined(CONFIG_OF_PROMTREE)
    	unsigned int unique_id;
    #endif
    #if defined(CONFIG_OF_KOBJ)
    	struct bin_attribute attr;
    #endif
    };
    

    这些 device_node 构成一棵树,根节点为: of_root。

    三、device_node 转换为 platform_device

    调用流程:

    arch_initcall_sync(of_platform_default_populate_init);
    	-->of_platform_default_populate
    		-->of_platform_populate
    			-->of_platform_bus_create
    				-->of_platform_device_create_pdata
    					-->of_device_alloc
    					-->of_device_add
    

    这时涉及到 initcall 调用问题,应该会在下一篇文章总结。

    of_device_alloc 代码:

    struct platform_device *of_device_alloc(struct device_node *np, const char *bus_id, struct device *parent)
    {
        ...
        platform_device_alloc       /* 分配 platform_device */
        ...
        of_address_to_resource      /* 解析 address 资源 */
        ...
        of_irq_to_resource_table    /* 解析 irq 资源 */
        ...
    }
    

    of_device_add 代码:

    int of_device_add(struct platform_device *ofdev)
    {
    	BUG_ON(ofdev->dev.of_node == NULL);
    
    	ofdev->name = dev_name(&ofdev->dev);
    	ofdev->id = PLATFORM_DEVID_NONE;
    
    	set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));
    
    	return device_add(&ofdev->dev);     /* 添加 device */
    }
    
    哪些 device_node 可以转换为 platform_device?
    	1. 根节点下含有 compatile 属性的子节点
    	2. 如果一个结点的 compatile 属性含有这些特殊的值 ("simple-bus", "simple-mfd", "isa", "arm,amba-bus") 之一,
    	   那么它的子结点(需含 compatile 属性)也可以转换为 platform_device
        3. i2c, spi 等总线节点下的子节点,应该交给对应的总线驱动程序来处理,它们不应该被转换为 platform_device。
    

    of_default_bus_match_table 表:

    const struct of_device_id of_default_bus_match_table[] = {
    	{ .compatible = "simple-bus", },
    	{ .compatible = "simple-mfd", },
    	{ .compatible = "isa", },
    #ifdef CONFIG_ARM_AMBA
    	{ .compatible = "arm,amba-bus", },
    #endif /* CONFIG_ARM_AMBA */
    	{} /* Empty terminated list */
    };
    
  • 相关阅读:
    Enterprise Library 企业库 V4.1
    跨域实现IFRAME自适应高度
    微软企业库4.1学习笔记(二)各功能之间的依赖关系以及对象创建
    微软企业库4.1学习笔记(三)企业库迁移和并行使用,以及企业库的扩展
    微软企业库4.1学习笔记(五)对象创建和依赖注入方法
    判断 iframe 是否加载完成的完美方法
    对JavaScript调用堆栈和setTimeout用法的深入研究
    工作流技术杂谈
    企业流程管理平台V2.0介绍(.NET平台下的工作流)
    c#操作oracle lob字段[转自 芽芽的回收站]
  • 原文地址:https://www.cnblogs.com/GyForever1004/p/10400266.html
Copyright © 2020-2023  润新知