depot/pkgs/os-specific/linux/device-tree/apply_overlays.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

103 lines
3.2 KiB
Python
Raw Normal View History

from argparse import ArgumentParser
from dataclasses import dataclass
from functools import cached_property
import json
from pathlib import Path
from libfdt import Fdt, FdtException, FDT_ERR_NOSPACE, fdt_overlay_apply
@dataclass
class Overlay:
name: str
filter: str
dtbo_file: Path
@cached_property
def fdt(self):
with self.dtbo_file.open("rb") as fd:
return Fdt(fd.read())
@cached_property
def compatible(self):
return get_compatible(self.fdt)
def get_compatible(fdt):
root_offset = fdt.path_offset("/")
return set(fdt.getprop(root_offset, "compatible").as_stringlist())
def apply_overlay(dt: Fdt, dto: Fdt) -> Fdt:
while True:
# we need to copy the buffers because they can be left in an inconsistent state
# if the operation fails (ref: fdtoverlay source)
result = dt.as_bytearray().copy()
err = fdt_overlay_apply(result, dto.as_bytearray().copy())
if err == 0:
new_dt = Fdt(result)
# trim the extra space from the final tree
new_dt.pack()
return new_dt
if err == -FDT_ERR_NOSPACE:
# not enough space, add some blank space and try again
# magic number of more space taken from fdtoverlay
dt.resize(dt.totalsize() + 65536)
continue
raise FdtException(err)
def main():
parser = ArgumentParser(description='Apply a list of overlays to a directory of device trees')
parser.add_argument("--source", type=Path, help="Source directory")
parser.add_argument("--destination", type=Path, help="Destination directory")
parser.add_argument("--overlays", type=Path, help="JSON file with overlay descriptions")
args = parser.parse_args()
source: Path = args.source
destination: Path = args.destination
overlays: Path = args.overlays
with overlays.open() as fd:
overlays_data = [
Overlay(
name=item["name"],
filter=item["filter"],
dtbo_file=Path(item["dtboFile"]),
)
for item in json.load(fd)
]
for source_dt in source.glob("**/*.dtb"):
rel_path = source_dt.relative_to(source)
print(f"Processing source device tree {rel_path}...")
with source_dt.open("rb") as fd:
dt = Fdt(fd.read())
dt_compatible = get_compatible(dt)
for overlay in overlays_data:
if overlay.filter and overlay.filter not in str(rel_path):
print(f" Skipping overlay {overlay.name}: filter does not match")
continue
if not overlay.compatible.intersection(dt_compatible):
print(f" Skipping overlay {overlay.name}: {overlay.compatible} is incompatible with {dt_compatible}")
continue
print(f" Applying overlay {overlay.name}")
dt = apply_overlay(dt, overlay.fdt)
print(f"Saving final device tree {rel_path}...")
dest_path = destination / rel_path
dest_path.parent.mkdir(parents=True, exist_ok=True)
with dest_path.open("wb") as fd:
fd.write(dt.as_bytearray())
if __name__ == '__main__':
main()