diff --git a/gp2040ce_bintools/builder.py b/gp2040ce_bintools/builder.py index 07824eb..b1693cd 100644 --- a/gp2040ce_bintools/builder.py +++ b/gp2040ce_bintools/builder.py @@ -6,7 +6,6 @@ SPDX-License-Identifier: GPL-3.0-or-later import argparse import copy import logging -import struct from typing import Optional from google.protobuf.message import Message @@ -14,19 +13,14 @@ from google.protobuf.message import Message from gp2040ce_bintools import core_parser from gp2040ce_bintools.rp2040 import get_bootsel_endpoints, read, write from gp2040ce_bintools.storage import (BOARD_CONFIG_BINARY_LOCATION, STORAGE_SIZE, USER_CONFIG_BINARY_LOCATION, - USER_CONFIG_BOOTSEL_ADDRESS, get_config_from_json, pad_config_to_storage_size, - serialize_config_with_footer) + USER_CONFIG_BOOTSEL_ADDRESS, convert_binary_to_uf2, get_config_from_json, + pad_config_to_storage_size, serialize_config_with_footer) logger = logging.getLogger(__name__) GP2040CE_START_ADDRESS = 0x10000000 GP2040CE_SIZE = 2 * 1024 * 1024 -UF2_FAMILY_ID = 0xE48BFF56 -UF2_MAGIC_FIRST = 0x0A324655 -UF2_MAGIC_SECOND = 0x9E5D5157 -UF2_MAGIC_FINAL = 0x0AB16F30 - ################# # LIBRARY ITEMS # @@ -115,38 +109,6 @@ def concatenate_firmware_and_storage_files(firmware_filename: str, write(endpoint_out, endpoint_in, GP2040CE_START_ADDRESS, bytes(new_binary)) -def convert_binary_to_uf2(binary: bytearray) -> bytearray: - """Convert a GP2040-CE binary payload to Microsoft's UF2 format. - - https://github.com/microsoft/uf2/tree/master#overview - - Args: - binary: bytearray content to convert to a UF2 payload - Returns: - the content in UF2 format - """ - size = len(binary) - blocks = (len(binary) // 256) + 1 if len(binary) % 256 else len(binary) // 256 - uf2 = bytearray() - - index = 0 - while index < size: - pad_count = 476 - len(binary[index:index+256]) - uf2 += struct.pack(' tuple[bytes, object, object]: """Read the firmware + config sections from a USB device. diff --git a/gp2040ce_bintools/storage.py b/gp2040ce_bintools/storage.py index 080c0bb..cf96cb0 100644 --- a/gp2040ce_bintools/storage.py +++ b/gp2040ce_bintools/storage.py @@ -6,6 +6,7 @@ SPDX-License-Identifier: GPL-3.0-or-later import argparse import binascii import logging +import struct from google.protobuf.json_format import MessageToJson from google.protobuf.json_format import Parse as JsonParse @@ -25,6 +26,11 @@ USER_CONFIG_BOOTSEL_ADDRESS = 0x10000000 + USER_CONFIG_BINARY_LOCATION FOOTER_SIZE = 12 FOOTER_MAGIC = b'\x65\xe3\xf1\xd2' +UF2_FAMILY_ID = 0xE48BFF56 +UF2_MAGIC_FIRST = 0x0A324655 +UF2_MAGIC_SECOND = 0x9E5D5157 +UF2_MAGIC_FINAL = 0x0AB16F30 + ################# # LIBRARY ITEMS # @@ -47,6 +53,39 @@ class ConfigMagicError(ConfigReadError): """Exception raised when the config section does not have the magic value in its footer.""" +def convert_binary_to_uf2(binary: bytearray, start: int = 0) -> bytearray: + """Convert a GP2040-CE binary payload to Microsoft's UF2 format. + + https://github.com/microsoft/uf2/tree/master#overview + + Args: + binary: bytearray content to convert to a UF2 payload + start: position offset to start at rather than flash start (for creating e.g. user config UF2s) + Returns: + the content in UF2 format + """ + size = len(binary) + blocks = (len(binary) // 256) + 1 if len(binary) % 256 else len(binary) // 256 + uf2 = bytearray() + + index = 0 + while index < size: + pad_count = 476 - len(binary[index:index+256]) + uf2 += struct.pack(' Message: """Read the config from a GP2040-CE storage section. diff --git a/tests/test_builder.py b/tests/test_builder.py index 8857128..d847d28 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -12,10 +12,9 @@ from decorator import decorator from gp2040ce_bintools import get_config_pb2 from gp2040ce_bintools.builder import (FirmwareLengthError, combine_firmware_and_config, - concatenate_firmware_and_storage_files, convert_binary_to_uf2, - get_gp2040ce_from_usb, pad_binary_up_to_board_config, - pad_binary_up_to_user_config, replace_config_in_binary, - write_new_config_to_filename, write_new_config_to_usb) + concatenate_firmware_and_storage_files, get_gp2040ce_from_usb, + pad_binary_up_to_board_config, pad_binary_up_to_user_config, + replace_config_in_binary, write_new_config_to_filename, write_new_config_to_usb) from gp2040ce_bintools.storage import (get_board_storage_section, get_config, get_config_footer, get_user_storage_section, serialize_config_with_footer) @@ -76,15 +75,6 @@ def test_concatenate_both_configs_to_file(tmp_path): assert footer_size == 3309 -def test_convert_binary_to_uf2(whole_board_with_board_config_dump): - """Do some sanity checks in the attempt to convert a binary to a UF2.""" - uf2 = convert_binary_to_uf2(whole_board_with_board_config_dump) - assert len(uf2) == 4194304 # binary is 8192 256 byte chunks, UF2 is 512 b per chunk - assert uf2[0:4] == b'\x55\x46\x32\x0a' == b'UF2\n' # proper magic - assert uf2[8:12] == bytearray(b'\x00\x20\x00\x00') # family ID set - assert uf2[524:528] == bytearray(b'\x00\x01\x00\x10') # address to write the second chunk - - @with_pb2s def test_concatenate_user_json_to_file(tmp_path): """Test that we write a file with firmware + JSON user config as expected.""" diff --git a/tests/test_storage.py b/tests/test_storage.py index 5587809..cb12e75 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -130,6 +130,24 @@ def test_config_from_whole_board_parses(whole_board_dump): assert config.hotkeyOptions.hotkey02.dpadMask == 1 +def test_convert_binary_to_uf2(whole_board_with_board_config_dump): + """Do some sanity checks in the attempt to convert a binary to a UF2.""" + uf2 = storage.convert_binary_to_uf2(whole_board_with_board_config_dump) + assert len(uf2) == 4194304 # binary is 8192 256 byte chunks, UF2 is 512 b per chunk + assert uf2[0:4] == b'\x55\x46\x32\x0a' == b'UF2\n' # proper magic + assert uf2[8:12] == bytearray(b'\x00\x20\x00\x00') # family ID set + assert uf2[524:528] == bytearray(b'\x00\x01\x00\x10') # address to write the second chunk + + +def test_convert_binary_to_uf2_with_offsets(whole_board_with_board_config_dump): + """Do some sanity checks in the attempt to convert a binary to a UF2.""" + uf2 = storage.convert_binary_to_uf2(whole_board_with_board_config_dump, start=storage.USER_CONFIG_BINARY_LOCATION) + assert len(uf2) == 4194304 # binary is 8192 256 byte chunks, UF2 is 512 b per chunk + assert uf2[0:4] == b'\x55\x46\x32\x0a' == b'UF2\n' # proper magic + assert uf2[8:12] == bytearray(b'\x00\x20\x00\x00') # family ID set + assert uf2[524:528] == bytearray(b'\x00\xc1\x1f\x10') # address to write the second chunk + + @with_pb2s def test_serialize_config_with_footer(storage_dump, config_binary): """Test that reserializing a read in config matches the original.