-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
29 changed files
with
3,734 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
/flipper/__pycache__ | ||
/flipper/assets/__pycache__ | ||
/flipper/utils/__pycache__ |
Binary file not shown.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import logging | ||
import argparse | ||
import sys | ||
import colorlog | ||
|
||
|
||
class App: | ||
def __init__(self, no_exit=False): | ||
# Argument Parser | ||
self.no_exit = no_exit | ||
self.parser = argparse.ArgumentParser() | ||
self.parser.add_argument("-d", "--debug", action="store_true", help="Debug") | ||
# Logging | ||
self.logger = colorlog.getLogger() | ||
# Application specific initialization | ||
self.init() | ||
|
||
def __call__(self, args=None): | ||
self.args, self.other_args = self.parser.parse_known_args(args=args) | ||
# configure log output | ||
self.log_level = logging.DEBUG if self.args.debug else logging.INFO | ||
self.logger.setLevel(self.log_level) | ||
if not self.logger.hasHandlers(): | ||
self.handler = colorlog.StreamHandler(sys.stdout) | ||
self.handler.setLevel(self.log_level) | ||
self.formatter = colorlog.ColoredFormatter( | ||
"%(log_color)s%(asctime)s [%(levelname)s] %(message)s", | ||
log_colors={ | ||
"DEBUG": "cyan", | ||
# "INFO": "white", | ||
"WARNING": "yellow", | ||
"ERROR": "red", | ||
"CRITICAL": "red,bg_white", | ||
}, | ||
) | ||
self.handler.setFormatter(self.formatter) | ||
self.logger.addHandler(self.handler) | ||
|
||
# execute requested function | ||
self.before() | ||
return_code = self.call() | ||
self.after() | ||
if isinstance(return_code, int): | ||
return self._exit(return_code) | ||
else: | ||
self.logger.error("Missing return code") | ||
return self._exit(255) | ||
|
||
def _exit(self, code): | ||
if self.no_exit: | ||
return code | ||
exit(code) | ||
|
||
def call(self): | ||
if "func" not in self.args: | ||
self.parser.error("Choose something to do") | ||
return self.args.func() | ||
|
||
def init(self): | ||
raise Exception("init() is not implemented") | ||
|
||
def before(self): | ||
pass | ||
|
||
def after(self): | ||
pass |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import json | ||
import logging | ||
import os | ||
import posixpath | ||
import tarfile | ||
from io import BytesIO | ||
|
||
from flipper.assets.coprobin import CoproBinary, get_stack_type | ||
from flipper.utils import file_sha256, timestamp | ||
|
||
CUBE_COPRO_PATH = "firmware" | ||
|
||
MANIFEST_TEMPLATE = { | ||
"manifest": {"version": 0, "timestamp": 0}, | ||
"copro": { | ||
"fus": {"version": {"major": 1, "minor": 2, "sub": 0}, "files": []}, | ||
"radio": { | ||
"version": {}, | ||
"files": [], | ||
}, | ||
}, | ||
} | ||
|
||
|
||
class Copro: | ||
COPRO_TAR_DIR = "core2_firmware" | ||
|
||
def __init__(self): | ||
self.version = None | ||
self.cube_dir = None | ||
self.mcu_copro = None | ||
self.logger = logging.getLogger(self.__class__.__name__) | ||
|
||
def loadCubeInfo(self, cube_dir, reference_cube_version): | ||
if not os.path.isdir(cube_dir): | ||
raise Exception(f'"{cube_dir}" doesn\'t exists') | ||
self.cube_dir = cube_dir | ||
self.mcu_copro = os.path.join(self.cube_dir, CUBE_COPRO_PATH) | ||
if not os.path.isdir(self.mcu_copro): | ||
raise Exception(f'"{self.mcu_copro}" doesn\'t exists') | ||
try: | ||
cube_manifest_file = os.path.join(self.cube_dir, "VERSION") | ||
with open(cube_manifest_file, "r") as cube_manifest: | ||
cube_version = cube_manifest.read().strip() | ||
except IOError: | ||
raise Exception(f"Failed to read version from {cube_manifest_file}") | ||
|
||
if not cube_version.startswith("v"): | ||
raise Exception(f"Invalid cube version: {cube_version}") | ||
cube_version = cube_version[1:] | ||
|
||
if cube_version != reference_cube_version: | ||
raise Exception( | ||
f"Unsupported cube version: {cube_version}, expecting {reference_cube_version}" | ||
) | ||
self.version = cube_version | ||
|
||
def _getFileName(self, name): | ||
return posixpath.join(self.COPRO_TAR_DIR, name) | ||
|
||
def _addFileReadPermission(self, tarinfo): | ||
tarinfo.mode = 0o644 | ||
return tarinfo | ||
|
||
def addFile(self, array, filename, **kwargs): | ||
source_file = os.path.join(self.mcu_copro, filename) | ||
self.output_tar.add( | ||
source_file, | ||
arcname=self._getFileName(filename), | ||
filter=self._addFileReadPermission, | ||
) | ||
array.append({"name": filename, "sha256": file_sha256(source_file), **kwargs}) | ||
|
||
def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None): | ||
self.output_tar = tarfile.open(output_file, "w:gz", format=tarfile.USTAR_FORMAT) | ||
fw_directory = tarfile.TarInfo(self.COPRO_TAR_DIR) | ||
fw_directory.mode = 0o755 | ||
fw_directory.type = tarfile.DIRTYPE | ||
self.output_tar.addfile(fw_directory) | ||
|
||
stack_file = os.path.join(self.mcu_copro, stack_file_name) | ||
# Form Manifest | ||
manifest = dict(MANIFEST_TEMPLATE) | ||
manifest["manifest"]["timestamp"] = timestamp() | ||
copro_bin = CoproBinary(stack_file) | ||
self.logger.info(f"Bundling {copro_bin.img_sig.get_version()}") | ||
stack_type_code = get_stack_type(stack_type) | ||
manifest["copro"]["radio"]["version"].update( | ||
{ | ||
"type": stack_type_code, | ||
"major": copro_bin.img_sig.version_major, | ||
"minor": copro_bin.img_sig.version_minor, | ||
"sub": copro_bin.img_sig.version_sub, | ||
"branch": copro_bin.img_sig.version_branch, | ||
"release": copro_bin.img_sig.version_build, | ||
} | ||
) | ||
if not stack_addr: | ||
stack_addr = copro_bin.get_flash_load_addr() | ||
self.logger.info(f"Using guessed flash address 0x{stack_addr:x}") | ||
|
||
# Old FUS Update | ||
self.addFile( | ||
manifest["copro"]["fus"]["files"], | ||
"stm32wb5x_FUS_fw_for_fus_0_5_3.bin", | ||
condition="==0.5.3", | ||
address="0x080EC000", | ||
) | ||
# New FUS Update | ||
self.addFile( | ||
manifest["copro"]["fus"]["files"], | ||
"stm32wb5x_FUS_fw.bin", | ||
condition=">0.5.3", | ||
address="0x080EC000", | ||
) | ||
# BLE Full Stack | ||
self.addFile( | ||
manifest["copro"]["radio"]["files"], | ||
stack_file_name, | ||
address=f"0x{stack_addr:X}", | ||
) | ||
|
||
# Save manifest | ||
manifest_data = json.dumps(manifest, indent=4).encode("utf-8") | ||
info = tarfile.TarInfo(self._getFileName("Manifest.json")) | ||
info.size = len(manifest_data) | ||
self.output_tar.addfile(info, BytesIO(manifest_data)) | ||
self.output_tar.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
import struct | ||
import math | ||
import os | ||
import os.path | ||
import sys | ||
|
||
|
||
# From lib/stm32wb_copro/wpan/interface/patterns/ble_thread/shci/shci.h | ||
__STACK_TYPE_CODES = { | ||
"BLE_FULL": 0x01, | ||
"BLE_HCI": 0x02, | ||
"BLE_LIGHT": 0x03, | ||
"BLE_BEACON": 0x04, | ||
"BLE_BASIC": 0x05, | ||
"BLE_FULL_EXT_ADV": 0x06, | ||
"BLE_HCI_EXT_ADV": 0x07, | ||
"THREAD_FTD": 0x10, | ||
"THREAD_MTD": 0x11, | ||
"ZIGBEE_FFD": 0x30, | ||
"ZIGBEE_RFD": 0x31, | ||
"MAC": 0x40, | ||
"BLE_THREAD_FTD_STATIC": 0x50, | ||
"BLE_THREAD_FTD_DYAMIC": 0x51, | ||
"802154_LLD_TESTS": 0x60, | ||
"802154_PHY_VALID": 0x61, | ||
"BLE_PHY_VALID": 0x62, | ||
"BLE_LLD_TESTS": 0x63, | ||
"BLE_RLV": 0x64, | ||
"802154_RLV": 0x65, | ||
"BLE_ZIGBEE_FFD_STATIC": 0x70, | ||
"BLE_ZIGBEE_RFD_STATIC": 0x71, | ||
"BLE_ZIGBEE_FFD_DYNAMIC": 0x78, | ||
"BLE_ZIGBEE_RFD_DYNAMIC": 0x79, | ||
"RLV": 0x80, | ||
"BLE_MAC_STATIC": 0x90, | ||
} | ||
|
||
|
||
class CoproException(ValueError): | ||
pass | ||
|
||
|
||
# Formats based on AN5185 | ||
class CoproFooterBase: | ||
SIG_BIN_SIZE = 5 * 4 | ||
_SIG_BIN_COMMON_SIZE = 2 * 4 | ||
|
||
def get_version(self): | ||
return ( | ||
f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, " | ||
f"branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" | ||
) | ||
|
||
def get_details(self): | ||
raise CoproException("Not implemented") | ||
|
||
def __init__(self, raw: bytes): | ||
if len(raw) != self.SIG_BIN_SIZE: | ||
raise CoproException("Invalid footer size") | ||
sig_common_part = raw[-self._SIG_BIN_COMMON_SIZE :] | ||
parts = struct.unpack("BBBBI", sig_common_part) | ||
self.version_major = parts[3] | ||
self.version_minor = parts[2] | ||
self.version_sub = parts[1] | ||
# AN5185 mismatch: swapping byte halves | ||
self.version_build = parts[0] & 0x0F | ||
self.version_branch = (parts[0] & 0xF0) >> 4 | ||
self.magic = parts[4] | ||
|
||
|
||
class CoproFusFooter(CoproFooterBase): | ||
FUS_MAGIC_IMG_STACK = 0x23372991 | ||
FUS_MAGIC_IMG_FUS = 0x32279221 | ||
FUS_MAGIC_IMG_OTHER = 0x42769811 | ||
|
||
FUS_BASE = 0x80F4000 | ||
FLASH_PAGE_SIZE = 4 * 1024 | ||
|
||
def __init__(self, raw: bytes): | ||
super().__init__(raw) | ||
if self.magic not in ( | ||
self.FUS_MAGIC_IMG_OTHER, | ||
self.FUS_MAGIC_IMG_FUS, | ||
self.FUS_MAGIC_IMG_STACK, | ||
): | ||
raise CoproException(f"Invalid FUS img magic {self.magic:x}") | ||
own_data = raw[: -self._SIG_BIN_COMMON_SIZE] | ||
parts = struct.unpack("IIBBBB", own_data) | ||
self.info1 = parts[0] | ||
self.info2 = parts[1] | ||
self.sram2b_1ks = parts[5] | ||
self.sram2a_1ks = parts[4] | ||
self.flash_4ks = parts[2] | ||
|
||
def get_details(self): | ||
return f"SRAM2b={self.sram2b_1ks}k SRAM2a={self.sram2a_1ks}k flash={self.flash_4ks}p" | ||
|
||
def is_stack(self): | ||
return self.magic == self.FUS_MAGIC_IMG_STACK | ||
|
||
def get_flash_pages(self, fullsize): | ||
return math.ceil(fullsize / self.FLASH_PAGE_SIZE) | ||
|
||
def get_flash_base(self, fullsize): | ||
if not self.is_stack(): | ||
raise CoproException("Not a stack image") | ||
return self.FUS_BASE - self.get_flash_pages(fullsize) * self.FLASH_PAGE_SIZE | ||
|
||
|
||
class CoproSigFooter(CoproFooterBase): | ||
SIG_MAGIC_ST = 0xD3A12C5E | ||
SIG_MAGIC_CUSTOMER = 0xE2B51D4A | ||
|
||
def __init__(self, raw: bytes): | ||
super().__init__(raw) | ||
if self.magic not in (self.SIG_MAGIC_ST, self.SIG_MAGIC_CUSTOMER): | ||
raise CoproException(f"Invalid FUS img magic {self.magic:x}") | ||
own_data = raw[: -self._SIG_BIN_COMMON_SIZE] | ||
parts = struct.unpack("IIBBH", own_data) | ||
self.reserved_1 = parts[0] | ||
self.reserved_2 = parts[1] | ||
self.size = parts[2] | ||
self.source = parts[3] | ||
self.reserved_34 = parts[4] | ||
|
||
def get_details(self): | ||
return f"Signature Src {self.source:x} size {self.size:x}" | ||
|
||
|
||
class CoproBinary: | ||
def __init__(self, binary_path): | ||
self.binary_path = binary_path | ||
self.img_sig_footer = None | ||
self.img_sig = None | ||
self.binary_size = -1 | ||
self._load() | ||
|
||
def _load(self): | ||
with open(self.binary_path, "rb") as fin: | ||
whole_file = fin.read() | ||
self.binary_size = len(whole_file) | ||
|
||
img_sig_footer_bin = whole_file[-CoproFooterBase.SIG_BIN_SIZE :] | ||
self.img_sig_footer = CoproSigFooter(img_sig_footer_bin) | ||
img_sig_size = self.img_sig_footer.size + CoproSigFooter.SIG_BIN_SIZE | ||
img_sig_bin = whole_file[ | ||
-(img_sig_size + CoproFusFooter.SIG_BIN_SIZE) : -img_sig_size | ||
] | ||
self.img_sig = CoproFusFooter(img_sig_bin) | ||
|
||
def is_valid(self): | ||
return self.img_sig_footer is not None and self.img_sig is not None | ||
|
||
def is_stack(self): | ||
return self.img_sig and self.img_sig.is_stack() | ||
|
||
def get_flash_load_addr(self): | ||
if not self.is_stack(): | ||
raise CoproException("Not a stack image") | ||
return self.img_sig.get_flash_base(self.binary_size) | ||
|
||
|
||
def get_stack_type(typestr: str): | ||
stack_code = __STACK_TYPE_CODES.get(typestr.upper(), None) | ||
if stack_code is None: | ||
raise CoproException(f"Unknown stack type {typestr}. See shci.h") | ||
return stack_code | ||
|
||
|
||
def _load_bin(binary_path: str): | ||
print(binary_path) | ||
copro_bin = CoproBinary(binary_path) | ||
print(copro_bin.img_sig.get_version()) | ||
if copro_bin.img_sig.is_stack(): | ||
print(f"\t>> FLASH AT {copro_bin.get_flash_load_addr():X}\n") | ||
|
||
|
||
def main(): | ||
coprodir = ( | ||
sys.argv[1] | ||
if len(sys.argv) > 1 | ||
else "../../../lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x" | ||
) | ||
for fn in os.listdir(coprodir): | ||
if not fn.endswith(".bin"): | ||
continue | ||
_load_bin(os.path.join(coprodir, fn)) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Oops, something went wrong.