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..91540cb --- /dev/null +++ b/lib_device_control/apps/dfu_control_server.xc @@ -0,0 +1,124 @@ +// Copyright 2026 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#include +#include + +#include "control.h" +#include "control_transport_shared.h" +#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. + * 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 + * + * 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 }; + timer dfu_timer; + unsigned dfu_time; + + while(1) + { + select + { + 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; + 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; + } + + 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) { + // 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; + + 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; + } + int32_t block_num = 0; + ret = dfu_handle_read_command(CONTROL_CMD_VALUE(cmd), block_num, local_payload, payload_len); + // TODO return actual payload length from dfu_handle_read_command and check against payload_len + // TODO return block number to caller for tracking progress of upload? + memcpy(payload, local_payload, payload_len); + 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 +} \ 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()