From 64671f9c47c7eb238bcd7bd53fa8529576c7accb Mon Sep 17 00:00:00 2001 From: Humphrey Bucknell Date: Wed, 11 Feb 2026 14:42:24 +0000 Subject: [PATCH 1/3] Adding cross-library support for DFU --- lib_device_control/apps/dfu_control_server.xc | 130 ++++++++++++++++++ .../inc/control_transport_shared.h | 7 + lib_device_control/lib_build_info.cmake | 5 + 3 files changed, 142 insertions(+) create mode 100644 lib_device_control/apps/dfu_control_server.xc diff --git a/lib_device_control/apps/dfu_control_server.xc b/lib_device_control/apps/dfu_control_server.xc new file mode 100644 index 0000000..cd954bf --- /dev/null +++ b/lib_device_control/apps/dfu_control_server.xc @@ -0,0 +1,130 @@ +// Copyright 2026 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#include +#include + +#include "control.h" +#include "dfu.h" +#include "dfu_commands.h" + +/* TODO - rework */ +/** + * Hardware build section + * + * Information about hardware available during factory programming + * + */ +// hardcoded value based on quadflash library +#define QUADFLASH_SPISPEC_SIZE_WORDS 28 + +struct dp_hardware_build { + uint32_t tag; + uint32_t build; /**< type of build such as prototype or test */ + union { + uint32_t words[QUADFLASH_SPISPEC_SIZE_WORDS]; +#ifdef __xcore__ + fl_QuadDeviceSpec structure[1]; +#endif + } spispec; + uint32_t checksum; +}; + +[[combinable]] +void dfu_control_server(server interface control dfu_control_interface /* fl_QSPIPorts * unsafe qspi_ports, */) { + uint8_t local_payload[I2C_DATA_MAX_BYTES]; + struct dfu_write_command_state dfu_write_command_state = {{false, 0}, false, false, false}; + struct dp_hardware_build hardware_build = { 0, 0, {{0}}, 0 }; + bool hardware_build_valid = false; + timer dfu_timer; + unsigned dfu_time; + + while(1) + { + select + { + //DFU cases + case dfu_control_interface.register_resources(control_resid_t resources[MAX_RESOURCES_PER_INTERFACE], + unsigned &num_resources): + resources[0] = RESOURCE_ID_DFU; + num_resources = 1; + // dfu_resource_registered = 1; + //debug_printf("DFU resource registered\n"); + break; + + case dfu_control_interface.write_command(control_resid_t resid, control_cmd_t cmd, + const uint8_t payload[payload_len], unsigned payload_len) -> control_ret_t ret: + if (payload_len > sizeof(local_payload)) { + ret = CONTROL_DATA_LENGTH_ERROR; + break; + } + // if (!is_flash_free_for_dfu(run_status)) { + // ret = CONTROL_ERROR; + // break; + // } + memcpy(local_payload, payload, payload_len); + ret = dfu_handle_write_command(cmd, local_payload, payload_len, dfu_write_command_state, hardware_build.spispec.structure); + if (dfu_write_command_state.timeout.enable) { + unsigned now; + dfu_timer :> now; + //debug_printf("now = %d\n", now); + dfu_time = now + dfu_write_command_state.timeout.delta; + } + if (dfu_write_command_state.needs_flash_connect) { + //debug_printf("page size %d\n", hardware_build.spispec.structure[0].pageSize); + if (hardware_build_valid) { + if (!dfu_is_flash_suitable(hardware_build.spispec.structure)) { + //debug_printf("DFU reported flash not suitable\n"); + // run_status = RUN_DFU_FLASH_SPEC_UNSUITABLE; + ret = CONTROL_ERROR; + } + else { + unsafe { + // if (fl_connectToOneDevice(*qspi_ports, hardware_build.spispec.structure[0]) != 0) { + // // run_status = RUN_DFU_FLASH_CONNECT_FAILED; + // ret = CONTROL_ERROR; + // //debug_printf("connected to flash for DFU\n"); + // } + } + } + } + else { + // run_status = RUN_DFU_FLASH_CONNECT_FAILED; + ret = CONTROL_ERROR; + } + dfu_write_command_state.flash_connected = true; + dfu_write_command_state.needs_flash_connect = false; + // if (ret == CONTROL_SUCCESS) + // run_status = RUN_DFU_IN_PROGRESS; + } + break; + + case dfu_control_interface.read_command(control_resid_t resid, control_cmd_t cmd, + uint8_t payload[payload_len], unsigned payload_len) -> control_ret_t ret: + if (payload_len > sizeof(local_payload)) { + ret = CONTROL_DATA_LENGTH_ERROR; + break; + } + // if (!is_flash_free_for_dfu(run_status)) { + // ret = CONTROL_ERROR; + // break; + // } + memcpy(local_payload, payload, payload_len); + ret = dfu_handle_read_command(cmd, local_payload, payload_len); + memcpy(payload, local_payload, payload_len); + break; + + case dfu_write_command_state.timeout.enable => dfu_timer when timerafter(dfu_time) :> void: + { timer tmr; + int t; + tmr :> t; + //debug_printf("t = %d\n", t); + } + //debug_printf("GPIO server: DFU timeout\n"); + dfu_timeout_detach(); + dfu_write_command_state.timeout.enable = false; + break; + + }// select + }//While 1 +} \ No newline at end of file diff --git a/lib_device_control/inc/control_transport_shared.h b/lib_device_control/inc/control_transport_shared.h index 0d93e07..0b10963 100644 --- a/lib_device_control/inc/control_transport_shared.h +++ b/lib_device_control/inc/control_transport_shared.h @@ -23,6 +23,13 @@ */ #define IS_CONTROL_CMD_READ(c) ((c) & 0x80) +/** + * Returns the command value with the read/write bit cleared. + * + * \param[in,out] c The transport command code convert to an application command code. + */ +#define CONTROL_CMD_VALUE(c) ((c) & ~0x80) + /** * Sets the read bit on a command code * diff --git a/lib_device_control/lib_build_info.cmake b/lib_device_control/lib_build_info.cmake index 6fa5870..eb67562 100644 --- a/lib_device_control/lib_build_info.cmake +++ b/lib_device_control/lib_build_info.cmake @@ -26,4 +26,9 @@ elseif (TRANSPORT_UPPER STREQUAL "XSCOPE") list(APPEND LIB_XC_SRCS adapters/transport_xscope.xc) endif() +# Add DFU control server source files based on configuration +if (CONTROL_APP_DFU) + list(APPEND LIB_XC_SRCS apps/dfu_control_server.xc) +endif() + XMOS_REGISTER_MODULE() From ad55bd9cda7ba9c95efcc5d80a5416bf684e89cf Mon Sep 17 00:00:00 2001 From: Humphrey Bucknell Date: Wed, 11 Feb 2026 16:45:54 +0000 Subject: [PATCH 2/3] Refactored dfu server --- lib_device_control/apps/dfu_control_server.xc | 126 ++++++++---------- 1 file changed, 53 insertions(+), 73 deletions(-) diff --git a/lib_device_control/apps/dfu_control_server.xc b/lib_device_control/apps/dfu_control_server.xc index cd954bf..de78215 100644 --- a/lib_device_control/apps/dfu_control_server.xc +++ b/lib_device_control/apps/dfu_control_server.xc @@ -8,6 +8,16 @@ #include "dfu.h" #include "dfu_commands.h" +/* FROM LIB_XUA - TODO - check reboot cycle time etc. 1*/ +/* Windows core USB/device driver stack may not like device coming off bus for + * a very short period of less than 500ms. Enforce at least 500ms by stalling. + * This may not have the desired effect depending on whether 'off the bus' + * requires device terminations disabled (PHY off). In that case we would be + * better off doing the reboot to DFU and then delaying PHY initialisation + * instead. Suggest revisiting. + */ +#define DELAY_BEFORE_REBOOT_TO_DFU_MS 500 + /* TODO - rework */ /** * Hardware build section @@ -15,27 +25,26 @@ * Information about hardware available during factory programming * */ -// hardcoded value based on quadflash library -#define QUADFLASH_SPISPEC_SIZE_WORDS 28 -struct dp_hardware_build { - uint32_t tag; - uint32_t build; /**< type of build such as prototype or test */ - union { - uint32_t words[QUADFLASH_SPISPEC_SIZE_WORDS]; -#ifdef __xcore__ - fl_QuadDeviceSpec structure[1]; -#endif - } spispec; - uint32_t checksum; -}; +// // hardcoded value based on quadflash library +// #define QUADFLASH_SPISPEC_SIZE_WORDS 28 + +// struct dp_hardware_build { +// uint32_t tag; +// uint32_t build; /**< type of build such as prototype or test */ +// union { +// uint32_t words[QUADFLASH_SPISPEC_SIZE_WORDS]; +// #ifdef __xcore__ +// fl_QuadDeviceSpec structure[1]; +// #endif +// } spispec; +// uint32_t checksum; +// }; [[combinable]] void dfu_control_server(server interface control dfu_control_interface /* fl_QSPIPorts * unsafe qspi_ports, */) { uint8_t local_payload[I2C_DATA_MAX_BYTES]; - struct dfu_write_command_state dfu_write_command_state = {{false, 0}, false, false, false}; - struct dp_hardware_build hardware_build = { 0, 0, {{0}}, 0 }; - bool hardware_build_valid = false; + struct dfu_write_command_state dfu_write_command_state = { false }; timer dfu_timer; unsigned dfu_time; @@ -43,13 +52,9 @@ void dfu_control_server(server interface control dfu_control_interface /* fl_QS { select { - //DFU cases - case dfu_control_interface.register_resources(control_resid_t resources[MAX_RESOURCES_PER_INTERFACE], - unsigned &num_resources): + case dfu_control_interface.register_resources(control_resid_t resources[MAX_RESOURCES_PER_INTERFACE], unsigned &num_resources): resources[0] = RESOURCE_ID_DFU; num_resources = 1; - // dfu_resource_registered = 1; - //debug_printf("DFU resource registered\n"); break; case dfu_control_interface.write_command(control_resid_t resid, control_cmd_t cmd, @@ -58,44 +63,21 @@ void dfu_control_server(server interface control dfu_control_interface /* fl_QS ret = CONTROL_DATA_LENGTH_ERROR; break; } - // if (!is_flash_free_for_dfu(run_status)) { - // ret = CONTROL_ERROR; - // break; - // } memcpy(local_payload, payload, payload_len); - ret = dfu_handle_write_command(cmd, local_payload, payload_len, dfu_write_command_state, hardware_build.spispec.structure); - if (dfu_write_command_state.timeout.enable) { - unsigned now; - dfu_timer :> now; - //debug_printf("now = %d\n", now); - dfu_time = now + dfu_write_command_state.timeout.delta; - } - if (dfu_write_command_state.needs_flash_connect) { - //debug_printf("page size %d\n", hardware_build.spispec.structure[0].pageSize); - if (hardware_build_valid) { - if (!dfu_is_flash_suitable(hardware_build.spispec.structure)) { - //debug_printf("DFU reported flash not suitable\n"); - // run_status = RUN_DFU_FLASH_SPEC_UNSUITABLE; - ret = CONTROL_ERROR; - } - else { - unsafe { - // if (fl_connectToOneDevice(*qspi_ports, hardware_build.spispec.structure[0]) != 0) { - // // run_status = RUN_DFU_FLASH_CONNECT_FAILED; - // ret = CONTROL_ERROR; - // //debug_printf("connected to flash for DFU\n"); - // } - } - } - } - else { - // run_status = RUN_DFU_FLASH_CONNECT_FAILED; - ret = CONTROL_ERROR; - } - dfu_write_command_state.flash_connected = true; - dfu_write_command_state.needs_flash_connect = false; - // if (ret == CONTROL_SUCCESS) - // run_status = RUN_DFU_IN_PROGRESS; + ret = dfu_handle_write_command(CONTROL_CMD_VALUE(cmd), local_payload, payload_len, dfu_write_command_state); + + // WAS for reset timeout handling - might be needed/helpful for poll timeout handling, TBC + // if (dfu_write_command_state.timeout.enable) { + // unsigned now; + // dfu_timer :> now; + // //debug_printf("now = %d\n", now); + // dfu_time = now + dfu_write_command_state.timeout.delta; + // } + if (dfu_write_command_state.needs_reboot) { + dfu_timer :> dfu_time; + dfu_timer when timerafter(dfu_time + (DELAY_BEFORE_REBOOT_TO_DFU_MS * XS1_TIMER_KHZ)) :> void; + // DFUDelay(DELAY_BEFORE_REBOOT_TO_DFU_MS * XS1_TIMER_KHZ); + device_reboot(); } break; @@ -105,25 +87,23 @@ void dfu_control_server(server interface control dfu_control_interface /* fl_QS ret = CONTROL_DATA_LENGTH_ERROR; break; } - // if (!is_flash_free_for_dfu(run_status)) { - // ret = CONTROL_ERROR; - // break; - // } + // TODO - fix length handling into and out of read_command() memcpy(local_payload, payload, payload_len); - ret = dfu_handle_read_command(cmd, local_payload, payload_len); + ret = dfu_handle_read_command(CONTROL_CMD_VALUE(cmd), local_payload, payload_len); memcpy(payload, local_payload, payload_len); break; - case dfu_write_command_state.timeout.enable => dfu_timer when timerafter(dfu_time) :> void: - { timer tmr; - int t; - tmr :> t; - //debug_printf("t = %d\n", t); - } - //debug_printf("GPIO server: DFU timeout\n"); - dfu_timeout_detach(); - dfu_write_command_state.timeout.enable = false; - break; + // WAS for reset timeout handling - might be needed/helpful for poll timeout handling, TBC + // case dfu_write_command_state.timeout.enable => dfu_timer when timerafter(dfu_time) :> void: + // { timer tmr; + // int t; + // tmr :> t; + // //debug_printf("t = %d\n", t); + // } + // //debug_printf("GPIO server: DFU timeout\n"); + // dfu_timeout_detach(); + // dfu_write_command_state.timeout.enable = false; + // break; }// select }//While 1 From 35719b1e8a4f1de25445e732a90b9dffddedf194 Mon Sep 17 00:00:00 2001 From: Humphrey Bucknell Date: Thu, 12 Feb 2026 10:47:16 +0000 Subject: [PATCH 3/3] Moved header(block_num) unpacking to server --- lib_device_control/apps/dfu_control_server.xc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib_device_control/apps/dfu_control_server.xc b/lib_device_control/apps/dfu_control_server.xc index de78215..ffd387a 100644 --- a/lib_device_control/apps/dfu_control_server.xc +++ b/lib_device_control/apps/dfu_control_server.xc @@ -8,6 +8,12 @@ #include "dfu.h" #include "dfu_commands.h" +// Non-USB transport; sending block number with payload. +struct dfu_dnload_header { + uint16_t block_num; + uint16_t pad; +}; + /* FROM LIB_XUA - TODO - check reboot cycle time etc. 1*/ /* Windows core USB/device driver stack may not like device coming off bus for * a very short period of less than 500ms. Enforce at least 500ms by stalling. @@ -59,12 +65,18 @@ void dfu_control_server(server interface control dfu_control_interface /* fl_QS case dfu_control_interface.write_command(control_resid_t resid, control_cmd_t cmd, const uint8_t payload[payload_len], unsigned payload_len) -> control_ret_t ret: + if (payload_len > sizeof(local_payload)) { ret = CONTROL_DATA_LENGTH_ERROR; break; } - memcpy(local_payload, payload, payload_len); - ret = dfu_handle_write_command(CONTROL_CMD_VALUE(cmd), local_payload, payload_len, dfu_write_command_state); + + size_t header_size = sizeof(struct dfu_dnload_header); + struct dfu_dnload_header header; + memcpy(&header, payload, header_size); + memcpy(local_payload, &payload[header_size], (payload_len - header_size)); + + ret = dfu_handle_write_command(CONTROL_CMD_VALUE(cmd), header.block_num, local_payload, (payload_len - header_size), dfu_write_command_state); // WAS for reset timeout handling - might be needed/helpful for poll timeout handling, TBC // if (dfu_write_command_state.timeout.enable) {