Skip to content

Commit 9537756

Browse files
Update vfs_manager & file_stream for out-of-order Universal Hex blocks.
When Universalh Hex was created two formats where specified, the segment format, which is currently used due to faster parsing, and the block format. The block format was design so that each 512-bytes of hex characters contain enough info to be self contained, however it was never tested in a systen that sent blocks out-of-order. The original vfs_manager expects all file blocks to arrive sequentially in order. This update adds a check of "self contained blocks" and a sticky flag that process these blocks whatever order they arrive and ignores all other non-self-contained vfs sectors received.
1 parent 83849c5 commit 9537756

File tree

6 files changed

+150
-21
lines changed

6 files changed

+150
-21
lines changed

source/daplink/drag-n-drop/file_stream.c

+98-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@
3030
#include "compiler.h"
3131
#include "validation.h"
3232

33+
// Set to 1 to enable debugging
34+
#define DEBUG_FILE_STREAM 0
35+
36+
#if DEBUG_FILE_STREAM
37+
#include "daplink_debug.h"
38+
#define stream_printf debug_msg
39+
#else
40+
#define stream_printf(...)
41+
#endif
42+
3343
typedef enum {
3444
STREAM_STATE_CLOSED,
3545
STREAM_STATE_OPEN,
@@ -75,9 +85,15 @@ static error_t open_hex(void *state);
7585
static error_t write_hex(void *state, const uint8_t *data, uint32_t size);
7686
static error_t close_hex(void *state);
7787

78-
stream_t stream[] = {
88+
static bool detect_uhex_blocks(const uint8_t *data, uint32_t size);
89+
static error_t open_uhex_blocks(void *state);
90+
static error_t write_uhex_blocks(void *state, const uint8_t *data, uint32_t size);
91+
static error_t close_uhex_blocks(void *state);
92+
93+
static stream_t stream[] = {
7994
{detect_bin, open_bin, write_bin, close_bin}, // STREAM_TYPE_BIN
8095
{detect_hex, open_hex, write_hex, close_hex}, // STREAM_TYPE_HEX
96+
{detect_uhex_blocks, open_uhex_blocks, write_uhex_blocks, close_uhex_blocks}, // STREAM_TYPE_UHEX_BLOCKS
8197
};
8298
COMPILER_ASSERT(ARRAY_SIZE(stream) == STREAM_TYPE_COUNT);
8399
// STREAM_TYPE_NONE must not be included in count
@@ -104,6 +120,7 @@ stream_type_t stream_start_identify(const uint8_t *data, uint32_t size)
104120

105121
for (i = STREAM_TYPE_START; i < STREAM_TYPE_COUNT; i++) {
106122
if (stream[i].detect(data, size)) {
123+
stream_printf("file_stream start_identify stream=%i\r\n", i);
107124
return i;
108125
}
109126
}
@@ -118,12 +135,48 @@ stream_type_t stream_type_from_name(const vfs_filename_t filename)
118135
if (0 == strncmp("BIN", &filename[8], 3)) {
119136
return STREAM_TYPE_BIN;
120137
} else if (0 == strncmp("HEX", &filename[8], 3)) {
121-
return STREAM_TYPE_HEX;
138+
return STREAM_TYPE_HEX_OR_UHEX;
122139
} else {
123140
return STREAM_TYPE_NONE;
124141
}
125142
}
126143

144+
bool stream_compatible(stream_type_t type_left, stream_type_t type_right)
145+
{
146+
if (type_left == type_right) {
147+
return true;
148+
}
149+
150+
if ((type_left == STREAM_TYPE_HEX_OR_UHEX &&
151+
(type_right == STREAM_TYPE_HEX || type_right == STREAM_TYPE_UHEX_BLOCKS)) ||
152+
(type_right == STREAM_TYPE_HEX_OR_UHEX &&
153+
(type_left == STREAM_TYPE_HEX || type_left == STREAM_TYPE_UHEX_BLOCKS))) {
154+
return true;
155+
}
156+
157+
return false;
158+
}
159+
160+
bool stream_self_contained_block(stream_type_t type, const uint8_t *data, uint32_t size)
161+
{
162+
switch (type) {
163+
case STREAM_TYPE_BIN:
164+
return false;
165+
166+
case STREAM_TYPE_HEX:
167+
// A hex stream can also be a Universal Hex stream
168+
return validate_uhex_block(data, size) ? true : false;
169+
170+
case STREAM_TYPE_UHEX_BLOCKS:
171+
// The Universal Hex stream can be ordered (sectors) or unordered (blocks)
172+
return validate_uhex_block(data, size) ? true : false;
173+
174+
default:
175+
util_assert(0);
176+
return false;
177+
}
178+
}
179+
127180
error_t stream_open(stream_type_t stream_type)
128181
{
129182
error_t status;
@@ -147,6 +200,7 @@ error_t stream_open(stream_type_t stream_type)
147200
current_stream = &stream[stream_type];
148201
// Initialize the specified stream
149202
status = current_stream->open(&shared_state);
203+
stream_printf("file_stream stream_open(type=%d); open ret=%d\r\n", stream_type, status);
150204

151205
if (ERROR_SUCCESS != status) {
152206
state = STREAM_STATE_ERROR;
@@ -170,6 +224,7 @@ error_t stream_write(const uint8_t *data, uint32_t size)
170224
stream_thread_assert();
171225
// Write to stream
172226
status = current_stream->write(&shared_state, data, size);
227+
stream_printf("file_stream stream_write(size=%d); write ret=%d\r\n", size, status);
173228

174229
if (ERROR_SUCCESS_DONE == status) {
175230
state = STREAM_STATE_END;
@@ -198,6 +253,7 @@ error_t stream_close(void)
198253
stream_thread_assert();
199254
// Close stream
200255
status = current_stream->close(&shared_state);
256+
stream_printf("file_stream stream_close; close ret=%d\r\n", status);
201257
state = STREAM_STATE_CLOSED;
202258
return status;
203259
}
@@ -289,6 +345,13 @@ static error_t close_bin(void *state)
289345

290346
static bool detect_hex(const uint8_t *data, uint32_t size)
291347
{
348+
// Both Universal Hex formats will pass the normal hex file validation,
349+
// but a Universal Hex in block format needs to be processed with the
350+
// STREAM_TYPE_UHEX_BLOCKS stream.
351+
// A Universal Hex in segment format can be be processed as a normal hex.
352+
if (1 == validate_uhex_block(data, size)) {
353+
return false;
354+
}
292355
return 1 == validate_hexfile(data);
293356
}
294357

@@ -315,6 +378,7 @@ static error_t write_hex(void *state, const uint8_t *data, uint32_t size)
315378
while (1) {
316379
// try to decode a block of hex data into bin data
317380
parse_status = parse_hex_blob(data, size, &block_amt_parsed, hex_state->bin_buffer, sizeof(hex_state->bin_buffer), &bin_start_address, &bin_buf_written);
381+
stream_printf("file_stream write_hex; parse_hex_blob ret=%d, bin_buf_written=%d\r\n", parse_status, bin_buf_written);
318382

319383
// the entire block of hex was decoded. This is a simple state
320384
if (HEX_PARSE_OK == parse_status) {
@@ -364,3 +428,35 @@ static error_t close_hex(void *state)
364428
status = flash_decoder_close();
365429
return status;
366430
}
431+
432+
/* Universal Hex, block format, file processing */
433+
/* https://tech.microbit.org/software/spec-universal-hex/ */
434+
/* The Universal Hex segment format is processed by the Intel Hex parser. */
435+
/* This stream is for the Universal Hex block format only. */
436+
437+
static bool detect_uhex_blocks(const uint8_t *data, uint32_t size)
438+
{
439+
return 1 == validate_uhex_block(data, size);
440+
}
441+
442+
static inline error_t open_uhex_blocks(void *state)
443+
{
444+
return open_hex(state);
445+
}
446+
447+
static inline error_t write_uhex_blocks(void *state, const uint8_t *data, uint32_t size)
448+
{
449+
error_t status = write_hex(state, data, size);
450+
451+
// The block containing the EoF record could arrive at any point
452+
if (ERROR_SUCCESS_DONE == status || ERROR_SUCCESS == status) {
453+
status = ERROR_SUCCESS_DONE_OR_CONTINUE;
454+
}
455+
456+
return status;
457+
}
458+
459+
static inline error_t close_uhex_blocks(void *state)
460+
{
461+
return close_hex(state);
462+
}

source/daplink/drag-n-drop/file_stream.h

+10
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,15 @@ typedef enum {
3636

3737
STREAM_TYPE_BIN = STREAM_TYPE_START,
3838
STREAM_TYPE_HEX,
39+
STREAM_TYPE_UHEX_BLOCKS,
3940

4041
// Add new stream types here
4142

4243
STREAM_TYPE_COUNT,
4344

45+
// Needed as both formats share the same .hex extension
46+
STREAM_TYPE_HEX_OR_UHEX,
47+
4448
STREAM_TYPE_NONE
4549
} stream_type_t;
4650

@@ -50,6 +54,12 @@ stream_type_t stream_start_identify(const uint8_t *data, uint32_t size);
5054
// Stateless function to identify a filestream by its name
5155
stream_type_t stream_type_from_name(const vfs_filename_t filename);
5256

57+
// Stateless function to identify if two streams are compatible
58+
bool stream_compatible(stream_type_t type_left, stream_type_t type_right);
59+
60+
// Stateless function to identify if a stream can take blocks out of order
61+
bool stream_self_contained_block(stream_type_t type, const uint8_t *data, uint32_t size);
62+
5363
error_t stream_open(stream_type_t stream_type);
5464

5565
error_t stream_write(const uint8_t *data, uint32_t size);

source/daplink/drag-n-drop/flash_decoder.c

+3
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@ error_t flash_decoder_close(void)
374374
if ((DECODER_STATE_DONE != prev_state) &&
375375
(flash_type != FLASH_DECODER_TYPE_TARGET) &&
376376
(status == ERROR_SUCCESS)) {
377+
flash_decoder_printf(" error: update incomplete\r\n");
378+
flash_decoder_printf(" prev_state=%d; flash_type=%d; status=%d\r\n",
379+
prev_state, flash_type, status);
377380
status = ERROR_IAP_UPDT_INCOMPLETE;
378381
}
379382

source/daplink/drag-n-drop/vfs_manager.c

+29-19
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ typedef struct {
8585
bool stream_started; // Stream processing started. This only gets reset remount
8686
bool stream_finished; // Stream processing is done. This only gets reset remount
8787
bool stream_optional_finish; // True if the stream processing can be considered done
88+
bool stream_ooo_blocks; // True if the stream data blocks can be processed out of order
8889
bool file_info_optional_finish; // True if the file transfer can be considered done
8990
bool transfer_timeout; // Set if the transfer was finished because of a timeout. This only gets reset remount
9091
stream_type_t stream; // Current stream or STREAM_TYPE_NONE is stream is closed. This only gets reset remount
@@ -112,6 +113,7 @@ static const file_transfer_state_t default_transfer_state = {
112113
false,
113114
false,
114115
false,
116+
false,
115117
STREAM_TYPE_NONE,
116118
};
117119

@@ -439,27 +441,39 @@ static void file_change_handler(const vfs_filename_t filename, vfs_file_change_t
439441
static void file_data_handler(uint32_t sector, const uint8_t *buf, uint32_t num_of_sectors)
440442
{
441443
stream_type_t stream;
442-
uint32_t size;
444+
uint32_t size = VFS_SECTOR_SIZE * num_of_sectors;
443445

444446
// this is the key for starting a file write - we dont care what file types are sent
445447
// just look for something unique (NVIC table, hex, srec, etc) until root dir is updated
446448
if (!file_transfer_state.stream_started) {
447449
// look for file types we can program
448-
stream = stream_start_identify((uint8_t *)buf, VFS_SECTOR_SIZE * num_of_sectors);
450+
stream = stream_start_identify((uint8_t *)buf, size);
449451

450452
if (STREAM_TYPE_NONE != stream) {
451453
transfer_stream_open(stream, sector);
452454
}
453455
}
454456

455457
if (file_transfer_state.stream_started) {
456-
// Ignore sectors coming before this file
457-
if (sector < file_transfer_state.start_sector) {
458-
return;
459-
}
458+
bool self_contained_block = stream_self_contained_block(file_transfer_state.stream, buf, size);
460459

461-
// sectors must be in order
462-
if (sector != file_transfer_state.file_next_sector) {
460+
if (self_contained_block) {
461+
vfs_mngr_printf("vfs_manager file_data_handler sector=%i\r\n", sector);
462+
vfs_mngr_printf(" sector self-contained\r\n");
463+
file_transfer_state.stream_ooo_blocks = true;
464+
} else if (file_transfer_state.stream_ooo_blocks) {
465+
// Invalid block for a self-contained stream, can be ignored
466+
vfs_mngr_printf("vfs_manager file_data_handler sector=%i\r\n", sector);
467+
vfs_mngr_printf(" invalid data for self-contained stream\r\n");
468+
vfs_mngr_printf(" size=%d data=%x,%x,%x,%x,...,%x,%x,%x,%x\r\n",
469+
size, buf[0], buf[1], buf[2], buf[3], buf[size - 4],
470+
buf[size - 3], buf[size - 2], buf[size - 1]);
471+
return;
472+
} else if (sector < file_transfer_state.start_sector) {
473+
// For ordered streams, ignore sectors coming before this file
474+
return;
475+
} else if (sector != file_transfer_state.file_next_sector) {
476+
// For ordered streams, sector must be the next in the sequence
463477
vfs_mngr_printf("vfs_manager file_data_handler sector=%i\r\n", sector);
464478

465479
if (sector < file_transfer_state.file_next_sector) {
@@ -482,7 +496,6 @@ static void file_data_handler(uint32_t sector, const uint8_t *buf, uint32_t num_
482496
}
483497

484498
// This sector could be part of the file so record it
485-
size = VFS_SECTOR_SIZE * num_of_sectors;
486499
file_transfer_state.size_transferred += size;
487500
file_transfer_state.file_next_sector = sector + num_of_sectors;
488501

@@ -584,15 +597,6 @@ static void transfer_update_file_info(vfs_file_t file, uint32_t start_sector, ui
584597
}
585598
}
586599

587-
// Initialize the stream if it has not been set
588-
if (STREAM_TYPE_NONE == file_transfer_state.stream) {
589-
file_transfer_state.stream = stream;
590-
591-
if (stream != STREAM_TYPE_NONE) {
592-
vfs_mngr_printf(" stream=%i\r\n", stream);
593-
}
594-
}
595-
596600
// Check - File size must either grow or be smaller than the size already transferred
597601
if ((size < file_transfer_state.file_size) && (size < file_transfer_state.size_transferred) && (size > 0)) {
598602
vfs_mngr_printf(" error: file size changed from %i to %i\r\n", file_transfer_state.file_size, size);
@@ -608,7 +612,7 @@ static void transfer_update_file_info(vfs_file_t file, uint32_t start_sector, ui
608612
}
609613

610614
// Check - stream must be the same
611-
if ((stream != STREAM_TYPE_NONE) && (stream != file_transfer_state.stream)) {
615+
if (stream != STREAM_TYPE_NONE && file_transfer_state.stream != STREAM_TYPE_NONE && !stream_compatible(file_transfer_state.stream, stream)) {
612616
vfs_mngr_printf(" error: changed types during transfer from %i to %i\r\n", file_transfer_state.stream, stream);
613617
transfer_update_state(ERROR_ERROR_DURING_TRANSFER);
614618
return;
@@ -761,6 +765,12 @@ static void transfer_update_state(error_t status)
761765
(file_transfer_state.size_transferred >= file_transfer_state.file_size) &&
762766
(file_transfer_state.file_size > 0) &&
763767
(file_transfer_state.start_sector == file_transfer_state.file_start_sector);
768+
if (file_transfer_state.stream_ooo_blocks &&
769+
file_transfer_state.file_size > 0 &&
770+
file_transfer_state.size_transferred >= file_transfer_state.file_size) {
771+
vfs_mngr_printf(" OoO file_info_optional_finish=%d\r\n", file_transfer_state.file_info_optional_finish);
772+
file_transfer_state.file_info_optional_finish = true;
773+
}
764774
transfer_timeout = file_transfer_state.transfer_timeout;
765775
transfer_started = (VFS_FILE_INVALID != file_transfer_state.file_to_program) ||
766776
(STREAM_TYPE_NONE != file_transfer_state.stream);

source/daplink/validation.c

+9
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,12 @@ uint8_t validate_hexfile(const uint8_t *buf)
9494
return ((buf[0] == ':') && ((buf[8] == '0') || (buf[8] == '2') || (buf[8] == '3') || (buf[8] == '4') || (buf[8] == '5'))) ? 1 : 0;
9595
}
9696
}
97+
98+
uint8_t validate_uhex_block(const uint8_t *buf, uint32_t size) {
99+
if (size != 512) {
100+
return 0;
101+
}
102+
return (memcmp(buf, (const void *)":02000004", 9) == 0) &&
103+
(memcmp(buf + 16, (const void *)":0400000A", 9) == 0) &&
104+
(buf[511] == '\n');
105+
}

source/daplink/validation.h

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ extern "C" {
3030

3131
uint8_t validate_bin_nvic(const uint8_t *buf);
3232
uint8_t validate_hexfile(const uint8_t *buf);
33+
uint8_t validate_uhex_block(const uint8_t *buf, uint32_t size);
3334

3435
/*!
3536
* @brief Baseline implementation of NVIC validator.

0 commit comments

Comments
 (0)