設備樹解析【轉】

轉自:http://blog.csdn.net/qq_27677015/article/details/53415848html

1、描述

        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中進行大量的冗餘編碼。  node

       Device Tree是一種描述硬件的數據結構,它起源於 OpenFirmware (OF)。在Linux 2.6中,ARM架構的板極硬件細節過多地被硬編碼在arch/arm/plat-xxx和arch/arm/mach-xxx,採用Device Tree後,許多硬件的細節能夠直接透過它傳遞給Linux,而再也不須要在kernel中進行大量的冗餘編碼。  linux

      Device Tree由一系列被命名的結點(node)和屬性(property)組成,而結點自己可包含子結點。所謂屬性,其實就是成對出現的name和value。在Device Tree中,可描述的信息包括(原先這些信息大多被hard code到kernel中):git

  • CPU的數量和類別
  • 內存基地址和大小
  • 總線和橋
  • 外設鏈接
  • 中斷控制器和中斷使用狀況
  • GPIO控制器和GPIO使用狀況
  • Clock控制器和Clock使用狀況

     它基本上就是畫一棵電路板上CPU、總線、設備組成的樹,Bootloader會將這棵樹傳遞給內核,而後內核能夠識別這棵樹,並根據它展開出Linux內核中的platform_device、i2c_client、spi_device等設備,而這些設備用到的內存、IRQ等資源,也被傳遞給了內核,內核會將這些資源綁定給展開的相應的設備。算法

     一般由
數組

2、相關結構體數據結構

一、U-Boot須要將設備樹在內存中的存儲地址傳給內核。該樹主要由三大部分組成:頭(Header)、結構塊(Structure block)、字符串塊(Strings block)。架構

設備樹在內存中的存儲佈局圖:ide



1.1 頭(header)
      頭主要描述設備樹的一些基本信息,例如設備樹大小,結構塊偏移地址,字符串塊偏移地址等。偏移地址是相對於設備樹頭的起始地址計算的。
函數

 

[cpp] 
 

struct   

  •   
  •     __be32 off_dt_struct;          
  •   
  •     __be32 off_mem_rsvmap;          
  •   
  •     __be32 last_comp_version;      
  •   
  •     __be32 dt_strings_size;      
  •   
  • };  


1.2 結構塊(struct block)

 

       設備樹結構塊是一個線性化的結構體,是設備樹的主體,以節點node的形式保存了目標單板上的設備信息。

       在結構塊中以宏OF_DT_BEGIN_NODE標誌一個節點的開始,以宏OF_DT_END_NODE標識一個節點的結束,整個結構塊以宏OF_DT_END結束。一個節點主要由如下幾部分組成。

(1)節點開始標誌:通常爲OF_DT_BEGIN_NODE。
(2)節點路徑或者節點的單元名(ersion<3以節點路徑表示,version>=0x10以節點單元名錶示)
(3)填充字段(對齊到四字節)
(4)節點屬性。每一個屬性以宏OF_DT_PROP開始,後面依次爲屬性值的字節長度(4字節)、屬性名稱在字符串塊中的偏移量(4字節)、屬性值和填充(對齊到四字節)。
(5)若是存在子節點,則定義子節點。
(6)節點結束標誌OF_DT_END_NODE。


1.3 字符串塊

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

1.4 設備樹源碼 DTS 表示

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

1.5 machine_desc結構

      內核提供了一個重要的結構體struct machine_desc ,這個結構體在內核移植中起到至關重要的做用,內核經過machine_desc結構體來控制系統體系架構相關部分的初始化。machine_desc結構體經過MACHINE_START宏來初始化,在代碼中, 經過在start_kernel->setup_arch中調用setup_machine_fdt來獲取。

[cpp] 

 struct  nr;   

  •   *name;   
  •     unsigned  atag_offset;   
  •   * *dt_compat;   
  •     unsigned  nr_irqs;   
  • #ifdef CONFIG_ZONE_DMA   
  •   
  •  video_start;   
  •  video_end;   
  •       
  •  reserve_lp0 :1;   
  •     unsigned  reserve_lp1 :1;   
  •  reserve_lp2 :1;   
  •      reboot_mode reboot_mode;   
  •  smp_operations *smp;   
  •      (*smp_init)();  
  •  (*fixup)( tag *,  **, meminfo *);  
  •      (*init_meminfo)();  
  •  (*reserve)();  
  •      (*map_io)();  
  •  (*init_early)();  
  •      (*init_irq)();  
  •  (*init_time)();  
  •      (*init_machine)();  
  •  (*init_late)();  
  •   
  •  (*handle_irq)( pt_regs *);  
  •   
  •  (*restart)( reboot_mode,   *);  
  • };  


1.6 設備節點結構體

[cpp] 

 struct   *name;      

  •   *type;   
  •     phandle phandle;  
  •   *full_name;   
  •   
  •  property *properties;   
  •      property *deadprops;   
  •  device_node *parent;   
  •      device_node *child;   
  •  device_node *sibling;   
  •      device_node *next;   
  •  device_node *allnext;   
  •      proc_dir_entry *pde;   
  •  kref kref;  
  •     unsigned  _flags;  
  •  *data;  
  •   
  •   *path_component_name;  
  •     unsigned  unique_id;  
  •  of_irq_controller *irq_trans;  
  •   
  • };  

 

1.7 屬性結構體

[cpp] 

 struct  *name;          

  •  length;          
  •      *value;          
  •  property *next;   
  •     unsigned  _flags;   
  •  unique_id;  
  • };  



3、設備樹初始化及解析

        分析Linux內核的源碼,能夠看到其對扁平設備樹的解析流程以下:

 

[cpp] 

 //kernel 初始化的代碼(init/main.c)  __init start_kernel()  

  •   
  •     setup_arch(&command_line);  
  • void **cmdline_p)  
  •   machine_desc *mdesc;  
  •   
  •     mdesc = setup_machine_fdt(__atags_pointer);  
  •  (!mdesc)  
  •         mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);  
  •  (mdesc->reboot_mode != REBOOT_HARD)  
  • ) _text;  
  •     init_mm.end_code = (unsigned ) _etext;  
  • ) _edata;  
  •     init_mm.brk = (unsigned ) _end;  
  • (meminfo.bank[0]), meminfo_cmp, NULL);  
  •    
  •  (mdesc->restart)  
  •         arm_pm_restart = mdesc->restart;  
  •   
  • }</span>  


(一) 函數獲取內核前期初始化所需的bootargs,cmd_line等系統引導參數

 

1. setup_machine_fdt()函數獲取內核前期初始化所需的bootargs,cmd_line等系統引導參數。

[cpp] 
 

const machine_desc * __init setup_machine_fdt(unsigned  dt_phys)  

  • {  
  •   machine_desc *mdesc, *mdesc_best = NULL;  
  •    
  • #ifdef CONFIG_ARCH_MULTIPLATFORM #endif   
  •   
  •      (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys)))  
  •  NULL;  
  •       
  •   
  •     mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);  
  •  (!mdesc) {  
  •           *prop;  
  •  size;  
  •         unsigned  dt_root;  
  •   
  •   
  • , &size);  
  •           
  •  (size > 0) {  
  •             early_print( );  
  •   
  •   
  •     __machine_arch_type = mdesc->nr;  
  •  mdesc;  
  • struct bool *params)  
  •  (!params)  
  •  ;  
  •           
  •   
  •     initial_boot_params = params;  
  •   
  •  (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) {  
  •         initial_boot_params = NULL;  
  •  ;  
  •     }  
  •   
  •   
  •     of_scan_flat_dt(early_init_dt_scan_root, NULL);  
  •   
  •  ;  
  • }  
  • int (*it)(unsigned  node,  *uname,  depth, *data), *data)  
  •   
  •  p = ((unsigned )initial_boot_params) + be32_to_cpu(initial_boot_params->off_dt_struct);  
  •      rc = 0;  
  •  depth = -1;  
  •    
  •  {  
  •           
  •   *pathp;  
  •   
  •   
  •  (tag == OF_DT_END_NODE) {  
  •             depth--;  
  • ;  
  •         }  
  •   
  •  (tag == OF_DT_NOP)  
  •             ;  
  •   
  •  (tag == OF_DT_END)  
  •             ;  
  •   
  •  (tag == OF_DT_PROP) {  
  •               
  •   
  •  (be32_to_cpu(initial_boot_params->version) < 0x10)  
  •                 p = ALIGN(p, sz >= 8 ? 8 : 4);  
  •   
  •             p += sz;  
  •   
  •             p = ALIGN(p, 4);  
  •   
  •             ;  
  •   
  •          (tag != OF_DT_BEGIN_NODE) {  
  •  -EINVAL;  
  •   
  •         depth++;  
  •   
  •         pathp = ( *)p;  
  •   
  •         p = ALIGN(p + strlen(pathp) + 1, 4);  
  •   
  •          (*pathp == )  
  •   
  •  (rc != 0)  
  • ;  
  •     }  (1);  
  •  rc;  
  • }  

1.1 chosen節點

       chosen 節點並不表明一個真實的設備,只是做爲一個爲固件和操做系統之間傳遞數據的地方,好比引導參數。chosen 節點裏的數據也不表明硬件。一般,chosen 節點在.dts 源文件中爲空,並在啓動時填充。在咱們的示例系統中,固件能夠往 chosen 節點添加如下信息:
chosen {
    bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200"; //節點屬性
    linux,initrd-start = <0x85500000>; //節點屬性
    linux,initrd-end = <0x855a3212>; //節點屬性
};
[cpp] 
 

int node,   *uname, depth,  *data)  

  • {  
  •  l;  
  •      *p;  
  •   
  •   
  •       
  •  (depth != 1 || !data || (strcmp(uname, ) != 0 && strcmp(uname, ) != 0))  
  •          0;  
  •   
  •   
  •     p = of_get_flat_dt_prop(node, , &l);  
  •  (p != NULL && l > 0)  
  •         strlcpy(data, p, min(()l, COMMAND_LINE_SIZE));  
  • *)data);  
  •  1;  
  • static __init early_init_dt_check_for_initrd(unsigned  node)  
  • {  
  •  len;  
  •   
  •     prop = of_get_flat_dt_prop(node, , &len);  
  •  (!prop)  
  •         ;  
  •   
  •     start = of_read_number(prop, len/4);  
  •   
  • , &len);  
  •      (!prop)  
  • ;  
  •       
  •   
  •     initrd_start = (unsigned )__va(start);  
  • )__va(end);  
  •     initrd_below_start_ok = 1;  
  •  )start, (unsigned  )end);  
  • void node,   *name,unsigned  *size)  
  • {  
  •  of_fdt_get_property(initial_boot_params, node, name, size);  
  • }  
  • void boot_param_header *blob,unsigned  node,   *name,unsigned  *size)  
  •   
  •  p = node;  
  •        
  •   
  •      {  
  •   
  •         u32 tag = be32_to_cpup((__be32 *)p);  
  •   *nstr;  
  •   
  •   
  •  (tag == OF_DT_NOP)  
  •             ;  
  •   
  •          (tag != OF_DT_PROP)  
  •  NULL;  
  •            
  •   
  •           
  •   
  •   
  •          (be32_to_cpu(blob->version) < 0x10)  
  •   
  •         nstr = of_fdt_get_string(blob, noff);  
  •  (nstr == NULL) {  
  •             pr_warning(  NULL;  
  •         }  
  •   
  •  (strcmp(name, nstr) == 0) {  
  •              (size)  
  •   
  •               
  •  ( *)p;  
  •         }  
  •   
  •         p += sz;  
  •  (1);  
  • char boot_param_header *blob, u32 offset)  
  • {  
  •   
  •      (( *)blob) + be32_to_cpu(blob->off_dt_strings) + offset;  
  • static u64 of_read_number( __be32 *cell,  size)  
  • {  
  •   
  •  (size--)  
  •         r = (r << 32) | be32_to_cpu(*(cell++));  
  •  r;  
  • }  


1.2 根節點"/"

       設備樹有且僅有一個根節點,即「/」,根節點下包含不少子節點,例入下圖,根節點爲"/",根節點的子節點爲"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,依此類推 
 {
    compatible = "sprd,spx15";
    #address-cells = <1>;
    #size-cells = <1>;
    interrupt-parent = <&gic>;


    chosen {
        bootargs = "loglevel=8 console=ttyS1,115200n8 init=/init root=/dev/ram0 rw";
        linux,initrd-start = <0x85500000>;
        linux,initrd-end = <0x855a3212>;
    };
}
因此本函數就是讀取根節點的"#address-cells","#size-cells"屬性
[cpp] 
 

int node,   *uname, depth,  *data)  

  • {  
  •   
  •      (depth != 0)  
  •  0;  
  •           
  •   
  • , NULL);  
  •      (prop)  
  •   
  •           
  •   
  •     prop = of_get_flat_dt_prop(node, , NULL);  
  •  (prop)  
  •         dt_root_addr_cells = be32_to_cpup(prop);  
  •  1;  
  • }  



1.3 memory節點
      memory節點用於描述目標板上物理內存範圍,通常稱做/memory節點,能夠有一個或多個。當有多個節點時,須要後跟單元地址予以區分;只有一個單元地址時,能夠不寫單元地址,默認爲0。此節點包含板上物理內存的屬性,通常要指定device_type(固定爲"memory")和reg屬性。其中reg的屬性值以<起始地址 空間大小>的形式給出,以下示例中目標板內存起始地址爲0x80000000,大小爲0x20000000字節。 
memory {
    device_type = "memory";
    reg = <0x80000000 0x20000000>;
};
[cpp] 
 

int node,   *uname, depth,  *data)  

  • {  
  •   
  •      *type = of_get_flat_dt_prop(node, , NULL);  
  •  l;  
  •   
  •  (type == NULL) {  
  •          (depth != 1 || strcmp(uname, ) != 0)  
  •  0;  
  •     }   (strcmp(type, ) != 0)  
  •  0;  
  •       
  •   
  •     reg = of_get_flat_dt_prop(node, , &l);  
  •  (reg == NULL)  
  •         reg = of_get_flat_dt_prop(node, , &l);  
  •  (reg == NULL)  
  •          0;  
  •   
  • (__be32));  
  •       
  •  ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {  
  •   
  •         base = dt_mem_next_cell(dt_root_addr_cells, &reg);  
  •  (size == 0)  
  •             ;  
  •  )base,(unsigned  )size);  
  •       
  •   
  •         early_init_dt_add_memory_arch(base, size);  
  •  0;  
  • }  


2. 經過比較根節點屬性compatible值指明和目標板爲同一系列的兼容的開發板名稱

     compatible制定系統的名稱。它包含"<manufacture>,<model>"格式的字符串。準確地肯定器件型號是很是重要的,而且咱們須要包含廠商的名字來避免名字空間衝突。由於操做系統會使用compatible這個值來決定怎樣在這個機器上運行,因此在這個屬性中放入正確的值是很是重要的。
[cpp] 
 

const * __init of_flat_dt_match_machine(  *default_match,  

  •                                               * (*get_next_compat)(  * **))  
  •   *data = NULL;  
  •   *best_data = default_match;  
  •       * *compat;  
  •  dt_root;  
  •     unsigned  best_score = ~1, score = 0;  
  •   
  •   
  •      ((data = get_next_compat(&compat))) {  
  •   
  •         score = of_flat_dt_match(dt_root, compat);  
  •   
  •          (score > 0 && score < best_score) {  
  •  (!best_data) {  
  •   *prop;  
  •          size;  
  • , &size);  
  •  (prop) {  
  •              (size > 0) {  
  • );  
  •  NULL;  
  •     }  
  •  best_data;  
  • //查找設備樹的根節點  __init of_get_flat_dt_root()  
  • {  
  •   
  •     unsigned  p = ((unsigned )initial_boot_params) +  
  •   
  •      (be32_to_cpup((__be32 *)p) == OF_DT_NOP)  
  •   
  •  ALIGN(p + strlen(( *)p) + 1, 4);  
  • }  
  • //arch/arm/kernel/devtree.c 是location counter。在__arch_info_begin 的位置上,放置全部文件中的  段的內容,而後緊接着是 __arch_info_end 的位置. 段中定義了設備的machine_desc結構。  
  •   
  • static  * __init arch_get_next_mach(  * **match)  
  • {  
  •    machine_desc *mdesc = __arch_info_begin;  
  •       machine_desc *m = mdesc;  
  •  (m >= __arch_info_end)  
  •  NULL;  
  •           
  •   
  •     *match = m->dt_compat;  
  •  m;  
  • }  
  • //與設備樹根節點進行match int node,   * *compat)  
  • {  
  •   
  •       
  •   
  •      of_fdt_match(initial_boot_params, node, compat);  
  • int boot_param_header *blob, unsigned  node,  * *compat)  
  • {  
  •  tmp, score = 0;  
  •           
  •  (!compat)  
  •          0;  
  •   
  •  (*compat) {  
  •           
  •  (tmp && (score == 0 || (tmp < score)))  
  •   
  •         compat++;  
  •  score;  
  • }  
  • int boot_param_header *blob,unsigned  node,   *compat)  
  •   *cp;  
  •  cplen, l, score = 0;  
  •       
  •   
  •     cp = of_fdt_get_property(blob, node, , &cplen);  
  •  (cp == NULL)  
  •          0;  
  •   
  •  (cplen > 0) {  
  •         score++;  
  •  (of_compat_cmp(cp, compat, strlen(compat)) == 0)  
  •              score;  
  •  0;  
  • }  


(二)、解析設備樹

       unflatten_device_tree()函數來解析dtb文件,構建一個由device_node結構鏈接而成的單項鍊表,並使用全局變量of_allnodes指針來保存這個鏈表的頭指針。內核調用OF提供的API函數獲取of_allnodes鏈表信息來初始化內核其餘子系統、設備。

[cpp] 
 

void)  

  • {  
  •   
  •     __unflatten_device_tree(initial_boot_params, &of_allnodes,early_init_dt_alloc_memory_arch);  
  •   
  • static __unflatten_device_tree( boot_param_header *blob,  
  •  device_node **mynodes,  
  •                                      * (*dt_alloc)(u64 size, u64 align))  
  •  size;  
  •  *start, *mem;  
  •      device_node **allnextp = mynodes;  
  •  (!blob) {  
  • ;  
  •   
  •  (be32_to_cpu(blob->magic) != OF_DT_HEADER) {  
  •         pr_err( ;  
  •     }  
  •   
  •  *)blob) + be32_to_cpu(blob->off_dt_struct);  
  •       
  • )unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);  
  •     size = ALIGN(size, 4);  
  •   
  •  device_node));  
  •     memset(mem, 0, size);  
  •   
  •   
  •  *)blob) + be32_to_cpu(blob->off_dt_struct);  
  •       
  •  (be32_to_cpup(start) != OF_DT_END)  
  •  (be32_to_cpup(mem + size) != 0xdeadbeef)  
  • static * unflatten_dt_node( boot_param_header *blob,  
  •                                  *mem, **p,  
  •  device_node *dad,  
  •                                  device_node ***allnextpp,  
  •  fpsize)  
  • {  
  •  device_node *np;  
  •      property *pp, **prev_pp = NULL;  
  •  *pathp;  
  •     u32 tag;  
  •  l, allocl;  
  •      has_name = 0;  
  •  new_format = 0;  
  •       
  •   
  •     tag = be32_to_cpup(*p);  
  •   
  •      (tag != OF_DT_BEGIN_NODE) {  
  •  mem;  
  •   
  •     pathp = *p;  
  •   
  •     *p = PTR_ALIGN(*p + l, 4);  
  •   
  •  ((*pathp) != ) {  
  •         new_format = 1;  
  •  (fpsize == 0) {  
  •             fpsize = 1;  
  • ;  
  •         }  {  
  •   
  •             allocl = fpsize;  
  •   
  • ( device_node) + allocl,__alignof__( device_node));  
  •   
  •   
  •       
  •  (allnextpp) {  
  •          *fn;  
  •   
  •         np->full_name = fn = (( *)np) + (*np);  
  •   
  •          (new_format) {  
  •  (dad && dad->parent) {  
  •                 strcpy(fn, dad->full_name);  
  • ;  
  •         }  
  •   
  •       
  •   
  •         prev_pp = &np->properties;  
  •   
  •   
  •  (dad != NULL) {  
  •             np->parent = dad;  
  •  (dad->next == NULL)  
  •                 dad->child = np;  
  •   
  •                 dad->next->sibling = np;  
  •   
  •  (1) {  
  •         u32 sz, noff;  
  •  *pname;  
  •        
  •   
  •         tag = be32_to_cpup(*p);  
  •   
  •          (tag == OF_DT_NOP) {  
  • ;  
  •   
  •  (tag != OF_DT_PROP)  
  •             ;  
  •   
  •         *p += 4;  
  •   
  •         sz = be32_to_cpup(*p);  
  •   
  •         noff = be32_to_cpup(*p + 4);  
  •   
  •         *p += 8;  
  •   
  •          (be32_to_cpu(blob->version) < 0x10)  
  •   
  •         pname = of_fdt_get_string(blob, noff);  
  •  (pname == NULL) {  
  •             pr_info( ;  
  •         }  
  •   
  •  (strcmp(pname, ) == 0)  
  •             has_name = 1;  
  •   
  •         l = strlen(pname) + 1;  
  •   
  •   
  •         pp = unflatten_dt_alloc(&mem, ( property),__alignof__( property));  
  •   
  •   
  •          (allnextpp) {  
  •  ((strcmp(pname, ) == 0) || (strcmp(pname, ) == 0)) {  
  •                  (np->phandle == 0)  
  •  (strcmp(pname, ) == 0)  
  •                 np->phandle = be32_to_cpup((__be32 *)*p);  
  •   
  •             pp->length = sz;  
  •   
  •   
  •   
  •             *prev_pp = pp;  
  •   
  •     }  
  •   
  •   
  •   
  •      (!has_name) {  
  •  *p1 = pathp, *ps = pathp, *pa = NULL;  
  •          sz;  
  •  (*p1) {  
  •  ((*p1) == )  
  •                 pa = p1;  
  •  ((*p1) == )  
  •                 ps = p1 + 1;  
  •  (pa < ps)  
  •             pa = p1;  
  • ( property) + sz,__alignof__( property));  
  •  (allnextpp) {  
  •             pp->name = ;  
  •  *)pp->value)[sz - 1] = 0;  
  •  *)pp->value);  
  •         }  
  •   
  •      (allnextpp) {  
  •   
  • , NULL);  
  •           
  • , NULL);  
  •       
  •  (!np->name)  
  •             np->name =   (!np->type)  
  •             np->type =    
  •       
  •  (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) {  
  •           
  •  (tag == OF_DT_NOP)  
  •             *p += 4;  
  •   
  •               
  •   
  •      (tag != OF_DT_END_NODE) {  
  •  mem;  
  •   
  •      mem;  
  • //從mem分配內存空間,*mem記錄分配了多大空間 static *unflatten_dt_alloc( **mem, unsigned  size,unsigned  align)  
  • {  
  •  *res;  
  •        
  •  res;  
  • }  
  • //一個特定的節點一般是以完整的路徑來引用,好比/external-bus/ethernet@0,0,不過當一個用戶真的想知道「哪一個設備是eth0」時,這將會很繁瑣。aliases節點能夠用來爲一個完整的設備路徑分配一個短的別名。好比: //aliases { //    serial0 = &uart0; //    serial1 = &uart1; //    serial2 = &uart2; //    serial3 = &uart3; //    ethernet0 = ð0; //    serial0 = &serial0; //}; //當須要爲設備指定一個標示符時,操做系統歡迎你們使用別名。 //設置內核輸出終端,以及遍歷「/aliases」節點下的全部的屬性,掛入相應鏈表 void * (*dt_alloc)(u64 size, u64 align))  
  • {  
  •  property *pp;  
  •       
  •   
  •     of_chosen = of_find_node_by_path();  
  •  (of_chosen == NULL)  
  •         of_chosen = of_find_node_by_path();  
  •   
  •   
  •      (of_chosen) {  
  •   *name;  
  •           
  • , NULL);  
  •           
  •  (name)  
  •             of_stdout = of_find_node_by_path(name);  
  •   
  •     of_aliases = of_find_node_by_path();  
  •  (!of_aliases)  
  •         ;  
  •   
  •   *start = pp->name;  
  •   *end = start + strlen(start);  
  •          device_node *np;  
  •  alias_prop *ap;  
  •          id, len;  
  •   
  •  (!strcmp(pp->name, ) ||  
  •             !strcmp(pp->name, ) ||  
  • ))  
  •             ;  
  •   
  •  (!np)  
  • ;  
  •           
  •   
  •              (isdigit(*(end-1)) && end > start)  
  •   
  •   
  •               
  •   
  •              (kstrtoint(end, 10, &id) < 0)  
  • ;  
  •           
  •   
  •             ap = dt_alloc((*ap) + len + 1, 4);  
  •  (!ap)  
  •                 ;  
  • (*ap) + len + 1);  
  •             ap->alias = start;  
  •   
  •             of_alias_add(ap, np, id, start, len);  
  • }  



4、OF提供的經常使用API函數     OF提供的函數主要集中在drivers/of/目錄下,有address.c,base.c,device.c,fdt.c,irq.c,platform.c等等 1. 用來查找在dtb中的根節點 unsigned long __init of_get_flat_dt_root(void) 2. 根據deice_node結構的full_name參數,在全局鏈表of_allnodes中,查找合適的device_node struct device_node *of_find_node_by_path(const char *path) 例如: struct device_node *cpus; cpus=of_find_node_by_path("/cpus"); 3. 若from=NULL,則在全局鏈表of_allnodes中根據name查找合適的device_node struct device_node *of_find_node_by_name(struct device_node *from,const char *name) 例如: struct device_node *np; np = of_find_node_by_name(NULL,"firewire"); 4. 根據設備類型查找相應的device_node struct device_node *of_find_node_by_type(struct device_node *from,const char *type) 例如: struct device_node *tsi_pci; tsi_pci= of_find_node_by_type(NULL,"pci"); 5. 根據compatible字符串查找device_node struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible) 6. 根據節點屬性的name查找device_node struct device_node *of_find_node_with_property(struct device_node *from,const char *prop_name) 7. 根據phandle查找device_node struct device_node *of_find_node_by_phandle(phandle handle) 8. 根據alias的name得到設備id號 int of_alias_get_id(struct device_node *np, const char *stem) 9. device node計數增長/減小 struct device_node *of_node_get(struct device_node *node) void of_node_put(struct device_node *node) 10. 根據property結構的name參數,在指定的device node中查找合適的property struct property *of_find_property(const struct device_node *np,const char *name,int *lenp) 11. 根據property結構的name參數,返回該屬性的屬性值 const void *of_get_property(const struct device_node *np, const char *name,int *lenp) 12. 根據compat參數與device node的compatible匹配,返回匹配度 int of_device_is_compatible(const struct device_node *device,const char *compat) 13. 得到父節點的device node struct device_node *of_get_parent(const struct device_node *node) 14. 將matches數組中of_device_id結構的name和type與device node的compatible和type匹配,返回匹配度最高的of_device_id結構 const struct of_device_id *of_match_node(const struct of_device_id *matches,const struct device_node *node) 15. 根據屬性名propname,讀出屬性值中的第index個u32數值給out_value int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index, u32 *out_value) 16. 根據屬性名propname,讀出該屬性的數組中sz個屬性值給out_values int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz) int of_property_read_u16_array(const struct device_node *np,const char *propname, u16 *out_values, size_t sz) int of_property_read_u32_array(const struct device_node *np,const char *propname, u32 *out_values,size_t sz) 17. 根據屬性名propname,讀出該屬性的u64屬性值 int of_property_read_u64(const struct device_node *np, const char *propname,u64 *out_value) 18. 根據屬性名propname,讀出該屬性的字符串屬性值 int of_property_read_string(struct device_node *np, const char *propname,const char **out_string) 19. 根據屬性名propname,讀出該字符串屬性值數組中的第index個字符串 int of_property_read_string_index(struct device_node *np, const char *propname,int index, const char **output) 20. 讀取屬性名propname中,字符串屬性值的個數 int of_property_count_strings(struct device_node *np, const char *propname) 21. 讀取該設備的第index個irq號 unsigned int irq_of_parse_and_map(struct device_node *dev, int index) 22. 讀取該設備的第index個irq號,並填充一個irq資源結構體 int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) 23. 獲取該設備的irq個數 int of_irq_count(struct device_node *dev) 24. 獲取設備寄存器地址,並填充寄存器資源結構體 int of_address_to_resource(struct device_node *dev, int index,struct resource *r) const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,unsigned int *flags) 25. 獲取通過映射的寄存器虛擬地址 void __iomem *of_iomap(struct device_node *np, int index) 24. 根據device_node查找返回該設備對應的platform_device結構 struct platform_device *of_find_device_by_node(struct device_node *np) 25. 根據device node,bus id以及父節點建立該設備的platform_device結構 struct platform_device *of_device_alloc(struct device_node *np,const char *bus_id,struct device *parent) static struct platform_device *of_platform_device_create_pdata(struct device_node *np,const char *bus_id,                                                             void *platform_data,struct device *parent) 26. 遍歷of_allnodes中的節點掛接到of_platform_bus_type總線上,因爲此時of_platform_bus_type總線上尚未驅動,因此此時不進行匹配 int of_platform_bus_probe(struct device_node *root,const struct of_device_id *matches,struct device *parent) 27. 遍歷of_allnodes中的全部節點,生成並初始化platform_device結構 int of_platform_populate(struct device_node *root,const struct of_device_id *matches,                         const struct of_dev_auxdata *lookup,struct device *parent) {     struct device_node *child;     int rc = 0;          //得到設備樹的根節點      root = root ? of_node_get(root) : of_find_node_by_path("/
相關文章
相關標籤/搜索