diff --git a/source/daplink/drag-n-drop/file_stream.c b/source/daplink/drag-n-drop/file_stream.c index 284ccc9f8..1fa209c01 100644 --- a/source/daplink/drag-n-drop/file_stream.c +++ b/source/daplink/drag-n-drop/file_stream.c @@ -24,6 +24,7 @@ #include "file_stream.h" #include "util.h" #include "intelhex.h" +#include "uf2.h" #include "flash_decoder.h" #include "error.h" #include "cmsis_os2.h" @@ -75,9 +76,15 @@ static error_t open_hex(void *state); static error_t write_hex(void *state, const uint8_t *data, uint32_t size); static error_t close_hex(void *state); +static bool detect_uf2(const uint8_t *data, uint32_t size); +static error_t open_uf2(void *state); +static error_t write_uf2(void *state, const uint8_t *data, uint32_t size); +static error_t close_uf2(void *state); + stream_t stream[] = { {detect_bin, open_bin, write_bin, close_bin}, // STREAM_TYPE_BIN {detect_hex, open_hex, write_hex, close_hex}, // STREAM_TYPE_HEX + {detect_uf2, open_uf2, write_uf2, close_uf2}, // STREAM_TYPE_UF2 }; COMPILER_ASSERT(ARRAY_SIZE(stream) == STREAM_TYPE_COUNT); // STREAM_TYPE_NONE must not be included in count @@ -119,6 +126,8 @@ stream_type_t stream_type_from_name(const vfs_filename_t filename) return STREAM_TYPE_BIN; } else if (0 == strncmp("HEX", &filename[8], 3)) { return STREAM_TYPE_HEX; + } else if (0 == strncmp("UF2", &filename[8], 3)) { + return STREAM_TYPE_UF2; } else { return STREAM_TYPE_NONE; } @@ -364,3 +373,50 @@ static error_t close_hex(void *state) status = flash_decoder_close(); return status; } + +/* UF2 file processing */ + +static bool detect_uf2(const uint8_t *data, uint32_t size) +{ + return 1 == validate_uf2block(data, size); +} + +static error_t open_uf2(void *state) +{ + error_t status; + status = flash_decoder_open(); + return status; +} + +static error_t write_uf2(void *state, const uint8_t *data, uint32_t size) +{ + error_t status; + uint32_t start_addr; + const flash_intf_t *flash_intf; + const UF2_Block *block; + + if (1 != validate_uf2block(data, size)) { + return ERROR_FD_UNSUPPORTED_UPDATE; + } + + block = (const UF2_Block *)data; + if (block->flags & UF2_FLAG_NOFLASH) { + return ERROR_SUCCESS; + } + + status = flash_decoder_get_flash(FLASH_DECODER_TYPE_TARGET, 0, false, &start_addr, &flash_intf); + if (ERROR_SUCCESS != status) { + return status; + } + + status = flash_decoder_write(start_addr + block->targetAddr, block->data, block->payloadSize); + + return status; +} + +static error_t close_uf2(void *state) +{ + error_t status; + status = flash_decoder_close(); + return status; +} diff --git a/source/daplink/drag-n-drop/file_stream.h b/source/daplink/drag-n-drop/file_stream.h index 50bf38e08..2e9cdca83 100644 --- a/source/daplink/drag-n-drop/file_stream.h +++ b/source/daplink/drag-n-drop/file_stream.h @@ -36,6 +36,7 @@ typedef enum { STREAM_TYPE_BIN = STREAM_TYPE_START, STREAM_TYPE_HEX, + STREAM_TYPE_UF2, // Add new stream types here diff --git a/source/daplink/drag-n-drop/uf2.h b/source/daplink/drag-n-drop/uf2.h new file mode 100644 index 000000000..04e772537 --- /dev/null +++ b/source/daplink/drag-n-drop/uf2.h @@ -0,0 +1,63 @@ +/** + +Microsoft UF2 + +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ +#ifndef UF2FORMAT_H +#define UF2FORMAT_H 1 + +#include +#include + +// All entries are little endian. + +#define UF2_MAGIC_START0 0x0A324655UL // "UF2\n" +#define UF2_MAGIC_START1 0x9E5D5157UL // Randomly selected +#define UF2_MAGIC_END 0x0AB16F30UL // Ditto + +// If set, the block is "comment" and should not be flashed to the device +#define UF2_FLAG_NOFLASH 0x00000001 + +typedef struct { + // 32 byte header + uint32_t magicStart0; + uint32_t magicStart1; + uint32_t flags; + uint32_t targetAddr; + uint32_t payloadSize; + uint32_t blockNo; + uint32_t numBlocks; + uint32_t reserved; + + // raw data; + uint8_t data[476]; + + // store magic also at the end to limit damage from partial block reads + uint32_t magicEnd; +} UF2_Block; + +#endif diff --git a/source/daplink/drag-n-drop/vfs_manager.c b/source/daplink/drag-n-drop/vfs_manager.c index d50ae01bc..647d0d374 100644 --- a/source/daplink/drag-n-drop/vfs_manager.c +++ b/source/daplink/drag-n-drop/vfs_manager.c @@ -435,7 +435,7 @@ static void file_change_handler(const vfs_filename_t filename, vfs_file_change_t } // Handler for file data arriving over USB. This function is responsible -// for detecting the start of a BIN/HEX file and performing programming +// for detecting the start of a BIN/HEX/UF2 file and performing programming static void file_data_handler(uint32_t sector, const uint8_t *buf, uint32_t num_of_sectors) { stream_type_t stream; diff --git a/source/daplink/validation.c b/source/daplink/validation.c index 0a3134f89..1c6bb4686 100644 --- a/source/daplink/validation.c +++ b/source/daplink/validation.c @@ -24,6 +24,7 @@ #include "target_config.h" #include "target_family.h" #include "target_board.h" +#include "drag-n-drop/uf2.h" static inline uint32_t test_range(const uint32_t test, const uint32_t min, const uint32_t max) { @@ -94,3 +95,24 @@ uint8_t validate_hexfile(const uint8_t *buf) return ((buf[0] == ':') && ((buf[8] == '0') || (buf[8] == '2') || (buf[8] == '3') || (buf[8] == '4') || (buf[8] == '5'))) ? 1 : 0; } } + +uint8_t validate_uf2block(const uint8_t *buf, uint32_t size) +{ + if (size != sizeof(UF2_Block)) { + return 0; + } + + const UF2_Block *block = (const UF2_Block *)buf; + + if (block->magicStart0 != UF2_MAGIC_START0 || + block->magicStart1 != UF2_MAGIC_START1 || + block->magicEnd != UF2_MAGIC_END) { + return 0; + } + + if (block->payloadSize > sizeof(block->data)) { + return 0; + } + + return 1; +} diff --git a/source/daplink/validation.h b/source/daplink/validation.h index 929d827fb..2c088a2ab 100644 --- a/source/daplink/validation.h +++ b/source/daplink/validation.h @@ -30,6 +30,7 @@ extern "C" { uint8_t validate_bin_nvic(const uint8_t *buf); uint8_t validate_hexfile(const uint8_t *buf); +uint8_t validate_uf2block(const uint8_t *buf, uint32_t size); /*! * @brief Baseline implementation of NVIC validator.