Separate DFU RT and Mode. Untested
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
							
								
								
									
										598
									
								
								src/class/dfu/dfu_mode_device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										598
									
								
								src/class/dfu/dfu_mode_device.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										132
									
								
								src/class/dfu/dfu_mode_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/class/dfu/dfu_mode_device.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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_ */ | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
| //--------------------------------------------------------------------+ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jeremiah McCarthy
					Jeremiah McCarthy