設備樹源碼解析

  1. //Based on Linux v3.14 source code
  2. Linux設備樹機制(Device Tree)
  3. 一、描述
  4. ARM Device Tree起源於OpenFirmware (OF),在過去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代碼,相當多數的代碼只是在描述板級細節,而這些板級細節對於內核來講,不過是垃圾,如板上的platform設備、resource、i2c_board_info、spi_board_info以及各種硬件的platform_data。爲了改變這種局面,Linux社區的大牛們參考了PowerPC等體系架構中使用的Flattened Device Tree(FDT),也採用了Device Tree結構,許多硬件的細節可以直接透過它傳遞給Linux,而不再需要在kernel中進行大量的冗餘編碼。

  5. Device Tree是一種描述硬件的數據結構,由一系列被命名的結點(node)和屬性(property)組成,而結點本身可包含子結點。所謂屬性,其實就是成對出現的name和value。在Device Tree中,可描述的信息包括(原先這些信息大多被hard code到kernel中):CPU的數量和類別,內存基地址和大小,總線和橋,外設連接,中斷控制器和中斷使用情況,GPIO控制器和GPIO使用情況,Clock控制器和Clock使用情況。

  6. 通常由.dts文件以文本方式對系統設備樹進行描述,經過Device Tree Compiler(dtc)將dts文件轉換成二進制文件binary device tree blob(dtb).dtb文件可由Linux內核解析,有了device tree就可以在不改動Linux內核的情況下,對不同的平臺實現無差異的支持,只需更換相應的dts文件,即可滿足。

  7. 二、相關結構體
  8. 1.U-Boot需要將設備樹在內存中的存儲地址傳給內核。該樹主要由三大部分組成:頭(Header)、結構塊(Structure block)、字符串塊(Strings block)。設備樹在內存中的存儲佈局圖。
  9.         ------------------------------
  10. base -> | struct boot_param_header |
  11.         ------------------------------
  12.         | (alignment gap) (*) |
  13.         ------------------------------
  14.         | memory reserve map |
  15.         ------------------------------
  16.         | (alignment gap) |
  17.         ------------------------------
  18.         | |
  19.         | device-tree structure |
  20.         | |
  21.         ------------------------------
  22.         | (alignment gap) |
  23.         ------------------------------
  24.         | |
  25.         | device-tree strings |
  26.         | |
  27. -----> ------------------------------
  28. |
  29. |
  30. --- (base + totalsize)
  31. 1.1 頭(header)
  32. 頭主要描述設備樹的一些基本信息,例如設備樹大小,結構塊偏移地址,字符串塊偏移地址等。偏移地址是相對於設備樹頭的起始地址計算的。
  33. struct boot_param_header {
  34.     __be32 magic;                //設備樹魔數,固定爲0xd00dfeed
  35.     __be32 totalsize;            //整個設備樹的大小
  36.     __be32 off_dt_struct;        //保存結構塊在整個設備樹中的偏移
  37.     __be32 off_dt_strings;        //保存的字符串塊在設備樹中的偏移
  38.     __be32 off_mem_rsvmap;        //保留內存區,該區保留了不能被內核動態分配的內存空間
  39.     __be32 version;            //設備樹版本
  40.     __be32 last_comp_version;    //向下兼容版本號
  41.     __be32 boot_cpuid_phys;    //爲在多核處理器中用於啓動的主cpu的物理id
  42.     __be32 dt_strings_size;    //字符串塊大小
  43.     __be32 dt_struct_size;     //結構塊大小
  44. };

  45. 1.2 結構塊(struct block)
  46. 設備樹結構塊是一個線性化的結構體,是設備樹的主體,以節點node的形式保存了目標單板上的設備信息。
  47. 在結構塊中以宏OF_DT_BEGIN_NODE標誌一個節點的開始,以宏OF_DT_END_NODE標識一個節點的結束,整個結構塊以宏OF_DT_END結束。一個節點主要由以下幾部分組成。
  48. (1)節點開始標誌:一般爲OF_DT_BEGIN_NODE。
  49. (2)節點路徑或者節點的單元名(ersion<3以節點路徑表示,version>=0x10以節點單元名錶示)
  50. (3)填充字段(對齊到四字節)
  51. (4)節點屬性。每個屬性以宏OF_DT_PROP開始,後面依次爲屬性值的字節長度(4字節)、屬性名稱在字符串塊中的偏移量(4字節)、屬性值和填充(對齊到四字節)。
  52. (5)如果存在子節點,則定義子節點。
  53. (6)節點結束標誌OF_DT_END_NODE。

  54. 1.3 字符串塊
  55. 通過節點的定義知道節點都有若干屬性,而不同的節點的屬性又有大量相同的屬性名稱,因此將這些屬性名稱提取出一張表,當節點需要應用某個屬性名稱時直接在屬性名字段保存該屬性名稱在字符串塊中的偏移量。

  56. 1.4 設備樹源碼 DTS 表示
  57. 設備樹源碼文件(.dts)以可讀可編輯的文本形式描述系統硬件配置設備樹,支持 C/C++方式的註釋,該結構有一個唯一的根節點“/,每個節點都有自己的名字並可以包含多個子節點。設備樹的數據格式遵循了 Open Firmware IEEE standard 1275。這個設備樹中有很多節點,每個節點都指定了節點單元名稱。每一個屬性後面都給出相應的值。以雙引號引出的內容爲 ASCII 字符串,以尖括號給出的是 32 位的16進制值。這個樹結構是啓動 Linux 內核所需節點和屬性簡化後的集合,包括了根節點的基本模式信息、CPU 和物理內存佈局,它還包括通過/chosen 節點傳遞給內核的命令行參數信息。

  58. 1.5 machine_desc結構
  59. 內核提供了一個重要的結構體struct machine_desc ,這個結構體在內核移植中起到相當重要的作用,內核通過machine_desc結構體來控制系統體系架構相關部分的初始化。machine_desc結構體通過MACHINE_START宏來初始化,在代碼中, 通過在start_kernel->setup_arch中調用setup_machine_fdt來獲取。
  60. struct machine_desc {
  61.     unsigned int nr; /* architecture number */
  62.     const char *name; /* architecture name */
  63.     unsigned long atag_offset; /* tagged list (relative) */
  64.     const char *const *dt_compat; /* array of device tree* 'compatible' strings */
  65.     unsigned int nr_irqs; /* number of IRQs */
  66.     
  67. #ifdef CONFIG_ZONE_DMA
  68.     phys_addr_t dma_zone_size; /* size of DMA-able area */
  69. #endif
  70.     
  71.     unsigned int video_start; /* start of video RAM */
  72.     unsigned int video_end; /* end of video RAM */
  73.     
  74.     unsigned char reserve_lp0 :1; /* never has lp0 */
  75.     unsigned char reserve_lp1 :1; /* never has lp1 */
  76.     unsigned char reserve_lp2 :1; /* never has lp2 */
  77.     enum reboot_mode reboot_mode; /* default restart mode */
  78.     struct smp_operations *smp; /* SMP operations */
  79.     bool (*smp_init)(void);
  80.     void (*fixup)(struct tag *, char **,struct meminfo *);
  81.     void (*init_meminfo)(void);
  82.     void (*reserve)(void);/* reserve mem blocks */
  83.     void (*map_io)(void);/* IO mapping function */
  84.     void (*init_early)(void);
  85.     void (*init_irq)(void);
  86.     void (*init_time)(void);
  87.     void (*init_machine)(void);
  88.     void (*init_late)(void);
  89. #ifdef CONFIG_MULTI_IRQ_HANDLER
  90.     void (*handle_irq)(struct pt_regs *);
  91. #endif
  92.     void (*restart)(enum reboot_mode, const char *);
  93. };

  94. 1.6 設備節點結構體
  95. struct device_node {
  96.     const char *name;    //設備name
  97.     const char *type; //設備類型
  98.     phandle phandle;
  99.     const char *full_name; //設備全稱,包括父設備名

  100.     struct property *properties; //設備屬性鏈表
  101.     struct property *deadprops; //removed properties
  102.     struct device_node *parent; //指向父節點
  103.     struct device_node *child; //指向子節點
  104.     struct device_node *sibling; //指向兄弟節點
  105.     struct device_node *next; //相同設備類型的下一個節點
  106.     struct device_node *allnext; //next in list of all nodes
  107.     struct proc_dir_entry *pde; //該節點對應的proc
  108.     struct kref kref;
  109.     unsigned long _flags;
  110.     void *data;
  111. #if defined(CONFIG_SPARC)
  112.     const char *path_component_name;
  113.     unsigned int unique_id;
  114.     struct of_irq_controller *irq_trans;
  115. #endif
  116. };

  117. 1.7 屬性結構體
  118. struct property {
  119.     char *name;        //屬性名
  120.     int length;        //屬性值長度
  121.     void *value;        //屬性值
  122.     struct property *next; //指向下一個屬性
  123.     unsigned long _flags; //標誌
  124.     unsigned int unique_id;
  125. };


  126. 三、設備樹初始化及解析
  127. 分析Linux內核的源碼,可以看到其對扁平設備樹的解析流程如下:
  128. (1)首先在內核入口處將從u-boot傳遞過來的鏡像基地址。
  129. (2)通過調用early_init_dt_scan()函數來獲取內核前期初始化所需的bootargs,cmd_line等系統引導參數。
  130. (3)根據bootargs,cmd_line等系統引導參數進入start_kernel()函數,進行內核的第二階段初始化。
  131. (4)調用unflatten_device_tree()函數來解析dtb文件,構建一個由device_node結構連接而成的單項鍊表,並使用全局變量of_allnodes指針來保存這個鏈表的頭指針。
  132. (5)內核調用OF提供的API函數獲取of_allnodes鏈表信息來初始化內核其他子系統、設備等。
  133. //kernel 初始化的代碼(init/main.c)
  134. asmlinkage void __init start_kernel(void)
  135. {
  136.     ...
  137.     //這個setup_arch就是各個架構自己的設置函數,哪個參與了編譯就調用哪個,arm架構應當是arch/arm/kernel/setup.c中的 setup_arch。
  138.     setup_arch(&command_line);
  139.     ...
  140. }

  141. void __init setup_arch(char **cmdline_p)
  142. {
  143.     const struct machine_desc *mdesc;
  144.      
  145.     setup_processor();
  146.     //setup_machine_fdt函數獲取內核前期初始化所需的bootargs,cmd_line等系統引導參數
  147.     mdesc = setup_machine_fdt(__atags_pointer);//__atags_pointer是bootloader傳遞參數的物理地址
  148.     if (!mdesc)
  149.         mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
  150.     machine_desc = mdesc;
  151.     machine_name = mdesc->name;
  152.         
  153.     if (mdesc->reboot_mode != REBOOT_HARD)
  154.         reboot_mode = mdesc->reboot_mode;
  155.         
  156.     init_mm.start_code = (unsigned long) _text;
  157.     init_mm.end_code = (unsigned long) _etext;
  158.     init_mm.end_data = (unsigned long) _edata;
  159.     init_mm.brk = (unsigned long) _end;

  160.     strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
  161.     *cmdline_p = cmd_line;
  162.  
  163.     parse_early_param();
  164.  
  165.     sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
  166.  
  167.     early_paging_init(mdesc, lookup_processor_type(read_cpuid_id()));
  168.     setup_dma_zone(mdesc);
  169.     sanity_check_meminfo();
  170.     arm_memblock_init(&meminfo, mdesc);
  171.  
  172.     paging_init(mdesc);
  173.     request_standard_resources(mdesc);
  174.  
  175.     if (mdesc->restart)
  176.         arm_pm_restart = mdesc->restart;
  177.  
  178.      //解析設備樹
  179.     unflatten_device_tree();
  180.  
  181.     ......
  182. }

  183. ()函數獲取內核前期初始化所需的bootargs,cmd_line等系統引導參數
  184. 1. setup_machine_fdt()函數獲取內核前期初始化所需的bootargs,cmd_line等系統引導參數。
  185. const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
  186. {
  187.     const struct machine_desc *mdesc, *mdesc_best = NULL;
  188.  
  189. #ifdef CONFIG_ARCH_MULTIPLATFORM
  190.     DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
  191.     MACHINE_END
  192.      
  193.     mdesc_best = &__mach_desc_GENERIC_DT;
  194. #endif

  195.     //bootloader傳遞參數的物理地址不爲空,並將物理地址轉化爲虛擬地址,
  196.     //通過函數early_init_dt_scan從設備樹中讀出bootargs,cmd_line等系統引導參數。
  197.     if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys)))
  198.         return NULL;
  199.     
  200.     //根據設備樹中根節點屬性"compatible"的屬性描述,找到系統中定義的最匹配的machine_desc結構,該結構控制系統體系架構相關部分的初始化
  201.     mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
  202.     if (!mdesc) {
  203.         const char *prop;
  204.         long size;
  205.         unsigned long dt_root;
  206.  
  207.         early_print("\nError: unrecognized/unsupported ""device tree compatible list:\n[ ");
  208.  
  209.         //找到設備樹的根節點,dt_root指向根節點的屬性地址處
  210.         dt_root = of_get_flat_dt_root();
  211.         //讀出根節點的"compatible"屬性的屬性值
  212.         prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
  213.         //將根節點的"compatible"屬性的屬性值打印出來
  214.         while (size > 0) {
  215.             early_print("'%s' ", prop);
  216.             size -= strlen(prop) + 1;
  217.             prop += strlen(prop) + 1;
  218.         }
  219.         early_print("]\n\n");
  220.         
  221.         dump_machine_table(); /* does not return */
  222.     }
  223.          
  224.     //Change machine number to match the mdesc we're using
  225.     __machine_arch_type = mdesc->nr;

  226.     return mdesc;
  227. }

  228. struct boot_param_header *initial_boot_params;
  229. bool __init early_init_dt_scan(void *params)
  230. {
  231.     if (!params)
  232.         return false;
  233.         
  234.     //參數params是bootloader傳遞參數的物理地址轉化爲的虛擬地址,保存設備樹起始地址
  235.     initial_boot_params = params;
  236.         
  237.     //驗證設備樹的magic 
  238.     if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) {
  239.         initial_boot_params = NULL;
  240.         return false;
  241.     }
  242.         
  243.     //從設備樹中讀取chosen節點的信息,包括命令行boot_command_line,initrd location及size
  244.     of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
  245.         
  246.     //得到根節點的{size,address}-cells信息
  247.     of_scan_flat_dt(early_init_dt_scan_root, NULL);
  248.         
  249.     //讀出設備樹的系統內存設置
  250.     of_scan_flat_dt(early_init_dt_scan_memory, NULL);
  251.         
  252.     return true;
  253. }

  254. int __init of_scan_flat_dt(int (*it)(unsigned long node,const char *uname, int depth,void *data),void *data)
  255. {
  256.     //找到設備樹中結構塊的地址
  257.     unsigned long p = ((unsigned long)initial_boot_params) + be32_to_cpu(initial_boot_params->off_dt_struct);
  258.     int rc = 0;
  259.     int depth = -1;
  260.  
  261.     do {
  262.         //獲得節點起始標誌,即OF_DT_BEGIN_NODE,OF_DT_PROP等
  263.         u32 tag = be32_to_cpup((__be32 *)p);
  264.         const char *pathp;
  265.         
  266.         p += 4;//跳過節點起始標誌
  267.         
  268.         //如果是OF_DT_END_NODE標誌,表示該節點結束,繼續下一結點
  269.         if (tag == OF_DT_END_NODE) {
  270.             depth--;
  271.             continue;
  272.         }
  273.         
  274.         //OF_DT_NOP標誌代表空節點
  275.         if (tag == OF_DT_NOP)
  276.             continue;
  277.             
  278.         //OF_DT_END標誌整個結構塊結束
  279.         if (tag == OF_DT_END)
  280.             break;
  281.             
  282.         //OF_DT_PROP標示屬性,Property:屬性值的字節長度、屬性名稱在字符串塊中的偏移量、屬性值和填充。
  283.         if (tag == OF_DT_PROP) {
  284.             //屬性值的字節大小
  285.             u32 sz = be32_to_cpup((__be32 *)p);
  286.             p += 8;//跳過屬性值的字節長度、屬性名稱在字符串塊中的偏移量
  287.             if (be32_to_cpu(initial_boot_params->version) < 0x10)
  288.                 p = ALIGN(p, sz >= 8 ? 8 : 4);
  289.             //跳過該屬性值的大小
  290.             p += sz;
  291.             //地址對齊
  292.             p = ALIGN(p, 4);
  293.             //表示一個屬性節點遍歷完成,因爲這裏並不是尋找屬性節點,而是找OF_DT_BEGIN_NODE開始的節點
  294.             continue;
  295.         }
  296.         
  297.         //若都不是以上節點類型,也不是節點開始標示(OF_DT_BEGIN_NODE),則出錯返回
  298.         if (tag != OF_DT_BEGIN_NODE) {
  299.             pr_err("Invalid tag %x in flat device tree!\n", tag);
  300.             return -EINVAL;
  301.         }
  302.         
  303.         //執行到這裏,標示tag=OF_DT_BEGIN_NODE,表示一個節點的開始,探索深度加1
  304.         depth++;
  305.         //節點路徑或者節點名
  306.         pathp = (char *)p;
  307.         //節點地址四字節對齊,然後p指向節點屬性地址
  308.         p = ALIGN(+ strlen(pathp) + 1, 4);
  309.         //如果是節點路徑,則返回路徑名的最後一段,假如爲/root/my_root,則返回my_root,即獲得節點名
  310.         if (*pathp == '/')
  311.             pathp = kbasename(pathp);
  312.         //調用相應的節點處理函數,p指向節點屬性地址
  313.         rc = it(p, pathp, depth, data);
  314.         if (rc != 0)
  315.             break;
  316.     } while (1);
  317.  
  318.     return rc;
  319. }

  320. 1.1 chosen節點
  321. //chosen 節點並不代表一個真實的設備,只是作爲一個爲固件和操作系統之間傳遞數據的地方,比如引導參數。chosen 節點裏的數據也不代表硬件。通常,chosen 節點在.dts 源文件中爲空,並在啓動時填充。在我們的示例系統中,固件可以往 chosen 節點添加以下信息:
  322. //chosen {
  323. //    bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200"; //節點屬性
  324. //    linux,initrd-start = <0x85500000>; //節點屬性
  325. //    linux,initrd-end = <0x855a3212>; //節點屬性
  326. //};
  327. int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,int depth, void *data)
  328. {
  329.     unsigned long l;
  330.     char *p;
  331.     
  332.     pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);

  333.     //depth深度要爲1,表示在根節點下(一般根節點/的depth爲0)
  334.     //data表示系統啓動命令行boot_command_line要分配空間
  335.     //檢查節點名是否爲chosen節點    
  336.     if (depth != 1 || !data || (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
  337.         return 0;
  338.     
  339.     //從設備樹的chosen節點中讀出initrd的起始、結束地址
  340.     early_init_dt_check_for_initrd(node);
  341.  
  342.     //設備樹的chosen節點中讀取bootargs屬性的屬性值,並拷貝給boot_command_line
  343.     p = of_get_flat_dt_prop(node, "bootargs", &l);
  344.     if (!= NULL && l > 0)
  345.         strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE));

  346.     pr_debug("Command line is: %s\n", (char*)data);
  347.      
  348.     return 1;
  349. }

  350. static void __init early_init_dt_check_for_initrd(unsigned long node)
  351. {
  352.     u64 start, end;
  353.     unsigned long len;
  354.     __be32 *prop;
  355.         
  356.     pr_debug("Looking for initrd properties... ");
  357.          
  358.     //返回該chosen節點中屬性名爲"linux,initrd-start"的地址
  359.     prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);
  360.     if (!prop)
  361.         return;
  362.     //從該地址讀出initrd-start的地址
  363.     start = of_read_number(prop, len/4);
  364.         
  365.     //返回該chosen節點中屬性名爲"linux,initrd-end"的地址
  366.     prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);
  367.     if (!prop)
  368.         return;
  369.     //從該地址讀出initrd-end的地址
  370.     end = of_read_number(prop, len/4);
  371.         
  372.     //將讀出的地址賦值給全局變量initrd_start和initrd_end,用於跟文件系統的掛載
  373.     initrd_start = (unsigned long)__va(start);
  374.     initrd_end = (unsigned long)__va(end);
  375.     initrd_below_start_ok = 1;
  376.         
  377.     pr_debug("initrd_start=0x%llx initrd_end=0x%llx\n",(unsigned long long)start, (unsigned long long)end);
  378. }

  379. void *__init of_get_flat_dt_prop(unsigned long node, const char *name,unsigned long *size)
  380. {
  381.     return of_fdt_get_property(initial_boot_params, node, name, size);
  382. }

  383. void *of_fdt_get_property(struct boot_param_header *blob,unsigned long node, const char *name,unsigned long *size)
  384. {
  385.     //p指向該chosen節點的節點屬性地址
  386.     unsigned long p = node;
  387.      
  388.     //因爲一個節點中可能包含多個屬性,所以這裏遍歷chosen節點中的所有屬性,找到屬性名爲name的屬性
  389.     do {
  390.         //取得該節點屬性的起始標誌OF_DT_PROP
  391.         u32 tag = be32_to_cpup((__be32 *)p);
  392.         u32 sz, noff;
  393.         const char *nstr;
  394.          
  395.         p += 4;//跳過節點屬性的起始標誌

  396.         //空節點則繼續
  397.         if (tag == OF_DT_NOP)
  398.             continue;
  399.         //非屬性標誌則返回NULL
  400.         if (tag != OF_DT_PROP)
  401.             return NULL;
  402.          
  403.         //運行到這裏表示爲屬性OF_DT_PROP
  404.         //取得該節點屬性的的屬性值size
  405.         sz = be32_to_cpup((__be32 *)p);
  406.         //取得該屬性名的在字符串塊中的偏移值
  407.         noff = be32_to_cpup((__be32 *)(+ 4));
  408.         p += 8;
  409.         //跳過對齊填充字段
  410.         if (be32_to_cpu(blob->version) < 0x10)
  411.             p = ALIGN(p, sz >= 8 ? 8 : 4);
  412.          
  413.         //在字符串塊取出該屬性的名稱
  414.         nstr = of_fdt_get_string(blob, noff);
  415.         if (nstr == NULL) {
  416.             pr_warning("Can't find property index name !\n");
  417.             return NULL;
  418.         }

  419.         //若名稱一致,表示找到我們要找的屬性
  420.         if (strcmp(name, nstr) == 0) {
  421.             if (size)
  422.                 *size = sz;//返回該屬性的屬性值size
  423.             //返回該屬性值所在的地址
  424.             return (void *)p;
  425.         }
  426.         //否則繼續下一個屬性
  427.         p += sz;
  428.         p = ALIGN(p, 4);
  429.     } while (1);
  430. }

  431. char *of_fdt_get_string(struct boot_param_header *blob, u32 offset)
  432. {
  433.     //從設備樹的字符串塊的offset處讀出name
  434.     return ((char *)blob) + be32_to_cpu(blob->off_dt_strings) + offset;
  435. }

  436. static inline u64 of_read_number(const __be32 *cell, int size)
  437. {
  438.     u64 r = 0;
  439.     //讀出屬性值,屬性值大小爲size
  440.     while (size--)
  441.         r = (<< 32) | be32_to_cpu(*(cell++));
  442.     return r;
  443. }

  444. 1.2 根節點"/"
  445. //設備樹有且僅有一個根節點,即“/”,根節點下包含很多子節點,例入下圖,根節點爲"/",根節點的子節點爲"chosen",根節點的屬性包含"compatible","#address-cells","#size-cells","interrupt-parent"等。屬性model指明瞭目標板平臺或模塊的名稱,屬性compatible值指明和目標板爲同一系列的兼容的開發板名稱。對於大多數32位平臺,屬性#address-cells和#size-cells的值一般爲1。#address-cells = <1>; 1表示地址32位,2表示地址64位。#size-cells = <1>;1表示rangs的每部分佔一個cell,依此類推 
  446. /*
  447. / {
  448.     compatible = "sprd,spx15";
  449.     #address-cells = <1>;
  450.     #size-cells = <1>;
  451.     interrupt-parent = <&gic>;

  452.     chosen {
  453.         bootargs = "loglevel=8 console=ttyS1,115200n8 init=/init root=/dev/ram0 rw";
  454.         linux,initrd-start = <0x85500000>;
  455.         linux,initrd-end = <0x855a3212>;
  456.     };
  457. }
  458. */
  459. //所以本函數就是讀取根節點的"#address-cells","#size-cells"屬性
  460. int __init early_init_dt_scan_root(unsigned long node, const char *uname,int depth, void *data)
  461. {
  462.     __be32 *prop;
  463.     
  464.     //根節點的探索深度depth一定爲0,否則不是根節點"/",node爲根節點的屬性地址    
  465.     if (depth != 0)
  466.         return 0;
  467.         
  468.     dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
  469.     dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
  470.         
  471.     //返回根節點節點屬性名爲"#size-cells"的地址
  472.     prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
  473.     if (prop)
  474.         dt_root_size_cells = be32_to_cpup(prop);//從該屬性獲得root size
  475.         
  476.     pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells);
  477.         
  478.     //返回根節點節點屬性名爲"#address-cells"的地址
  479.     prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
  480.     if (prop)
  481.         dt_root_addr_cells = be32_to_cpup(prop);//從該屬性獲得root address
  482.     pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells);
  483.         
  484.     return 1;
  485. }

  486. 1.3 memory節點
  487. //memory節點用於描述目標板上物理內存範圍,一般稱作/memory節點,可以有一個或多個。當有多個節點時,需要後跟單元地址予以區分;只有一個單元地址時,可以不寫單元地址,默認爲0。此節點包含板上物理內存的屬性,一般要指定device_type(固定爲"memory")和reg屬性。其中reg的屬性值以<起始地址 空間大小>的形式給出,如下示例中目標板內存起始地址爲0x80000000,大小爲0x20000000字節。 
  488. //memory {
  489. //    device_type = "memory";
  490. //    reg = <0x80000000 0x20000000>;
  491. //};
  492. int __init early_init_dt_scan_memory(unsigned long node, const char *uname,int depth, void *data)
  493. {
  494.     //獲取該節點中屬性"device_type"的屬性值
  495.     char *type = of_get_flat_dt_prop(node, "device_type", NULL);
  496.     __be32 *reg, *endp;
  497.     unsigned long l;
  498.     
  499.     //檢查"device_type"的屬性值,確定該節點是否爲memory節點
  500.     if (type == NULL) {
  501.         if (depth != 1 || strcmp(uname, "memory@0") != 0)
  502.             return 0;
  503.     } else if (strcmp(type, "memory") != 0)
  504.         return 0;
  505.     
  506.     //從該memory節點中獲取屬性"linux,usable-memory"的屬性值,及屬性值大小l
  507.     reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
  508.     if (reg == NULL)
  509.         reg = of_get_flat_dt_prop(node, "reg", &l);
  510.     if (reg == NULL)
  511.         return 0;
  512.     
  513.     //reg爲屬性值的起始地址,endp爲結束地址
  514.     endp = reg + (/ sizeof(__be32));
  515.     
  516.     pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n",
  517.                         uname, l, reg[0], reg[1], reg[2], reg[3]);
  518.     
  519.     while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
  520.         u64 base, size;
  521.     
  522.         //讀出物理內存的起始地址以及size
  523.         base = dt_mem_next_cell(dt_root_addr_cells, &reg);
  524.         size = dt_mem_next_cell(dt_root_size_cells, &reg);
  525.     
  526.         if (size == 0)
  527.             continue;
  528.         pr_debug(" - %llx , %llx\n", (unsigned long long)base,(unsigned long long)size);
  529.     
  530.         //添加物理內存到memblock中進行管理,這裏不再展開
  531.         early_init_dt_add_memory_arch(base, size);
  532.     }
  533.     
  534.     return 0;
  535. }


  536. 2. 通過比較根節點屬性compatible值指明和目標板爲同一系列的兼容的開發板名稱
  537. //compatible制定系統的名稱。它包含","格式的字符串。準確地確定器件型號是非常重要的,並且我們需要包含廠商的名字來避免名字空間衝突。因爲操作系統會使用compatible這個值來決定怎樣在這個機器上運行,所以在這個屬性中放入正確的值是非常重要的。
  538. const void * __init of_flat_dt_match_machine(const void *default_match,
  539.                                             const void * (*get_next_compat)(const char * const**))
  540. {
  541.     const void *data = NULL;
  542.     const void *best_data = default_match;
  543.     const char *const *compat;
  544.     unsigned long dt_root;
  545.     unsigned int best_score = ~1, score = 0;
  546.     
  547.     //讀出設備樹的根節點,dt_root指向根節點的屬性地址處
  548.     dt_root = of_get_flat_dt_root();

  549.     //調用arch_get_next_mach,遍歷該系統中的所有machine_desc結構,返回給data,並且返回該結構的compatible
  550.     while ((data = get_next_compat(&compat))) {
  551.         //將系統中的所有machine_desc結構的compatible字符串與設備樹根節點的compatible屬性進行match
  552.         score = of_flat_dt_match(dt_root, compat);
  553.         //返回與根節點屬性compatible屬性值最匹配的machine_desc結構
  554.         if (score > 0 && score < best_score) {
  555.             best_data = data;
  556.             best_score = score;
  557.         }
  558.     }
  559.     
  560.     if (!best_data) {
  561.         const char *prop;
  562.         long size;
  563.     
  564.         pr_err("\n unrecognized device tree list:\n[ ");
  565.      
  566.         prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
  567.         if (prop) {
  568.             while (size > 0) {
  569.                 printk("'%s' ", prop);
  570.                 size -= strlen(prop) + 1;
  571.                 prop += strlen(prop) + 1;
  572.             }
  573.         }
  574.         printk("]\n\n");
  575.         return NULL;
  576.     }
  577.      
  578.     pr_info("Machine model: %s\n", of_flat_dt_get_machine_name());
  579.     
  580.     return best_data;
  581. }


  582. //查找設備樹的根節點
  583. unsigned long __init of_get_flat_dt_root(void)
  584. {
  585.     //找到設備樹的設備塊起始地址
  586.     unsigned long p = ((unsigned long)initial_boot_params) +
  587.                                     be32_to_cpu(initial_boot_params->off_dt_struct);
  588.     
  589.     //跳過空節點 
  590.     while (be32_to_cpup((__be32 *)p) == OF_DT_NOP)
  591.         p += 4;
  592.     BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE);
  593.     p += 4;
  594.     //第一個節點就是根節點,p指向根節點的屬性地址處
  595.     return ALIGN(+ strlen((char *)p) + 1, 4);
  596. }

  597. //arch/arm/kernel/devtree.c
  598. __arch_info_begin 和 __arch_info_end是在 arch/arm/kernel/vmlinux.lds.S中:
  599. 00034: __arch_info_begin = .;
  600. 00035: *(.arch.info.init)
  601. 00036: __arch_info_end = .;
  602. 這裏是聲明瞭兩個變量:__arch_info_begin 和 __arch_info_end,其中等號後面的"."是location counter。在__arch_info_begin 的位置上,放置所有文件中的 ".arch.info.init" 段的內容,然後緊接着是 __arch_info_end 的位置.".arch.info.init" 段中定義了設備的machine_desc結構。
  603. //這裏就是取出一個machine_desc結構
  604. static const void * __init arch_get_next_mach(const char *const **match)
  605. {
  606.     static const struct machine_desc *mdesc = __arch_info_begin;
  607.     const struct machine_desc *= mdesc;
  608.         
  609.     if (>= __arch_info_end)
  610.         return NULL;
  611.         
  612.     mdesc++;//指針後移,確保下次取出下一個machine_desc結構
  613.     *match = m->dt_compat;
  614.     return m;//返回當前的machine_desc結構
  615. }

  616. //與設備樹根節點進行match
  617. int __init of_flat_dt_match(unsigned long node, const char *const *compat)
  618. {
  619.     //initial_boot_params指向設備樹起始地址
  620.     //node指向根節點的屬性地址
  621.     //compat爲系統中machine_desc結構的compatible字符串
  622.     return of_fdt_match(initial_boot_params, node, compat);
  623. }

  624. int of_fdt_match(struct boot_param_header *blob, unsigned long node,const char *const *compat)
  625. {
  626.     unsigned int tmp, score = 0;
  627.         
  628.     if (!compat)
  629.         return 0;
  630.     
  631.     //遍歷compatible字符串數組    
  632.     while (*compat) {
  633.         //返回compatible的匹配值
  634.         tmp = of_fdt_is_compatible(blob, node, *compat);
  635.         if (tmp && (score == 0 || (tmp < score)))
  636.             score = tmp;//返回最大的匹配值
  637.         compat++;//下一個字符串
  638.     }
  639.         
  640.     return score;
  641. }

  642. int of_fdt_is_compatible(struct boot_param_header *blob,unsigned long node, const char *compat)
  643. {
  644.     const char *cp;
  645.     unsigned long cplen, l, score = 0;
  646.     
  647.     //從根節點中讀出屬性"compatible"的屬性值
  648.     cp = of_fdt_get_property(blob, node, "compatible", &cplen);
  649.     if (cp == NULL)
  650.         return 0;
  651.     
  652.     //比較compatible的指定的屬性字符串的一致性
  653.     while (cplen > 0) {
  654.         score++;
  655.         if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
  656.             return score;
  657.         l = strlen(cp) + 1;
  658.         cp += l;
  659.         cplen -= l;
  660.     }
  661.         
  662.     return 0;
  663. }


  664. ()、解析設備樹
  665. //unflatten_device_tree()函數來解析dtb文件,構建一個由device_node結構連接而成的單項鍊表,並使用全局變量of_allnodes指針來保存這個鏈表的頭指針。內核調用OF提供的API函數獲取of_allnodes鏈表信息來初始化內核其他子系統、設備。
  666. void __init unflatten_device_tree(void)
  667. {
  668.     //解析設備樹,將所有的設備節點鏈入全局鏈表    of_allnodes中
  669.     __unflatten_device_tree(initial_boot_params, &of_allnodes,early_init_dt_alloc_memory_arch);
  670.      
  671.     //設置內核輸出終端,以及遍歷“/aliases”節點下的所有的屬性,掛入相應鏈表
  672.     of_alias_scan(early_init_dt_alloc_memory_arch);
  673. }

  674. static void __unflatten_device_tree(struct boot_param_header *blob,
  675.                                     struct device_node **mynodes,
  676.                                     void * (*dt_alloc)(u64 size, u64 align))
  677. {
  678.     unsigned long size;
  679.     void *start, *mem;
  680.     struct device_node **allnextp = mynodes;
  681.     
  682.     pr_debug(" -> unflatten_device_tree()\n");
  683.      
  684.     if (!blob) {
  685.         pr_debug("No device tree pointer\n");
  686.         return;
  687.     }
  688.      
  689.     pr_debug("Unflattening device tree:\n");
  690.     pr_debug("magic: %08x\n", be32_to_cpu(blob->magic));
  691.     pr_debug("size: %08x\n", be32_to_cpu(blob->totalsize));
  692.     pr_debug("version: %08x\n", be32_to_cpu(blob->version));
  693.     
  694.     //檢查設備樹magic
  695.     if (be32_to_cpu(blob->magic) != OF_DT_HEADER) {
  696.         pr_err("Invalid device tree blob header\n");
  697.         return;
  698.     }
  699.     
  700.     //找到設備樹的設備節點起始地址
  701.     start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
  702.     //第一次調用mem傳0,allnextpp傳NULL,實際上是爲了計算整個設備樹所要的空間
  703.     size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
  704.     size = ALIGN(size, 4);//4字節對齊
  705.      
  706.     pr_debug(" size is %lx, allocating...\n", size);
  707.      
  708.     //調用early_init_dt_alloc_memory_arch函數,爲設備樹分配內存空間
  709.     mem = dt_alloc(size + 4, __alignof__(struct device_node));
  710.     memset(mem, 0, size);
  711.     
  712.     //設備樹結束處賦值0xdeadbeef,爲了後邊檢查是否有數據溢出
  713.     *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef); 
  714.     pr_debug(" unflattening %p...\n", mem);
  715.      
  716.     //再次獲取設備樹的設備節點起始地址
  717.     start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
  718.     //mem爲設備樹分配的內存空間,allnextp指向全局變量of_allnodes,生成整個設備樹
  719.     unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
  720.     if (be32_to_cpup(start) != OF_DT_END)
  721.         pr_warning("Weird tag at end of tree: %08x\n", be32_to_cpup(start));
  722.     if (be32_to_cpup(mem + size) != 0xdeadbeef)
  723.         pr_warning("End of tree marker overwritten: %08x\n",be32_to_cpup(mem + size));
  724.     *allnextp = NULL;
  725.      
  726.     pr_debug(" <- unflatten_device_tree()\n");
  727. }

  728. static void * unflatten_dt_node(struct boot_param_header *blob,
  729.                                 void *mem,void **p,
  730.                                 struct device_node *dad,
  731.                                 struct device_node ***allnextpp,
  732.                                 unsigned long fpsize)
  733. {
  734.     struct device_node *np;
  735.     struct property *pp, **prev_pp = NULL;
  736.     char *pathp;
  737.     u32 tag;
  738.     unsigned int l, allocl;
  739.     int has_name = 0;
  740.     int new_format = 0;
  741.     
  742.     //*p指向設備樹的設備塊起始地址
  743.     tag = be32_to_cpup(*p);
  744.     //每個有孩子的設備節點,其tag一定是OF_DT_BEGIN_NODE
  745.     if (tag != OF_DT_BEGIN_NODE) {
  746.         pr_err("Weird tag at start of node: %x\n", tag);
  747.         return mem;
  748.     }

  749.     *+= 4;//地址+4,跳過tag,這樣指向節點的名稱或者節點路徑名
  750.     pathp = *p;//獲得節點名或者節點路徑名
  751.     l = allocl = strlen(pathp) + 1;//該節點名稱的長度
  752.     *= PTR_ALIGN(*+ l, 4);//地址對齊後,*p指向該節點屬性的地址
  753.     
  754.     //如果是節點名則進入,若是節點路徑名則(*pathp) == '/'
  755.     if ((*pathp) != '/') {
  756.         new_format = 1;
  757.         if (fpsize == 0) {//fpsize=0
  758.             fpsize = 1;
  759.             allocl = 2;
  760.             l = 1;
  761.             *pathp = '\0';
  762.         } else {
  763.             fpsize += l;//代分配的長度=本節點名稱長度+父親節點絕對路徑的長度
  764.             allocl = fpsize;
  765.         }
  766.     }
  767.     
  768.     //分配一個設備節點device_node結構,*mem記錄分配了多大空間,最終會累加計算出該設備樹總共分配的空間大小
  769.     np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,__alignof__(struct device_node));

  770.     //第一次調用unflatten_dt_node時,allnextpp=NULL
  771.     //第一次調用unflatten_dt_node時,allnextpp指向全局變量of_allnodes的地址
  772.     if (allnextpp) {
  773.         char *fn;
  774.         //full_name保存完整的節點名,即包括各級父節點的名稱
  775.         np->full_name = fn = ((char *)np) + sizeof(*np);
  776.         //若new_format=1,表示pathp保存的是節點名,而不是節點路徑名,所以需要加上父節點的name
  777.         if (new_format) {
  778.             if (dad && dad->parent) {
  779.                 strcpy(fn, dad->full_name);//把父親節點絕對路徑先拷貝
  780.                 fn += strlen(fn);
  781.             }
  782.             *(fn++) = '/';
  783.         }
  784.         memcpy(fn, pathp, l);//拷貝本節點的名稱
  785.     
  786.         //prev_pp指向節點的屬性鏈表
  787.         prev_pp = &np->properties;

  788.         //當前節點插入全局鏈表of_allnodes
  789.         **allnextpp = np;
  790.         *allnextpp = &np->allnext;

  791.         //若父親節點不爲空,則設置該節點的parent
  792.         if (dad != NULL) {
  793.             np->parent = dad;//指向父親節點
  794.             if (dad->next == NULL)//第一個孩子
  795.                 dad->child = np;//child指向第一個孩子
  796.             else
  797.                 dad->next->sibling = np;//把np插入next,這樣孩子節點形成鏈表
  798.             dad->next = np;
  799.         }
  800.         kref_init(&np->kref);
  801.     }

  802.     //分析該節點的屬性
  803.     while (1) {
  804.         u32 sz, noff;
  805.         char *pname;
  806.      
  807.         //前邊已經將*p移到指向節點屬性的地址處,取出屬性標識
  808.         tag = be32_to_cpup(*p);
  809.         //空屬性,則跳過
  810.         if (tag == OF_DT_NOP) {
  811.             *+= 4;
  812.             continue;
  813.         }
  814.         //tag不是屬性則退出,對於有孩子節點退出時爲OF_DT_BEGIN_NODE,對於葉子節點退出時爲OF_DT_END_NODE
  815.         if (tag != OF_DT_PROP)
  816.             break;
  817.         //地址加4,跳過tag
  818.         *+= 4;
  819.         //獲得屬性值的大小,是以爲佔多少整形指針計算的
  820.         sz = be32_to_cpup(*p);
  821.         //獲取屬性名稱在節點的字符串塊中的偏移
  822.         noff = be32_to_cpup(*+ 4);
  823.         //地址加8,跳過屬性值的大小和屬性名稱在節點的字符串塊中的偏移
  824.         *+= 8;
  825.         //地址對齊後,*P指向屬性值所在的地址
  826.         if (be32_to_cpu(blob->version) < 0x10)
  827.             *= PTR_ALIGN(*p, sz >= 8 ? 8 : 4);
  828.         
  829.         //從節點的字符串塊中noff偏移處,得到該屬性的name
  830.         pname = of_fdt_get_string(blob, noff);
  831.         if (pname == NULL) {
  832.             pr_info("Can't find property name in list !\n");
  833.             break;
  834.         }

  835.         //如果有名稱爲name的屬性,表示變量has_name爲1
  836.         if (strcmp(pname, "name") == 0)
  837.             has_name = 1;
  838.         //計算該屬性name的大小
  839.         l = strlen(pname) + 1;

  840.         //爲該屬性分配一個屬性結構,即struct property,
  841.         //*mem記錄分配了多大空間,最終會累加計算出該設備樹總共分配的空間大小
  842.         pp = unflatten_dt_alloc(&mem, sizeof(struct property),__alignof__(struct property));

  843.         //第一次調用unflatten_dt_node時,allnextpp=NULL
  844.         //第一次調用unflatten_dt_node時,allnextpp指向全局變量of_allnodes的地址
  845.         if (allnextpp) {
  846.             if ((strcmp(pname, "phandle") == 0) || (strcmp(pname, "linux,phandle") == 0)) {
  847.                 if (np->phandle == 0)
  848.                     np->phandle = be32_to_cpup((__be32*)*p);
  849.             }
  850.             if (strcmp(pname, "ibm,phandle") == 0)
  851.                 np->phandle = be32_to_cpup((__be32 *)*p);
  852.             pp->name = pname;//屬性名
  853.             pp->length = sz;//屬性值長度
  854.             pp->value = *p;//屬性值

  855.             //屬性插入該節點的屬性鏈表np->properties
  856.             *prev_pp = pp;
  857.             prev_pp = &pp->next;
  858.         }
  859.         *= PTR_ALIGN((*p) + sz, 4);//指向下一個屬性
  860.     }
  861.     //至此遍歷完該節點的所有屬性

  862.     //如果該節點沒有"name"的屬性,則爲該節點生成一個name屬性,插入該節點的屬性鏈表
  863.     if (!has_name) {
  864.         char *p1 = pathp, *ps = pathp, *pa = NULL;
  865.         int sz;
  866.      
  867.         while (*p1) {
  868.             if ((*p1) == '@')
  869.                 pa = p1;
  870.             if ((*p1) == '/')
  871.                 ps = p1 + 1;
  872.                 p1++;
  873.         }
  874.         if (pa < ps)
  875.             pa = p1;
  876.         sz = (pa - ps) + 1;
  877.         pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,__alignof__(struct property));
  878.         if (allnextpp) {
  879.             pp->name = "name";
  880.             pp->length = sz;
  881.             pp->value = pp + 1;
  882.             *prev_pp = pp;
  883.             prev_pp = &pp->next;
  884.             memcpy(pp->value, ps, sz - 1);
  885.             ((char *)pp->value)[sz - 1] = 0;
  886.             pr_debug("fixed up name for %s -> %s\n", pathp,(char *)pp->value);
  887.         }
  888.     }

  889.     //若設置了allnextpp指針
  890.     if (allnextpp) {
  891.         *prev_pp = NULL;
  892.         //設置節點的名稱
  893.         np->name = of_get_property(np, "name", NULL);
  894.         //設置該節點對應的設備類型
  895.         np->type = of_get_property(np, "device_type", NULL);
  896.     
  897.         if (!np->name)
  898.             np->name = "";
  899.         if (!np->type)
  900.             np->type = "";
  901.     }

  902.     //前邊在遍歷屬性時,tag不是屬性則退出
  903.     //對於有孩子節點退出時tag爲OF_DT_BEGIN_NODE,對於葉子節點退出時tag爲OF_DT_END_NODE
  904.     while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) {
  905.         //空屬性則指向下個屬性
  906.         if (tag == OF_DT_NOP)
  907.             *+= 4;
  908.         else
  909.             //OF_DT_BEGIN_NODE則表明其還有子節點,所以遞歸分析其子節點
  910.             mem = unflatten_dt_node(blob, mem, p, np, allnextpp,fpsize);
  911.         tag = be32_to_cpup(*p);
  912.     }

  913.     //對於葉子節點或者分析完成
  914.     if (tag != OF_DT_END_NODE) {
  915.         pr_err("Weird tag at end of node: %x\n", tag);
  916.         return mem;
  917.     }
  918.     *+= 4;
  919.     //mem返回整個設備樹所分配的內存大小,即設備樹佔的內存空間
  920.     return mem;
  921. }


  922. //從mem分配內存空間,*mem記錄分配了多大空間
  923. static void *unflatten_dt_alloc(void **mem, unsigned long size,unsigned long align)
  924. {
  925.     void *res;
  926.      
  927.     *mem = PTR_ALIGN(*mem, align);
  928.     res = *mem;
  929.     *mem += size;
  930.         
  931.     return res;
  932. }

  933. //一個特定的節點通常是以完整的路徑來引用,比如/external-bus/ethernet@0,0,不過當一個用戶真的想知道“哪個設備是eth0”時,這將會很繁瑣。aliases節點可以用來爲一個完整的設備路徑分配一個短的別名。比如:
  934. //aliases {
  935. //    serial0 = &uart0;
  936. //    serial1 = &uart1;
  937. //    serial2 = &uart2;
  938. //    serial3 = &uart3;
  939. //    ethernet0 = &eth0;
  940. //    serial0 = &serial0;
  941. //};
  942. //當需要爲設備指定一個標示符時,操作系統歡迎大家使用別名。
  943. //設置內核輸出終端,以及遍歷“/aliases”節點下的所有的屬性,掛入相應鏈表
  944. void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
  945. {
  946.     struct property *pp;
  947.     
  948.     //根據全局的device_node結構的鏈表of_allnodes,查找節點名爲“/chosen”或者“/chosen@0”的節點,賦值給全局變量of_chosen
  949.     of_chosen = of_find_node_by_path("/chosen");
  950.     if (of_chosen == NULL)
  951.         of_chosen = of_find_node_by_path("/chosen@0");
  952.     
  953.     //找到的話,則在該節點查找"linux,stdout-path"    屬性
  954.     //"linux,stdout-path"的屬性值,常常爲標準終端設備的節點路徑名,內核會以此作爲默認終端
  955.     if (of_chosen) {
  956.         const char *name;
  957.         //返回屬性"linux,stdout-path"的屬性值
  958.         name = of_get_property(of_chosen, "linux,stdout-path", NULL);
  959.         //根據屬性值查找設備節點device_node,即內核默認終端的設備節點,賦值給全局變量of_stdout
  960.         if (name)
  961.             of_stdout = of_find_node_by_path(name);
  962.     }
  963.     
  964.     //據全局鏈表of_allnodes,查找節點名爲“/aliases”的節點,賦值給全局變量of_aliases
  965.     of_aliases = of_find_node_by_path("/aliases");
  966.     if (!of_aliases)
  967.         return;
  968.      
  969.     //遍歷“/aliases”節點下的所有的屬性
  970.     for_each_property_of_node(of_aliases, pp) {
  971.         const char *start = pp->name;//屬性名
  972.         const char *end = start + strlen(start);//屬性名結尾
  973.         struct device_node *np;
  974.         struct alias_prop *ap;
  975.         int id, len;
  976.         
  977.         //跳過"name"、"phandle"和"linux,phandle"的屬性
  978.         if (!strcmp(pp->name, "name") ||
  979.             !strcmp(pp->name, "phandle") ||
  980.             !strcmp(pp->name, "linux,phandle"))
  981.             continue;
  982.             
  983.             //根據屬性值找到對應的設備節點
  984.             np = of_find_node_by_path(pp->value);
  985.             if (!np)
  986.                 continue;
  987.         
  988.             //去除屬性名中結尾的數字,即設備id
  989.             while (isdigit(*(end-1)) && end > start)
  990.                 end--;
  991.             //len爲屬性名去掉結尾數字序號的長度
  992.             len = end - start;
  993.         
  994.             //此時end指向屬性名中結尾的數字,即開始時start指向“&uart0”,end指向字符串結尾。
  995.             //經過上步操作,start仍指向“&uart0”字符串開始處,而end指向字符‘0’。
  996.             //將end字符串轉化爲10進制數,賦值給id,作爲設備的id號
  997.             if (kstrtoint(end, 10, &id) < 0)
  998.                 continue;
  999.         
  1000.             //分配alias_prop結構
  1001.             ap = dt_alloc(sizeof(*ap) + len + 1, 4);
  1002.             if (!ap)
  1003.                 continue;
  1004.             memset(ap, 0, sizeof(*ap) + len + 1);
  1005.             ap->alias = start;
  1006.             //將該設備的aliases指向對應的device_node,並且鏈入aliases_lookup鏈表中
  1007.             of_alias_add(ap, np, id, start, len);
  1008.         }
  1009. }

  1010. 四、OF提供的常用API函數
  1011. //OF提供的函數主要集中在drivers/of/目錄下,有address.c,base.c,device.c,fdt.c,irq.c,platform.c等等
  1012. 1. 用來查找在dtb中的根節點
  1013. unsigned long __init of_get_flat_dt_root(void)

  1014. 2. 根據deice_node結構的full_name參數,在全局鏈表of_allnodes中,查找合適的device_node
  1015. struct device_node *of_find_node_by_path(const char *path)
  1016. 例如:
  1017. struct device_node *cpus;
  1018. cpus=of_find_node_by_path("/cpus");

  1019. 3. 若from=NULL,則在全局鏈表of_allnodes中根據name查找合適的device_node
  1020. struct device_node *of_find_node_by_name(struct device_node *from,const char *name)
  1021. 例如:
  1022. struct device_node *np;
  1023. np = of_find_node_by_name(NULL,"firewire");

  1024. 4. 根據設備類型查找相應的device_node
  1025. struct device_node *of_find_node_by_type(struct device_node *from,const char *type)
  1026. 例如:
  1027. struct device_node *tsi_pci;
  1028. tsi_pci= of_find_node_by_type(NULL,"pci");

  1029. 5. 根據compatible字符串查找device_node
  1030. struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

  1031. 6. 根據節點屬性的name查找device_node
  1032. struct device_node *of_find_node_with_property(struct device_node *from,const char *prop_name)

  1033. 7. 根據phandle查找device_node
  1034. struct device_node *of_find_node_by_phandle(phandle handle)

  1035. 8. 根據alias的name獲得設備id號
  1036. int of_alias_get_id(struct device_node *np, const char *stem)

  1037. 9. device node計數增加/減少
  1038. struct device_node *of_node_get(struct device_node *node)
  1039. void of_node_put(struct device_node *node)

  1040. 10. 根據property結構的name參數,在指定的device node中查找合適的property
  1041. struct property *of_find_property(const struct device_node *np,const char *name,int *lenp)

  1042. 11. 根據property結構的name參數,返回該屬性的屬性值
  1043. const void *of_get_property(const struct device_node *np, const char *name,int *lenp)

  1044. 12. 根據compat參數與device node的compatible匹配,返回匹配度
  1045. int of_device_is_compatible(const struct device_node *device,const char *compat)

  1046. 13. 獲得父節點的device node
  1047. struct device_node *of_get_parent(const struct device_node *node)

  1048. 14. 將matches數組中of_device_id結構的name和type與device node的compatible和type匹配,返回匹配度最高的of_device_id結構
  1049. const struct of_device_id *of_match_node(const struct of_device_id *matches,const struct device_node *node)

  1050. 15. 根據屬性名propname,讀出屬性值中的第index個u32數值給out_value
  1051. int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index, u32 *out_value)

  1052. 16. 根據屬性名propname,讀出該屬性的數組中sz個屬性值給out_values
  1053. int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz)
  1054. int of_property_read_u16_array(const struct device_node *np,const char *propname, u16 *out_values, size_t sz)
  1055. int of_property_read_u32_array(const struct device_node *np,const char *propname, u32 *out_values,size_t sz)

  1056. 17. 根據屬性名propname,讀出該屬性的u64屬性值
  1057. int of_property_read_u64(const struct device_node *np, const char *propname,u64 *out_value)

  1058. 18. 根據屬性名propname,讀出該屬性的字符串屬性值
  1059. int of_property_read_string(struct device_node *np, const char *propname,const char **out_string)

  1060. 19. 根據屬性名propname,讀出該字符串屬性值數組中的第index個字符串
  1061. int of_property_read_string_index(struct device_node *np, const char *propname,int index, const char **output)

  1062. 20. 讀取屬性名propname中,字符串屬性值的個數
  1063. int of_property_count_strings(struct device_node *np, const char *propname)

  1064. 21. 讀取該設備的第index個irq號
  1065. unsigned int irq_of_parse_and_map(struct device_node *dev, int index)

  1066. 22. 讀取該設備的第index個irq號,並填充一個irq資源結構體
  1067. int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)

  1068. 23. 獲取該設備的irq個數
  1069. int of_irq_count(struct device_node *dev)

  1070. 24. 獲取設備寄存器地址,並填充寄存器資源結構體
  1071. int of_address_to_resource(struct device_node *dev, int index,struct resource *r)
  1072. const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,unsigned int *flags)

  1073. 25. 獲取經過映射的寄存器虛擬地址
  1074. void __iomem *of_iomap(struct device_node *np, int index)

  1075. 24. 根據device_node查找返回該設備對應的platform_device結構
  1076. struct platform_device *of_find_device_by_node(struct device_node *np)

  1077. 25. 根據device node,bus id以及父節點創建該設備的platform_device結構
  1078. struct platform_device *of_device_alloc(struct device_node *np,const char *bus_id,struct device *parent)
  1079. static struct platform_device *of_platform_device_create_pdata(struct device_node *np,const char *bus_id,
  1080.                                                             void *platform_data,struct device *parent)

  1081. 26. 遍歷of_allnodes中的節點掛接到of_platform_bus_type總線上,由於此時of_platform_bus_type總線上還沒有驅動,所以此時不進行匹配
  1082. int of_platform_bus_probe(struct device_node *root,const struct of_device_id *matches,struct device *parent)

  1083. 27. 遍歷of_allnodes中的所有節點,生成並初始化platform_device結構
  1084. int of_platform_populate(struct device_node *root,const struct of_device_id *matches,
  1085.                         const struct of_dev_auxdata *lookup,struct device *parent)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章