Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ltchiptool

Universal, easy-to-use GUI flashing/dumping tool for BK7231, RTL8710B and RTL8720C. Also contains some CLI utilities for binary firmware manipulation.
Universal, easy-to-use GUI flashing/dumping tool for BK7231, LN882H, RTL8710B and RTL8720C. Also contains some CLI utilities for binary firmware manipulation.

<div align="center">

Expand Down
1 change: 1 addition & 0 deletions ltchiptool/commands/soc.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"rtltool": "ltchiptool/soc/ambz/util/rtltool.py",
"ambztool": "ltchiptool/soc/ambz/util/ambztool.py",
"ambz2tool": "ltchiptool/soc/ambz2/util/ambz2tool.py",
"ln882htool": "ltchiptool/soc/ln882h/util/ln882htool.py",
}


Expand Down
2 changes: 1 addition & 1 deletion ltchiptool/soc/amb/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from datastruct import DataStruct
from datastruct.fields import adapter, alignto, bitfield, field

FF_16 = b"\xFF" * 16
FF_16 = b"\xff" * 16


class FlashSpeed(IntEnum):
Expand Down
4 changes: 2 additions & 2 deletions ltchiptool/soc/ambz/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ def check_xip_binary(
) -> Optional[Tuple[int, int, bytes]]:
if data[0:8] != header:
return None
if data[16:32] != b"\xFF" * 16:
if data[16:32] != b"\xff" * 16:
return None
length, start = unpack("<II", data[8:16])
return start, length, data[32:]


def check_bootloader_binary(data: bytes) -> Optional[Tuple[int, int, bytes]]:
return check_xip_binary(data, header=b"\x99\x99\x96\x96\x3F\xCC\x66\xFC")
return check_xip_binary(data, header=b"\x99\x99\x96\x96\x3f\xcc\x66\xfc")


class AmebaZBinary(SocInterface, ABC):
Expand Down
4 changes: 2 additions & 2 deletions ltchiptool/soc/ambz/flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def flash_sw_reset(self) -> None:
port.baudrate = 115200
sleep(0.1)
# try software reset by writing the family ID, preceded by 55AA
magic_word = b"\x55\xAA" + self.family.id.to_bytes(length=4, byteorder="big")
magic_word = b"\x55\xaa" + self.family.id.to_bytes(length=4, byteorder="big")
port.write(magic_word)
sleep(0.5)
port.baudrate = prev_baudrate
Expand Down Expand Up @@ -178,7 +178,7 @@ def flash_get_chip_info(self) -> List[Tuple[str, str]]:
syscfg2 = letoint(data[256 + 512 + 16 + 8 : 256 + 512 + 16 + 8 + 4])

system_data = data[256 + 512 + 16 + 16 : 256 + 512 + 16 + 16 + 128].ljust(
4096, b"\xFF"
4096, b"\xff"
)
system = SystemData.unpack(system_data)

Expand Down
2 changes: 1 addition & 1 deletion ltchiptool/soc/ambz/util/ambzcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def read_efuse_otp(offset: int = 0) -> bytes:
return (
b"\x02\x48\x01\x4b"
b"\x98\x47\x03\xe0"
b"\x21\x3C\x00\x00" # EFUSE_OTP_Read32B()
b"\x21\x3c\x00\x00" # EFUSE_OTP_Read32B()
) + inttole32(AMBZ_DATA_ADDRESS + offset)

@staticmethod
Expand Down
2 changes: 1 addition & 1 deletion ltchiptool/soc/ambz/util/ambztool.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def read(self, io: IO[bytes], n: int) -> bytes:
# increment saved address
self.address += n
# add padding to force sending N+4 packet size
data = data.ljust(n + 4, b"\xFF")
data = data.ljust(n + 4, b"\xff")
return data


Expand Down
4 changes: 2 additions & 2 deletions ltchiptool/soc/ambz/util/rtltool.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
CMD_XMD = b"\x07" # Go xmodem mode (write RAM/Flash mode)
CMD_EFS = b"\x17" # Erase Flash Sectors
CMD_RBF = b"\x19" # Read Block Flash
CMD_ABRT = b"\x1B" # End xmodem mode (write RAM/Flash mode)
CMD_ABRT = b"\x1b" # End xmodem mode (write RAM/Flash mode)
CMD_GFS = b"\x21" # FLASH Get Status
CMD_SFS = b"\x26" # FLASH Set Status

Expand Down Expand Up @@ -283,7 +283,7 @@ def send_xmodem(self, stream, offset, size, retry=3):
if not data: # end of stream
print("send: at EOF")
return False
data = data.ljust(packet_size, b"\xFF")
data = data.ljust(packet_size, b"\xff")
pkt = (
struct.pack("<BBBI", ord(cmd), sequence, 0xFF - sequence, offset)
+ data
Expand Down
12 changes: 6 additions & 6 deletions ltchiptool/soc/ambz2/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def elf2bin(self, input: str, ota_idx: int) -> List[FirmwareBinary]:
nmap_ota1 = self.board.toolchain.nm(input)

# build the partition table
ptable = PartitionTable(user_data=b"\xFF" * 256)
ptable = PartitionTable(user_data=b"\xff" * 256)
for region, type in config.ptable.items():
offset, length, _ = self.board.region(region)
hash_key = config.keys.hash_keys[region]
Expand Down Expand Up @@ -205,11 +205,11 @@ def elf2bin(self, input: str, ota_idx: int) -> List[FirmwareBinary]:
with output.write() as f:
f.write(data)
with out_ptab.write() as f:
ptab = data[ptab_offset:ptab_end].rstrip(b"\xFF")
ptab = data[ptab_offset:ptab_end].rstrip(b"\xff")
ptab = pad_data(ptab, 0x20, 0xFF)
f.write(ptab)
with out_boot.write() as f:
boot = data[boot_offset:boot_end].rstrip(b"\xFF")
boot = data[boot_offset:boot_end].rstrip(b"\xff")
boot = pad_data(boot, 0x20, 0xFF)
f.write(boot)
with out_ota1.write() as f:
Expand All @@ -230,15 +230,15 @@ def detect_file_type(
return Detection.make("Realtek AmebaZ2 Flash Image", offset=0)

if (
data[0x40:0x44] != b"\xFF\xFF\xFF\xFF"
data[0x40:0x44] != b"\xff\xff\xff\xff"
and data[0x48] == ImageType.BOOT.value
):
return Detection.make("Realtek AmebaZ2 Bootloader", offset=0x4000)

if (
data[0xE0:0xE8].strip(b"\xFF")
data[0xE0:0xE8].strip(b"\xff")
and data[0xE8] == ImageType.FWHS_S.value
and data[0x1A0:0x1A8].strip(b"\xFF")
and data[0x1A0:0x1A8].strip(b"\xff")
and data[0x1A8] == SectionType.SRAM.value
):
return Detection.make("Realtek AmebaZ2 Firmware", offset=None)
Expand Down
2 changes: 1 addition & 1 deletion ltchiptool/soc/ambz2/util/models/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from .partitions import Bootloader, Firmware, PartitionTable
from .utils import FF_32

FLASH_CALIBRATION = b"\x99\x99\x96\x96\x3F\xCC\x66\xFC\xC0\x33\xCC\x03\xE5\xDC\x31\x62"
FLASH_CALIBRATION = b"\x99\x99\x96\x96\x3f\xcc\x66\xfc\xc0\x33\xcc\x03\xe5\xdc\x31\x62"


@dataclass
Expand Down
6 changes: 3 additions & 3 deletions ltchiptool/soc/ambz2/util/models/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

from datastruct import Adapter, Context

FF_48 = b"\xFF" * 48
FF_32 = b"\xFF" * 32
FF_16 = b"\xFF" * 16
FF_48 = b"\xff" * 48
FF_32 = b"\xff" * 32
FF_16 = b"\xff" * 16

T = TypeVar("T")

Expand Down
8 changes: 4 additions & 4 deletions ltchiptool/soc/bk72xx/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def to_address(offs: int) -> int:
def check_app_code_crc(data: bytes) -> Union[bool, None]:
# b #0x40
# ldr pc, [pc, #0x14]
if data[0:8] == b"\x2F\x07\xB5\x94\x35\xFF\x2A\x9B":
if data[0:8] == b"\x2f\x07\xb5\x94\x35\xff\x2a\x9b":
crc = CRC16.CMS.calc(data[0:32])
crc_found = betoint(data[32:34])
if crc == crc_found:
Expand Down Expand Up @@ -183,13 +183,13 @@ def elf2bin(self, input: str, ota_idx: int) -> List[FirmwareBinary]:
with out_ug.write() as ug:
hdr = BytesIO()
ota_bin = ota_data.getvalue()
hdr.write(b"\x55\xAA\x55\xAA")
hdr.write(b"\x55\xaa\x55\xaa")
hdr.write(pad_data(version.encode(), 12, 0x00))
hdr.write(inttobe32(len(ota_bin)))
hdr.write(inttobe32(sum(ota_bin)))
ug.write(hdr.getvalue())
ug.write(inttobe32(sum(hdr.getvalue())))
ug.write(b"\xAA\x55\xAA\x55")
ug.write(b"\xaa\x55\xaa\x55")
ug.write(ota_bin)

# close all files
Expand Down Expand Up @@ -218,7 +218,7 @@ def detect_file_type(
return Detection.make_unsupported("Beken Encrypted App")

# raw firmware binary
if data[0:8] == b"\x0E\x00\x00\xEA\x14\xF0\x9F\xE5":
if data[0:8] == b"\x0e\x00\x00\xea\x14\xf0\x9f\xe5":
return Detection.make_unsupported("Raw ARM Binary")

# RBL file for OTA - 'download' partition
Expand Down
4 changes: 2 additions & 2 deletions ltchiptool/soc/bk72xx/util/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __init__(self, coeffs: Union[bytes, str] = None) -> None:
def crc(self, data: ByteSource, type: DataType = None) -> DataGenerator:
for block in geniter(data, 32):
if len(block) < 32:
block += b"\xFF" * (32 - len(block))
block += b"\xff" * (32 - len(block))
crc = CRC16.CMS.calc(block)
block += inttobe16(crc)
if type:
Expand All @@ -54,7 +54,7 @@ def crc(self, data: ByteSource, type: DataType = None) -> DataGenerator:

def uncrc(self, data: ByteSource, check: bool = True) -> ByteGenerator:
for block in geniter(data, 34):
if check and block != b"\xFF" * 34:
if check and block != b"\xff" * 34:
crc = CRC16.CMS.calc(block[0:32])
crc_found = betoint(block[32:34])
if crc != crc_found:
Expand Down
4 changes: 4 additions & 0 deletions ltchiptool/soc/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ def get(cls, family: Family) -> "SocInterface":
if family.is_child_of("realtek-ambz2"):
from .ambz2 import AmebaZ2Main
return AmebaZ2Main(family)
if family.is_child_of("lightning-ln882h"):
from .ln882h import LN882hMain
return LN882hMain(family)
# fmt: on
raise NotImplementedError(f"Unsupported family - {family.name}")

Expand All @@ -38,6 +41,7 @@ def get_family_names(cls) -> List[str]:
"beken-72xx",
"realtek-ambz",
"realtek-ambz2",
"lightning-ln882h",
]

#########################
Expand Down
7 changes: 7 additions & 0 deletions ltchiptool/soc/ln882h/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) Etienne Le Cousin 2025-01-02.

from .main import LN882hMain

__all__ = [
"LN882hMain",
]
118 changes: 118 additions & 0 deletions ltchiptool/soc/ln882h/binary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Copyright (c) Etienne Le Cousin 2025-01-02.

from abc import ABC
from logging import warning
from os import stat
from os.path import dirname, isfile
from shutil import copyfile
from typing import List

from ltchiptool import SocInterface
from ltchiptool.util.fileio import chext, chname
from ltchiptool.util.fwbinary import FirmwareBinary

from .util import OTATOOL, MakeImageTool
from .util.models import PartDescInfo, part_type_str2num


class LN882hBinary(SocInterface, ABC):
def elf2bin(self, input: str, ota_idx: int) -> List[FirmwareBinary]:
toolchain = self.board.toolchain
flash_layout = self.board["flash"]

# find bootloader image
input_boot = chname(input, "boot.bin")
if not isfile(input_boot):
raise FileNotFoundError("Bootloader image not found")

# build output names
output = FirmwareBinary(
location=input,
name="firmware",
offset=0,
title="Flash Image",
description="Complete image with boot for flashing at offset 0",
public=True,
)
out_boot = FirmwareBinary(
location=input,
name="boot",
offset=self.board.region("boot")[0],
title="Bootloader Image",
)
out_ptab = FirmwareBinary(
location=input,
name="part_tab",
offset=self.board.region("part_tab")[0],
title="Partition Table",
)
out_app = FirmwareBinary(
location=input,
name="app",
offset=self.board.region("app")[0],
title="Application Image",
description="Firmware partition image for direct flashing",
public=True,
)
out_ota = FirmwareBinary(
location=input,
name="ota",
offset=self.board.region("ota")[0],
title="OTA Image",
description="Compressed App image for OTA flashing",
public=True,
)
# print graph element
output.graph(1)

input_bin = chext(input, "bin")
# objcopy ELF -> raw BIN
toolchain.objcopy(input, input_bin)

# Make Image Tool
# fmt: off
mkimage = MakeImageTool()
mkimage.boot_filepath = input_boot
mkimage.app_filepath = input_bin
mkimage.flashimage_filepath = output.path
mkimage.ver_str = "1.0"
mkimage.swd_crp = 0
mkimage.readPartCfg = lambda : True
# fmt: off

# find all partitions
for name, layout in flash_layout.items():
(offset, _, length) = layout.partition("+")
part_info = PartDescInfo(
parttype = part_type_str2num(name.upper()),
startaddr = int(offset, 16),
partsize = int(length, 16)
)
mkimage._MakeImageTool__part_desc_info_list.append(part_info)

if not mkimage.doAllWork():
raise RuntimeError("MakeImageTool: Fail to generate image")

# write all parts to files
with out_boot.write() as f:
f.write(mkimage._MakeImageTool__partbuf_bootram)
with out_ptab.write() as f:
f.write(mkimage._MakeImageTool__partbuf_parttab)
with out_app.write() as f:
f.write(mkimage._MakeImageTool__partbuf_app)

# Make ota image
ota_tool = OTATOOL()
ota_tool.input_filepath = output.path
ota_tool.output_dir = dirname(input)
if not ota_tool.doAllWork():
raise RuntimeError("MakeImageTool: Fail to generate OTA image")

copyfile(ota_tool.output_filepath, out_ota.path)
_, ota_size, _ = self.board.region("ota")
if stat(out_ota.path).st_size > ota_size:
warning(
f"OTA size too large: {out_ota.filename} > {ota_size} (0x{ota_size:X})"
)

return output.group()
Loading