Skip to content

Commit

Permalink
✨ feat: 提交项目
Browse files Browse the repository at this point in the history
  • Loading branch information
kalicyh committed Sep 19, 2024
1 parent 4ff7642 commit 4f29863
Show file tree
Hide file tree
Showing 29 changed files with 3,734 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

/flipper/__pycache__
/flipper/assets/__pycache__
/flipper/utils/__pycache__
Binary file added flipper.ico
Binary file not shown.
Empty file added flipper/__init__.py
Empty file.
66 changes: 66 additions & 0 deletions flipper/app.py
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 added flipper/assets/__init__.py
Empty file.
128 changes: 128 additions & 0 deletions flipper/assets/copro.py
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()
191 changes: 191 additions & 0 deletions flipper/assets/coprobin.py
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()
Loading

0 comments on commit 4f29863

Please sign in to comment.