From 1138f8cc704e09af3335e253e5f244fde90d982f Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Fri, 26 Mar 2021 15:30:43 -0400 Subject: [PATCH 01/23] Add DFU Class per Version 1.1 Spec --- src/class/dfu/dfu.h | 107 ++++++ src/class/dfu/dfu_rt_device.c | 673 ++++++++++++++++++++++++++++++++-- src/class/dfu/dfu_rt_device.h | 117 ++++-- src/common/tusb_types.h | 19 + src/device/usbd.c | 8 +- src/device/usbd.h | 15 +- src/device/usbd_control.c | 2 +- src/tusb_option.h | 4 + 8 files changed, 881 insertions(+), 64 deletions(-) create mode 100644 src/class/dfu/dfu.h diff --git a/src/class/dfu/dfu.h b/src/class/dfu/dfu.h new file mode 100644 index 000000000..39ce567da --- /dev/null +++ b/src/class/dfu/dfu.h @@ -0,0 +1,107 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 XMOS LIMITED + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DFU_H_ +#define _TUSB_DFU_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Common Definitions +//--------------------------------------------------------------------+ +// DFU Protocol +typedef enum +{ + DFU_PROTOCOL_RT = 0x01, + DFU_PROTOCOL_DFU = 0x02, +} dfu_protocol_type_t; + +// DFU Descriptor Type +typedef enum +{ + DFU_DESC_FUNCTIONAL = 0x21, +} dfu_descriptor_type_t; + +// DFU Requests +typedef enum { + DFU_REQUEST_DETACH = 0, + DFU_REQUEST_DNLOAD = 1, + DFU_REQUEST_UPLOAD = 2, + DFU_REQUEST_GETSTATUS = 3, + DFU_REQUEST_CLRSTATUS = 4, + DFU_REQUEST_GETSTATE = 5, + DFU_REQUEST_ABORT = 6, +} dfu_requests_t; + +// DFU States +typedef enum { + APP_IDLE = 0, + APP_DETACH = 1, + DFU_IDLE = 2, + DFU_DNLOAD_SYNC = 3, + DFU_DNBUSY = 4, + DFU_DNLOAD_IDLE = 5, + DFU_MANIFEST_SYNC = 6, + DFU_MANIFEST = 7, + DFU_MANIFEST_WAIT_RESET = 8, + DFU_UPLOAD_IDLE = 9, + DFU_ERROR = 10, +} dfu_mode_state_t; + +// DFU Status +typedef enum { + DFU_STATUS_OK = 0x00, + DFU_STATUS_ERRTARGET = 0x01, + DFU_STATUS_ERRFILE = 0x02, + DFU_STATUS_ERRWRITE = 0x03, + DFU_STATUS_ERRERASE = 0x04, + DFU_STATUS_ERRCHECK_ERASED = 0x05, + DFU_STATUS_ERRPROG = 0x06, + DFU_STATUS_ERRVERIFY = 0x07, + DFU_STATUS_ERRADDRESS = 0x08, + DFU_STATUS_ERRNOTDONE = 0x09, + DFU_STATUS_ERRFIRMWARE = 0x0A, + DFU_STATUS_ERRVENDOR = 0x0B, + DFU_STATUS_ERRUSBR = 0x0C, + DFU_STATUS_ERRPOR = 0x0D, + DFU_STATUS_ERRUNKNOWN = 0x0E, + DFU_STATUS_ERRSTALLEDPKT = 0x0F, +} dfu_mode_device_status_t; + +#define DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK (1 << 0) +#define DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK (1 << 1) +#define DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK (1 << 2) +#define DFU_FUNC_ATTR_WILL_DETACH_BITMASK (1 << 3) + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DFU_H_ */ diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c index 5700615be..965d1661e 100644 --- a/src/class/dfu/dfu_rt_device.c +++ b/src/class/dfu/dfu_rt_device.c @@ -34,34 +34,176 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ -typedef enum { - DFU_REQUEST_DETACH = 0, - DFU_REQUEST_DNLOAD = 1, - DFU_REQUEST_UPLOAD = 2, - DFU_REQUEST_GETSTATUS = 3, - DFU_REQUEST_CLRSTATUS = 4, - DFU_REQUEST_GETSTATE = 5, - DFU_REQUEST_ABORT = 6, -} dfu_requests_t; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +typedef struct TU_ATTR_PACKED +{ + dfu_mode_device_status_t status; + dfu_mode_state_t state; + uint8_t attrs; + bool blk_transfer_in_proc; + + uint8_t itf_num; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_DFU_TRANSFER_BUFFER_SIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_DFU_TRANSFER_BUFFER_SIZE]; +} dfu_state_ctx_t; typedef struct TU_ATTR_PACKED { - uint8_t status; - uint8_t poll_timeout[3]; - uint8_t state; - uint8_t istring; -} dfu_status_t; + uint8_t bStatus; + uint8_t bwPollTimeout[3]; + uint8_t bState; + uint8_t iString; +} dfu_status_req_payload_t; + +TU_VERIFY_STATIC( sizeof(dfu_status_req_payload_t) == 6, "size is not correct"); + +// Only a single dfu state is allowed +CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_state_ctx; + +static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request); +static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request); +static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength); +static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request); + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= 2 + +static tu_lookup_entry_t const _dfu_request_lookup[] = +{ + { .key = DFU_REQUEST_DETACH , .data = "DETACH" }, + { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" }, + { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" }, + { .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" }, + { .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" }, + { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" }, + { .key = DFU_REQUEST_ABORT , .data = "ABORT" }, +}; + +static tu_lookup_table_t const _dfu_request_table = +{ + .count = TU_ARRAY_SIZE(_dfu_request_lookup), + .items = _dfu_request_lookup +}; + +static tu_lookup_entry_t const _dfu_mode_state_lookup[] = +{ + { .key = APP_IDLE , .data = "APP_IDLE" }, + { .key = APP_DETACH , .data = "APP_DETACH" }, + { .key = DFU_IDLE , .data = "DFU_IDLE" }, + { .key = DFU_DNLOAD_SYNC , .data = "DFU_DNLOAD_SYNC" }, + { .key = DFU_DNBUSY , .data = "DFU_DNBUSY" }, + { .key = DFU_DNLOAD_IDLE , .data = "DFU_DNLOAD_IDLE" }, + { .key = DFU_MANIFEST_SYNC , .data = "DFU_MANIFEST_SYNC" }, + { .key = DFU_MANIFEST , .data = "DFU_MANIFEST" }, + { .key = DFU_MANIFEST_WAIT_RESET , .data = "DFU_MANIFEST_WAIT_RESET" }, + { .key = DFU_UPLOAD_IDLE , .data = "DFU_UPLOAD_IDLE" }, + { .key = DFU_ERROR , .data = "DFU_ERROR" }, +}; + +static tu_lookup_table_t const _dfu_mode_state_table = +{ + .count = TU_ARRAY_SIZE(_dfu_mode_state_lookup), + .items = _dfu_mode_state_lookup +}; + +static tu_lookup_entry_t const _dfu_mode_status_lookup[] = +{ + { .key = DFU_STATUS_OK , .data = "OK" }, + { .key = DFU_STATUS_ERRTARGET , .data = "errTARGET" }, + { .key = DFU_STATUS_ERRFILE , .data = "errFILE" }, + { .key = DFU_STATUS_ERRWRITE , .data = "errWRITE" }, + { .key = DFU_STATUS_ERRERASE , .data = "errERASE" }, + { .key = DFU_STATUS_ERRCHECK_ERASED , .data = "errCHECK_ERASED" }, + { .key = DFU_STATUS_ERRPROG , .data = "errPROG" }, + { .key = DFU_STATUS_ERRVERIFY , .data = "errVERIFY" }, + { .key = DFU_STATUS_ERRADDRESS , .data = "errADDRESS" }, + { .key = DFU_STATUS_ERRNOTDONE , .data = "errNOTDONE" }, + { .key = DFU_STATUS_ERRFIRMWARE , .data = "errFIRMWARE" }, + { .key = DFU_STATUS_ERRVENDOR , .data = "errVENDOR" }, + { .key = DFU_STATUS_ERRUSBR , .data = "errUSBR" }, + { .key = DFU_STATUS_ERRPOR , .data = "errPOR" }, + { .key = DFU_STATUS_ERRUNKNOWN , .data = "errUNKNOWN" }, + { .key = DFU_STATUS_ERRSTALLEDPKT , .data = "errSTALLEDPKT" }, +}; + +static tu_lookup_table_t const _dfu_mode_status_table = +{ + .count = TU_ARRAY_SIZE(_dfu_mode_status_lookup), + .items = _dfu_mode_status_lookup +}; + +#endif + +#define dfu_rtd_debug_print_context() \ +{ \ + TU_LOG2(" DFU at State: %s\r\n Status: %s\r\n", \ + tu_lookup_find(&_dfu_mode_state_table, _dfu_state_ctx.state), \ + tu_lookup_find(&_dfu_mode_status_table, _dfu_state_ctx.status) ); \ +} //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ void dfu_rtd_init(void) { + if ( tud_dfu_runtime_init_cb ) { + _dfu_state_ctx.state = (tud_dfu_runtime_init_cb() == DFU_PROTOCOL_DFU) ? APP_DETACH : APP_IDLE; + } else { + _dfu_state_ctx.state = APP_IDLE; + } + + _dfu_state_ctx.status = DFU_STATUS_OK; + _dfu_state_ctx.attrs = tud_dfu_runtime_init_attrs_cb(); + _dfu_state_ctx.blk_transfer_in_proc = false; + + dfu_rtd_debug_print_context(); } void dfu_rtd_reset(uint8_t rhport) { - (void) rhport; + if ( tud_dfu_runtime_usb_reset_cb ) + { + tud_dfu_runtime_usb_reset_cb(rhport, _dfu_state_ctx.state); + } + + switch (_dfu_state_ctx.state) + { + case APP_DETACH: + { + _dfu_state_ctx.state = DFU_IDLE; + } + break; + + case DFU_IDLE: + case DFU_DNLOAD_SYNC: + case DFU_DNBUSY: + case DFU_DNLOAD_IDLE: + case DFU_MANIFEST_SYNC: + case DFU_MANIFEST: + case DFU_MANIFEST_WAIT_RESET: + case DFU_UPLOAD_IDLE: + { + _dfu_state_ctx.state = (tud_dfu_runtime_firmware_valid_check_cb()) ? APP_IDLE : DFU_ERROR; + } + break; + + case DFU_ERROR: + default: + { + _dfu_state_ctx.state = APP_IDLE; + } + break; + } + + _dfu_state_ctx.status = DFU_STATUS_OK; + _dfu_state_ctx.attrs = tud_dfu_runtime_init_attrs_cb(); + _dfu_state_ctx.blk_transfer_in_proc = false; + dfu_rtd_debug_print_context(); } uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) @@ -69,9 +211,10 @@ uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, ui (void) rhport; (void) max_len; - // Ensure this is DFU Runtime + // Ensure this is DFU Runtime or Mode TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && - itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT, 0); + ( (itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT) + | (itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU) ), 0); uint8_t const * p_desc = tu_desc_next( itf_desc ); uint16_t drv_len = sizeof(tusb_desc_interface_t); @@ -90,9 +233,16 @@ uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, ui // return false to stall control endpoint (e.g unsupported request) bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { - // nothing to do with DATA and ACK stage + if ( (stage == CONTROL_STAGE_DATA) && (request->bRequest == DFU_DNLOAD_SYNC) ) + { + dfu_mode_req_dnload_reply(rhport, request); + return true; + } + + // nothing to do with any other DATA or ACK stage if ( stage != CONTROL_STAGE_SETUP ) return true; + TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); // dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request @@ -106,34 +256,487 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request // Handle class request only from here TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); - switch ( request->bRequest ) + switch (request->bRequest) { case DFU_REQUEST_DETACH: - tud_control_status(rhport, request); - tud_dfu_runtime_reboot_to_dfu_cb(); - break; - + case DFU_REQUEST_DNLOAD: + case DFU_REQUEST_UPLOAD: case DFU_REQUEST_GETSTATUS: + case DFU_REQUEST_CLRSTATUS: + case DFU_REQUEST_GETSTATE: + case DFU_REQUEST_ABORT: { - // status = OK, poll timeout = 0, state = app idle, istring = 0 - uint8_t status_response[6] = { 0, 0, 0, 0, 0, 0 }; - tud_control_xfer(rhport, request, status_response, sizeof(status_response)); + return dfu_state_machine(rhport, request); } break; - default: return false; // stall unsupported request + default: + { + TU_LOG2(" DFU Nonstandard Request: %u\r\n", request->bRequest); + return ( tud_dfu_runtime_req_nonstandard_cb ) ? tud_dfu_runtime_req_nonstandard_cb(rhport, stage, request) : false; + } + break; + } + + return true; +} +void tud_dfu_runtime_set_status(dfu_mode_device_status_t status) +{ + _dfu_state_ctx.status = status; +} + +static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength) +{ + TU_VERIFY( wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE); + uint16_t retval = tud_dfu_runtime_req_upload_data_cb(block_num, (uint8_t *)&_dfu_state_ctx.epin_buf, wLength); + tud_control_xfer(rhport, request, &_dfu_state_ctx.epin_buf, retval); + return retval; +} + +static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request) +{ + dfu_status_req_payload_t resp; + + resp.bStatus = _dfu_state_ctx.status; + if ( tud_dfu_runtime_get_poll_timeout_cb ) + { + tud_dfu_runtime_get_poll_timeout_cb((uint8_t *)&resp.bwPollTimeout); + } else { + memset((uint8_t *)&resp.bwPollTimeout, 0x00, 3); + } + resp.bState = _dfu_state_ctx.state; + resp.iString = ( tud_dfu_runtime_get_status_desc_table_index_cb ) ? tud_dfu_runtime_get_status_desc_table_index_cb() : 0; + + tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_req_payload_t)); +} + +static void dfu_req_getstate_reply(uint8_t rhport, tusb_control_request_t const * request) +{ + tud_control_xfer(rhport, request, &_dfu_state_ctx.state, 1); +} + +static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request) +{ + // TODO: add "zero" copy mode so the buffer we read into can be provided by the user + // if they wish, there still will be the internal control buffer copy to this buffer + // but this mode would provide zero copy from the class driver to the application + + // setup for data phase + tud_control_xfer(rhport, request, &_dfu_state_ctx.epout_buf, request->wLength); +} + +static void dfu_runtime_start_status_poll_timeout() +{ + uint8_t bwPollTimeout[3] = {0,0,0}; + + if ( tud_dfu_runtime_get_poll_timeout_cb ) + { + tud_dfu_runtime_get_poll_timeout_cb((uint8_t *)&bwPollTimeout); + } + + tud_dfu_runtime_start_poll_timeout_cb((uint8_t *)&bwPollTimeout); +} + +void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) +{ + dfu_runtime_start_status_poll_timeout(); + tud_dfu_runtime_req_dnload_data_cb(request->wValue, (uint8_t *)&_dfu_state_ctx.epout_buf, request->wLength); + _dfu_state_ctx.blk_transfer_in_proc = false; +} + +void tud_dfu_runtime_poll_timeout_done() +{ + if (_dfu_state_ctx.state == DFU_DNBUSY) + { + _dfu_state_ctx.state = DFU_DNLOAD_SYNC; + } else if (_dfu_state_ctx.state == DFU_MANIFEST) + { + _dfu_state_ctx.state = ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) == 0) + ? DFU_MANIFEST_WAIT_RESET : DFU_MANIFEST_SYNC; + } +} + +static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request) +{ + TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest)); + TU_LOG2(" DFU State Machine: %s\r\n", tu_lookup_find(&_dfu_mode_state_table, _dfu_state_ctx.state)); + + switch (_dfu_state_ctx.state) + { + case APP_IDLE: + { + switch (request->bRequest) + { + case DFU_REQUEST_DETACH: + { + _dfu_state_ctx.state = APP_DETACH; + if ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_WILL_DETACH_BITMASK) == 1) + { + tud_dfu_runtime_reboot_to_dfu_cb(); + } else { + tud_dfu_runtime_detach_start_timer_cb(request->wValue); + } + } + break; + + case DFU_REQUEST_GETSTATUS: + { + dfu_req_getstatus_reply(rhport, request); + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case APP_DETACH: + { + switch (request->bRequest) + { + case DFU_REQUEST_GETSTATUS: + { + dfu_req_getstatus_reply(rhport, request); + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + default: + { + _dfu_state_ctx.state = APP_IDLE; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_IDLE: + { + switch (request->bRequest) + { + case DFU_REQUEST_DNLOAD: + { + if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK) != 0) + && (request->wLength > 0) ) + { + _dfu_state_ctx.state = DFU_DNLOAD_SYNC; + _dfu_state_ctx.blk_transfer_in_proc = true; + dfu_req_dnload_setup(rhport, request); + } else { + _dfu_state_ctx.state = DFU_ERROR; + } + } + break; + + case DFU_REQUEST_UPLOAD: + { + if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK) != 0) ) + { + _dfu_state_ctx.state = DFU_UPLOAD_IDLE; + dfu_req_upload(rhport, request, request->wValue, request->wLength); + } else { + _dfu_state_ctx.state = DFU_ERROR; + } + } + break; + + case DFU_REQUEST_GETSTATUS: + { + dfu_req_getstatus_reply(rhport, request); + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + case DFU_REQUEST_ABORT: + { + ; // do nothing, but don't stall so continue on + } + break; + + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_DNLOAD_SYNC: + { + switch (request->bRequest) + { + case DFU_REQUEST_GETSTATUS: + { + if ( _dfu_state_ctx.blk_transfer_in_proc ) + { + _dfu_state_ctx.state = DFU_DNBUSY; + dfu_req_getstatus_reply(rhport, request); + } else { + _dfu_state_ctx.state = DFU_DNLOAD_IDLE; + dfu_req_getstatus_reply(rhport, request); + } + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_DNBUSY: + { + switch (request->bRequest) + { + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_DNLOAD_IDLE: + { + switch (request->bRequest) + { + case DFU_REQUEST_DNLOAD: + { + if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK) != 0) + && (request->wLength > 0) ) + { + _dfu_state_ctx.state = DFU_DNLOAD_SYNC; + _dfu_state_ctx.blk_transfer_in_proc = true; + + dfu_req_dnload_setup(rhport, request); + } else { + if ( tud_dfu_runtime_device_data_done_check_cb() ) + { + _dfu_state_ctx.state = DFU_MANIFEST_SYNC; + tud_control_status(rhport, request); + } else { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall + } + } + } + break; + + case DFU_REQUEST_GETSTATUS: + { + dfu_req_getstatus_reply(rhport, request); + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + case DFU_REQUEST_ABORT: + { + if ( tud_dfu_runtime_abort_cb ) + { + tud_dfu_runtime_abort_cb(); + } + _dfu_state_ctx.state = DFU_IDLE; + } + break; + + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_MANIFEST_SYNC: + { + switch (request->bRequest) + { + case DFU_REQUEST_GETSTATUS: + { + if ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) == 0) + { + _dfu_state_ctx.state = DFU_MANIFEST; + dfu_req_getstatus_reply(rhport, request); + } else { + if ( tud_dfu_runtime_firmware_valid_check_cb() ) + { + _dfu_state_ctx.state = DFU_IDLE; + } + dfu_req_getstatus_reply(rhport, request); + } + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_MANIFEST: + { + switch (request->bRequest) + { + default: + { + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_MANIFEST_WAIT_RESET: + { + // technically we should never even get here, but we will handle it just in case + TU_LOG2(" DFU was in DFU_MANIFEST_WAIT_RESET and got unexpected request: %u\r\n", request->bRequest); + switch (request->bRequest) + { + default: + { + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_UPLOAD_IDLE: + { + switch (request->bRequest) + { + case DFU_REQUEST_UPLOAD: + { + if (dfu_req_upload(rhport, request, request->wValue, request->wLength) != request->wLength) + { + _dfu_state_ctx.state = DFU_IDLE; + } + } + break; + + case DFU_REQUEST_GETSTATUS: + { + dfu_req_getstatus_reply(rhport, request); + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + case DFU_REQUEST_ABORT: + { + if (tud_dfu_runtime_abort_cb) + { + tud_dfu_runtime_abort_cb(); + } + _dfu_state_ctx.state = DFU_IDLE; + } + break; + + default: + { + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_ERROR: + { + switch (request->bRequest) + { + case DFU_REQUEST_GETSTATUS: + { + dfu_req_getstatus_reply(rhport, request); + } + break; + + case DFU_REQUEST_CLRSTATUS: + { + _dfu_state_ctx.state = DFU_IDLE; + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + default: + { + return false; // stall on all other requests + } + break; + } + } + break; + + default: + _dfu_state_ctx.state = DFU_ERROR; + TU_LOG2(" DFU ERROR: Unexpected state\r\nStalling control pipe\r\n"); + return false; // Unexpected state, stall and change to error } return true; } -bool dfu_rtd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) -{ - (void) rhport; - (void) ep_addr; - (void) result; - (void) xferred_bytes; - return true; -} #endif diff --git a/src/class/dfu/dfu_rt_device.h b/src/class/dfu/dfu_rt_device.h index cff43d035..0d8f67a9b 100644 --- a/src/class/dfu/dfu_rt_device.h +++ b/src/class/dfu/dfu_rt_device.h @@ -29,36 +29,106 @@ #include "common/tusb_common.h" #include "device/usbd.h" +#include "dfu.h" #ifdef __cplusplus extern "C" { #endif - -//--------------------------------------------------------------------+ -// Common Definitions -//--------------------------------------------------------------------+ - -// DFU Protocol -typedef enum -{ - DFU_PROTOCOL_RT = 1, - DFU_PROTOCOL_DFU = 2, -} dfu_protocol_type_t; - -// DFU Descriptor Type -typedef enum -{ - DFU_DESC_FUNCTIONAL = 0x21, -} dfu_descriptor_type_t; - - //--------------------------------------------------------------------+ // Application Callback API (weak is optional) //--------------------------------------------------------------------+ +// Allow the application to update the status as required. +// Is set to DFU_STATUS_OK during internal initialization and USB reset +// Value is not checked to allow for custom statuses to be used +void tud_dfu_runtime_set_status(dfu_mode_device_status_t status); -// Invoked when received new data -TU_ATTR_WEAK void tud_dfu_runtime_reboot_to_dfu_cb(void); +// Invoked when a DFU_DETACH request is received and bitWillDetatch is set +void tud_dfu_runtime_reboot_to_dfu_cb(); + +// Invoked when a DFU_DETACH request is received and bitWillDetatch is not set +// This should start a timer for wTimeout ms +// The application should then look for a USB reset signal to occur before +// the timeout has elapsed. The reset signal will invoke tud_dfu_runtime_usb_reset_cb. +// If this reset signal is received before the timeout, then the application must +// switch to DFU mode. +// If the timer expires, the app does not need to do anything. +// NOTE: This callback should return immediately, and not implement the delay +// internally, as this can will hold up the USB stack +TU_ATTR_WEAK void tud_dfu_runtime_detach_start_timer_cb(uint16_t wTimeout); + +// Invoked when a nonstandard request is received +// Use may be vendor specific. +// Return false to stall +TU_ATTR_WEAK bool tud_dfu_runtime_req_nonstandard_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + +// Invoked when a reset is received to check if firmware is valid +bool tud_dfu_runtime_firmware_valid_check_cb(); + +// Invoked during initialization of the dfu driver to set attributes +// Return byte set with bitmasks: +// DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK +// DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK +// DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK +// DFU_FUNC_ATTR_WILL_DETACH_BITMASK +// Note: This should match the USB descriptor +uint8_t tud_dfu_runtime_init_attrs_cb(); + +// Invoked during initialization of the dfu driver to start as RT or DFU +// This will determine if the internal state machine will begin in +// APP_IDLE or DFU_IDLE +TU_ATTR_WEAK dfu_protocol_type_t tud_dfu_runtime_init_cb(); + +// Invoked during a DFU_GETSTATUS request to get for the string index +// to the status description string table. +TU_ATTR_WEAK uint8_t tud_dfu_runtime_get_status_desc_table_index_cb(); + +// Invoked during a USB reset +// Lets the app know that a USB reset has occurred so it can act accordingly, +// See tud_dfu_runtime_detach_start_timer_cb for how to handle the APP_DETACH state +// Other states will be application specific +TU_ATTR_WEAK void tud_dfu_runtime_usb_reset_cb(uint8_t rhport, dfu_mode_state_t state); + +// Invoked during a DFU_GETSTATUS request to set the timeout in ms to use +// before the subsequent DFU_GETSTATUS requests. +// The purpose of this value is to allow the device to tell the host +// how long to wait between the DFU_DNLOAD and DFU_GETSTATUS checks +// to allow the device to have time to erase, write memory, etc. +// ms_timeout is a pointer to array of length 3. +// Refer to the USB DFU class specification for more details +TU_ATTR_WEAK void tud_dfu_runtime_get_poll_timeout_cb(uint8_t *ms_timeout); + +// Invoked when a DFU_DNLOAD request is received +// This should start a timer for ms_timeout ms +// When the timer has elapsed, tud_dfu_runtime_poll_timeout_done must be called +// NOTE: This callback should return immediately, and not implement the delay +// internally, as this will hold up the class stack from receiving any packets +// during the DFU_DNLOAD_SYNC and DFU_DNBUSY states +void tud_dfu_runtime_start_poll_timeout_cb(uint8_t *ms_timeout); + +// Must be called when the poll_timeout has elapsed +void tud_dfu_runtime_poll_timeout_done(); + +// Invoked when a DFU_DNLOAD request is received +// This callback takes the wBlockNum chunk of length length and provides it +// to the application at the data pointer. This data is only valid for this +// call, so the app must use it not or copy it. +void tud_dfu_runtime_req_dnload_data_cb(uint16_t wBlockNum, uint8_t* data, uint16_t length); + +// Invoked during the last DFU_DNLOAD request, signifying that the host believes +// it is done transmitting data. +// Return true if the application agrees there is no more data +// Return false if the device disagrees, which will stall the pipe, and the Host +// should initiate a recovery procedure +bool tud_dfu_runtime_device_data_done_check_cb(); + +// Invoked when the Host has terminated a download or upload transfer +TU_ATTR_WEAK void tud_dfu_runtime_abort_cb(); + +// Invoked when a DFU_UPLOAD request is received +// This callback must populate data with up to length bytes +// Return the number of bytes to write +uint16_t tud_dfu_runtime_req_upload_data_cb(uint16_t block_num, uint8_t* data, uint16_t length); //--------------------------------------------------------------------+ // Internal Class Driver API @@ -67,8 +137,11 @@ void dfu_rtd_init(void); void dfu_rtd_reset(uint8_t rhport); uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); -bool dfu_rtd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +//--------------------------------------------------------------------+ +void dfu_mode_init(void); +void dfu_mode_reset(uint8_t rhport); +void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request); #ifdef __cplusplus } #endif diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index ba3fe2c41..a462b498f 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -421,6 +421,25 @@ typedef struct TU_ATTR_PACKED char url[]; } tusb_desc_webusb_url_t; +// DFU Functional Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength; + uint8_t bDescriptorType; + + struct TU_ATTR_PACKED { + uint8_t bitCanDnload : 1; + uint8_t bitCanUpload : 1; + uint8_t bitManifestationTolerant : 1; + uint8_t bitWillDetach : 1; + uint8_t reserved : 4; + } bmAttributes; + + uint16_t wDetachTimeOut; + uint16_t wTransferSize; + uint16_t bcdDFUVersion; +} tusb_desc_dfu_functional_t; + /*------------------------------------------------------------------*/ /* Types *------------------------------------------------------------------*/ diff --git a/src/device/usbd.c b/src/device/usbd.c index 4fc221888..28c9acdd2 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -1,4 +1,4 @@ -/* +/* * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) @@ -182,7 +182,7 @@ static usbd_class_driver_t const _usbd_driver[] = .reset = dfu_rtd_reset, .open = dfu_rtd_open, .control_xfer_cb = dfu_rtd_control_xfer_cb, - .xfer_cb = dfu_rtd_xfer_cb, + .xfer_cb = NULL, .sof = NULL }, #endif @@ -1270,9 +1270,9 @@ bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) /** * usbd_edpt_close will disable an endpoint. - * + * * In progress transfers on this EP may be delivered after this call. - * + * */ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) { diff --git a/src/device/usbd.h b/src/device/usbd.h index b5f7dceba..f9700811f 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -1,4 +1,4 @@ -/* +/* * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) @@ -529,7 +529,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb //------------- DFU Runtime -------------// #define TUD_DFU_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC) -#define TUD_DFU_APP_SUBCLASS 0x01u +#define TUD_DFU_APP_SUBCLASS (APP_SUBCLASS_DFU_RUNTIME) // Length of template descriptr: 18 bytes #define TUD_DFU_RT_DESC_LEN (9 + 9) @@ -542,6 +542,17 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb /* Function */ \ 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) +// Length of template descriptr: 18 bytes +#define TUD_DFU_MODE_DESC_LEN (9 + 9) + +// DFU runtime descriptor +// Interface number, string index, alternate setting, attributes, detach timeout, transfer size +#define TUD_DFU_MODE_DESCRIPTOR(_itfnum, _stridx, _alt_setting, _attr, _timeout, _xfer_size) \ + /* Interface */ \ + 9, TUSB_DESC_INTERFACE, _itfnum, _alt_setting, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_DFU, _stridx, \ + /* Function */ \ + 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) + //------------- CDC-ECM -------------// diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index d15380199..724c652e6 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -107,7 +107,7 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo _ctrl_xfer.buffer = (uint8_t*) buffer; _ctrl_xfer.total_xferred = 0U; _ctrl_xfer.data_len = tu_min16(len, request->wLength); - + if (request->wLength > 0U) { if(_ctrl_xfer.data_len > 0U) diff --git a/src/tusb_option.h b/src/tusb_option.h index 6cbdefbf1..1af3ee672 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -237,6 +237,10 @@ #define CFG_TUD_DFU_RUNTIME 0 #endif +#ifndef CFG_TUD_DFU_TRANSFER_BUFFER_SIZE + #define CFG_TUD_DFU_TRANSFER_BUFFER_SIZE 64 +#endif + #ifndef CFG_TUD_NET #define CFG_TUD_NET 0 #endif From fb7b47cf07a7a39d39d22502164fec5925fb163e Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Fri, 26 Mar 2021 17:32:03 -0400 Subject: [PATCH 02/23] Minor cleanup --- src/class/dfu/dfu_rt_device.c | 20 +++++++++----------- src/class/dfu/dfu_rt_device.h | 4 ---- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c index 965d1661e..0cbc8eeda 100644 --- a/src/class/dfu/dfu_rt_device.c +++ b/src/class/dfu/dfu_rt_device.c @@ -66,6 +66,7 @@ CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_state_ctx; static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request); static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request); static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength); +static void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request); static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request); //--------------------------------------------------------------------+ @@ -280,6 +281,7 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request return true; } + void tud_dfu_runtime_set_status(dfu_mode_device_status_t status) { _dfu_state_ctx.status = status; @@ -325,21 +327,17 @@ static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * tud_control_xfer(rhport, request, &_dfu_state_ctx.epout_buf, request->wLength); } -static void dfu_runtime_start_status_poll_timeout() +static void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) { - uint8_t bwPollTimeout[3] = {0,0,0}; + uint8_t bwPollTimeout[3] = {0,0,0}; - if ( tud_dfu_runtime_get_poll_timeout_cb ) - { - tud_dfu_runtime_get_poll_timeout_cb((uint8_t *)&bwPollTimeout); - } + if ( tud_dfu_runtime_get_poll_timeout_cb ) + { + tud_dfu_runtime_get_poll_timeout_cb((uint8_t *)&bwPollTimeout); + } - tud_dfu_runtime_start_poll_timeout_cb((uint8_t *)&bwPollTimeout); -} + tud_dfu_runtime_start_poll_timeout_cb((uint8_t *)&bwPollTimeout); -void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) -{ - dfu_runtime_start_status_poll_timeout(); tud_dfu_runtime_req_dnload_data_cb(request->wValue, (uint8_t *)&_dfu_state_ctx.epout_buf, request->wLength); _dfu_state_ctx.blk_transfer_in_proc = false; } diff --git a/src/class/dfu/dfu_rt_device.h b/src/class/dfu/dfu_rt_device.h index 0d8f67a9b..9e7ae029e 100644 --- a/src/class/dfu/dfu_rt_device.h +++ b/src/class/dfu/dfu_rt_device.h @@ -138,10 +138,6 @@ void dfu_rtd_reset(uint8_t rhport); uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); -//--------------------------------------------------------------------+ -void dfu_mode_init(void); -void dfu_mode_reset(uint8_t rhport); -void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request); #ifdef __cplusplus } #endif From 164d3e82e32e1039ba8e471fbc949699c396c9fe Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Mon, 5 Apr 2021 11:13:42 -0400 Subject: [PATCH 03/23] Fix incorrect DNLOAD request len passed to app Fixes bug where the app callback was getting the length of the status request transfer rather than the length of the data stage payload. TODO: Right now this returns the expected length, when it really should be returning the transfer length. --- src/class/dfu/dfu_rt_device.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c index 0cbc8eeda..7d40d2057 100644 --- a/src/class/dfu/dfu_rt_device.c +++ b/src/class/dfu/dfu_rt_device.c @@ -46,8 +46,9 @@ typedef struct TU_ATTR_PACKED bool blk_transfer_in_proc; uint8_t itf_num; - CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_DFU_TRANSFER_BUFFER_SIZE]; - CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_DFU_TRANSFER_BUFFER_SIZE]; + uint16_t last_block_num; + uint16_t last_transfer_len; + CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_TRANSFER_BUFFER_SIZE]; } dfu_state_ctx_t; typedef struct TU_ATTR_PACKED @@ -161,6 +162,8 @@ void dfu_rtd_init(void) _dfu_state_ctx.status = DFU_STATUS_OK; _dfu_state_ctx.attrs = tud_dfu_runtime_init_attrs_cb(); _dfu_state_ctx.blk_transfer_in_proc = false; + _dfu_state_ctx.last_block_num = 0; + _dfu_state_ctx.last_transfer_len = 0; dfu_rtd_debug_print_context(); } @@ -204,6 +207,8 @@ void dfu_rtd_reset(uint8_t rhport) _dfu_state_ctx.status = DFU_STATUS_OK; _dfu_state_ctx.attrs = tud_dfu_runtime_init_attrs_cb(); _dfu_state_ctx.blk_transfer_in_proc = false; + _dfu_state_ctx.last_block_num = 0; + _dfu_state_ctx.last_transfer_len = 0; dfu_rtd_debug_print_context(); } @@ -290,8 +295,8 @@ void tud_dfu_runtime_set_status(dfu_mode_device_status_t status) static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength) { TU_VERIFY( wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE); - uint16_t retval = tud_dfu_runtime_req_upload_data_cb(block_num, (uint8_t *)&_dfu_state_ctx.epin_buf, wLength); - tud_control_xfer(rhport, request, &_dfu_state_ctx.epin_buf, retval); + uint16_t retval = tud_dfu_runtime_req_upload_data_cb(block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, wLength); + tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, retval); return retval; } @@ -319,12 +324,14 @@ static void dfu_req_getstate_reply(uint8_t rhport, tusb_control_request_t const static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request) { + _dfu_state_ctx.last_block_num = request->wValue; + _dfu_state_ctx.last_transfer_len = request->wLength; // TODO: add "zero" copy mode so the buffer we read into can be provided by the user // if they wish, there still will be the internal control buffer copy to this buffer // but this mode would provide zero copy from the class driver to the application // setup for data phase - tud_control_xfer(rhport, request, &_dfu_state_ctx.epout_buf, request->wLength); + tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, request->wLength); } static void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) @@ -338,8 +345,12 @@ static void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t con tud_dfu_runtime_start_poll_timeout_cb((uint8_t *)&bwPollTimeout); - tud_dfu_runtime_req_dnload_data_cb(request->wValue, (uint8_t *)&_dfu_state_ctx.epout_buf, request->wLength); + // TODO: I want the real xferred len, not what is expected. May need to change usbd.c to do this. + tud_dfu_runtime_req_dnload_data_cb(_dfu_state_ctx.last_block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, _dfu_state_ctx.last_transfer_len); _dfu_state_ctx.blk_transfer_in_proc = false; + + _dfu_state_ctx.last_block_num = 0; + _dfu_state_ctx.last_transfer_len = 0; } void tud_dfu_runtime_poll_timeout_done() From c5b8ef15299e9d5787602b39805efbb0ca385110 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Mon, 5 Apr 2021 16:32:58 -0400 Subject: [PATCH 04/23] Separate DFU RT and Mode. Untested --- src/class/dfu/dfu.h | 91 +++++ src/class/dfu/dfu_mode_device.c | 598 ++++++++++++++++++++++++++++ src/class/dfu/dfu_mode_device.h | 132 +++++++ src/class/dfu/dfu_rt_device.c | 674 +++----------------------------- src/class/dfu/dfu_rt_device.h | 70 +--- src/common/tusb_common.h | 2 +- src/device/usbd.c | 12 + src/tusb.h | 6 +- src/tusb_option.h | 6 +- 9 files changed, 905 insertions(+), 686 deletions(-) create mode 100644 src/class/dfu/dfu_mode_device.c create mode 100644 src/class/dfu/dfu_mode_device.h diff --git a/src/class/dfu/dfu.h b/src/class/dfu/dfu.h index 39ce567da..2040e415c 100644 --- a/src/class/dfu/dfu.h +++ b/src/class/dfu/dfu.h @@ -100,6 +100,97 @@ typedef enum { #define DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK (1 << 2) #define DFU_FUNC_ATTR_WILL_DETACH_BITMASK (1 << 3) +// DFU Status Request Payload +typedef struct TU_ATTR_PACKED +{ + uint8_t bStatus; + uint8_t bwPollTimeout[3]; + uint8_t bState; + uint8_t iString; +} dfu_status_req_payload_t; + +TU_VERIFY_STATIC( sizeof(dfu_status_req_payload_t) == 6, "size is not correct"); + + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= 2 + +static tu_lookup_entry_t const _dfu_request_lookup[] = +{ + { .key = DFU_REQUEST_DETACH , .data = "DETACH" }, + { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" }, + { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" }, + { .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" }, + { .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" }, + { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" }, + { .key = DFU_REQUEST_ABORT , .data = "ABORT" }, +}; + +static tu_lookup_table_t const _dfu_request_table = +{ + .count = TU_ARRAY_SIZE(_dfu_request_lookup), + .items = _dfu_request_lookup +}; + +static tu_lookup_entry_t const _dfu_mode_state_lookup[] = +{ + { .key = APP_IDLE , .data = "APP_IDLE" }, + { .key = APP_DETACH , .data = "APP_DETACH" }, + { .key = DFU_IDLE , .data = "DFU_IDLE" }, + { .key = DFU_DNLOAD_SYNC , .data = "DFU_DNLOAD_SYNC" }, + { .key = DFU_DNBUSY , .data = "DFU_DNBUSY" }, + { .key = DFU_DNLOAD_IDLE , .data = "DFU_DNLOAD_IDLE" }, + { .key = DFU_MANIFEST_SYNC , .data = "DFU_MANIFEST_SYNC" }, + { .key = DFU_MANIFEST , .data = "DFU_MANIFEST" }, + { .key = DFU_MANIFEST_WAIT_RESET , .data = "DFU_MANIFEST_WAIT_RESET" }, + { .key = DFU_UPLOAD_IDLE , .data = "DFU_UPLOAD_IDLE" }, + { .key = DFU_ERROR , .data = "DFU_ERROR" }, +}; + +static tu_lookup_table_t const _dfu_mode_state_table = +{ + .count = TU_ARRAY_SIZE(_dfu_mode_state_lookup), + .items = _dfu_mode_state_lookup +}; + +static tu_lookup_entry_t const _dfu_mode_status_lookup[] = +{ + { .key = DFU_STATUS_OK , .data = "OK" }, + { .key = DFU_STATUS_ERRTARGET , .data = "errTARGET" }, + { .key = DFU_STATUS_ERRFILE , .data = "errFILE" }, + { .key = DFU_STATUS_ERRWRITE , .data = "errWRITE" }, + { .key = DFU_STATUS_ERRERASE , .data = "errERASE" }, + { .key = DFU_STATUS_ERRCHECK_ERASED , .data = "errCHECK_ERASED" }, + { .key = DFU_STATUS_ERRPROG , .data = "errPROG" }, + { .key = DFU_STATUS_ERRVERIFY , .data = "errVERIFY" }, + { .key = DFU_STATUS_ERRADDRESS , .data = "errADDRESS" }, + { .key = DFU_STATUS_ERRNOTDONE , .data = "errNOTDONE" }, + { .key = DFU_STATUS_ERRFIRMWARE , .data = "errFIRMWARE" }, + { .key = DFU_STATUS_ERRVENDOR , .data = "errVENDOR" }, + { .key = DFU_STATUS_ERRUSBR , .data = "errUSBR" }, + { .key = DFU_STATUS_ERRPOR , .data = "errPOR" }, + { .key = DFU_STATUS_ERRUNKNOWN , .data = "errUNKNOWN" }, + { .key = DFU_STATUS_ERRSTALLEDPKT , .data = "errSTALLEDPKT" }, +}; + +static tu_lookup_table_t const _dfu_mode_status_table = +{ + .count = TU_ARRAY_SIZE(_dfu_mode_status_lookup), + .items = _dfu_mode_status_lookup +}; + +#endif + +#define dfu_debug_print_context() \ +{ \ + TU_LOG2(" DFU at State: %s\r\n Status: %s\r\n", \ + tu_lookup_find(&_dfu_mode_state_table, _dfu_state_ctx.state), \ + tu_lookup_find(&_dfu_mode_status_table, _dfu_state_ctx.status) ); \ +} + + #ifdef __cplusplus } #endif diff --git a/src/class/dfu/dfu_mode_device.c b/src/class/dfu/dfu_mode_device.c new file mode 100644 index 000000000..e66fb823c --- /dev/null +++ b/src/class/dfu/dfu_mode_device.c @@ -0,0 +1,598 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 XMOS LIMITED + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_MODE) + +#include "dfu_mode_device.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +typedef struct TU_ATTR_PACKED +{ + dfu_mode_device_status_t status; + dfu_mode_state_t state; + uint8_t attrs; + bool blk_transfer_in_proc; + + uint8_t itf_num; + uint16_t last_block_num; + uint16_t last_transfer_len; + CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_TRANSFER_BUFFER_SIZE]; +} dfu_mode_state_ctx_t; + +// Only a single dfu state is allowed +CFG_TUSB_MEM_SECTION static dfu_mode_state_ctx_t _dfu_state_ctx; + + +static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request); +static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request); +static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength); +static void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request); +static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const * request); + + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void dfu_mode_init(void) +{ + _dfu_state_ctx.state = DFU_IDLE; + _dfu_state_ctx.status = DFU_STATUS_OK; + _dfu_state_ctx.attrs = tud_dfu_mode_init_attrs_cb(); + _dfu_state_ctx.blk_transfer_in_proc = false; + _dfu_state_ctx.last_block_num = 0; + _dfu_state_ctx.last_transfer_len = 0; + + dfu_debug_print_context(); +} + +void dfu_mode_reset(uint8_t rhport) +{ + if ( tud_dfu_mode_usb_reset_cb ) + { + tud_dfu_mode_usb_reset_cb(rhport, &_dfu_state_ctx.state); + } else { + switch (_dfu_state_ctx.state) + { + case DFU_IDLE: + case DFU_DNLOAD_SYNC: + case DFU_DNBUSY: + case DFU_DNLOAD_IDLE: + case DFU_MANIFEST_SYNC: + case DFU_MANIFEST: + case DFU_MANIFEST_WAIT_RESET: + case DFU_UPLOAD_IDLE: + { + _dfu_state_ctx.state = (tud_dfu_mode_firmware_valid_check_cb()) ? APP_IDLE : DFU_ERROR; + } + break; + + case DFU_ERROR: + default: + { + _dfu_state_ctx.state = APP_IDLE; + } + break; + } + } + + if(_dfu_state_ctx.state == APP_IDLE) + { + tud_dfu_mode_reboot_to_rt_cb(); + } + + _dfu_state_ctx.status = DFU_STATUS_OK; + _dfu_state_ctx.attrs = tud_dfu_mode_init_attrs_cb(); + _dfu_state_ctx.blk_transfer_in_proc = false; + _dfu_state_ctx.last_block_num = 0; + _dfu_state_ctx.last_transfer_len = 0; + dfu_debug_print_context(); +} + +uint16_t dfu_mode_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) +{ + (void) rhport; + (void) max_len; + + // Ensure this is DFU Mode + TU_VERIFY((itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS) && + (itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU), 0); + + uint8_t const * p_desc = tu_desc_next( itf_desc ); + uint16_t drv_len = sizeof(tusb_desc_interface_t); + + if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) ) + { + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + return drv_len; +} + +// Invoked when a control transfer occurred on an interface of this class +// Driver response accordingly to the request and the transfer stage (setup/data/ack) +// return false to stall control endpoint (e.g unsupported request) +bool dfu_mode_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + if ( (stage == CONTROL_STAGE_DATA) && (request->bRequest == DFU_DNLOAD_SYNC) ) + { + dfu_mode_req_dnload_reply(rhport, request); + return true; + } + + // nothing to do with any other DATA or ACK stage + if ( stage != CONTROL_STAGE_SETUP ) return true; + + TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); + + // dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request + if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type && + TUSB_REQ_SET_INTERFACE == request->bRequest ) + { + tud_control_status(rhport, request); + return true; + } + + // Handle class request only from here + TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + switch (request->bRequest) + { + case DFU_REQUEST_DETACH: + case DFU_REQUEST_DNLOAD: + case DFU_REQUEST_UPLOAD: + case DFU_REQUEST_GETSTATUS: + case DFU_REQUEST_CLRSTATUS: + case DFU_REQUEST_GETSTATE: + case DFU_REQUEST_ABORT: + { + return dfu_mode_state_machine(rhport, request); + } + break; + + default: + { + TU_LOG2(" DFU Nonstandard Request: %u\r\n", request->bRequest); + return ( tud_dfu_mode_req_nonstandard_cb ) ? tud_dfu_mode_req_nonstandard_cb(rhport, stage, request) : false; + } + break; + } + + return true; +} + +static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength) +{ + TU_VERIFY( wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE); + uint16_t retval = tud_dfu_mode_req_upload_data_cb(block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, wLength); + tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, retval); + return retval; +} + +static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request) +{ + dfu_status_req_payload_t resp; + + resp.bStatus = _dfu_state_ctx.status; + if ( tud_dfu_mode_get_poll_timeout_cb ) + { + tud_dfu_mode_get_poll_timeout_cb((uint8_t *)&resp.bwPollTimeout); + } else { + memset((uint8_t *)&resp.bwPollTimeout, 0x00, 3); + } + resp.bState = _dfu_state_ctx.state; + resp.iString = ( tud_dfu_mode_get_status_desc_table_index_cb ) ? tud_dfu_mode_get_status_desc_table_index_cb() : 0; + + tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_req_payload_t)); +} + +static void dfu_req_getstate_reply(uint8_t rhport, tusb_control_request_t const * request) +{ + tud_control_xfer(rhport, request, &_dfu_state_ctx.state, 1); +} + +static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request) +{ + _dfu_state_ctx.last_block_num = request->wValue; + _dfu_state_ctx.last_transfer_len = request->wLength; + // TODO: add "zero" copy mode so the buffer we read into can be provided by the user + // if they wish, there still will be the internal control buffer copy to this buffer + // but this mode would provide zero copy from the class driver to the application + + // setup for data phase + tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, request->wLength); +} + +static void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) +{ + uint8_t bwPollTimeout[3] = {0,0,0}; + + if ( tud_dfu_mode_get_poll_timeout_cb ) + { + tud_dfu_mode_get_poll_timeout_cb((uint8_t *)&bwPollTimeout); + } + + tud_dfu_mode_start_poll_timeout_cb((uint8_t *)&bwPollTimeout); + + // TODO: I want the real xferred len, not what is expected. May need to change usbd.c to do this. + tud_dfu_mode_req_dnload_data_cb(_dfu_state_ctx.last_block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, _dfu_state_ctx.last_transfer_len); + _dfu_state_ctx.blk_transfer_in_proc = false; + + _dfu_state_ctx.last_block_num = 0; + _dfu_state_ctx.last_transfer_len = 0; +} + +void tud_dfu_mode_poll_timeout_done() +{ + if (_dfu_state_ctx.state == DFU_DNBUSY) + { + _dfu_state_ctx.state = DFU_DNLOAD_SYNC; + } else if (_dfu_state_ctx.state == DFU_MANIFEST) + { + _dfu_state_ctx.state = ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) == 0) + ? DFU_MANIFEST_WAIT_RESET : DFU_MANIFEST_SYNC; + } +} + +static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const * request) +{ + TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest)); + TU_LOG2(" DFU State Machine: %s\r\n", tu_lookup_find(&_dfu_mode_state_table, _dfu_state_ctx.state)); + + switch (_dfu_state_ctx.state) + { + case DFU_IDLE: + { + switch (request->bRequest) + { + case DFU_REQUEST_DNLOAD: + { + if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK) != 0) + && (request->wLength > 0) ) + { + _dfu_state_ctx.state = DFU_DNLOAD_SYNC; + _dfu_state_ctx.blk_transfer_in_proc = true; + dfu_req_dnload_setup(rhport, request); + } else { + _dfu_state_ctx.state = DFU_ERROR; + } + } + break; + + case DFU_REQUEST_UPLOAD: + { + if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK) != 0) ) + { + _dfu_state_ctx.state = DFU_UPLOAD_IDLE; + dfu_req_upload(rhport, request, request->wValue, request->wLength); + } else { + _dfu_state_ctx.state = DFU_ERROR; + } + } + break; + + case DFU_REQUEST_GETSTATUS: + { + dfu_req_getstatus_reply(rhport, request); + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + case DFU_REQUEST_ABORT: + { + ; // do nothing, but don't stall so continue on + } + break; + + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_DNLOAD_SYNC: + { + switch (request->bRequest) + { + case DFU_REQUEST_GETSTATUS: + { + if ( _dfu_state_ctx.blk_transfer_in_proc ) + { + _dfu_state_ctx.state = DFU_DNBUSY; + dfu_req_getstatus_reply(rhport, request); + } else { + _dfu_state_ctx.state = DFU_DNLOAD_IDLE; + dfu_req_getstatus_reply(rhport, request); + } + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_DNBUSY: + { + switch (request->bRequest) + { + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_DNLOAD_IDLE: + { + switch (request->bRequest) + { + case DFU_REQUEST_DNLOAD: + { + if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK) != 0) + && (request->wLength > 0) ) + { + _dfu_state_ctx.state = DFU_DNLOAD_SYNC; + _dfu_state_ctx.blk_transfer_in_proc = true; + + dfu_req_dnload_setup(rhport, request); + } else { + if ( tud_dfu_mode_device_data_done_check_cb() ) + { + _dfu_state_ctx.state = DFU_MANIFEST_SYNC; + tud_control_status(rhport, request); + } else { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall + } + } + } + break; + + case DFU_REQUEST_GETSTATUS: + { + dfu_req_getstatus_reply(rhport, request); + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + case DFU_REQUEST_ABORT: + { + if ( tud_dfu_mode_abort_cb ) + { + tud_dfu_mode_abort_cb(); + } + _dfu_state_ctx.state = DFU_IDLE; + } + break; + + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_MANIFEST_SYNC: + { + switch (request->bRequest) + { + case DFU_REQUEST_GETSTATUS: + { + if ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) == 0) + { + _dfu_state_ctx.state = DFU_MANIFEST; + dfu_req_getstatus_reply(rhport, request); + } else { + if ( tud_dfu_mode_firmware_valid_check_cb() ) + { + _dfu_state_ctx.state = DFU_IDLE; + } + dfu_req_getstatus_reply(rhport, request); + } + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + default: + { + _dfu_state_ctx.state = DFU_ERROR; + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_MANIFEST: + { + switch (request->bRequest) + { + default: + { + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_MANIFEST_WAIT_RESET: + { + // technically we should never even get here, but we will handle it just in case + TU_LOG2(" DFU was in DFU_MANIFEST_WAIT_RESET and got unexpected request: %u\r\n", request->bRequest); + switch (request->bRequest) + { + default: + { + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_UPLOAD_IDLE: + { + switch (request->bRequest) + { + case DFU_REQUEST_UPLOAD: + { + if (dfu_req_upload(rhport, request, request->wValue, request->wLength) != request->wLength) + { + _dfu_state_ctx.state = DFU_IDLE; + } + } + break; + + case DFU_REQUEST_GETSTATUS: + { + dfu_req_getstatus_reply(rhport, request); + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + case DFU_REQUEST_ABORT: + { + if (tud_dfu_mode_abort_cb) + { + tud_dfu_mode_abort_cb(); + } + _dfu_state_ctx.state = DFU_IDLE; + } + break; + + default: + { + return false; // stall on all other requests + } + break; + } + } + break; + + case DFU_ERROR: + { + switch (request->bRequest) + { + case DFU_REQUEST_GETSTATUS: + { + dfu_req_getstatus_reply(rhport, request); + } + break; + + case DFU_REQUEST_CLRSTATUS: + { + _dfu_state_ctx.state = DFU_IDLE; + } + break; + + case DFU_REQUEST_GETSTATE: + { + dfu_req_getstate_reply(rhport, request); + } + break; + + default: + { + return false; // stall on all other requests + } + break; + } + } + break; + + default: + _dfu_state_ctx.state = DFU_ERROR; + TU_LOG2(" DFU ERROR: Unexpected state\r\nStalling control pipe\r\n"); + return false; // Unexpected state, stall and change to error + } + + return true; +} + + +bool dfu_mode_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) rhport; + (void) ep_addr; + (void) result; + (void) xferred_bytes; + return true; +} + + + #endif diff --git a/src/class/dfu/dfu_mode_device.h b/src/class/dfu/dfu_mode_device.h new file mode 100644 index 000000000..32bf58bbd --- /dev/null +++ b/src/class/dfu/dfu_mode_device.h @@ -0,0 +1,132 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 XMOS LIMITED + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DFU_MODE_DEVICE_H_ +#define _TUSB_DFU_MODE_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" +#include "dfu.h" + +#ifdef __cplusplus + extern "C" { +#endif + + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ +// Invoked when a reset is received to check if firmware is valid +bool tud_dfu_mode_firmware_valid_check_cb(); + +// Invoked when the device must reboot to dfu runtime mode +void tud_dfu_mode_reboot_to_rt_cb(); + +// Invoked during initialization of the dfu driver to set attributes +// Return byte set with bitmasks: +// DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK +// DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK +// DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK +// DFU_FUNC_ATTR_WILL_DETACH_BITMASK +// Note: This should match the USB descriptor +uint8_t tud_dfu_mode_init_attrs_cb(); + +// Invoked during a DFU_GETSTATUS request to get for the string index +// to the status description string table. +TU_ATTR_WEAK uint8_t tud_dfu_mode_get_status_desc_table_index_cb(); + +// Invoked during a USB reset +// Lets the app perform custom behavior on a USB reset. +// If not defined, the default behavior remains the DFU specification of +// Checking the firmware valid and changing to the error state or rebooting to runtime +// Note: If used, the application must perform the reset logic for all states. +// Changing the state to APP_IDLE will result in tud_dfu_mode_reboot_to_rt_cb being called +TU_ATTR_WEAK void tud_dfu_mode_usb_reset_cb(uint8_t rhport, dfu_mode_state_t *state); + +// Invoked during a DFU_GETSTATUS request to set the timeout in ms to use +// before the subsequent DFU_GETSTATUS requests. +// The purpose of this value is to allow the device to tell the host +// how long to wait between the DFU_DNLOAD and DFU_GETSTATUS checks +// to allow the device to have time to erase, write memory, etc. +// ms_timeout is a pointer to array of length 3. +// Refer to the USB DFU class specification for more details +TU_ATTR_WEAK void tud_dfu_mode_get_poll_timeout_cb(uint8_t *ms_timeout); + +// Invoked when a DFU_DNLOAD request is received +// This should start a timer for ms_timeout ms +// When the timer has elapsed, tud_dfu_runtime_poll_timeout_done must be called +// NOTE: This callback should return immediately, and not implement the delay +// internally, as this will hold up the class stack from receiving any packets +// during the DFU_DNLOAD_SYNC and DFU_DNBUSY states +void tud_dfu_mode_start_poll_timeout_cb(uint8_t *ms_timeout); + +// Must be called when the poll_timeout has elapsed +void tud_dfu_mode_poll_timeout_done(); + +// Invoked when a DFU_DNLOAD request is received +// This callback takes the wBlockNum chunk of length length and provides it +// to the application at the data pointer. This data is only valid for this +// call, so the app must use it not or copy it. +void tud_dfu_mode_req_dnload_data_cb(uint16_t wBlockNum, uint8_t* data, uint16_t length); + +// Invoked during the last DFU_DNLOAD request, signifying that the host believes +// it is done transmitting data. +// Return true if the application agrees there is no more data +// Return false if the device disagrees, which will stall the pipe, and the Host +// should initiate a recovery procedure +bool tud_dfu_mode_device_data_done_check_cb(); + +// Invoked when the Host has terminated a download or upload transfer +TU_ATTR_WEAK void tud_dfu_mode_abort_cb(); + +// Invoked when a DFU_UPLOAD request is received +// This callback must populate data with up to length bytes +// Return the number of bytes to write +uint16_t tud_dfu_mode_req_upload_data_cb(uint16_t block_num, uint8_t* data, uint16_t length); + +// Invoked when a nonstandard request is received +// Use may be vendor specific. +// Return false to stall +TU_ATTR_WEAK bool tud_dfu_mode_req_nonstandard_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + +// Invoked during a DFU_GETSTATUS request to get for the string index +// to the status description string table. +TU_ATTR_WEAK uint8_t tud_dfu_mode_get_status_desc_table_index_cb(); +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void dfu_mode_init(void); +void dfu_mode_reset(uint8_t rhport); +uint16_t dfu_mode_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +bool dfu_mode_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +bool dfu_mode_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DFU_MODE_DEVICE_H_ */ diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c index 7d40d2057..806bfba2e 100644 --- a/src/class/dfu/dfu_rt_device.c +++ b/src/class/dfu/dfu_rt_device.c @@ -40,176 +40,39 @@ //--------------------------------------------------------------------+ typedef struct TU_ATTR_PACKED { - dfu_mode_device_status_t status; - dfu_mode_state_t state; - uint8_t attrs; - bool blk_transfer_in_proc; - - uint8_t itf_num; - uint16_t last_block_num; - uint16_t last_transfer_len; - CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_TRANSFER_BUFFER_SIZE]; -} dfu_state_ctx_t; - -typedef struct TU_ATTR_PACKED -{ - uint8_t bStatus; - uint8_t bwPollTimeout[3]; - uint8_t bState; - uint8_t iString; -} dfu_status_req_payload_t; - -TU_VERIFY_STATIC( sizeof(dfu_status_req_payload_t) == 6, "size is not correct"); + dfu_mode_device_status_t status; + dfu_mode_state_t state; + uint8_t attrs; +} dfu_rt_state_ctx_t; // Only a single dfu state is allowed -CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_state_ctx; +CFG_TUSB_MEM_SECTION static dfu_rt_state_ctx_t _dfu_state_ctx; -static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request); -static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request); -static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength); -static void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request); -static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request); - -//--------------------------------------------------------------------+ -// Debug -//--------------------------------------------------------------------+ -#if CFG_TUSB_DEBUG >= 2 - -static tu_lookup_entry_t const _dfu_request_lookup[] = -{ - { .key = DFU_REQUEST_DETACH , .data = "DETACH" }, - { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" }, - { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" }, - { .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" }, - { .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" }, - { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" }, - { .key = DFU_REQUEST_ABORT , .data = "ABORT" }, -}; - -static tu_lookup_table_t const _dfu_request_table = -{ - .count = TU_ARRAY_SIZE(_dfu_request_lookup), - .items = _dfu_request_lookup -}; - -static tu_lookup_entry_t const _dfu_mode_state_lookup[] = -{ - { .key = APP_IDLE , .data = "APP_IDLE" }, - { .key = APP_DETACH , .data = "APP_DETACH" }, - { .key = DFU_IDLE , .data = "DFU_IDLE" }, - { .key = DFU_DNLOAD_SYNC , .data = "DFU_DNLOAD_SYNC" }, - { .key = DFU_DNBUSY , .data = "DFU_DNBUSY" }, - { .key = DFU_DNLOAD_IDLE , .data = "DFU_DNLOAD_IDLE" }, - { .key = DFU_MANIFEST_SYNC , .data = "DFU_MANIFEST_SYNC" }, - { .key = DFU_MANIFEST , .data = "DFU_MANIFEST" }, - { .key = DFU_MANIFEST_WAIT_RESET , .data = "DFU_MANIFEST_WAIT_RESET" }, - { .key = DFU_UPLOAD_IDLE , .data = "DFU_UPLOAD_IDLE" }, - { .key = DFU_ERROR , .data = "DFU_ERROR" }, -}; - -static tu_lookup_table_t const _dfu_mode_state_table = -{ - .count = TU_ARRAY_SIZE(_dfu_mode_state_lookup), - .items = _dfu_mode_state_lookup -}; - -static tu_lookup_entry_t const _dfu_mode_status_lookup[] = -{ - { .key = DFU_STATUS_OK , .data = "OK" }, - { .key = DFU_STATUS_ERRTARGET , .data = "errTARGET" }, - { .key = DFU_STATUS_ERRFILE , .data = "errFILE" }, - { .key = DFU_STATUS_ERRWRITE , .data = "errWRITE" }, - { .key = DFU_STATUS_ERRERASE , .data = "errERASE" }, - { .key = DFU_STATUS_ERRCHECK_ERASED , .data = "errCHECK_ERASED" }, - { .key = DFU_STATUS_ERRPROG , .data = "errPROG" }, - { .key = DFU_STATUS_ERRVERIFY , .data = "errVERIFY" }, - { .key = DFU_STATUS_ERRADDRESS , .data = "errADDRESS" }, - { .key = DFU_STATUS_ERRNOTDONE , .data = "errNOTDONE" }, - { .key = DFU_STATUS_ERRFIRMWARE , .data = "errFIRMWARE" }, - { .key = DFU_STATUS_ERRVENDOR , .data = "errVENDOR" }, - { .key = DFU_STATUS_ERRUSBR , .data = "errUSBR" }, - { .key = DFU_STATUS_ERRPOR , .data = "errPOR" }, - { .key = DFU_STATUS_ERRUNKNOWN , .data = "errUNKNOWN" }, - { .key = DFU_STATUS_ERRSTALLEDPKT , .data = "errSTALLEDPKT" }, -}; - -static tu_lookup_table_t const _dfu_mode_status_table = -{ - .count = TU_ARRAY_SIZE(_dfu_mode_status_lookup), - .items = _dfu_mode_status_lookup -}; - -#endif - -#define dfu_rtd_debug_print_context() \ -{ \ - TU_LOG2(" DFU at State: %s\r\n Status: %s\r\n", \ - tu_lookup_find(&_dfu_mode_state_table, _dfu_state_ctx.state), \ - tu_lookup_find(&_dfu_mode_status_table, _dfu_state_ctx.status) ); \ -} +static void dfu_rt_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request); //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ void dfu_rtd_init(void) { - if ( tud_dfu_runtime_init_cb ) { - _dfu_state_ctx.state = (tud_dfu_runtime_init_cb() == DFU_PROTOCOL_DFU) ? APP_DETACH : APP_IDLE; - } else { - _dfu_state_ctx.state = APP_IDLE; - } - + _dfu_state_ctx.state = APP_IDLE; _dfu_state_ctx.status = DFU_STATUS_OK; _dfu_state_ctx.attrs = tud_dfu_runtime_init_attrs_cb(); - _dfu_state_ctx.blk_transfer_in_proc = false; - _dfu_state_ctx.last_block_num = 0; - _dfu_state_ctx.last_transfer_len = 0; - - dfu_rtd_debug_print_context(); + dfu_debug_print_context(); } void dfu_rtd_reset(uint8_t rhport) { - if ( tud_dfu_runtime_usb_reset_cb ) + if (((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_WILL_DETACH_BITMASK) == 0) + && (_dfu_state_ctx.state == DFU_REQUEST_DETACH)) { - tud_dfu_runtime_usb_reset_cb(rhport, _dfu_state_ctx.state); - } - - switch (_dfu_state_ctx.state) - { - case APP_DETACH: - { - _dfu_state_ctx.state = DFU_IDLE; - } - break; - - case DFU_IDLE: - case DFU_DNLOAD_SYNC: - case DFU_DNBUSY: - case DFU_DNLOAD_IDLE: - case DFU_MANIFEST_SYNC: - case DFU_MANIFEST: - case DFU_MANIFEST_WAIT_RESET: - case DFU_UPLOAD_IDLE: - { - _dfu_state_ctx.state = (tud_dfu_runtime_firmware_valid_check_cb()) ? APP_IDLE : DFU_ERROR; - } - break; - - case DFU_ERROR: - default: - { - _dfu_state_ctx.state = APP_IDLE; - } - break; + tud_dfu_runtime_reboot_to_dfu_cb(); } + _dfu_state_ctx.state = APP_IDLE; _dfu_state_ctx.status = DFU_STATUS_OK; _dfu_state_ctx.attrs = tud_dfu_runtime_init_attrs_cb(); - _dfu_state_ctx.blk_transfer_in_proc = false; - _dfu_state_ctx.last_block_num = 0; - _dfu_state_ctx.last_transfer_len = 0; - dfu_rtd_debug_print_context(); + dfu_debug_print_context(); } uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) @@ -217,10 +80,9 @@ uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, ui (void) rhport; (void) max_len; - // Ensure this is DFU Runtime or Mode - TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && - ( (itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT) - | (itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU) ), 0); + // Ensure this is DFU Runtime + TU_VERIFY((itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS) && + (itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT), 0); uint8_t const * p_desc = tu_desc_next( itf_desc ); uint16_t drv_len = sizeof(tusb_desc_interface_t); @@ -239,16 +101,9 @@ uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, ui // return false to stall control endpoint (e.g unsupported request) bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { - if ( (stage == CONTROL_STAGE_DATA) && (request->bRequest == DFU_DNLOAD_SYNC) ) - { - dfu_mode_req_dnload_reply(rhport, request); - return true; - } - - // nothing to do with any other DATA or ACK stage + // nothing to do with DATA or ACK stage if ( stage != CONTROL_STAGE_SETUP ) return true; - TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); // dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request @@ -262,23 +117,48 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request // Handle class request only from here TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest)); switch (request->bRequest) { case DFU_REQUEST_DETACH: - case DFU_REQUEST_DNLOAD: - case DFU_REQUEST_UPLOAD: - case DFU_REQUEST_GETSTATUS: - case DFU_REQUEST_CLRSTATUS: - case DFU_REQUEST_GETSTATE: - case DFU_REQUEST_ABORT: { - return dfu_state_machine(rhport, request); + if (_dfu_state_ctx.state == APP_IDLE) + { + _dfu_state_ctx.state = APP_DETACH; + if ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_WILL_DETACH_BITMASK) == 1) + { + tud_dfu_runtime_reboot_to_dfu_cb(); + } else { + tud_dfu_runtime_detach_start_timer_cb(request->wValue); + } + } else { + TU_LOG2(" DFU Unexpected request during state %s: %u\r\n", tu_lookup_find(&_dfu_mode_state_table, _dfu_state_ctx.state), request->bRequest); + return false; + } + } + break; + + case DFU_REQUEST_GETSTATUS: + { + dfu_status_req_payload_t resp; + resp.bStatus = _dfu_state_ctx.status; + memset((uint8_t *)&resp.bwPollTimeout, 0x00, 3); // Value is ignored + resp.bState = _dfu_state_ctx.state; + resp.iString = ( tud_dfu_runtime_get_status_desc_table_index_cb ) ? tud_dfu_runtime_get_status_desc_table_index_cb() : 0; + + tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_req_payload_t)); + } + break; + + case DFU_REQUEST_GETSTATE: + { + tud_control_xfer(rhport, request, &_dfu_state_ctx.state, 1); } break; default: { - TU_LOG2(" DFU Nonstandard Request: %u\r\n", request->bRequest); + TU_LOG2(" DFU Nonstandard Runtime Request: %u\r\n", request->bRequest); return ( tud_dfu_runtime_req_nonstandard_cb ) ? tud_dfu_runtime_req_nonstandard_cb(rhport, stage, request) : false; } break; @@ -292,460 +172,12 @@ void tud_dfu_runtime_set_status(dfu_mode_device_status_t status) _dfu_state_ctx.status = status; } -static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength) +void tud_dfu_runtime_detach_timer_elapsed() { - TU_VERIFY( wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE); - uint16_t retval = tud_dfu_runtime_req_upload_data_cb(block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, wLength); - tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, retval); - return retval; -} - -static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request) -{ - dfu_status_req_payload_t resp; - - resp.bStatus = _dfu_state_ctx.status; - if ( tud_dfu_runtime_get_poll_timeout_cb ) + if (_dfu_state_ctx.state == DFU_REQUEST_DETACH) { - tud_dfu_runtime_get_poll_timeout_cb((uint8_t *)&resp.bwPollTimeout); - } else { - memset((uint8_t *)&resp.bwPollTimeout, 0x00, 3); - } - resp.bState = _dfu_state_ctx.state; - resp.iString = ( tud_dfu_runtime_get_status_desc_table_index_cb ) ? tud_dfu_runtime_get_status_desc_table_index_cb() : 0; - - tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_req_payload_t)); -} - -static void dfu_req_getstate_reply(uint8_t rhport, tusb_control_request_t const * request) -{ - tud_control_xfer(rhport, request, &_dfu_state_ctx.state, 1); -} - -static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request) -{ - _dfu_state_ctx.last_block_num = request->wValue; - _dfu_state_ctx.last_transfer_len = request->wLength; - // TODO: add "zero" copy mode so the buffer we read into can be provided by the user - // if they wish, there still will be the internal control buffer copy to this buffer - // but this mode would provide zero copy from the class driver to the application - - // setup for data phase - tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, request->wLength); -} - -static void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) -{ - uint8_t bwPollTimeout[3] = {0,0,0}; - - if ( tud_dfu_runtime_get_poll_timeout_cb ) - { - tud_dfu_runtime_get_poll_timeout_cb((uint8_t *)&bwPollTimeout); - } - - tud_dfu_runtime_start_poll_timeout_cb((uint8_t *)&bwPollTimeout); - - // TODO: I want the real xferred len, not what is expected. May need to change usbd.c to do this. - tud_dfu_runtime_req_dnload_data_cb(_dfu_state_ctx.last_block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, _dfu_state_ctx.last_transfer_len); - _dfu_state_ctx.blk_transfer_in_proc = false; - - _dfu_state_ctx.last_block_num = 0; - _dfu_state_ctx.last_transfer_len = 0; -} - -void tud_dfu_runtime_poll_timeout_done() -{ - if (_dfu_state_ctx.state == DFU_DNBUSY) - { - _dfu_state_ctx.state = DFU_DNLOAD_SYNC; - } else if (_dfu_state_ctx.state == DFU_MANIFEST) - { - _dfu_state_ctx.state = ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) == 0) - ? DFU_MANIFEST_WAIT_RESET : DFU_MANIFEST_SYNC; + _dfu_state_ctx.state = APP_IDLE; } } -static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request) -{ - TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest)); - TU_LOG2(" DFU State Machine: %s\r\n", tu_lookup_find(&_dfu_mode_state_table, _dfu_state_ctx.state)); - - switch (_dfu_state_ctx.state) - { - case APP_IDLE: - { - switch (request->bRequest) - { - case DFU_REQUEST_DETACH: - { - _dfu_state_ctx.state = APP_DETACH; - if ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_WILL_DETACH_BITMASK) == 1) - { - tud_dfu_runtime_reboot_to_dfu_cb(); - } else { - tud_dfu_runtime_detach_start_timer_cb(request->wValue); - } - } - break; - - case DFU_REQUEST_GETSTATUS: - { - dfu_req_getstatus_reply(rhport, request); - } - break; - - case DFU_REQUEST_GETSTATE: - { - dfu_req_getstate_reply(rhport, request); - } - break; - - default: - { - _dfu_state_ctx.state = DFU_ERROR; - return false; // stall on all other requests - } - break; - } - } - break; - - case APP_DETACH: - { - switch (request->bRequest) - { - case DFU_REQUEST_GETSTATUS: - { - dfu_req_getstatus_reply(rhport, request); - } - break; - - case DFU_REQUEST_GETSTATE: - { - dfu_req_getstate_reply(rhport, request); - } - break; - - default: - { - _dfu_state_ctx.state = APP_IDLE; - return false; // stall on all other requests - } - break; - } - } - break; - - case DFU_IDLE: - { - switch (request->bRequest) - { - case DFU_REQUEST_DNLOAD: - { - if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK) != 0) - && (request->wLength > 0) ) - { - _dfu_state_ctx.state = DFU_DNLOAD_SYNC; - _dfu_state_ctx.blk_transfer_in_proc = true; - dfu_req_dnload_setup(rhport, request); - } else { - _dfu_state_ctx.state = DFU_ERROR; - } - } - break; - - case DFU_REQUEST_UPLOAD: - { - if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK) != 0) ) - { - _dfu_state_ctx.state = DFU_UPLOAD_IDLE; - dfu_req_upload(rhport, request, request->wValue, request->wLength); - } else { - _dfu_state_ctx.state = DFU_ERROR; - } - } - break; - - case DFU_REQUEST_GETSTATUS: - { - dfu_req_getstatus_reply(rhport, request); - } - break; - - case DFU_REQUEST_GETSTATE: - { - dfu_req_getstate_reply(rhport, request); - } - break; - - case DFU_REQUEST_ABORT: - { - ; // do nothing, but don't stall so continue on - } - break; - - default: - { - _dfu_state_ctx.state = DFU_ERROR; - return false; // stall on all other requests - } - break; - } - } - break; - - case DFU_DNLOAD_SYNC: - { - switch (request->bRequest) - { - case DFU_REQUEST_GETSTATUS: - { - if ( _dfu_state_ctx.blk_transfer_in_proc ) - { - _dfu_state_ctx.state = DFU_DNBUSY; - dfu_req_getstatus_reply(rhport, request); - } else { - _dfu_state_ctx.state = DFU_DNLOAD_IDLE; - dfu_req_getstatus_reply(rhport, request); - } - } - break; - - case DFU_REQUEST_GETSTATE: - { - dfu_req_getstate_reply(rhport, request); - } - break; - - default: - { - _dfu_state_ctx.state = DFU_ERROR; - return false; // stall on all other requests - } - break; - } - } - break; - - case DFU_DNBUSY: - { - switch (request->bRequest) - { - default: - { - _dfu_state_ctx.state = DFU_ERROR; - return false; // stall on all other requests - } - break; - } - } - break; - - case DFU_DNLOAD_IDLE: - { - switch (request->bRequest) - { - case DFU_REQUEST_DNLOAD: - { - if( ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK) != 0) - && (request->wLength > 0) ) - { - _dfu_state_ctx.state = DFU_DNLOAD_SYNC; - _dfu_state_ctx.blk_transfer_in_proc = true; - - dfu_req_dnload_setup(rhport, request); - } else { - if ( tud_dfu_runtime_device_data_done_check_cb() ) - { - _dfu_state_ctx.state = DFU_MANIFEST_SYNC; - tud_control_status(rhport, request); - } else { - _dfu_state_ctx.state = DFU_ERROR; - return false; // stall - } - } - } - break; - - case DFU_REQUEST_GETSTATUS: - { - dfu_req_getstatus_reply(rhport, request); - } - break; - - case DFU_REQUEST_GETSTATE: - { - dfu_req_getstate_reply(rhport, request); - } - break; - - case DFU_REQUEST_ABORT: - { - if ( tud_dfu_runtime_abort_cb ) - { - tud_dfu_runtime_abort_cb(); - } - _dfu_state_ctx.state = DFU_IDLE; - } - break; - - default: - { - _dfu_state_ctx.state = DFU_ERROR; - return false; // stall on all other requests - } - break; - } - } - break; - - case DFU_MANIFEST_SYNC: - { - switch (request->bRequest) - { - case DFU_REQUEST_GETSTATUS: - { - if ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) == 0) - { - _dfu_state_ctx.state = DFU_MANIFEST; - dfu_req_getstatus_reply(rhport, request); - } else { - if ( tud_dfu_runtime_firmware_valid_check_cb() ) - { - _dfu_state_ctx.state = DFU_IDLE; - } - dfu_req_getstatus_reply(rhport, request); - } - } - break; - - case DFU_REQUEST_GETSTATE: - { - dfu_req_getstate_reply(rhport, request); - } - break; - - default: - { - _dfu_state_ctx.state = DFU_ERROR; - return false; // stall on all other requests - } - break; - } - } - break; - - case DFU_MANIFEST: - { - switch (request->bRequest) - { - default: - { - return false; // stall on all other requests - } - break; - } - } - break; - - case DFU_MANIFEST_WAIT_RESET: - { - // technically we should never even get here, but we will handle it just in case - TU_LOG2(" DFU was in DFU_MANIFEST_WAIT_RESET and got unexpected request: %u\r\n", request->bRequest); - switch (request->bRequest) - { - default: - { - return false; // stall on all other requests - } - break; - } - } - break; - - case DFU_UPLOAD_IDLE: - { - switch (request->bRequest) - { - case DFU_REQUEST_UPLOAD: - { - if (dfu_req_upload(rhport, request, request->wValue, request->wLength) != request->wLength) - { - _dfu_state_ctx.state = DFU_IDLE; - } - } - break; - - case DFU_REQUEST_GETSTATUS: - { - dfu_req_getstatus_reply(rhport, request); - } - break; - - case DFU_REQUEST_GETSTATE: - { - dfu_req_getstate_reply(rhport, request); - } - break; - - case DFU_REQUEST_ABORT: - { - if (tud_dfu_runtime_abort_cb) - { - tud_dfu_runtime_abort_cb(); - } - _dfu_state_ctx.state = DFU_IDLE; - } - break; - - default: - { - return false; // stall on all other requests - } - break; - } - } - break; - - case DFU_ERROR: - { - switch (request->bRequest) - { - case DFU_REQUEST_GETSTATUS: - { - dfu_req_getstatus_reply(rhport, request); - } - break; - - case DFU_REQUEST_CLRSTATUS: - { - _dfu_state_ctx.state = DFU_IDLE; - } - break; - - case DFU_REQUEST_GETSTATE: - { - dfu_req_getstate_reply(rhport, request); - } - break; - - default: - { - return false; // stall on all other requests - } - break; - } - } - break; - - default: - _dfu_state_ctx.state = DFU_ERROR; - TU_LOG2(" DFU ERROR: Unexpected state\r\nStalling control pipe\r\n"); - return false; // Unexpected state, stall and change to error - } - - return true; -} - - #endif diff --git a/src/class/dfu/dfu_rt_device.h b/src/class/dfu/dfu_rt_device.h index 9e7ae029e..23703862d 100644 --- a/src/class/dfu/dfu_rt_device.h +++ b/src/class/dfu/dfu_rt_device.h @@ -43,28 +43,26 @@ // Value is not checked to allow for custom statuses to be used void tud_dfu_runtime_set_status(dfu_mode_device_status_t status); -// Invoked when a DFU_DETACH request is received and bitWillDetatch is set +// Invoked when a DFU_DETACH request is received and bitWillDetach is set void tud_dfu_runtime_reboot_to_dfu_cb(); -// Invoked when a DFU_DETACH request is received and bitWillDetatch is not set +// Invoked when a DFU_DETACH request is received and bitWillDetach is not set // This should start a timer for wTimeout ms -// The application should then look for a USB reset signal to occur before -// the timeout has elapsed. The reset signal will invoke tud_dfu_runtime_usb_reset_cb. -// If this reset signal is received before the timeout, then the application must -// switch to DFU mode. -// If the timer expires, the app does not need to do anything. +// When the timer has elapsed, the app must call tud_dfu_runtime_detach_timer_elapsed +// If a USB reset is called while the timer is running, the class will call +// tud_dfu_runtime_reboot_to_dfu_cb. // NOTE: This callback should return immediately, and not implement the delay // internally, as this can will hold up the USB stack TU_ATTR_WEAK void tud_dfu_runtime_detach_start_timer_cb(uint16_t wTimeout); +// Invoke when the dfu runtime detach timer has elapsed +void tud_dfu_runtime_detach_timer_elapsed(); + // Invoked when a nonstandard request is received // Use may be vendor specific. // Return false to stall TU_ATTR_WEAK bool tud_dfu_runtime_req_nonstandard_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); -// Invoked when a reset is received to check if firmware is valid -bool tud_dfu_runtime_firmware_valid_check_cb(); - // Invoked during initialization of the dfu driver to set attributes // Return byte set with bitmasks: // DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK @@ -74,62 +72,10 @@ bool tud_dfu_runtime_firmware_valid_check_cb(); // Note: This should match the USB descriptor uint8_t tud_dfu_runtime_init_attrs_cb(); -// Invoked during initialization of the dfu driver to start as RT or DFU -// This will determine if the internal state machine will begin in -// APP_IDLE or DFU_IDLE -TU_ATTR_WEAK dfu_protocol_type_t tud_dfu_runtime_init_cb(); - // Invoked during a DFU_GETSTATUS request to get for the string index // to the status description string table. TU_ATTR_WEAK uint8_t tud_dfu_runtime_get_status_desc_table_index_cb(); -// Invoked during a USB reset -// Lets the app know that a USB reset has occurred so it can act accordingly, -// See tud_dfu_runtime_detach_start_timer_cb for how to handle the APP_DETACH state -// Other states will be application specific -TU_ATTR_WEAK void tud_dfu_runtime_usb_reset_cb(uint8_t rhport, dfu_mode_state_t state); - -// Invoked during a DFU_GETSTATUS request to set the timeout in ms to use -// before the subsequent DFU_GETSTATUS requests. -// The purpose of this value is to allow the device to tell the host -// how long to wait between the DFU_DNLOAD and DFU_GETSTATUS checks -// to allow the device to have time to erase, write memory, etc. -// ms_timeout is a pointer to array of length 3. -// Refer to the USB DFU class specification for more details -TU_ATTR_WEAK void tud_dfu_runtime_get_poll_timeout_cb(uint8_t *ms_timeout); - -// Invoked when a DFU_DNLOAD request is received -// This should start a timer for ms_timeout ms -// When the timer has elapsed, tud_dfu_runtime_poll_timeout_done must be called -// NOTE: This callback should return immediately, and not implement the delay -// internally, as this will hold up the class stack from receiving any packets -// during the DFU_DNLOAD_SYNC and DFU_DNBUSY states -void tud_dfu_runtime_start_poll_timeout_cb(uint8_t *ms_timeout); - -// Must be called when the poll_timeout has elapsed -void tud_dfu_runtime_poll_timeout_done(); - -// Invoked when a DFU_DNLOAD request is received -// This callback takes the wBlockNum chunk of length length and provides it -// to the application at the data pointer. This data is only valid for this -// call, so the app must use it not or copy it. -void tud_dfu_runtime_req_dnload_data_cb(uint16_t wBlockNum, uint8_t* data, uint16_t length); - -// Invoked during the last DFU_DNLOAD request, signifying that the host believes -// it is done transmitting data. -// Return true if the application agrees there is no more data -// Return false if the device disagrees, which will stall the pipe, and the Host -// should initiate a recovery procedure -bool tud_dfu_runtime_device_data_done_check_cb(); - -// Invoked when the Host has terminated a download or upload transfer -TU_ATTR_WEAK void tud_dfu_runtime_abort_cb(); - -// Invoked when a DFU_UPLOAD request is received -// This callback must populate data with up to length bytes -// Return the number of bytes to write -uint16_t tud_dfu_runtime_req_upload_data_cb(uint16_t block_num, uint8_t* data, uint16_t length); - //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index f10be88ab..859d9d47d 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -1,4 +1,4 @@ -/* +/* * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) diff --git a/src/device/usbd.c b/src/device/usbd.c index 28c9acdd2..91838b39c 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -187,6 +187,18 @@ static usbd_class_driver_t const _usbd_driver[] = }, #endif + #if CFG_TUD_DFU_MODE + { + DRIVER_NAME("DFU-MODE") + .init = dfu_mode_init, + .reset = dfu_mode_reset, + .open = dfu_mode_open, + .control_xfer_cb = dfu_mode_control_xfer_cb, + .xfer_cb = dfu_mode_xfer_cb, + .sof = NULL + }, + #endif + #if CFG_TUD_NET { DRIVER_NAME("NET") diff --git a/src/tusb.h b/src/tusb.h index cc82c440d..c9558515c 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -1,4 +1,4 @@ -/* +/* * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) @@ -96,6 +96,10 @@ #include "class/dfu/dfu_rt_device.h" #endif + #if CFG_TUD_DFU_MODE + #include "class/dfu/dfu_mode_device.h" + #endif + #if CFG_TUD_NET #include "class/net/net_device.h" #endif diff --git a/src/tusb_option.h b/src/tusb_option.h index 1af3ee672..3cd3df9f2 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -234,7 +234,11 @@ #endif #ifndef CFG_TUD_DFU_RUNTIME - #define CFG_TUD_DFU_RUNTIME 0 + #define CFG_TUD_DFU_RUNTIME 0 +#endif + +#ifndef CFG_TUD_DFU_MODE + #define CFG_TUD_DFU_MODE 0 #endif #ifndef CFG_TUD_DFU_TRANSFER_BUFFER_SIZE From fdc91f8d2c3364a9a5cea2b0efff2167c9bf4786 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Mon, 5 Apr 2021 16:48:09 -0400 Subject: [PATCH 05/23] Fix bug during initialization of DFU Mode --- src/class/dfu/dfu_mode_device.c | 45 ++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/class/dfu/dfu_mode_device.c b/src/class/dfu/dfu_mode_device.c index e66fb823c..68f55510b 100644 --- a/src/class/dfu/dfu_mode_device.c +++ b/src/class/dfu/dfu_mode_device.c @@ -67,7 +67,7 @@ static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const //--------------------------------------------------------------------+ void dfu_mode_init(void) { - _dfu_state_ctx.state = DFU_IDLE; + _dfu_state_ctx.state = APP_DETACH; // After init, reset will occur. We want to be in APP_DETACH to move to DFU_IDLE _dfu_state_ctx.status = DFU_STATUS_OK; _dfu_state_ctx.attrs = tud_dfu_mode_init_attrs_cb(); _dfu_state_ctx.blk_transfer_in_proc = false; @@ -79,31 +79,36 @@ void dfu_mode_init(void) void dfu_mode_reset(uint8_t rhport) { - if ( tud_dfu_mode_usb_reset_cb ) + if (_dfu_state_ctx.state == APP_DETACH) { - tud_dfu_mode_usb_reset_cb(rhport, &_dfu_state_ctx.state); + _dfu_state_ctx.state = DFU_IDLE; } else { - switch (_dfu_state_ctx.state) + if ( tud_dfu_mode_usb_reset_cb ) { - case DFU_IDLE: - case DFU_DNLOAD_SYNC: - case DFU_DNBUSY: - case DFU_DNLOAD_IDLE: - case DFU_MANIFEST_SYNC: - case DFU_MANIFEST: - case DFU_MANIFEST_WAIT_RESET: - case DFU_UPLOAD_IDLE: + tud_dfu_mode_usb_reset_cb(rhport, &_dfu_state_ctx.state); + } else { + switch (_dfu_state_ctx.state) { - _dfu_state_ctx.state = (tud_dfu_mode_firmware_valid_check_cb()) ? APP_IDLE : DFU_ERROR; - } - break; + case DFU_IDLE: + case DFU_DNLOAD_SYNC: + case DFU_DNBUSY: + case DFU_DNLOAD_IDLE: + case DFU_MANIFEST_SYNC: + case DFU_MANIFEST: + case DFU_MANIFEST_WAIT_RESET: + case DFU_UPLOAD_IDLE: + { + _dfu_state_ctx.state = (tud_dfu_mode_firmware_valid_check_cb()) ? APP_IDLE : DFU_ERROR; + } + break; - case DFU_ERROR: - default: - { - _dfu_state_ctx.state = APP_IDLE; + case DFU_ERROR: + default: + { + _dfu_state_ctx.state = APP_IDLE; + } + break; } - break; } } From bc2cb99780a0bc0359195a4be8e556f71da5a70b Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Mon, 5 Apr 2021 17:06:27 -0400 Subject: [PATCH 06/23] Remove unreachable callback --- src/class/dfu/dfu_mode_device.c | 10 ---------- src/class/dfu/dfu_mode_device.h | 1 - src/device/usbd.c | 2 +- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/class/dfu/dfu_mode_device.c b/src/class/dfu/dfu_mode_device.c index 68f55510b..59cea7673 100644 --- a/src/class/dfu/dfu_mode_device.c +++ b/src/class/dfu/dfu_mode_device.c @@ -590,14 +590,4 @@ static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const } -bool dfu_mode_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) -{ - (void) rhport; - (void) ep_addr; - (void) result; - (void) xferred_bytes; - return true; -} - - #endif diff --git a/src/class/dfu/dfu_mode_device.h b/src/class/dfu/dfu_mode_device.h index 32bf58bbd..11b748c3b 100644 --- a/src/class/dfu/dfu_mode_device.h +++ b/src/class/dfu/dfu_mode_device.h @@ -122,7 +122,6 @@ void dfu_mode_init(void); void dfu_mode_reset(uint8_t rhport); uint16_t dfu_mode_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); bool dfu_mode_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); -bool dfu_mode_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); #ifdef __cplusplus diff --git a/src/device/usbd.c b/src/device/usbd.c index 91838b39c..de0e7b0b7 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -194,7 +194,7 @@ static usbd_class_driver_t const _usbd_driver[] = .reset = dfu_mode_reset, .open = dfu_mode_open, .control_xfer_cb = dfu_mode_control_xfer_cb, - .xfer_cb = dfu_mode_xfer_cb, + .xfer_cb = NULL, .sof = NULL }, #endif From c39b7b8177acbab23e82fee295d5f3205aa9752c Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Mon, 5 Apr 2021 17:52:33 -0400 Subject: [PATCH 07/23] Add DFU runtime and mode "class" With the runtime and mode portions in separate classes, a single application should only be building with one or the other enabled. In some applications both might be desired at build time. The CFG_TUD_DFU_RUNTIME_AND_MODE option creates a DFU class, which asks the application which mode to initialize to. This allows a runtime change between RT and DFU mode, by just reinitializing tusb. --- src/class/dfu/dfu_mode_device.c | 4 +- src/class/dfu/dfu_rt_and_mode_device.c | 114 +++++++++++++++++++++++++ src/class/dfu/dfu_rt_and_mode_device.h | 57 +++++++++++++ src/class/dfu/dfu_rt_device.c | 2 +- src/device/usbd.c | 12 +++ src/tusb.h | 6 ++ src/tusb_option.h | 10 +++ 7 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 src/class/dfu/dfu_rt_and_mode_device.c create mode 100644 src/class/dfu/dfu_rt_and_mode_device.h diff --git a/src/class/dfu/dfu_mode_device.c b/src/class/dfu/dfu_mode_device.c index 59cea7673..0b80b103d 100644 --- a/src/class/dfu/dfu_mode_device.c +++ b/src/class/dfu/dfu_mode_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_MODE) +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_MODE) || (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME_AND_MODE) #include "dfu_mode_device.h" #include "device/usbd_pvt.h" @@ -590,4 +590,4 @@ static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const } - #endif +#endif diff --git a/src/class/dfu/dfu_rt_and_mode_device.c b/src/class/dfu/dfu_rt_and_mode_device.c new file mode 100644 index 000000000..d555a1d58 --- /dev/null +++ b/src/class/dfu/dfu_rt_and_mode_device.c @@ -0,0 +1,114 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 XMOS LIMITED + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME_AND_MODE) + +#include "dfu_rt_and_mode_device.h" + +#include "dfu_mode_device.h" +#include "dfu_rt_device.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +typedef struct +{ + void (* init ) (void); + void (* reset ) (uint8_t rhport); + uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len); + bool (* control_xfer_cb ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +} dfu_local_driver_t; + +static dfu_local_driver_t ctx = +{ + .init = NULL, + .reset = NULL, + .open = NULL, + .control_xfer_cb = NULL +}; + +static dfu_protocol_type_t mode = 0x00; + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void dfu_init(void) +{ + mode = dfu_init_in_mode_cb(); + + switch (mode) + { + case DFU_PROTOCOL_RT: + { + ctx.init = dfu_rtd_init; + ctx.reset = dfu_rtd_reset; + ctx.open = dfu_rtd_open; + ctx.control_xfer_cb = dfu_rtd_control_xfer_cb; + } + break; + + case DFU_PROTOCOL_DFU: + { + ctx.init = dfu_mode_init; + ctx.reset = dfu_mode_reset; + ctx.open = dfu_mode_open; + ctx.control_xfer_cb = dfu_mode_control_xfer_cb; + } + break; + + default: + { + TU_LOG2(" DFU Unexpected mode: %u\r\n", mode); + } + break; + } + + ctx.init(); +} + +void dfu_reset(uint8_t rhport) +{ + ctx.reset(rhport); +} + +uint16_t dfu_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) +{ + return ctx.open(rhport, itf_desc, max_len); +} + +bool dfu_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + return ctx.control_xfer_cb(rhport, stage, request); +} + +#endif diff --git a/src/class/dfu/dfu_rt_and_mode_device.h b/src/class/dfu/dfu_rt_and_mode_device.h new file mode 100644 index 000000000..9734a031c --- /dev/null +++ b/src/class/dfu/dfu_rt_and_mode_device.h @@ -0,0 +1,57 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 XMOS LIMITED + * + * 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_DFU_RT_AND_MODE_DEVICE_H_ +#define _TUSB_DFU_RT_AND_MODE_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" +#include "dfu.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ +// Invoked when the driver needs to determine the callbacks to use +dfu_protocol_type_t dfu_init_in_mode_cb(); + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void dfu_init(void); +void dfu_reset(uint8_t rhport); +uint16_t dfu_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +bool dfu_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_DFU_RT_AND_MODE_DEVICE_H_ */ diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c index 806bfba2e..d1d028d69 100644 --- a/src/class/dfu/dfu_rt_device.c +++ b/src/class/dfu/dfu_rt_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME) +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME) || (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME_AND_MODE) #include "dfu_rt_device.h" #include "device/usbd_pvt.h" diff --git a/src/device/usbd.c b/src/device/usbd.c index de0e7b0b7..6f2262feb 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -199,6 +199,18 @@ static usbd_class_driver_t const _usbd_driver[] = }, #endif + #if CFG_TUD_DFU_RUNTIME_AND_MODE + { + DRIVER_NAME("DFU-RT-MODE") + .init = dfu_init, + .reset = dfu_reset, + .open = dfu_open, + .control_xfer_cb = dfu_control_xfer_cb, + .xfer_cb = NULL, + .sof = NULL + }, + #endif + #if CFG_TUD_NET { DRIVER_NAME("NET") diff --git a/src/tusb.h b/src/tusb.h index c9558515c..6eab4bee2 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -100,6 +100,12 @@ #include "class/dfu/dfu_mode_device.h" #endif + #if CFG_TUD_DFU_RUNTIME_AND_MODE + #include "class/dfu/dfu_rt_and_mode_device.h" + #include "class/dfu/dfu_rt_device.h" + #include "class/dfu/dfu_mode_device.h" + #endif + #if CFG_TUD_NET #include "class/net/net_device.h" #endif diff --git a/src/tusb_option.h b/src/tusb_option.h index 3cd3df9f2..4c2204964 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -241,6 +241,10 @@ #define CFG_TUD_DFU_MODE 0 #endif +#ifndef CFG_TUD_DFU_RUNTIME_AND_MODE + #define CFG_TUD_DFU_RUNTIME_AND_MODE 0 +#endif + #ifndef CFG_TUD_DFU_TRANSFER_BUFFER_SIZE #define CFG_TUD_DFU_TRANSFER_BUFFER_SIZE 64 #endif @@ -285,6 +289,12 @@ #error Control Endpoint Max Packet Size cannot be larger than 64 #endif +#if (CFG_TUD_DFU_RUNTIME_AND_MODE && CFG_TUD_DFU_RUNTIME) \ + || (CFG_TUD_DFU_RUNTIME_AND_MODE && CFG_TUD_DFU_MODE) \ + || (CFG_TUD_DFU_RUNTIME && CFG_TUD_DFU_MODE) + #error Only one of CFG_TUD_DFU_RUNTIME_AND_MODE, CFG_TUD_DFU_RUNTIME, and CFG_TUD_DFU_MODE can be enabled in a single build +#endif + #endif /* _TUSB_OPTION_H_ */ /** @} */ From 2e2dc7bdc5928dfa027291ca8c6d6792698e5b9a Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Wed, 7 Apr 2021 17:05:04 -0400 Subject: [PATCH 08/23] Revise per initial comments Returns the RT driver to the function state of previous iteration, which did not support the will_detach. Behavior should be fine without this feature. This removes much of the added bloat to track state, and handle requests in the APP_DETACH state which is no longer required. Removes the optional bloat added to the RT driver, such as responding to GETSTATE requests. Fixes the DFU Mode to extract the attr bits from the functional descriptor when opened. Fixes some incorrect bitwise if checks. Also, updates some naming of functions to be consistent with the rest of the library. --- src/class/dfu/dfu_mode_device.c | 19 ++++--- src/class/dfu/dfu_mode_device.h | 8 +-- src/class/dfu/dfu_rt_and_mode_device.c | 18 +++--- src/class/dfu/dfu_rt_and_mode_device.h | 10 ++-- src/class/dfu/dfu_rt_device.c | 76 +++----------------------- src/class/dfu/dfu_rt_device.h | 35 ------------ src/class/vendor/vendor_device.c | 2 +- src/common/tusb_types.h | 18 +++--- src/device/usbd.c | 16 +++--- 9 files changed, 55 insertions(+), 147 deletions(-) diff --git a/src/class/dfu/dfu_mode_device.c b/src/class/dfu/dfu_mode_device.c index 0b80b103d..7ad0f5168 100644 --- a/src/class/dfu/dfu_mode_device.c +++ b/src/class/dfu/dfu_mode_device.c @@ -65,11 +65,11 @@ static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ -void dfu_mode_init(void) +void dfu_moded_init(void) { _dfu_state_ctx.state = APP_DETACH; // After init, reset will occur. We want to be in APP_DETACH to move to DFU_IDLE _dfu_state_ctx.status = DFU_STATUS_OK; - _dfu_state_ctx.attrs = tud_dfu_mode_init_attrs_cb(); + _dfu_state_ctx.attrs = 0; _dfu_state_ctx.blk_transfer_in_proc = false; _dfu_state_ctx.last_block_num = 0; _dfu_state_ctx.last_transfer_len = 0; @@ -77,7 +77,7 @@ void dfu_mode_init(void) dfu_debug_print_context(); } -void dfu_mode_reset(uint8_t rhport) +void dfu_moded_reset(uint8_t rhport) { if (_dfu_state_ctx.state == APP_DETACH) { @@ -118,14 +118,13 @@ void dfu_mode_reset(uint8_t rhport) } _dfu_state_ctx.status = DFU_STATUS_OK; - _dfu_state_ctx.attrs = tud_dfu_mode_init_attrs_cb(); _dfu_state_ctx.blk_transfer_in_proc = false; _dfu_state_ctx.last_block_num = 0; _dfu_state_ctx.last_transfer_len = 0; dfu_debug_print_context(); } -uint16_t dfu_mode_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) +uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) { (void) rhport; (void) max_len; @@ -139,6 +138,9 @@ uint16_t dfu_mode_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, u if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) ) { + tusb_desc_dfu_functional_t *dfu_desc = (tusb_desc_dfu_functional_t *)p_desc; + _dfu_state_ctx.attrs = (uint8_t)dfu_desc->bAttributes; + drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); } @@ -149,7 +151,7 @@ uint16_t dfu_mode_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, u // Invoked when a control transfer occurred on an interface of this class // Driver response accordingly to the request and the transfer stage (setup/data/ack) // return false to stall control endpoint (e.g unsupported request) -bool dfu_mode_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { if ( (stage == CONTROL_STAGE_DATA) && (request->bRequest == DFU_DNLOAD_SYNC) ) { @@ -266,7 +268,7 @@ void tud_dfu_mode_poll_timeout_done() _dfu_state_ctx.state = DFU_DNLOAD_SYNC; } else if (_dfu_state_ctx.state == DFU_MANIFEST) { - _dfu_state_ctx.state = ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) == 0) + _dfu_state_ctx.state = ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) != 0) ? DFU_MANIFEST_WAIT_RESET : DFU_MANIFEST_SYNC; } } @@ -394,7 +396,6 @@ static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const { _dfu_state_ctx.state = DFU_DNLOAD_SYNC; _dfu_state_ctx.blk_transfer_in_proc = true; - dfu_req_dnload_setup(rhport, request); } else { if ( tud_dfu_mode_device_data_done_check_cb() ) @@ -447,7 +448,7 @@ static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const { case DFU_REQUEST_GETSTATUS: { - if ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) == 0) + if ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK) != 0) { _dfu_state_ctx.state = DFU_MANIFEST; dfu_req_getstatus_reply(rhport, request); diff --git a/src/class/dfu/dfu_mode_device.h b/src/class/dfu/dfu_mode_device.h index 11b748c3b..aacb1911c 100644 --- a/src/class/dfu/dfu_mode_device.h +++ b/src/class/dfu/dfu_mode_device.h @@ -118,10 +118,10 @@ TU_ATTR_WEAK uint8_t tud_dfu_mode_get_status_desc_table_index_cb(); //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ -void dfu_mode_init(void); -void dfu_mode_reset(uint8_t rhport); -uint16_t dfu_mode_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); -bool dfu_mode_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +void dfu_moded_init(void); +void dfu_moded_reset(uint8_t rhport); +uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); #ifdef __cplusplus diff --git a/src/class/dfu/dfu_rt_and_mode_device.c b/src/class/dfu/dfu_rt_and_mode_device.c index d555a1d58..8b6a8a766 100644 --- a/src/class/dfu/dfu_rt_and_mode_device.c +++ b/src/class/dfu/dfu_rt_and_mode_device.c @@ -62,9 +62,9 @@ static dfu_protocol_type_t mode = 0x00; //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ -void dfu_init(void) +void dfu_d_init(void) { - mode = dfu_init_in_mode_cb(); + mode = tud_dfu_init_in_mode_cb(); switch (mode) { @@ -79,10 +79,10 @@ void dfu_init(void) case DFU_PROTOCOL_DFU: { - ctx.init = dfu_mode_init; - ctx.reset = dfu_mode_reset; - ctx.open = dfu_mode_open; - ctx.control_xfer_cb = dfu_mode_control_xfer_cb; + ctx.init = dfu_moded_init; + ctx.reset = dfu_moded_reset; + ctx.open = dfu_moded_open; + ctx.control_xfer_cb = dfu_moded_control_xfer_cb; } break; @@ -96,17 +96,17 @@ void dfu_init(void) ctx.init(); } -void dfu_reset(uint8_t rhport) +void dfu_d_reset(uint8_t rhport) { ctx.reset(rhport); } -uint16_t dfu_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) +uint16_t dfu_d_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) { return ctx.open(rhport, itf_desc, max_len); } -bool dfu_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +bool dfu_d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { return ctx.control_xfer_cb(rhport, stage, request); } diff --git a/src/class/dfu/dfu_rt_and_mode_device.h b/src/class/dfu/dfu_rt_and_mode_device.h index 9734a031c..b6eafbba4 100644 --- a/src/class/dfu/dfu_rt_and_mode_device.h +++ b/src/class/dfu/dfu_rt_and_mode_device.h @@ -39,15 +39,15 @@ // Application Callback API (weak is optional) //--------------------------------------------------------------------+ // Invoked when the driver needs to determine the callbacks to use -dfu_protocol_type_t dfu_init_in_mode_cb(); +dfu_protocol_type_t tud_dfu_init_in_mode_cb(); //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ -void dfu_init(void); -void dfu_reset(uint8_t rhport); -uint16_t dfu_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); -bool dfu_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +void dfu_d_init(void); +void dfu_d_reset(uint8_t rhport); +uint16_t dfu_d_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +bool dfu_d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); #ifdef __cplusplus diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c index d1d028d69..729b8e3e0 100644 --- a/src/class/dfu/dfu_rt_device.c +++ b/src/class/dfu/dfu_rt_device.c @@ -38,41 +38,17 @@ //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ -typedef struct TU_ATTR_PACKED -{ - dfu_mode_device_status_t status; - dfu_mode_state_t state; - uint8_t attrs; -} dfu_rt_state_ctx_t; - -// Only a single dfu state is allowed -CFG_TUSB_MEM_SECTION static dfu_rt_state_ctx_t _dfu_state_ctx; - -static void dfu_rt_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request); //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ void dfu_rtd_init(void) { - _dfu_state_ctx.state = APP_IDLE; - _dfu_state_ctx.status = DFU_STATUS_OK; - _dfu_state_ctx.attrs = tud_dfu_runtime_init_attrs_cb(); - dfu_debug_print_context(); } void dfu_rtd_reset(uint8_t rhport) { - if (((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_WILL_DETACH_BITMASK) == 0) - && (_dfu_state_ctx.state == DFU_REQUEST_DETACH)) - { - tud_dfu_runtime_reboot_to_dfu_cb(); - } - - _dfu_state_ctx.state = APP_IDLE; - _dfu_state_ctx.status = DFU_STATUS_OK; - _dfu_state_ctx.attrs = tud_dfu_runtime_init_attrs_cb(); - dfu_debug_print_context(); + (void) rhport; } uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) @@ -117,67 +93,29 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request // Handle class request only from here TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); - TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest)); + TU_LOG2(" DFU RT Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest)); switch (request->bRequest) { case DFU_REQUEST_DETACH: { - if (_dfu_state_ctx.state == APP_IDLE) - { - _dfu_state_ctx.state = APP_DETACH; - if ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_WILL_DETACH_BITMASK) == 1) - { - tud_dfu_runtime_reboot_to_dfu_cb(); - } else { - tud_dfu_runtime_detach_start_timer_cb(request->wValue); - } - } else { - TU_LOG2(" DFU Unexpected request during state %s: %u\r\n", tu_lookup_find(&_dfu_mode_state_table, _dfu_state_ctx.state), request->bRequest); - return false; - } + tud_control_status(rhport, request); + tud_dfu_runtime_reboot_to_dfu_cb(); } break; case DFU_REQUEST_GETSTATUS: { dfu_status_req_payload_t resp; - resp.bStatus = _dfu_state_ctx.status; - memset((uint8_t *)&resp.bwPollTimeout, 0x00, 3); // Value is ignored - resp.bState = _dfu_state_ctx.state; - resp.iString = ( tud_dfu_runtime_get_status_desc_table_index_cb ) ? tud_dfu_runtime_get_status_desc_table_index_cb() : 0; - + // Status = OK, Poll timeout is ignored during RT, State = APP_IDLE, IString = 0 + memset(&resp, 0x00, sizeof(dfu_status_req_payload_t)); tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_req_payload_t)); } break; - case DFU_REQUEST_GETSTATE: - { - tud_control_xfer(rhport, request, &_dfu_state_ctx.state, 1); - } - break; - - default: - { - TU_LOG2(" DFU Nonstandard Runtime Request: %u\r\n", request->bRequest); - return ( tud_dfu_runtime_req_nonstandard_cb ) ? tud_dfu_runtime_req_nonstandard_cb(rhport, stage, request) : false; - } - break; + default: return false; // stall unsupported request } return true; } -void tud_dfu_runtime_set_status(dfu_mode_device_status_t status) -{ - _dfu_state_ctx.status = status; -} - -void tud_dfu_runtime_detach_timer_elapsed() -{ - if (_dfu_state_ctx.state == DFU_REQUEST_DETACH) - { - _dfu_state_ctx.state = APP_IDLE; - } -} - #endif diff --git a/src/class/dfu/dfu_rt_device.h b/src/class/dfu/dfu_rt_device.h index 23703862d..465bf9d99 100644 --- a/src/class/dfu/dfu_rt_device.h +++ b/src/class/dfu/dfu_rt_device.h @@ -38,44 +38,9 @@ //--------------------------------------------------------------------+ // Application Callback API (weak is optional) //--------------------------------------------------------------------+ -// Allow the application to update the status as required. -// Is set to DFU_STATUS_OK during internal initialization and USB reset -// Value is not checked to allow for custom statuses to be used -void tud_dfu_runtime_set_status(dfu_mode_device_status_t status); - // Invoked when a DFU_DETACH request is received and bitWillDetach is set void tud_dfu_runtime_reboot_to_dfu_cb(); -// Invoked when a DFU_DETACH request is received and bitWillDetach is not set -// This should start a timer for wTimeout ms -// When the timer has elapsed, the app must call tud_dfu_runtime_detach_timer_elapsed -// If a USB reset is called while the timer is running, the class will call -// tud_dfu_runtime_reboot_to_dfu_cb. -// NOTE: This callback should return immediately, and not implement the delay -// internally, as this can will hold up the USB stack -TU_ATTR_WEAK void tud_dfu_runtime_detach_start_timer_cb(uint16_t wTimeout); - -// Invoke when the dfu runtime detach timer has elapsed -void tud_dfu_runtime_detach_timer_elapsed(); - -// Invoked when a nonstandard request is received -// Use may be vendor specific. -// Return false to stall -TU_ATTR_WEAK bool tud_dfu_runtime_req_nonstandard_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); - -// Invoked during initialization of the dfu driver to set attributes -// Return byte set with bitmasks: -// DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK -// DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK -// DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK -// DFU_FUNC_ATTR_WILL_DETACH_BITMASK -// Note: This should match the USB descriptor -uint8_t tud_dfu_runtime_init_attrs_cb(); - -// Invoked during a DFU_GETSTATUS request to get for the string index -// to the status description string table. -TU_ATTR_WEAK uint8_t tud_dfu_runtime_get_status_desc_table_index_cb(); - //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index 3fcea89c4..d39830a6a 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -1,4 +1,4 @@ -/* +/* * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index a462b498f..7703ebb7b 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -427,13 +427,17 @@ typedef struct TU_ATTR_PACKED uint8_t bLength; uint8_t bDescriptorType; - struct TU_ATTR_PACKED { - uint8_t bitCanDnload : 1; - uint8_t bitCanUpload : 1; - uint8_t bitManifestationTolerant : 1; - uint8_t bitWillDetach : 1; - uint8_t reserved : 4; - } bmAttributes; + union { + struct TU_ATTR_PACKED { + uint8_t bitCanDnload : 1; + uint8_t bitCanUpload : 1; + uint8_t bitManifestationTolerant : 1; + uint8_t bitWillDetach : 1; + uint8_t reserved : 4; + } bmAttributes; + + uint8_t bAttributes; + }; uint16_t wDetachTimeOut; uint16_t wTransferSize; diff --git a/src/device/usbd.c b/src/device/usbd.c index 6f2262feb..92e521ec9 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -190,10 +190,10 @@ static usbd_class_driver_t const _usbd_driver[] = #if CFG_TUD_DFU_MODE { DRIVER_NAME("DFU-MODE") - .init = dfu_mode_init, - .reset = dfu_mode_reset, - .open = dfu_mode_open, - .control_xfer_cb = dfu_mode_control_xfer_cb, + .init = dfu_moded_init, + .reset = dfu_moded_reset, + .open = dfu_moded_open, + .control_xfer_cb = dfu_moded_control_xfer_cb, .xfer_cb = NULL, .sof = NULL }, @@ -202,10 +202,10 @@ static usbd_class_driver_t const _usbd_driver[] = #if CFG_TUD_DFU_RUNTIME_AND_MODE { DRIVER_NAME("DFU-RT-MODE") - .init = dfu_init, - .reset = dfu_reset, - .open = dfu_open, - .control_xfer_cb = dfu_control_xfer_cb, + .init = dfu_d_init, + .reset = dfu_d_reset, + .open = dfu_d_open, + .control_xfer_cb = dfu_d_control_xfer_cb, .xfer_cb = NULL, .sof = NULL }, From ae851bba31781eacfadc7e69b414cd1da6960314 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Wed, 7 Apr 2021 17:15:26 -0400 Subject: [PATCH 09/23] Resolve gcc warnings for no parameter functions --- src/class/dfu/dfu_mode_device.h | 16 ++++++++-------- src/class/dfu/dfu_rt_and_mode_device.h | 2 +- src/class/dfu/dfu_rt_device.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/class/dfu/dfu_mode_device.h b/src/class/dfu/dfu_mode_device.h index aacb1911c..c25910b23 100644 --- a/src/class/dfu/dfu_mode_device.h +++ b/src/class/dfu/dfu_mode_device.h @@ -40,10 +40,10 @@ // Application Callback API (weak is optional) //--------------------------------------------------------------------+ // Invoked when a reset is received to check if firmware is valid -bool tud_dfu_mode_firmware_valid_check_cb(); +bool tud_dfu_mode_firmware_valid_check_cb(void); // Invoked when the device must reboot to dfu runtime mode -void tud_dfu_mode_reboot_to_rt_cb(); +void tud_dfu_mode_reboot_to_rt_cb(void); // Invoked during initialization of the dfu driver to set attributes // Return byte set with bitmasks: @@ -52,11 +52,11 @@ void tud_dfu_mode_reboot_to_rt_cb(); // DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK // DFU_FUNC_ATTR_WILL_DETACH_BITMASK // Note: This should match the USB descriptor -uint8_t tud_dfu_mode_init_attrs_cb(); +uint8_t tud_dfu_mode_init_attrs_cb(void); // Invoked during a DFU_GETSTATUS request to get for the string index // to the status description string table. -TU_ATTR_WEAK uint8_t tud_dfu_mode_get_status_desc_table_index_cb(); +TU_ATTR_WEAK uint8_t tud_dfu_mode_get_status_desc_table_index_cb(void); // Invoked during a USB reset // Lets the app perform custom behavior on a USB reset. @@ -84,7 +84,7 @@ TU_ATTR_WEAK void tud_dfu_mode_get_poll_timeout_cb(uint8_t *ms_timeout); void tud_dfu_mode_start_poll_timeout_cb(uint8_t *ms_timeout); // Must be called when the poll_timeout has elapsed -void tud_dfu_mode_poll_timeout_done(); +void tud_dfu_mode_poll_timeout_done(void); // Invoked when a DFU_DNLOAD request is received // This callback takes the wBlockNum chunk of length length and provides it @@ -97,10 +97,10 @@ void tud_dfu_mode_req_dnload_data_cb(uint16_t wBlockNum, uint8_t* data, uint16_t // Return true if the application agrees there is no more data // Return false if the device disagrees, which will stall the pipe, and the Host // should initiate a recovery procedure -bool tud_dfu_mode_device_data_done_check_cb(); +bool tud_dfu_mode_device_data_done_check_cb(void); // Invoked when the Host has terminated a download or upload transfer -TU_ATTR_WEAK void tud_dfu_mode_abort_cb(); +TU_ATTR_WEAK void tud_dfu_mode_abort_cb(void); // Invoked when a DFU_UPLOAD request is received // This callback must populate data with up to length bytes @@ -114,7 +114,7 @@ TU_ATTR_WEAK bool tud_dfu_mode_req_nonstandard_cb(uint8_t rhport, uint8_t stage, // Invoked during a DFU_GETSTATUS request to get for the string index // to the status description string table. -TU_ATTR_WEAK uint8_t tud_dfu_mode_get_status_desc_table_index_cb(); +TU_ATTR_WEAK uint8_t tud_dfu_mode_get_status_desc_table_index_cb(void); //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ diff --git a/src/class/dfu/dfu_rt_and_mode_device.h b/src/class/dfu/dfu_rt_and_mode_device.h index b6eafbba4..b311bb7a5 100644 --- a/src/class/dfu/dfu_rt_and_mode_device.h +++ b/src/class/dfu/dfu_rt_and_mode_device.h @@ -39,7 +39,7 @@ // Application Callback API (weak is optional) //--------------------------------------------------------------------+ // Invoked when the driver needs to determine the callbacks to use -dfu_protocol_type_t tud_dfu_init_in_mode_cb(); +dfu_protocol_type_t tud_dfu_init_in_mode_cb(void); //--------------------------------------------------------------------+ // Internal Class Driver API diff --git a/src/class/dfu/dfu_rt_device.h b/src/class/dfu/dfu_rt_device.h index 465bf9d99..0e5fd005a 100644 --- a/src/class/dfu/dfu_rt_device.h +++ b/src/class/dfu/dfu_rt_device.h @@ -39,7 +39,7 @@ // Application Callback API (weak is optional) //--------------------------------------------------------------------+ // Invoked when a DFU_DETACH request is received and bitWillDetach is set -void tud_dfu_runtime_reboot_to_dfu_cb(); +void tud_dfu_runtime_reboot_to_dfu_cb(void); //--------------------------------------------------------------------+ // Internal Class Driver API From 7b45b38fe4b7d2ebd197463cf12f091a6f68ac91 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Mon, 12 Apr 2021 11:17:01 -0400 Subject: [PATCH 10/23] Remove DFU mode and rt --- src/class/dfu/dfu_mode_device.c | 2 +- src/class/dfu/dfu_rt_and_mode_device.c | 114 ------------------------- src/class/dfu/dfu_rt_and_mode_device.h | 57 ------------- src/class/dfu/dfu_rt_device.c | 2 +- src/device/usbd.c | 12 --- src/tusb.h | 6 -- src/tusb_option.h | 10 --- 7 files changed, 2 insertions(+), 201 deletions(-) delete mode 100644 src/class/dfu/dfu_rt_and_mode_device.c delete mode 100644 src/class/dfu/dfu_rt_and_mode_device.h diff --git a/src/class/dfu/dfu_mode_device.c b/src/class/dfu/dfu_mode_device.c index 7ad0f5168..5f8c714c0 100644 --- a/src/class/dfu/dfu_mode_device.c +++ b/src/class/dfu/dfu_mode_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_MODE) || (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME_AND_MODE) +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_MODE) #include "dfu_mode_device.h" #include "device/usbd_pvt.h" diff --git a/src/class/dfu/dfu_rt_and_mode_device.c b/src/class/dfu/dfu_rt_and_mode_device.c deleted file mode 100644 index 8b6a8a766..000000000 --- a/src/class/dfu/dfu_rt_and_mode_device.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 XMOS LIMITED - * - * 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. - * - * This file is part of the TinyUSB stack. - */ - -#include "tusb_option.h" - -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME_AND_MODE) - -#include "dfu_rt_and_mode_device.h" - -#include "dfu_mode_device.h" -#include "dfu_rt_device.h" -#include "device/usbd_pvt.h" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ - -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ -typedef struct -{ - void (* init ) (void); - void (* reset ) (uint8_t rhport); - uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len); - bool (* control_xfer_cb ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); -} dfu_local_driver_t; - -static dfu_local_driver_t ctx = -{ - .init = NULL, - .reset = NULL, - .open = NULL, - .control_xfer_cb = NULL -}; - -static dfu_protocol_type_t mode = 0x00; - -//--------------------------------------------------------------------+ -// USBD Driver API -//--------------------------------------------------------------------+ -void dfu_d_init(void) -{ - mode = tud_dfu_init_in_mode_cb(); - - switch (mode) - { - case DFU_PROTOCOL_RT: - { - ctx.init = dfu_rtd_init; - ctx.reset = dfu_rtd_reset; - ctx.open = dfu_rtd_open; - ctx.control_xfer_cb = dfu_rtd_control_xfer_cb; - } - break; - - case DFU_PROTOCOL_DFU: - { - ctx.init = dfu_moded_init; - ctx.reset = dfu_moded_reset; - ctx.open = dfu_moded_open; - ctx.control_xfer_cb = dfu_moded_control_xfer_cb; - } - break; - - default: - { - TU_LOG2(" DFU Unexpected mode: %u\r\n", mode); - } - break; - } - - ctx.init(); -} - -void dfu_d_reset(uint8_t rhport) -{ - ctx.reset(rhport); -} - -uint16_t dfu_d_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) -{ - return ctx.open(rhport, itf_desc, max_len); -} - -bool dfu_d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) -{ - return ctx.control_xfer_cb(rhport, stage, request); -} - -#endif diff --git a/src/class/dfu/dfu_rt_and_mode_device.h b/src/class/dfu/dfu_rt_and_mode_device.h deleted file mode 100644 index b311bb7a5..000000000 --- a/src/class/dfu/dfu_rt_and_mode_device.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 XMOS LIMITED - * - * 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. - * - * This file is part of the TinyUSB stack. - */ - -#ifndef _TUSB_DFU_RT_AND_MODE_DEVICE_H_ -#define _TUSB_DFU_RT_AND_MODE_DEVICE_H_ - -#include "common/tusb_common.h" -#include "device/usbd.h" -#include "dfu.h" - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// Application Callback API (weak is optional) -//--------------------------------------------------------------------+ -// Invoked when the driver needs to determine the callbacks to use -dfu_protocol_type_t tud_dfu_init_in_mode_cb(void); - -//--------------------------------------------------------------------+ -// Internal Class Driver API -//--------------------------------------------------------------------+ -void dfu_d_init(void); -void dfu_d_reset(uint8_t rhport); -uint16_t dfu_d_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); -bool dfu_d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); - - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_DFU_RT_AND_MODE_DEVICE_H_ */ diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c index 729b8e3e0..9da2e1e4f 100644 --- a/src/class/dfu/dfu_rt_device.c +++ b/src/class/dfu/dfu_rt_device.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME) || (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME_AND_MODE) +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME) #include "dfu_rt_device.h" #include "device/usbd_pvt.h" diff --git a/src/device/usbd.c b/src/device/usbd.c index 92e521ec9..af34e53ac 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -199,18 +199,6 @@ static usbd_class_driver_t const _usbd_driver[] = }, #endif - #if CFG_TUD_DFU_RUNTIME_AND_MODE - { - DRIVER_NAME("DFU-RT-MODE") - .init = dfu_d_init, - .reset = dfu_d_reset, - .open = dfu_d_open, - .control_xfer_cb = dfu_d_control_xfer_cb, - .xfer_cb = NULL, - .sof = NULL - }, - #endif - #if CFG_TUD_NET { DRIVER_NAME("NET") diff --git a/src/tusb.h b/src/tusb.h index 6eab4bee2..c9558515c 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -100,12 +100,6 @@ #include "class/dfu/dfu_mode_device.h" #endif - #if CFG_TUD_DFU_RUNTIME_AND_MODE - #include "class/dfu/dfu_rt_and_mode_device.h" - #include "class/dfu/dfu_rt_device.h" - #include "class/dfu/dfu_mode_device.h" - #endif - #if CFG_TUD_NET #include "class/net/net_device.h" #endif diff --git a/src/tusb_option.h b/src/tusb_option.h index 4c2204964..3cd3df9f2 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -241,10 +241,6 @@ #define CFG_TUD_DFU_MODE 0 #endif -#ifndef CFG_TUD_DFU_RUNTIME_AND_MODE - #define CFG_TUD_DFU_RUNTIME_AND_MODE 0 -#endif - #ifndef CFG_TUD_DFU_TRANSFER_BUFFER_SIZE #define CFG_TUD_DFU_TRANSFER_BUFFER_SIZE 64 #endif @@ -289,12 +285,6 @@ #error Control Endpoint Max Packet Size cannot be larger than 64 #endif -#if (CFG_TUD_DFU_RUNTIME_AND_MODE && CFG_TUD_DFU_RUNTIME) \ - || (CFG_TUD_DFU_RUNTIME_AND_MODE && CFG_TUD_DFU_MODE) \ - || (CFG_TUD_DFU_RUNTIME && CFG_TUD_DFU_MODE) - #error Only one of CFG_TUD_DFU_RUNTIME_AND_MODE, CFG_TUD_DFU_RUNTIME, and CFG_TUD_DFU_MODE can be enabled in a single build -#endif - #endif /* _TUSB_OPTION_H_ */ /** @} */ From 01661b3f281c4e044a370b33bf6617b8d2481407 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 22 Apr 2021 14:56:52 -0400 Subject: [PATCH 11/23] Replace dfu_mode with dfu --- src/class/dfu/dfu.h | 24 +++---- .../dfu/{dfu_mode_device.c => dfu_device.c} | 64 +++++++++---------- .../dfu/{dfu_mode_device.h => dfu_device.h} | 34 +++++----- src/tusb.h | 2 +- 4 files changed, 62 insertions(+), 62 deletions(-) rename src/class/dfu/{dfu_mode_device.c => dfu_device.c} (87%) rename src/class/dfu/{dfu_mode_device.h => dfu_device.h} (81%) diff --git a/src/class/dfu/dfu.h b/src/class/dfu/dfu.h index 2040e415c..af08a29f7 100644 --- a/src/class/dfu/dfu.h +++ b/src/class/dfu/dfu.h @@ -73,7 +73,7 @@ typedef enum { DFU_MANIFEST_WAIT_RESET = 8, DFU_UPLOAD_IDLE = 9, DFU_ERROR = 10, -} dfu_mode_state_t; +} dfu_state_t; // DFU Status typedef enum { @@ -93,7 +93,7 @@ typedef enum { DFU_STATUS_ERRPOR = 0x0D, DFU_STATUS_ERRUNKNOWN = 0x0E, DFU_STATUS_ERRSTALLEDPKT = 0x0F, -} dfu_mode_device_status_t; +} dfu_device_status_t; #define DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK (1 << 0) #define DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK (1 << 1) @@ -134,7 +134,7 @@ static tu_lookup_table_t const _dfu_request_table = .items = _dfu_request_lookup }; -static tu_lookup_entry_t const _dfu_mode_state_lookup[] = +static tu_lookup_entry_t const _dfu_state_lookup[] = { { .key = APP_IDLE , .data = "APP_IDLE" }, { .key = APP_DETACH , .data = "APP_DETACH" }, @@ -149,13 +149,13 @@ static tu_lookup_entry_t const _dfu_mode_state_lookup[] = { .key = DFU_ERROR , .data = "DFU_ERROR" }, }; -static tu_lookup_table_t const _dfu_mode_state_table = +static tu_lookup_table_t const _dfu_state_table = { - .count = TU_ARRAY_SIZE(_dfu_mode_state_lookup), - .items = _dfu_mode_state_lookup + .count = TU_ARRAY_SIZE(_dfu_state_lookup), + .items = _dfu_state_lookup }; -static tu_lookup_entry_t const _dfu_mode_status_lookup[] = +static tu_lookup_entry_t const _dfu_status_lookup[] = { { .key = DFU_STATUS_OK , .data = "OK" }, { .key = DFU_STATUS_ERRTARGET , .data = "errTARGET" }, @@ -175,10 +175,10 @@ static tu_lookup_entry_t const _dfu_mode_status_lookup[] = { .key = DFU_STATUS_ERRSTALLEDPKT , .data = "errSTALLEDPKT" }, }; -static tu_lookup_table_t const _dfu_mode_status_table = +static tu_lookup_table_t const _dfu_status_table = { - .count = TU_ARRAY_SIZE(_dfu_mode_status_lookup), - .items = _dfu_mode_status_lookup + .count = TU_ARRAY_SIZE(_dfu_status_lookup), + .items = _dfu_status_lookup }; #endif @@ -186,8 +186,8 @@ static tu_lookup_table_t const _dfu_mode_status_table = #define dfu_debug_print_context() \ { \ TU_LOG2(" DFU at State: %s\r\n Status: %s\r\n", \ - tu_lookup_find(&_dfu_mode_state_table, _dfu_state_ctx.state), \ - tu_lookup_find(&_dfu_mode_status_table, _dfu_state_ctx.status) ); \ + tu_lookup_find(&_dfu_state_table, _dfu_state_ctx.state), \ + tu_lookup_find(&_dfu_status_table, _dfu_state_ctx.status) ); \ } diff --git a/src/class/dfu/dfu_mode_device.c b/src/class/dfu/dfu_device.c similarity index 87% rename from src/class/dfu/dfu_mode_device.c rename to src/class/dfu/dfu_device.c index 5f8c714c0..98213d320 100644 --- a/src/class/dfu/dfu_mode_device.c +++ b/src/class/dfu/dfu_device.c @@ -28,7 +28,7 @@ #if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_MODE) -#include "dfu_mode_device.h" +#include "dfu_device.h" #include "device/usbd_pvt.h" //--------------------------------------------------------------------+ @@ -40,8 +40,8 @@ //--------------------------------------------------------------------+ typedef struct TU_ATTR_PACKED { - dfu_mode_device_status_t status; - dfu_mode_state_t state; + dfu_device_status_t status; + dfu_state_t state; uint8_t attrs; bool blk_transfer_in_proc; @@ -49,17 +49,17 @@ typedef struct TU_ATTR_PACKED uint16_t last_block_num; uint16_t last_transfer_len; CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_TRANSFER_BUFFER_SIZE]; -} dfu_mode_state_ctx_t; +} dfu_state_ctx_t; // Only a single dfu state is allowed -CFG_TUSB_MEM_SECTION static dfu_mode_state_ctx_t _dfu_state_ctx; +CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_state_ctx; static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request); static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request); static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength); -static void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request); -static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const * request); +static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request); +static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request); //--------------------------------------------------------------------+ @@ -83,9 +83,9 @@ void dfu_moded_reset(uint8_t rhport) { _dfu_state_ctx.state = DFU_IDLE; } else { - if ( tud_dfu_mode_usb_reset_cb ) + if ( tud_dfu_usb_reset_cb ) { - tud_dfu_mode_usb_reset_cb(rhport, &_dfu_state_ctx.state); + tud_dfu_usb_reset_cb(rhport, &_dfu_state_ctx.state); } else { switch (_dfu_state_ctx.state) { @@ -98,7 +98,7 @@ void dfu_moded_reset(uint8_t rhport) case DFU_MANIFEST_WAIT_RESET: case DFU_UPLOAD_IDLE: { - _dfu_state_ctx.state = (tud_dfu_mode_firmware_valid_check_cb()) ? APP_IDLE : DFU_ERROR; + _dfu_state_ctx.state = (tud_dfu_firmware_valid_check_cb()) ? APP_IDLE : DFU_ERROR; } break; @@ -114,7 +114,7 @@ void dfu_moded_reset(uint8_t rhport) if(_dfu_state_ctx.state == APP_IDLE) { - tud_dfu_mode_reboot_to_rt_cb(); + tud_dfu_reboot_to_rt_cb(); } _dfu_state_ctx.status = DFU_STATUS_OK; @@ -155,7 +155,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_reque { if ( (stage == CONTROL_STAGE_DATA) && (request->bRequest == DFU_DNLOAD_SYNC) ) { - dfu_mode_req_dnload_reply(rhport, request); + dfu_req_dnload_reply(rhport, request); return true; } @@ -185,14 +185,14 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_reque case DFU_REQUEST_GETSTATE: case DFU_REQUEST_ABORT: { - return dfu_mode_state_machine(rhport, request); + return dfu_state_machine(rhport, request); } break; default: { TU_LOG2(" DFU Nonstandard Request: %u\r\n", request->bRequest); - return ( tud_dfu_mode_req_nonstandard_cb ) ? tud_dfu_mode_req_nonstandard_cb(rhport, stage, request) : false; + return ( tud_dfu_req_nonstandard_cb ) ? tud_dfu_req_nonstandard_cb(rhport, stage, request) : false; } break; } @@ -203,7 +203,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_reque static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength) { TU_VERIFY( wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE); - uint16_t retval = tud_dfu_mode_req_upload_data_cb(block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, wLength); + uint16_t retval = tud_dfu_req_upload_data_cb(block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, wLength); tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, retval); return retval; } @@ -213,14 +213,14 @@ static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const dfu_status_req_payload_t resp; resp.bStatus = _dfu_state_ctx.status; - if ( tud_dfu_mode_get_poll_timeout_cb ) + if ( tud_dfu_get_poll_timeout_cb ) { - tud_dfu_mode_get_poll_timeout_cb((uint8_t *)&resp.bwPollTimeout); + tud_dfu_get_poll_timeout_cb((uint8_t *)&resp.bwPollTimeout); } else { memset((uint8_t *)&resp.bwPollTimeout, 0x00, 3); } resp.bState = _dfu_state_ctx.state; - resp.iString = ( tud_dfu_mode_get_status_desc_table_index_cb ) ? tud_dfu_mode_get_status_desc_table_index_cb() : 0; + resp.iString = ( tud_dfu_get_status_desc_table_index_cb ) ? tud_dfu_get_status_desc_table_index_cb() : 0; tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_req_payload_t)); } @@ -242,26 +242,26 @@ static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, request->wLength); } -static void dfu_mode_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) +static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) { uint8_t bwPollTimeout[3] = {0,0,0}; - if ( tud_dfu_mode_get_poll_timeout_cb ) + if ( tud_dfu_get_poll_timeout_cb ) { - tud_dfu_mode_get_poll_timeout_cb((uint8_t *)&bwPollTimeout); + tud_dfu_get_poll_timeout_cb((uint8_t *)&bwPollTimeout); } - tud_dfu_mode_start_poll_timeout_cb((uint8_t *)&bwPollTimeout); + tud_dfu_start_poll_timeout_cb((uint8_t *)&bwPollTimeout); // TODO: I want the real xferred len, not what is expected. May need to change usbd.c to do this. - tud_dfu_mode_req_dnload_data_cb(_dfu_state_ctx.last_block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, _dfu_state_ctx.last_transfer_len); + tud_dfu_req_dnload_data_cb(_dfu_state_ctx.last_block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, _dfu_state_ctx.last_transfer_len); _dfu_state_ctx.blk_transfer_in_proc = false; _dfu_state_ctx.last_block_num = 0; _dfu_state_ctx.last_transfer_len = 0; } -void tud_dfu_mode_poll_timeout_done() +void tud_dfu_poll_timeout_done() { if (_dfu_state_ctx.state == DFU_DNBUSY) { @@ -273,10 +273,10 @@ void tud_dfu_mode_poll_timeout_done() } } -static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const * request) +static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request) { TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest)); - TU_LOG2(" DFU State Machine: %s\r\n", tu_lookup_find(&_dfu_mode_state_table, _dfu_state_ctx.state)); + TU_LOG2(" DFU State Machine: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_state_ctx.state)); switch (_dfu_state_ctx.state) { @@ -398,7 +398,7 @@ static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const _dfu_state_ctx.blk_transfer_in_proc = true; dfu_req_dnload_setup(rhport, request); } else { - if ( tud_dfu_mode_device_data_done_check_cb() ) + if ( tud_dfu_device_data_done_check_cb() ) { _dfu_state_ctx.state = DFU_MANIFEST_SYNC; tud_control_status(rhport, request); @@ -424,9 +424,9 @@ static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const case DFU_REQUEST_ABORT: { - if ( tud_dfu_mode_abort_cb ) + if ( tud_dfu_abort_cb ) { - tud_dfu_mode_abort_cb(); + tud_dfu_abort_cb(); } _dfu_state_ctx.state = DFU_IDLE; } @@ -453,7 +453,7 @@ static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const _dfu_state_ctx.state = DFU_MANIFEST; dfu_req_getstatus_reply(rhport, request); } else { - if ( tud_dfu_mode_firmware_valid_check_cb() ) + if ( tud_dfu_firmware_valid_check_cb() ) { _dfu_state_ctx.state = DFU_IDLE; } @@ -533,9 +533,9 @@ static bool dfu_mode_state_machine(uint8_t rhport, tusb_control_request_t const case DFU_REQUEST_ABORT: { - if (tud_dfu_mode_abort_cb) + if (tud_dfu_abort_cb) { - tud_dfu_mode_abort_cb(); + tud_dfu_abort_cb(); } _dfu_state_ctx.state = DFU_IDLE; } diff --git a/src/class/dfu/dfu_mode_device.h b/src/class/dfu/dfu_device.h similarity index 81% rename from src/class/dfu/dfu_mode_device.h rename to src/class/dfu/dfu_device.h index c25910b23..3bc1f4ae4 100644 --- a/src/class/dfu/dfu_mode_device.h +++ b/src/class/dfu/dfu_device.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _TUSB_DFU_MODE_DEVICE_H_ -#define _TUSB_DFU_MODE_DEVICE_H_ +#ifndef _TUSB_DFU_DEVICE_H_ +#define _TUSB_DFU_DEVICE_H_ #include "common/tusb_common.h" #include "device/usbd.h" @@ -40,10 +40,10 @@ // Application Callback API (weak is optional) //--------------------------------------------------------------------+ // Invoked when a reset is received to check if firmware is valid -bool tud_dfu_mode_firmware_valid_check_cb(void); +bool tud_dfu_firmware_valid_check_cb(void); // Invoked when the device must reboot to dfu runtime mode -void tud_dfu_mode_reboot_to_rt_cb(void); +void tud_dfu_reboot_to_rt_cb(void); // Invoked during initialization of the dfu driver to set attributes // Return byte set with bitmasks: @@ -52,19 +52,19 @@ void tud_dfu_mode_reboot_to_rt_cb(void); // DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK // DFU_FUNC_ATTR_WILL_DETACH_BITMASK // Note: This should match the USB descriptor -uint8_t tud_dfu_mode_init_attrs_cb(void); +uint8_t tud_dfu_init_attrs_cb(void); // Invoked during a DFU_GETSTATUS request to get for the string index // to the status description string table. -TU_ATTR_WEAK uint8_t tud_dfu_mode_get_status_desc_table_index_cb(void); +TU_ATTR_WEAK uint8_t tud_dfu_get_status_desc_table_index_cb(void); // Invoked during a USB reset // Lets the app perform custom behavior on a USB reset. // If not defined, the default behavior remains the DFU specification of // Checking the firmware valid and changing to the error state or rebooting to runtime // Note: If used, the application must perform the reset logic for all states. -// Changing the state to APP_IDLE will result in tud_dfu_mode_reboot_to_rt_cb being called -TU_ATTR_WEAK void tud_dfu_mode_usb_reset_cb(uint8_t rhport, dfu_mode_state_t *state); +// Changing the state to APP_IDLE will result in tud_dfu_reboot_to_rt_cb being called +TU_ATTR_WEAK void tud_dfu_usb_reset_cb(uint8_t rhport, dfu_state_t *state); // Invoked during a DFU_GETSTATUS request to set the timeout in ms to use // before the subsequent DFU_GETSTATUS requests. @@ -73,7 +73,7 @@ TU_ATTR_WEAK void tud_dfu_mode_usb_reset_cb(uint8_t rhport, dfu_mode_state_t *st // to allow the device to have time to erase, write memory, etc. // ms_timeout is a pointer to array of length 3. // Refer to the USB DFU class specification for more details -TU_ATTR_WEAK void tud_dfu_mode_get_poll_timeout_cb(uint8_t *ms_timeout); +TU_ATTR_WEAK void tud_dfu_get_poll_timeout_cb(uint8_t *ms_timeout); // Invoked when a DFU_DNLOAD request is received // This should start a timer for ms_timeout ms @@ -81,40 +81,40 @@ TU_ATTR_WEAK void tud_dfu_mode_get_poll_timeout_cb(uint8_t *ms_timeout); // NOTE: This callback should return immediately, and not implement the delay // internally, as this will hold up the class stack from receiving any packets // during the DFU_DNLOAD_SYNC and DFU_DNBUSY states -void tud_dfu_mode_start_poll_timeout_cb(uint8_t *ms_timeout); +void tud_dfu_start_poll_timeout_cb(uint8_t *ms_timeout); // Must be called when the poll_timeout has elapsed -void tud_dfu_mode_poll_timeout_done(void); +void tud_dfu_poll_timeout_done(void); // Invoked when a DFU_DNLOAD request is received // This callback takes the wBlockNum chunk of length length and provides it // to the application at the data pointer. This data is only valid for this // call, so the app must use it not or copy it. -void tud_dfu_mode_req_dnload_data_cb(uint16_t wBlockNum, uint8_t* data, uint16_t length); +void tud_dfu_req_dnload_data_cb(uint16_t wBlockNum, uint8_t* data, uint16_t length); // Invoked during the last DFU_DNLOAD request, signifying that the host believes // it is done transmitting data. // Return true if the application agrees there is no more data // Return false if the device disagrees, which will stall the pipe, and the Host // should initiate a recovery procedure -bool tud_dfu_mode_device_data_done_check_cb(void); +bool tud_dfu_device_data_done_check_cb(void); // Invoked when the Host has terminated a download or upload transfer -TU_ATTR_WEAK void tud_dfu_mode_abort_cb(void); +TU_ATTR_WEAK void tud_dfu_abort_cb(void); // Invoked when a DFU_UPLOAD request is received // This callback must populate data with up to length bytes // Return the number of bytes to write -uint16_t tud_dfu_mode_req_upload_data_cb(uint16_t block_num, uint8_t* data, uint16_t length); +uint16_t tud_dfu_req_upload_data_cb(uint16_t block_num, uint8_t* data, uint16_t length); // Invoked when a nonstandard request is received // Use may be vendor specific. // Return false to stall -TU_ATTR_WEAK bool tud_dfu_mode_req_nonstandard_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +TU_ATTR_WEAK bool tud_dfu_req_nonstandard_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); // Invoked during a DFU_GETSTATUS request to get for the string index // to the status description string table. -TU_ATTR_WEAK uint8_t tud_dfu_mode_get_status_desc_table_index_cb(void); +TU_ATTR_WEAK uint8_t tud_dfu_get_status_desc_table_index_cb(void); //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ diff --git a/src/tusb.h b/src/tusb.h index c9558515c..56504550c 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -97,7 +97,7 @@ #endif #if CFG_TUD_DFU_MODE - #include "class/dfu/dfu_mode_device.h" + #include "class/dfu/dfu_device.h" #endif #if CFG_TUD_NET From 88dea7a0a8f7cb1bc21eaa45a3abb9a912b030d6 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 22 Apr 2021 15:02:50 -0400 Subject: [PATCH 12/23] Move debug from .h to .c --- src/class/dfu/dfu.h | 80 ----------------------------------- src/class/dfu/dfu_device.c | 77 +++++++++++++++++++++++++++++++++ src/class/dfu/dfu_rt_device.c | 9 +++- 3 files changed, 84 insertions(+), 82 deletions(-) diff --git a/src/class/dfu/dfu.h b/src/class/dfu/dfu.h index af08a29f7..18de3bf99 100644 --- a/src/class/dfu/dfu.h +++ b/src/class/dfu/dfu.h @@ -111,86 +111,6 @@ typedef struct TU_ATTR_PACKED TU_VERIFY_STATIC( sizeof(dfu_status_req_payload_t) == 6, "size is not correct"); - -//--------------------------------------------------------------------+ -// Debug -//--------------------------------------------------------------------+ -#if CFG_TUSB_DEBUG >= 2 - -static tu_lookup_entry_t const _dfu_request_lookup[] = -{ - { .key = DFU_REQUEST_DETACH , .data = "DETACH" }, - { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" }, - { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" }, - { .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" }, - { .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" }, - { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" }, - { .key = DFU_REQUEST_ABORT , .data = "ABORT" }, -}; - -static tu_lookup_table_t const _dfu_request_table = -{ - .count = TU_ARRAY_SIZE(_dfu_request_lookup), - .items = _dfu_request_lookup -}; - -static tu_lookup_entry_t const _dfu_state_lookup[] = -{ - { .key = APP_IDLE , .data = "APP_IDLE" }, - { .key = APP_DETACH , .data = "APP_DETACH" }, - { .key = DFU_IDLE , .data = "DFU_IDLE" }, - { .key = DFU_DNLOAD_SYNC , .data = "DFU_DNLOAD_SYNC" }, - { .key = DFU_DNBUSY , .data = "DFU_DNBUSY" }, - { .key = DFU_DNLOAD_IDLE , .data = "DFU_DNLOAD_IDLE" }, - { .key = DFU_MANIFEST_SYNC , .data = "DFU_MANIFEST_SYNC" }, - { .key = DFU_MANIFEST , .data = "DFU_MANIFEST" }, - { .key = DFU_MANIFEST_WAIT_RESET , .data = "DFU_MANIFEST_WAIT_RESET" }, - { .key = DFU_UPLOAD_IDLE , .data = "DFU_UPLOAD_IDLE" }, - { .key = DFU_ERROR , .data = "DFU_ERROR" }, -}; - -static tu_lookup_table_t const _dfu_state_table = -{ - .count = TU_ARRAY_SIZE(_dfu_state_lookup), - .items = _dfu_state_lookup -}; - -static tu_lookup_entry_t const _dfu_status_lookup[] = -{ - { .key = DFU_STATUS_OK , .data = "OK" }, - { .key = DFU_STATUS_ERRTARGET , .data = "errTARGET" }, - { .key = DFU_STATUS_ERRFILE , .data = "errFILE" }, - { .key = DFU_STATUS_ERRWRITE , .data = "errWRITE" }, - { .key = DFU_STATUS_ERRERASE , .data = "errERASE" }, - { .key = DFU_STATUS_ERRCHECK_ERASED , .data = "errCHECK_ERASED" }, - { .key = DFU_STATUS_ERRPROG , .data = "errPROG" }, - { .key = DFU_STATUS_ERRVERIFY , .data = "errVERIFY" }, - { .key = DFU_STATUS_ERRADDRESS , .data = "errADDRESS" }, - { .key = DFU_STATUS_ERRNOTDONE , .data = "errNOTDONE" }, - { .key = DFU_STATUS_ERRFIRMWARE , .data = "errFIRMWARE" }, - { .key = DFU_STATUS_ERRVENDOR , .data = "errVENDOR" }, - { .key = DFU_STATUS_ERRUSBR , .data = "errUSBR" }, - { .key = DFU_STATUS_ERRPOR , .data = "errPOR" }, - { .key = DFU_STATUS_ERRUNKNOWN , .data = "errUNKNOWN" }, - { .key = DFU_STATUS_ERRSTALLEDPKT , .data = "errSTALLEDPKT" }, -}; - -static tu_lookup_table_t const _dfu_status_table = -{ - .count = TU_ARRAY_SIZE(_dfu_status_lookup), - .items = _dfu_status_lookup -}; - -#endif - -#define dfu_debug_print_context() \ -{ \ - TU_LOG2(" DFU at State: %s\r\n Status: %s\r\n", \ - tu_lookup_find(&_dfu_state_table, _dfu_state_ctx.state), \ - tu_lookup_find(&_dfu_status_table, _dfu_state_ctx.status) ); \ -} - - #ifdef __cplusplus } #endif diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index 98213d320..f3763adc7 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -61,6 +61,83 @@ static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * re static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request); static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request); +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= 2 + +static tu_lookup_entry_t const _dfu_request_lookup[] = +{ + { .key = DFU_REQUEST_DETACH , .data = "DETACH" }, + { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" }, + { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" }, + { .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" }, + { .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" }, + { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" }, + { .key = DFU_REQUEST_ABORT , .data = "ABORT" }, +}; + +static tu_lookup_table_t const _dfu_request_table = +{ + .count = TU_ARRAY_SIZE(_dfu_request_lookup), + .items = _dfu_request_lookup +}; + +static tu_lookup_entry_t const _dfu_state_lookup[] = +{ + { .key = APP_IDLE , .data = "APP_IDLE" }, + { .key = APP_DETACH , .data = "APP_DETACH" }, + { .key = DFU_IDLE , .data = "DFU_IDLE" }, + { .key = DFU_DNLOAD_SYNC , .data = "DFU_DNLOAD_SYNC" }, + { .key = DFU_DNBUSY , .data = "DFU_DNBUSY" }, + { .key = DFU_DNLOAD_IDLE , .data = "DFU_DNLOAD_IDLE" }, + { .key = DFU_MANIFEST_SYNC , .data = "DFU_MANIFEST_SYNC" }, + { .key = DFU_MANIFEST , .data = "DFU_MANIFEST" }, + { .key = DFU_MANIFEST_WAIT_RESET , .data = "DFU_MANIFEST_WAIT_RESET" }, + { .key = DFU_UPLOAD_IDLE , .data = "DFU_UPLOAD_IDLE" }, + { .key = DFU_ERROR , .data = "DFU_ERROR" }, +}; + +static tu_lookup_table_t const _dfu_state_table = +{ + .count = TU_ARRAY_SIZE(_dfu_state_lookup), + .items = _dfu_state_lookup +}; + +static tu_lookup_entry_t const _dfu_status_lookup[] = +{ + { .key = DFU_STATUS_OK , .data = "OK" }, + { .key = DFU_STATUS_ERRTARGET , .data = "errTARGET" }, + { .key = DFU_STATUS_ERRFILE , .data = "errFILE" }, + { .key = DFU_STATUS_ERRWRITE , .data = "errWRITE" }, + { .key = DFU_STATUS_ERRERASE , .data = "errERASE" }, + { .key = DFU_STATUS_ERRCHECK_ERASED , .data = "errCHECK_ERASED" }, + { .key = DFU_STATUS_ERRPROG , .data = "errPROG" }, + { .key = DFU_STATUS_ERRVERIFY , .data = "errVERIFY" }, + { .key = DFU_STATUS_ERRADDRESS , .data = "errADDRESS" }, + { .key = DFU_STATUS_ERRNOTDONE , .data = "errNOTDONE" }, + { .key = DFU_STATUS_ERRFIRMWARE , .data = "errFIRMWARE" }, + { .key = DFU_STATUS_ERRVENDOR , .data = "errVENDOR" }, + { .key = DFU_STATUS_ERRUSBR , .data = "errUSBR" }, + { .key = DFU_STATUS_ERRPOR , .data = "errPOR" }, + { .key = DFU_STATUS_ERRUNKNOWN , .data = "errUNKNOWN" }, + { .key = DFU_STATUS_ERRSTALLEDPKT , .data = "errSTALLEDPKT" }, +}; + +static tu_lookup_table_t const _dfu_status_table = +{ + .count = TU_ARRAY_SIZE(_dfu_status_lookup), + .items = _dfu_status_lookup +}; + +#endif + +#define dfu_debug_print_context() \ +{ \ + TU_LOG2(" DFU at State: %s\r\n Status: %s\r\n", \ + tu_lookup_find(&_dfu_state_table, _dfu_state_ctx.state), \ + tu_lookup_find(&_dfu_status_table, _dfu_state_ctx.status) ); \ +} //--------------------------------------------------------------------+ // USBD Driver API diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c index 9da2e1e4f..faeae9b68 100644 --- a/src/class/dfu/dfu_rt_device.c +++ b/src/class/dfu/dfu_rt_device.c @@ -93,11 +93,11 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request // Handle class request only from here TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); - TU_LOG2(" DFU RT Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest)); switch (request->bRequest) { case DFU_REQUEST_DETACH: { + TU_LOG2(" DFU RT Request: DETACH\r\n"); tud_control_status(rhport, request); tud_dfu_runtime_reboot_to_dfu_cb(); } @@ -105,6 +105,7 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request case DFU_REQUEST_GETSTATUS: { + TU_LOG2(" DFU RT Request: GETSTATUS\r\n"); dfu_status_req_payload_t resp; // Status = OK, Poll timeout is ignored during RT, State = APP_IDLE, IString = 0 memset(&resp, 0x00, sizeof(dfu_status_req_payload_t)); @@ -112,7 +113,11 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request } break; - default: return false; // stall unsupported request + default: + { + TU_LOG2(" DFU RT Unexpected Request: %d\r\n", request->bRequest); + return false; // stall unsupported request + } } return true; From 0936a76dc905a104f03cd1135fc22a8652ee8c7f Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 22 Apr 2021 15:59:49 -0400 Subject: [PATCH 13/23] Remove nonstd behaviour --- src/class/dfu/dfu_device.c | 2 +- src/class/dfu/dfu_device.h | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index f3763adc7..85e884d0d 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -269,7 +269,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_reque default: { TU_LOG2(" DFU Nonstandard Request: %u\r\n", request->bRequest); - return ( tud_dfu_req_nonstandard_cb ) ? tud_dfu_req_nonstandard_cb(rhport, stage, request) : false; + return false; // stall unsupported request } break; } diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h index 3bc1f4ae4..28a241b3a 100644 --- a/src/class/dfu/dfu_device.h +++ b/src/class/dfu/dfu_device.h @@ -107,11 +107,6 @@ TU_ATTR_WEAK void tud_dfu_abort_cb(void); // Return the number of bytes to write uint16_t tud_dfu_req_upload_data_cb(uint16_t block_num, uint8_t* data, uint16_t length); -// Invoked when a nonstandard request is received -// Use may be vendor specific. -// Return false to stall -TU_ATTR_WEAK bool tud_dfu_req_nonstandard_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); - // Invoked during a DFU_GETSTATUS request to get for the string index // to the status description string table. TU_ATTR_WEAK uint8_t tud_dfu_get_status_desc_table_index_cb(void); From 18e9d253e986670f77a0f15f687eec258011605f Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 22 Apr 2021 16:04:09 -0400 Subject: [PATCH 14/23] Remove usb reset callback --- src/class/dfu/dfu_device.c | 41 +++++++++++++++++--------------------- src/class/dfu/dfu_device.h | 8 -------- 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index 85e884d0d..085c2b455 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -160,32 +160,27 @@ void dfu_moded_reset(uint8_t rhport) { _dfu_state_ctx.state = DFU_IDLE; } else { - if ( tud_dfu_usb_reset_cb ) + switch (_dfu_state_ctx.state) { - tud_dfu_usb_reset_cb(rhport, &_dfu_state_ctx.state); - } else { - switch (_dfu_state_ctx.state) + case DFU_IDLE: + case DFU_DNLOAD_SYNC: + case DFU_DNBUSY: + case DFU_DNLOAD_IDLE: + case DFU_MANIFEST_SYNC: + case DFU_MANIFEST: + case DFU_MANIFEST_WAIT_RESET: + case DFU_UPLOAD_IDLE: { - case DFU_IDLE: - case DFU_DNLOAD_SYNC: - case DFU_DNBUSY: - case DFU_DNLOAD_IDLE: - case DFU_MANIFEST_SYNC: - case DFU_MANIFEST: - case DFU_MANIFEST_WAIT_RESET: - case DFU_UPLOAD_IDLE: - { - _dfu_state_ctx.state = (tud_dfu_firmware_valid_check_cb()) ? APP_IDLE : DFU_ERROR; - } - break; - - case DFU_ERROR: - default: - { - _dfu_state_ctx.state = APP_IDLE; - } - break; + _dfu_state_ctx.state = (tud_dfu_firmware_valid_check_cb()) ? APP_IDLE : DFU_ERROR; } + break; + + case DFU_ERROR: + default: + { + _dfu_state_ctx.state = APP_IDLE; + } + break; } } diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h index 28a241b3a..e92935fd7 100644 --- a/src/class/dfu/dfu_device.h +++ b/src/class/dfu/dfu_device.h @@ -58,14 +58,6 @@ uint8_t tud_dfu_init_attrs_cb(void); // to the status description string table. TU_ATTR_WEAK uint8_t tud_dfu_get_status_desc_table_index_cb(void); -// Invoked during a USB reset -// Lets the app perform custom behavior on a USB reset. -// If not defined, the default behavior remains the DFU specification of -// Checking the firmware valid and changing to the error state or rebooting to runtime -// Note: If used, the application must perform the reset logic for all states. -// Changing the state to APP_IDLE will result in tud_dfu_reboot_to_rt_cb being called -TU_ATTR_WEAK void tud_dfu_usb_reset_cb(uint8_t rhport, dfu_state_t *state); - // Invoked during a DFU_GETSTATUS request to set the timeout in ms to use // before the subsequent DFU_GETSTATUS requests. // The purpose of this value is to allow the device to tell the host From 289af581bb796f730324ac663b3f20d57d03e487 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 22 Apr 2021 16:06:06 -0400 Subject: [PATCH 15/23] Remove uunused code --- src/class/dfu/dfu_device.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h index e92935fd7..322cc6496 100644 --- a/src/class/dfu/dfu_device.h +++ b/src/class/dfu/dfu_device.h @@ -45,15 +45,6 @@ bool tud_dfu_firmware_valid_check_cb(void); // Invoked when the device must reboot to dfu runtime mode void tud_dfu_reboot_to_rt_cb(void); -// Invoked during initialization of the dfu driver to set attributes -// Return byte set with bitmasks: -// DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK -// DFU_FUNC_ATTR_CAN_UPLOAD_BITMASK -// DFU_FUNC_ATTR_MANIFESTATION_TOLERANT_BITMASK -// DFU_FUNC_ATTR_WILL_DETACH_BITMASK -// Note: This should match the USB descriptor -uint8_t tud_dfu_init_attrs_cb(void); - // Invoked during a DFU_GETSTATUS request to get for the string index // to the status description string table. TU_ATTR_WEAK uint8_t tud_dfu_get_status_desc_table_index_cb(void); From e54d9d10af40f4d83410f1e9cd94c47138c7fe7f Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 22 Apr 2021 16:39:43 -0400 Subject: [PATCH 16/23] Add const --- src/class/dfu/dfu_device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index 085c2b455..ed7d381f0 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -158,7 +158,7 @@ void dfu_moded_reset(uint8_t rhport) { if (_dfu_state_ctx.state == APP_DETACH) { - _dfu_state_ctx.state = DFU_IDLE; + _dfu_state_ctx.state = DFU_IDLE; } else { switch (_dfu_state_ctx.state) { @@ -210,7 +210,7 @@ uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) ) { - tusb_desc_dfu_functional_t *dfu_desc = (tusb_desc_dfu_functional_t *)p_desc; + tusb_desc_dfu_functional_t const *dfu_desc = (tusb_desc_dfu_functional_t const *)p_desc; _dfu_state_ctx.attrs = (uint8_t)dfu_desc->bAttributes; drv_len += tu_desc_len(p_desc); From 45e401e69df87353f528d0e3ce313d5af5a4f90c Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 22 Apr 2021 16:42:54 -0400 Subject: [PATCH 17/23] Remove unused alt_setting --- src/device/usbd.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/device/usbd.h b/src/device/usbd.h index f9700811f..3d71b5e35 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -546,10 +546,10 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb #define TUD_DFU_MODE_DESC_LEN (9 + 9) // DFU runtime descriptor -// Interface number, string index, alternate setting, attributes, detach timeout, transfer size -#define TUD_DFU_MODE_DESCRIPTOR(_itfnum, _stridx, _alt_setting, _attr, _timeout, _xfer_size) \ +// Interface number, string index, attributes, detach timeout, transfer size +#define TUD_DFU_MODE_DESCRIPTOR(_itfnum, _stridx, _attr, _timeout, _xfer_size) \ /* Interface */ \ - 9, TUSB_DESC_INTERFACE, _itfnum, _alt_setting, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_DFU, _stridx, \ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_DFU, _stridx, \ /* Function */ \ 9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101) From cc440ade81a72df3a605e0abc427a284b8bbc424 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 22 Apr 2021 16:47:05 -0400 Subject: [PATCH 18/23] Remove custom status description table --- src/class/dfu/dfu_device.c | 2 +- src/class/dfu/dfu_device.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index ed7d381f0..f0a90073b 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -292,7 +292,7 @@ static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const memset((uint8_t *)&resp.bwPollTimeout, 0x00, 3); } resp.bState = _dfu_state_ctx.state; - resp.iString = ( tud_dfu_get_status_desc_table_index_cb ) ? tud_dfu_get_status_desc_table_index_cb() : 0; + resp.iString = 0; tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_req_payload_t)); } diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h index 322cc6496..d19115555 100644 --- a/src/class/dfu/dfu_device.h +++ b/src/class/dfu/dfu_device.h @@ -90,9 +90,6 @@ TU_ATTR_WEAK void tud_dfu_abort_cb(void); // Return the number of bytes to write uint16_t tud_dfu_req_upload_data_cb(uint16_t block_num, uint8_t* data, uint16_t length); -// Invoked during a DFU_GETSTATUS request to get for the string index -// to the status description string table. -TU_ATTR_WEAK uint8_t tud_dfu_get_status_desc_table_index_cb(void); //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ From 8c80ddeb542007efd547eced028d9bb3e933d434 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 22 Apr 2021 17:00:57 -0400 Subject: [PATCH 19/23] Fix statte check on DATA stage --- src/class/dfu/dfu_device.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index f0a90073b..59369a78f 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -58,7 +58,7 @@ CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_state_ctx; static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request); static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request); static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength); -static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request); +static void dfu_req_dnload_reply(void); static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request); //--------------------------------------------------------------------+ @@ -225,9 +225,9 @@ uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, // return false to stall control endpoint (e.g unsupported request) bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { - if ( (stage == CONTROL_STAGE_DATA) && (request->bRequest == DFU_DNLOAD_SYNC) ) + if ( (stage == CONTROL_STAGE_DATA) && (_dfu_state_ctx.state == DFU_DNLOAD_SYNC) ) { - dfu_req_dnload_reply(rhport, request); + dfu_req_dnload_reply(); return true; } @@ -314,7 +314,7 @@ static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, request->wLength); } -static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) +static void dfu_req_dnload_reply(void) { uint8_t bwPollTimeout[3] = {0,0,0}; @@ -333,7 +333,7 @@ static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * _dfu_state_ctx.last_transfer_len = 0; } -void tud_dfu_poll_timeout_done() +void tud_dfu_poll_timeout_done(void) { if (_dfu_state_ctx.state == DFU_DNBUSY) { From b8e5885c2b97725e30ff57e6adc8cb7696711f28 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 22 Apr 2021 17:45:33 -0400 Subject: [PATCH 20/23] Removes polltimeout behaviour and restructures Moves dfu_req_dnload_reply to ACK stage of a DNREQUEST. Removes unneeded variables due to other simplifications. --- src/class/dfu/dfu_device.c | 78 +++++++++++++++----------------------- src/class/dfu/dfu_device.h | 28 ++------------ 2 files changed, 34 insertions(+), 72 deletions(-) diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index 59369a78f..25588aabc 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -44,10 +44,6 @@ typedef struct TU_ATTR_PACKED dfu_state_t state; uint8_t attrs; bool blk_transfer_in_proc; - - uint8_t itf_num; - uint16_t last_block_num; - uint16_t last_transfer_len; CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_TRANSFER_BUFFER_SIZE]; } dfu_state_ctx_t; @@ -58,7 +54,7 @@ CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_state_ctx; static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request); static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const * request); static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength); -static void dfu_req_dnload_reply(void); +static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request); static bool dfu_state_machine(uint8_t rhport, tusb_control_request_t const * request); //--------------------------------------------------------------------+ @@ -148,8 +144,6 @@ void dfu_moded_init(void) _dfu_state_ctx.status = DFU_STATUS_OK; _dfu_state_ctx.attrs = 0; _dfu_state_ctx.blk_transfer_in_proc = false; - _dfu_state_ctx.last_block_num = 0; - _dfu_state_ctx.last_transfer_len = 0; dfu_debug_print_context(); } @@ -191,8 +185,6 @@ void dfu_moded_reset(uint8_t rhport) _dfu_state_ctx.status = DFU_STATUS_OK; _dfu_state_ctx.blk_transfer_in_proc = false; - _dfu_state_ctx.last_block_num = 0; - _dfu_state_ctx.last_transfer_len = 0; dfu_debug_print_context(); } @@ -225,23 +217,20 @@ uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, // return false to stall control endpoint (e.g unsupported request) bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { - if ( (stage == CONTROL_STAGE_DATA) && (_dfu_state_ctx.state == DFU_DNLOAD_SYNC) ) - { - dfu_req_dnload_reply(); - return true; - } - - // nothing to do with any other DATA or ACK stage - if ( stage != CONTROL_STAGE_SETUP ) return true; + // nothing to do with DATA stage + if ( stage == CONTROL_STAGE_DATA ) return true; TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE); - // dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request - if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type && - TUSB_REQ_SET_INTERFACE == request->bRequest ) + if(stage == CONTROL_STAGE_SETUP) { - tud_control_status(rhport, request); - return true; + // dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request + if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type && + TUSB_REQ_SET_INTERFACE == request->bRequest ) + { + tud_control_status(rhport, request); + return true; + } } // Handle class request only from here @@ -249,15 +238,27 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_reque switch (request->bRequest) { - case DFU_REQUEST_DETACH: case DFU_REQUEST_DNLOAD: + { + if ( (stage == CONTROL_STAGE_ACK) + && ((_dfu_state_ctx.attrs & DFU_FUNC_ATTR_CAN_DOWNLOAD_BITMASK) != 0) + && (_dfu_state_ctx.state == DFU_DNLOAD_SYNC)) + { + dfu_req_dnload_reply(rhport, request); + return true; + } + } // fallthrough + case DFU_REQUEST_DETACH: case DFU_REQUEST_UPLOAD: case DFU_REQUEST_GETSTATUS: case DFU_REQUEST_CLRSTATUS: case DFU_REQUEST_GETSTATE: case DFU_REQUEST_ABORT: { - return dfu_state_machine(rhport, request); + if(stage == CONTROL_STAGE_SETUP) + { + return dfu_state_machine(rhport, request); + } } break; @@ -285,12 +286,7 @@ static void dfu_req_getstatus_reply(uint8_t rhport, tusb_control_request_t const dfu_status_req_payload_t resp; resp.bStatus = _dfu_state_ctx.status; - if ( tud_dfu_get_poll_timeout_cb ) - { - tud_dfu_get_poll_timeout_cb((uint8_t *)&resp.bwPollTimeout); - } else { - memset((uint8_t *)&resp.bwPollTimeout, 0x00, 3); - } + memset((uint8_t *)&resp.bwPollTimeout, 0x00, 3); resp.bState = _dfu_state_ctx.state; resp.iString = 0; @@ -304,8 +300,6 @@ static void dfu_req_getstate_reply(uint8_t rhport, tusb_control_request_t const static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * request) { - _dfu_state_ctx.last_block_num = request->wValue; - _dfu_state_ctx.last_transfer_len = request->wLength; // TODO: add "zero" copy mode so the buffer we read into can be provided by the user // if they wish, there still will be the internal control buffer copy to this buffer // but this mode would provide zero copy from the class driver to the application @@ -314,26 +308,14 @@ static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, request->wLength); } -static void dfu_req_dnload_reply(void) +static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) { - uint8_t bwPollTimeout[3] = {0,0,0}; - - if ( tud_dfu_get_poll_timeout_cb ) - { - tud_dfu_get_poll_timeout_cb((uint8_t *)&bwPollTimeout); - } - - tud_dfu_start_poll_timeout_cb((uint8_t *)&bwPollTimeout); - - // TODO: I want the real xferred len, not what is expected. May need to change usbd.c to do this. - tud_dfu_req_dnload_data_cb(_dfu_state_ctx.last_block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, _dfu_state_ctx.last_transfer_len); + (void) rhport; + tud_dfu_req_dnload_data_cb(request->wValue, (uint8_t *)&_dfu_state_ctx.transfer_buf, request->wLength); _dfu_state_ctx.blk_transfer_in_proc = false; - - _dfu_state_ctx.last_block_num = 0; - _dfu_state_ctx.last_transfer_len = 0; } -void tud_dfu_poll_timeout_done(void) +void tud_dfu_dnload_complete(void) { if (_dfu_state_ctx.state == DFU_DNBUSY) { diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h index d19115555..0c567ff45 100644 --- a/src/class/dfu/dfu_device.h +++ b/src/class/dfu/dfu_device.h @@ -45,36 +45,16 @@ bool tud_dfu_firmware_valid_check_cb(void); // Invoked when the device must reboot to dfu runtime mode void tud_dfu_reboot_to_rt_cb(void); -// Invoked during a DFU_GETSTATUS request to get for the string index -// to the status description string table. -TU_ATTR_WEAK uint8_t tud_dfu_get_status_desc_table_index_cb(void); - -// Invoked during a DFU_GETSTATUS request to set the timeout in ms to use -// before the subsequent DFU_GETSTATUS requests. -// The purpose of this value is to allow the device to tell the host -// how long to wait between the DFU_DNLOAD and DFU_GETSTATUS checks -// to allow the device to have time to erase, write memory, etc. -// ms_timeout is a pointer to array of length 3. -// Refer to the USB DFU class specification for more details -TU_ATTR_WEAK void tud_dfu_get_poll_timeout_cb(uint8_t *ms_timeout); - -// Invoked when a DFU_DNLOAD request is received -// This should start a timer for ms_timeout ms -// When the timer has elapsed, tud_dfu_runtime_poll_timeout_done must be called -// NOTE: This callback should return immediately, and not implement the delay -// internally, as this will hold up the class stack from receiving any packets -// during the DFU_DNLOAD_SYNC and DFU_DNBUSY states -void tud_dfu_start_poll_timeout_cb(uint8_t *ms_timeout); - -// Must be called when the poll_timeout has elapsed -void tud_dfu_poll_timeout_done(void); - // Invoked when a DFU_DNLOAD request is received // This callback takes the wBlockNum chunk of length length and provides it // to the application at the data pointer. This data is only valid for this // call, so the app must use it not or copy it. void tud_dfu_req_dnload_data_cb(uint16_t wBlockNum, uint8_t* data, uint16_t length); +// Must be called when the application is done using the last block of data +// provided by tud_dfu_req_dnload_data_cb +void tud_dfu_dnload_complete(void); + // Invoked during the last DFU_DNLOAD request, signifying that the host believes // it is done transmitting data. // Return true if the application agrees there is no more data From f830800d00520ed2a91d9095e77738f4558324b2 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Thu, 29 Apr 2021 16:04:18 -0400 Subject: [PATCH 21/23] Fix typo and clean up reset --- src/class/dfu/dfu_device.c | 44 ++++++-------------------------------- src/class/dfu/dfu_device.h | 3 --- 2 files changed, 6 insertions(+), 41 deletions(-) diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index 25588aabc..da9f189aa 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -140,7 +140,7 @@ static tu_lookup_table_t const _dfu_status_table = //--------------------------------------------------------------------+ void dfu_moded_init(void) { - _dfu_state_ctx.state = APP_DETACH; // After init, reset will occur. We want to be in APP_DETACH to move to DFU_IDLE + _dfu_state_ctx.state = DFU_IDLE; _dfu_state_ctx.status = DFU_STATUS_OK; _dfu_state_ctx.attrs = 0; _dfu_state_ctx.blk_transfer_in_proc = false; @@ -150,39 +150,7 @@ void dfu_moded_init(void) void dfu_moded_reset(uint8_t rhport) { - if (_dfu_state_ctx.state == APP_DETACH) - { - _dfu_state_ctx.state = DFU_IDLE; - } else { - switch (_dfu_state_ctx.state) - { - case DFU_IDLE: - case DFU_DNLOAD_SYNC: - case DFU_DNBUSY: - case DFU_DNLOAD_IDLE: - case DFU_MANIFEST_SYNC: - case DFU_MANIFEST: - case DFU_MANIFEST_WAIT_RESET: - case DFU_UPLOAD_IDLE: - { - _dfu_state_ctx.state = (tud_dfu_firmware_valid_check_cb()) ? APP_IDLE : DFU_ERROR; - } - break; - - case DFU_ERROR: - default: - { - _dfu_state_ctx.state = APP_IDLE; - } - break; - } - } - - if(_dfu_state_ctx.state == APP_IDLE) - { - tud_dfu_reboot_to_rt_cb(); - } - + _dfu_state_ctx.state = DFU_IDLE; _dfu_state_ctx.status = DFU_STATUS_OK; _dfu_state_ctx.blk_transfer_in_proc = false; dfu_debug_print_context(); @@ -276,8 +244,8 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_reque static uint16_t dfu_req_upload(uint8_t rhport, tusb_control_request_t const * request, uint16_t block_num, uint16_t wLength) { TU_VERIFY( wLength <= CFG_TUD_DFU_TRANSFER_BUFFER_SIZE); - uint16_t retval = tud_dfu_req_upload_data_cb(block_num, (uint8_t *)&_dfu_state_ctx.transfer_buf, wLength); - tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, retval); + uint16_t retval = tud_dfu_req_upload_data_cb(block_num, (uint8_t *)_dfu_state_ctx.transfer_buf, wLength); + tud_control_xfer(rhport, request, _dfu_state_ctx.transfer_buf, retval); return retval; } @@ -305,13 +273,13 @@ static void dfu_req_dnload_setup(uint8_t rhport, tusb_control_request_t const * // but this mode would provide zero copy from the class driver to the application // setup for data phase - tud_control_xfer(rhport, request, &_dfu_state_ctx.transfer_buf, request->wLength); + tud_control_xfer(rhport, request, _dfu_state_ctx.transfer_buf, request->wLength); } static void dfu_req_dnload_reply(uint8_t rhport, tusb_control_request_t const * request) { (void) rhport; - tud_dfu_req_dnload_data_cb(request->wValue, (uint8_t *)&_dfu_state_ctx.transfer_buf, request->wLength); + tud_dfu_req_dnload_data_cb(request->wValue, (uint8_t *)_dfu_state_ctx.transfer_buf, request->wLength); _dfu_state_ctx.blk_transfer_in_proc = false; } diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h index 0c567ff45..aa8ee23d4 100644 --- a/src/class/dfu/dfu_device.h +++ b/src/class/dfu/dfu_device.h @@ -42,9 +42,6 @@ // Invoked when a reset is received to check if firmware is valid bool tud_dfu_firmware_valid_check_cb(void); -// Invoked when the device must reboot to dfu runtime mode -void tud_dfu_reboot_to_rt_cb(void); - // Invoked when a DFU_DNLOAD request is received // This callback takes the wBlockNum chunk of length length and provides it // to the application at the data pointer. This data is only valid for this From dab1ed6b327a41976a2d4968d80747ac35fde113 Mon Sep 17 00:00:00 2001 From: Jeremiah McCarthy Date: Wed, 5 May 2021 17:42:38 -0400 Subject: [PATCH 22/23] Add example to be tested Update API description. --- examples/device/dfu/CMakeLists.txt | 41 +++++ examples/device/dfu/Makefile | 12 ++ examples/device/dfu/src/main.c | 188 ++++++++++++++++++++++ examples/device/dfu/src/tusb_config.h | 90 +++++++++++ examples/device/dfu/src/usb_descriptors.c | 167 +++++++++++++++++++ src/class/dfu/dfu_device.h | 3 +- 6 files changed, 500 insertions(+), 1 deletion(-) create mode 100644 examples/device/dfu/CMakeLists.txt create mode 100644 examples/device/dfu/Makefile create mode 100644 examples/device/dfu/src/main.c create mode 100644 examples/device/dfu/src/tusb_config.h create mode 100644 examples/device/dfu/src/usb_descriptors.c diff --git a/examples/device/dfu/CMakeLists.txt b/examples/device/dfu/CMakeLists.txt new file mode 100644 index 000000000..f4bf75c26 --- /dev/null +++ b/examples/device/dfu/CMakeLists.txt @@ -0,0 +1,41 @@ +# use directory name for project id +get_filename_component(PROJECT ${CMAKE_CURRENT_SOURCE_DIR} NAME) +set(PROJECT ${BOARD}-${PROJECT}) + +# TOP is absolute path to root directory of TinyUSB git repo +set(TOP "../../..") +get_filename_component(TOP "${TOP}" REALPATH) + +# Check for -DFAMILY= +if(FAMILY STREQUAL "rp2040") + cmake_minimum_required(VERSION 3.12) + set(PICO_SDK_PATH ${TOP}/hw/mcu/raspberrypi/pico-sdk) + include(${PICO_SDK_PATH}/pico_sdk_init.cmake) + project(${PROJECT}) + pico_sdk_init() + add_executable(${PROJECT}) + + include(${TOP}/hw/bsp/${FAMILY}/family.cmake) + + # Example source + target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + + # Example include + target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + + # Example defines + target_compile_definitions(${PROJECT} PUBLIC + CFG_TUSB_OS=OPT_OS_PICO + ) + + target_link_libraries(${PROJECT} pico_stdlib pico_fix_rp2040_usb_device_enumeration) + pico_add_extra_outputs(${PROJECT}) + +else() + message(FATAL_ERROR "Invalid FAMILY specified") +endif() diff --git a/examples/device/dfu/Makefile b/examples/device/dfu/Makefile new file mode 100644 index 000000000..69b633fea --- /dev/null +++ b/examples/device/dfu/Makefile @@ -0,0 +1,12 @@ +include ../../../tools/top.mk +include ../../make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +EXAMPLE_SOURCE += $(wildcard src/*.c) +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../rules.mk diff --git a/examples/device/dfu/src/main.c b/examples/device/dfu/src/main.c new file mode 100644 index 000000000..3849de44d --- /dev/null +++ b/examples/device/dfu/src/main.c @@ -0,0 +1,188 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + */ + + /* + * After device is enumerated in dfu mode run the following commands + * + * To transfer firmware from host to device: + * + * $ dfu-util -D [filename] + * + * To transfer firmware from device to host: + * + * $ dfu-util -U [filename] + * + */ + +#include +#include +#include + +#include "bsp/board.h" +#include "tusb.h" + + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ +#ifndef DFU_VERBOSE +#define DFU_VERBOSE 0 +#endif + +/* Blink pattern + * - 1000 ms : device should reboot + * - 250 ms : device not mounted + * - 1000 ms : device mounted + * - 2500 ms : device is suspended + */ +enum { + BLINK_DFU_MODE = 100, + BLINK_NOT_MOUNTED = 250, + BLINK_MOUNTED = 1000, + BLINK_SUSPENDED = 2500, +}; + +static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; + +void led_blinking_task(void); + +/*------------- MAIN -------------*/ +int main(void) +{ + board_init(); + + tusb_init(); + + while (1) + { + tud_task(); // tinyusb device task + led_blinking_task(); + } + + return 0; +} + +//--------------------------------------------------------------------+ +// Device callbacks +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) +{ + blink_interval_ms = BLINK_MOUNTED; +} + +// Invoked when device is unmounted +void tud_umount_cb(void) +{ + blink_interval_ms = BLINK_NOT_MOUNTED; +} + +// Invoked when usb bus is suspended +// remote_wakeup_en : if host allow us to perform remote wakeup +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +void tud_suspend_cb(bool remote_wakeup_en) +{ + (void) remote_wakeup_en; + blink_interval_ms = BLINK_SUSPENDED; +} + +// Invoked when usb bus is resumed +void tud_resume_cb(void) +{ + blink_interval_ms = BLINK_MOUNTED; +} + +// Invoked on DFU_DETACH request to reboot to the bootloader +void tud_dfu_runtime_reboot_to_dfu_cb(void) +{ + blink_interval_ms = BLINK_DFU_MODE; +} + +//--------------------------------------------------------------------+ +// Class callbacks +//--------------------------------------------------------------------+ +bool tud_dfu_firmware_valid_check_cb(void) +{ + TU_LOG2(" Firmware check\r\n"); + return true; +} + +void tud_dfu_req_dnload_data_cb(uint16_t wBlockNum, uint8_t* data, uint16_t length) +{ + TU_LOG2(" Received BlockNum %u of length %u\r\n", wBlockNum, length); + +#if DFU_VERBOSE + for(uint16_t i=0; i 31 ) { + chr_count = 31; + } + + // Convert ASCII string into UTF-16 + for(uint8_t i=0; i Date: Wed, 5 May 2021 18:00:32 -0400 Subject: [PATCH 23/23] Fix typo --- examples/device/dfu/src/usb_descriptors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/device/dfu/src/usb_descriptors.c b/examples/device/dfu/src/usb_descriptors.c index 5f8cf50e8..77662542d 100644 --- a/examples/device/dfu/src/usb_descriptors.c +++ b/examples/device/dfu/src/usb_descriptors.c @@ -94,7 +94,7 @@ enum uint8_t const desc_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF1_NUM_TOTAL, 0, CONFIG_1_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + TUD_CONFIG_DESCRIPTOR(1, ITF1_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), // Interface number, string index, attributes, detach timeout, transfer size */ TUD_DFU_MODE_DESCRIPTOR(ITF1_NUM_DFU_MODE, 0, FUNC_ATTRS, 1000, CFG_TUD_DFU_TRANSFER_BUFFER_SIZE),