1
1
import sys
2
+ import typing
2
3
import asyncio
3
4
import logging
4
5
import argparse
17
18
LOGGER = logging .getLogger (__name__ )
18
19
19
20
21
+ def compute_crc16 (data : bytes ) -> int :
22
+ poly = 0x1021
23
+ crc = 0x0000
24
+
25
+ for byte in data :
26
+ for _ in range (8 ):
27
+ msb = 1 if (crc & 0x8000 ) else 0
28
+
29
+ crc <<= 1
30
+ crc &= 0xFFFF
31
+
32
+ if byte & 0x80 :
33
+ crc |= 0x0001
34
+
35
+ if msb :
36
+ crc ^= poly
37
+
38
+ byte <<= 1
39
+
40
+ return crc
41
+
42
+
43
+ def get_firmware_crcs (firmware : bytes ) -> typing .Tuple [int , int ]:
44
+ # There is room for *two* CRCs in the firmware file: the expected and the computed
45
+ firmware_wihout_crcs = (
46
+ firmware [: c .ubl .IMAGE_CRC_OFFSET ]
47
+ + firmware [c .ubl .IMAGE_CRC_OFFSET + 4 :]
48
+ + b"\x00 \x00 "
49
+ )
50
+
51
+ # We only use the first one. The second one is written by the bootloader into flash.
52
+ real_crc = int .from_bytes (
53
+ firmware [c .ubl .IMAGE_CRC_OFFSET : c .ubl .IMAGE_CRC_OFFSET + 2 ], "little"
54
+ )
55
+
56
+ return real_crc , compute_crc16 (firmware_wihout_crcs )
57
+
58
+
20
59
async def write_firmware (firmware : bytes , radio_path : str ):
21
60
if len (firmware ) != c .ubl .IMAGE_SIZE :
22
61
raise ValueError (
23
62
f"Firmware is the wrong size."
24
63
f" Expected { c .ubl .IMAGE_SIZE } , got { len (firmware )} "
25
64
)
26
65
66
+ expected_crc , computed_crc = get_firmware_crcs (firmware )
67
+
68
+ if expected_crc != computed_crc :
69
+ raise ValueError (
70
+ f"Firmware CRC is incorrect. "
71
+ f"Expected 0x{ expected_crc :04X} , got 0x{ computed_crc :04X} "
72
+ )
73
+
27
74
znp = ZNP (CONFIG_SCHEMA ({"device" : {"path" : radio_path }}))
28
75
29
76
# The bootloader handshake must be the very first command
@@ -63,7 +110,6 @@ async def write_firmware(firmware: bytes, radio_path: str):
63
110
assert write_rsp .Status == c .ubl .BootloaderStatus .SUCCESS
64
111
65
112
# Now we have to read it all back
66
- # TODO: figure out how the CRC is computed!
67
113
for offset in range (0 , c .ubl .IMAGE_SIZE , buffer_size ):
68
114
address = offset // c .ubl .FLASH_WORD_SIZE
69
115
LOGGER .info (
@@ -79,7 +125,7 @@ async def write_firmware(firmware: bytes, radio_path: str):
79
125
assert read_rsp .FlashWordAddr == address
80
126
assert read_rsp .Data == firmware [offset : offset + buffer_size ]
81
127
82
- # This seems to cause the firmware to compute and verify the CRC
128
+ # This seems to cause the bootloader to compute and verify the CRC
83
129
enable_rsp = await znp .request_callback_rsp (
84
130
request = c .UBL .EnableReq .Req (), callback = c .UBL .EnableRsp .Callback (partial = True ),
85
131
)
0 commit comments