DTB 관련 API는 다음과 같이 구분된다.
- fdt로 시작하는 API는 DTB(Device Tree Blob)를 대상으로 동작한다.
- of로 시작하는 API는 링크드 리스트 구조의 expanded format으로 변환된 Device Tree를 대상으로 동작한다.
- DTB는 unflatten_device_tree() 함수에의해 변환된다.
 
fdt_get_name()
scripts/dtc/libfdt/fdt_ro.c
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
{
        const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
        int err;
        if (((err = fdt_check_header(fdt)) != 0)
            || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 
                        goto fail;
        if (len)
                *len = strlen(nh->name);
        return nh->name;
 fail:
        if (len)
                *len = err;
        return NULL;
}
- fdt가 가리키는 DTB에서 nodeoffset에 위치한 노드에서 노드명 주소와 노드명 길이를 알아온다.
_fdt_offset_ptr()
scripts/dtc/libfdt/libfdt_internal.h
static inline const void *_fdt_offset_ptr(const void *fdt, int offset)
{
        return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
}
DTB의 structure 블럭 시작 주소에 offset를 더한 주소를 리턴한다..
fdt_offset_ptr()
scripts/dtc/libfdt/fdt.c
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
{
        const char *p; 
        if (fdt_version(fdt) >= 0x11)
                if (((offset + len) < offset)
                    || ((offset + len) > fdt_size_dt_struct(fdt)))
                        return NULL;
        p = _fdt_offset_ptr(fdt, offset);
        if (p + len < p)
                return NULL;
        return p;
}
DTB structure 블럭에서 offset 만큼 떨어진 주소를 알아오는데 그 주소의 위치가 DTB structure 블럭을 벗어난 경우 에러로 null을 리턴한다.
- if (fdt_version(fdt) >= 0x11
- fdt 주소에 위치한 DTB 버전이 0x11이상인 경우 len이 음수이거나 물리주소를 초과한 경우
 
- if (((offset + len) < offset) || ((offset + len) > fdt_size_dt_struct(fdt))) return NULL;
- offset+len이 DTB structure 블럭 사이즈를 초과한 경우 null로 리턴
 
- p = _fdt_offset_ptr(fdt, offset);
- DTB structure 블럭에서 offset 만큼 떨어진 주소를 알아온다.
 
-  if (p + len < p) return NULL;
- 알아온 주소가 물리 주소를 초과한 경우 null을 리턴한다.
 
unflatten_dt_alloc()
drivers/of/fdt.c
static void *unflatten_dt_alloc(void **mem, unsigned long size,
                                       unsigned long align)
{
        void *res;
        *mem = PTR_ALIGN(*mem, align);
        res = *mem;
        *mem += size;
        return res; 
}
- 메모리 주소 mem을 align 단위로 round up한 후 리턴하고 메모리 주소 mem에 사이즈를 더한다.
fdt_next_tag()
scripts/dtc/libfdt/fdt.c
uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
{
        const uint32_t *tagp, *lenp;
        uint32_t tag;
        int offset = startoffset;
        const char *p;
        *nextoffset = -FDT_ERR_TRUNCATED;
        tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
        if (!tagp)
                return FDT_END; /* premature end */
        tag = fdt32_to_cpu(*tagp);
        offset += FDT_TAGSIZE;
        *nextoffset = -FDT_ERR_BADSTRUCTURE;
        switch (tag) {
        case FDT_BEGIN_NODE:
                /* skip name */
                do {
                        p = fdt_offset_ptr(fdt, offset++, 1);
                } while (p && (*p != '\0'));
                if (!p)
                        return FDT_END; /* premature end */
                break;
        case FDT_PROP:
                lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
                if (!lenp)
                        return FDT_END; /* premature end */
                /* skip-name offset, length and value */
                offset += sizeof(struct fdt_property) - FDT_TAGSIZE
                        + fdt32_to_cpu(*lenp);
                break;
        case FDT_END:
        case FDT_END_NODE:
        case FDT_NOP:
                break;
        default:
                return FDT_END;
        }
        if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
                return FDT_END; /* premature end */
        *nextoffset = FDT_TAGALIGN(offset);
        return tag;
}
startoffset에 위치한 태그의 값을 알아오고 startoffset 다음에 위치한 태그 offset를 출력 인수 nextoffset에 저장한다.
- startoffset 위치의 태그번호를 리턴하는데 태그 종류에 따라 출력 인수 nextoffset에 저장되는 offset가 다음과 같이 다르다.
- FDT_BEGIN_NODE인 경우 4 byte 태그 및 이름을 skip 한 4 byte round up한 offset
- FDT_PROP인 경우 4 byte 태그, 속성 길이, name offset, 속성명을 skip한 4 byte round up한 offset
- 그 외의 태그는 4 byte 태그만 더한 offset
 
아래 그림은 fdt_next_tag()를 두 번 반복 수행하는 모습을 보여준다.
fdt_first_property_offset()
scripts/dtc/libfdt/fdt_ro.c
int fdt_first_property_offset(const void *fdt, int nodeoffset)
{
        int offset;
        if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
                return offset;
        return _nextprop(fdt, offset);
}
노드 내의 처음 속성 offset를 리턴한다. 속성이 없으면 -값 에러를 리턴한다.
- if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) return offset;
- nodeoffset에 위치한 태그가 노드 시작이 아닌 경우 -값 에러(offset)를 리턴한다.
 
- return _nextprop(fdt, offset);
- 처음 발견되는 속성 offset를 리턴한다.
 
아래 그림은 노드에서 처음 발견되는 속성에 대한 offset를 알아오는 것을 표현하였다.
_fdt_check_node_offset()
scripts/dtc/libfdt/fdt.c
int _fdt_check_node_offset(const void *fdt, int offset)
{
        if ((offset < 0) || (offset % FDT_TAGSIZE)
            || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
                return -FDT_ERR_BADOFFSET;
        return offset;
}
주어진 offset에 있는 태그가 시작 노드인 경우에만 offset를 리턴하고 그렇지 않으면 -값 에러를 리턴한다.
- offset가 0보다 작거나 4 byte align 되어 있지 않거나 현재 태그가 노드 시작이 아니면 -값의 에러를 리턴하고 그렇지 않은 경우 offset를 리턴한다.
_nextprop()
scripts/dtc/libfdt/fdt_ro.c
static int _nextprop(const void *fdt, int offset)
{
        uint32_t tag;
        int nextoffset;
        do {
                tag = fdt_next_tag(fdt, offset, &nextoffset);
                switch (tag) {
                case FDT_END:
                        if (nextoffset >= 0)
                                return -FDT_ERR_BADSTRUCTURE;
                        else
                                return nextoffset;
                case FDT_PROP:
                        return offset;
                }
                offset = nextoffset;
        } while (tag == FDT_NOP);
        return -FDT_ERR_NOTFOUND;
}
해당 노드 내에서 다음 속성 태그 offset을 알아온다. 만일 노드가 끝나면 -값으로 에러를 리턴한다.
- 태그가 FDT_NOP인 경우에 루프를 돌며 다음 태그를 읽어오다가 FDT_PROP를 만나면 해당 offset를 리턴한다. 만일 FDT_END를 만나는 경우 -값으로 에러를 리턴한다.
아래 그림은 _nextprop()를 호출할 때 해당 노드내에서 다음 속성에 대한 offset를 읽어 오는데 노드가 바뀌거나 끝나는 경우 에러를 리턴하는 것을 보여준다.
fdt_next_property_offset()
scripts/dtc/libfdt/fdt_ro.c
int fdt_next_property_offset(const void *fdt, int offset)
{
        if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) 
                return offset;
                
        return _nextprop(fdt, offset);
}
노드 내의 다음 속성 offset를 리턴한다. 속성이 없으면 -값 에러를 리턴한다.
- if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) return offset;
- offset에 위치한 태그가 속성이 아닌 경우 -값 에러(offset)를 리턴한다.
 
- return _nextprop(fdt, offset);
- 다음 발견되는 속성 offset를 리턴한다.
 
_fdt_check_prop_offset()
scripts/dtc/libfdt/fdt.c()
int _fdt_check_prop_offset(const void *fdt, int offset)
{
        if ((offset < 0) || (offset % FDT_TAGSIZE)
            || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
                return -FDT_ERR_BADOFFSET;
        return offset;
}
주어진 offset에 있는 태그가 속성인 경우에만 offset를 리턴하고 그렇지 않으면 -값 에러를 리턴한다.
- offset가 0보다 작거나 4 byte align 되어 있지 않거나 현재 태그가 속성이 아니면 -값의 에러를 리턴하고 그렇지 않은 경우 offset를 리턴한다.
fdt_getprop_by_offset()
scripts/dtc/libfdt/fdt_ro.c
const void *fdt_getprop_by_offset(const void *fdt, int offset,
                                  const char **namep, int *lenp)
{
        const struct fdt_property *prop;
                
        prop = fdt_get_property_by_offset(fdt, offset, lenp);
        if (!prop)      
                return NULL;
        if (namep)
                *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
        return prop->data;
}
속성 값을 리턴하는데 namep 출력 인수는 속성명이 저장된 string 블록의 주소를 저장하고 , lenp에는 데이터 길이를 저장한다. 리턴 값이 음수인 경우는 에러이다.
fdt_get_property_by_offset()
scripts/dtc/libfdt/fdt_ro.c
const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
                                                      int offset,
                                                      int *lenp)
{
        int err;
        const struct fdt_property *prop;
        if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
                if (lenp)
                        *lenp = err;
                return NULL;
        }
        prop = _fdt_offset_ptr(fdt, offset);
        if (lenp)
                *lenp = fdt32_to_cpu(prop->len);
        return prop;
}
offset 위치의 속성 태그에 위치한 주소를 fdt_property 구조체 포인터로 캐스트하여 리턴하고 lenp 출력 인수에는 데이터 길이를 리턴한다.
아래 그림은 속성값이 있는 곳의 주소를 fdt_property 구조체 포인터로 캐스트하여 알아오는 것을 보여준다.
fdt_string()
scripts/dtc/libfdt/fdt_ro.c
const char *fdt_string(const void *fdt, int stroffset)
{
        return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
}
DTB에서 주어진 문자열 offset(stroffset)에 대한 주소를 알아온다.
fdt_next_node()
scripts/dtc/libfdt/fdt.c
int fdt_next_node(const void *fdt, int offset, int *depth)
{
        int nextoffset = 0;
        uint32_t tag;
        if (offset >= 0)
                if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
                        return nextoffset;
        do {
                offset = nextoffset;
                tag = fdt_next_tag(fdt, offset, &nextoffset); 
                switch (tag) {
                case FDT_PROP:
                case FDT_NOP:
                        break;
                case FDT_BEGIN_NODE:
                        if (depth)
                                (*depth)++;
                        break;
                case FDT_END_NODE:
                        if (depth && ((--(*depth)) < 0))
                                return nextoffset;
                        break;
                case FDT_END:
                        if ((nextoffset >= 0)
                            || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
                                return -FDT_ERR_NOTFOUND;
                        else
                                return nextoffset;
                }
        } while (tag != FDT_BEGIN_NODE);
        return offset;
}
다음 노드의 offset를 리턴하고 출력 인수에 depth를 저장한다.
- if (offset >= 0) if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) return nextoffset;
- 주어진 offset가 노드가 아닌 경우 null을 리턴한다.
 
- 루프를 돌며 태그 번호와 다음 태그 offset을 알아오고 태그에 따라 다음과 같이 동작한다.
- FDT_PROP이거나 FDT_NOP인 경우 skip 한다.
- FDT_BEGIN_NODE인 경우 depth 증가하게 되고 루프를 벗어나게 된다.
- FDT_END_NODE인 경우 depth가 0이상이면서 감소 시킨 depth가 0보다 작은 경우 nextoffset를 리턴한다. (보통 또 다른 시작 노드)
- FDT_END인 경우 음수 값인 에러를 리턴한다.
 
DTB 헤더 관련 API
scripts/dtc/libfdt/libfdt.h
#define fdt_get_header(fdt, field) \
        (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
#define fdt_magic(fdt)                  (fdt_get_header(fdt, magic))
#define fdt_totalsize(fdt)              (fdt_get_header(fdt, totalsize))
#define fdt_off_dt_struct(fdt)          (fdt_get_header(fdt, off_dt_struct))
#define fdt_off_dt_strings(fdt)         (fdt_get_header(fdt, off_dt_strings))
#define fdt_off_mem_rsvmap(fdt)         (fdt_get_header(fdt, off_mem_rsvmap))
#define fdt_version(fdt)                (fdt_get_header(fdt, version))
#define fdt_last_comp_version(fdt)      (fdt_get_header(fdt, last_comp_version))
#define fdt_boot_cpuid_phys(fdt)        (fdt_get_header(fdt, boot_cpuid_phys))
#define fdt_size_dt_strings(fdt)        (fdt_get_header(fdt, size_dt_strings))
#define fdt_size_dt_struct(fdt)         (fdt_get_header(fdt, size_dt_struct))
- 디바이스 트리의 헤더에는 10가지의 정보가 있는데 각각에 대해 함수로 제공된다..
- fdt_magic()
- DTB 매직 넘버를 알아온다.
 
- fdt_totalsize()
- DTB 전체 사이즈를 알아온다.
 
- fdt_off_dt_struct()
- structure 블럭 시작 offset를 알아온다.
 
- fdt_off_dt_strings()
- string 블럭 시작 offset를 알아온다.
 
- fdt_off_mem_rsvmap()
- memory reserved 블럭 시작 offset를 알아온다.
 
- fdt_version()
- DTB 버전 정보를 알아온다.
 
- fdt_last_comp_version
- DTB 마지막 호환 버전을 알아온다.
 
- fdt_boot_cpuid_phys()
- fdt_size_dt_strings()
- string 블럭 사이즈를 알아온다.
 
- fdt_sizae_dt_struct()
- structure 블럭 사이즈를 알아온다.
 
다음은 rpi2 DTB 헤더의 hex 값이다.
구조체
fdt_header 구조체
scripts/dtc/libfdt/fdt.h
struct fdt_header { 
        uint32_t magic;                  /* magic word FDT_MAGIC */
        uint32_t totalsize;              /* total size of DT block */
        uint32_t off_dt_struct;          /* offset to structure */
        uint32_t off_dt_strings;         /* offset to strings */
        uint32_t off_mem_rsvmap;         /* offset to memory reserve map */
        uint32_t version;                /* format version */
        uint32_t last_comp_version;      /* last compatible version */
                
        /* version 2 fields below */
        uint32_t boot_cpuid_phys;        /* Which physical CPU id we're
                                            booting on */
        /* version 3 fields below */
        uint32_t size_dt_strings;        /* size of the strings block */
                
        /* version 17 fields below */
        uint32_t size_dt_struct;         /* size of the structure block */
};
fdt_node_header 구조체
scripts/dtc/libfdt/fdt.h
struct fdt_node_header {
        uint32_t tag;
        char name[0];
};
- tag
- 1=FDT_BEGIN_NODE
- 2=FDT_END_NODE
- 3=FDT_PROP
- 4=FDT_NOP
- 9=FDT_END
 
- name[0]
- tag가 1번인 경우 노드명의 첫 글자
 
device_node 구조체
include/linux/of.h
struct device_node {
        const char *name;
        const char *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;
        struct  kobject kobj;
        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
};
- name
- 주소 없는 compact 스타일 노드명이 담긴다.
- 예) “”, “chosen”, “uart”
 
- type
- 디바이스 타입명이 담긴다.
- 예) “memory”, “cpu”, “pci”, “ethernet-phy”, “network”
 
-  phandle
- 노드내 “phandle” 속성이 가리키는 노드
 
- full_name
- full path 노드명이 담긴다.
- 예) “/”, “/chosen”, “/soc/uart@7e201000”
 
- fwnode
- 0=FWNODE_INVALID
- 1=FWNODE_OF (초기값)
- 2=FWNODE_ACPI
 
- properties
- 속성값이 있는 경우 property 노드를 가리키고 없으면 null
 
- deadprops
- parent
- 부모 노드를 가리키고 자신이 루트 노드인 경우 null
 
- child
- 자식 노드들 중 첫 노드를 가리키고 없으면 null
 
- sibling
- 바로 다음 형재 노드를 가리키고 없으면 null
 
- kboj
- object 라이프 사이클을 관리
 
- _flags
- data
- 4바이트로 표현 가능한 노드 값이 있거나 데이터 위치를 가리키고 없으면 null
 
property 구조체
include/linux/of.h
struct property {
        char    *name;
        int     length;
        void    *value;
        struct property *next;
        unsigned long _flags;
        unsigned int unique_id;
        struct bin_attribute attr;
};
- name
- 속성명
- 예) “name”, “stdout-path”, “reg”
 
- length
- 속성 값 길이
 
- value
- 속성 값이 위치한 곳을 가리키고 없으면 null
 
- next
- 노드내의 다음 속성을 가리키고 없으면 null
 
- _flags
- unique_id
- attr
참고
- DTB – 구조 | 문c
- DTB – 라즈베리파이2 샘플 소스 | 문c
- DTB (of API) | 문c






