1 from argparse
import ArgumentParser
2 from dataclasses
import dataclass
3 from functools
import cached_property
5 from pathlib
import Path
7 from libfdt
import Fdt
, FdtException
, FDT_ERR_NOSPACE
, fdt_overlay_apply
18 with self
.dtbo_file
.open("rb") as fd
:
23 return get_compatible(self
.fdt
)
26 def get_compatible(fdt
):
27 root_offset
= fdt
.path_offset("/")
28 return set(fdt
.getprop(root_offset
, "compatible").as_stringlist())
31 def apply_overlay(dt
: Fdt
, dto
: Fdt
) -> Fdt
:
33 # we need to copy the buffers because they can be left in an inconsistent state
34 # if the operation fails (ref: fdtoverlay source)
35 result
= dt
.as_bytearray().copy()
36 err
= fdt_overlay_apply(result
, dto
.as_bytearray().copy())
40 # trim the extra space from the final tree
44 if err
== -FDT_ERR_NOSPACE
:
45 # not enough space, add some blank space and try again
46 # magic number of more space taken from fdtoverlay
47 dt
.resize(dt
.totalsize() + 65536)
50 raise FdtException(err
)
53 parser
= ArgumentParser(description
='Apply a list of overlays to a directory of device trees')
54 parser
.add_argument("--source", type=Path
, help="Source directory")
55 parser
.add_argument("--destination", type=Path
, help="Destination directory")
56 parser
.add_argument("--overlays", type=Path
, help="JSON file with overlay descriptions")
57 args
= parser
.parse_args()
59 source
: Path
= args
.source
60 destination
: Path
= args
.destination
61 overlays
: Path
= args
.overlays
63 with overlays
.open() as fd
:
67 filter=item
["filter"],
68 dtbo_file
=Path(item
["dtboFile"]),
70 for item
in json
.load(fd
)
73 for source_dt
in source
.glob("**/*.dtb"):
74 rel_path
= source_dt
.relative_to(source
)
76 print(f
"Processing source device tree {rel_path}...")
77 with source_dt
.open("rb") as fd
:
80 dt_compatible
= get_compatible(dt
)
82 for overlay
in overlays_data
:
83 if overlay
.filter and overlay
.filter not in str(rel_path
):
84 print(f
" Skipping overlay {overlay.name}: filter does not match")
87 if not overlay
.compatible
.intersection(dt_compatible
):
88 print(f
" Skipping overlay {overlay.name}: {overlay.compatible} is incompatible with {dt_compatible}")
91 print(f
" Applying overlay {overlay.name}")
92 dt
= apply_overlay(dt
, overlay
.fdt
)
94 print(f
"Saving final device tree {rel_path}...")
95 dest_path
= destination
/ rel_path
96 dest_path
.parent
.mkdir(parents
=True, exist_ok
=True)
97 with dest_path
.open("wb") as fd
:
98 fd
.write(dt
.as_bytearray())
101 if __name__
== '__main__':