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

内核版本: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 */
};

原文地址:https://www.cnblogs.com/GyForever1004/p/10400266.html

时间: 2024-08-30 04:16:35

设备树学习:内核对设备树的处理的相关文章

Smart210学习记录------块设备

转自:http://bbs.chinaunix.net/thread-2017377-1-1.html 本章的目的用尽可能最简单的方法写出一个能用的块设备驱动.所谓的能用,是指我们可以对这个驱动生成的块设备进行mkfs,mount和读写文件.为了尽可能简单,这个驱动的规模不是1000行,也不是500行,而是100行以内. 这里插一句,我们不打算在这里介绍如何写模块,理由是介绍的文章已经满天飞舞了.如果你能看得懂.并且成功地编译.运行了这段代码,我们认为你已经达到了本教程的入学资格,当然,如果你不

Linux设备驱动程序学习笔记(一)

1.设备驱动程序扮演的角色:       设备程序是一个独立的“黑盒子”,使其某个特定硬件响应一个定义良好的内部编程接口,这些接口完全隐藏了设备的工作细节.用户的操作通过一组标准化的调用执行,而这些调用独立于特定的驱动程序.将这些调用映射到作用于实际硬件的设备特有操作上,则是设备驱动程序的任务.2.驱动程序的作用:        驱动程序应该处理如何使用硬件可用的问题,而将怎样使用硬件的问题留给上层应用.(提供机制,而不是策略)3.内核功能划分:        进程管理    内存管理    文

0915-----Linux设备驱动 学习笔记----------一个简单的字符设备驱动程序

0.前言 研究生生活一切都在步入正轨,我也开始了新的学习,因为实在不想搞存储,所以就决定跟师兄学习设备驱动,看了两星期书,终于有点头绪了,开始记录吧! 1.准备工作 a)查看内核版本 uname -r b)安装内核源码树(http://www.cnblogs.com/Jezze/archive/2011/12/23/2299871.html) 在www.linux.org上下载源码编译,这里是.xz格式,需要安装解压工具,xz-utils: 解压方法示例:xz -d linux-3.1-rc4.

系统虚拟化学习笔记——PCI设备

内容摘自<系统虚拟化:原理与实现> PCI 总线架构 PCI总线是典型的树结构.把北桥中host-PCI桥看做根,总线中其他PCI-PCI桥,PCI-ISA桥(ISA总线转PCI总线桥)等桥设备和 直接连PCI总线的设备看做节点,整个PCI架构可以概括成下图: 通过桥,PCI可以很容易被扩展,并且与其他总线相互挂接,构成整个系统的总线网络.与HOST-PCI桥相连的总线称为总线0, 其他层次总线的编号,是在BIOS(或者操作系统)枚举设备时确定的. 设备标识符 设备标识符可以看做设备在PCI总

Linux Platform设备驱动学习与小结

Platform 设备先被注册然后platfrom驱动加载时会调用驱动程序中的probe()入口函数,扫描系统中已注册的设备,通过.Name域找到匹配设备后将驱动和设备绑定.一个驱动可以对应多个设备,但是一个设备只对一个驱动.Linux下的虚拟总线platform对应设备platform_device,对应的驱动为platform_driver.一个很不恰当的例子:设备好比男人,驱动好比女人,platform作为媒人,将两个对上眼的(name域相同)的相匹配到一起.然后男人(device)到她(

【android学习】安卓设备实现触摸事件的监听,跨进程事件注入

最近想实现触摸屏的事件记录,并且分别做时间戳标记,类似于adb shell 下的getevent/setevent,查了很多资料,下面是一些总结. 实现方法: 1.利用onclick方法等.这种方法只能用于自己的Activity和View中,如果要检测其他Activity,比如键盘就不行了. 2.从linux内核入手.在事件分发前截获事件,可以对任意Activity实现事件捕获. android触摸事件的流程: http://blog.csdn.net/sunnyfans/article/det

字符设备驱动体验,字符设备驱动学习

字符设备驱动学习 在Linux系统中,驱动程序通常采用内核模块的程序结构来进行编码.因此,编译/安装一个驱动程序,其实质就是编译/安装一个内核模块. 一.编译安装字符设备驱动程序 memdev文件中:在这个文件里和真实的硬件无关,只是虚构了一个数组 1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/init.h> 4 #include <linux/cdev.h> 5

linux驱动学习(1)——字符设备驱动开发

(一)驱动程序介绍 (a)Linux驱动程序学习 知识结构: 1. Linux驱动程序设计模式(40%) 2. 内核相关知识(30%) 3. 硬件相关知识(30%) (b)驱动分类: ①字符设备: 字符设备是一种按字节来访问的设备,字符驱动则负责驱动字符设备,这样的驱动通常实现 open, close,read和 write 系统调用. ②块设备: 在大部分的 Unix 系统, 块设备不能按字节处理数据,只能一次传送一个或多个长度是512字节( 或一个更大的 2 次幂的数 )的整块数据,而Lin

Trie树学习2

数组实现的Trie树 字符容量有限,可以使用链表实现更为大容量的Trie #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <map> #include <set> #include <algorithm> #include <cstdlib> #