Open FFBoard
Open source force feedback firmware
dfu_device.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2021 XMOS LIMITED
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 * This file is part of the TinyUSB stack.
25 */
26
27#include "tusb_option.h"
28
29#if (CFG_TUD_ENABLED && CFG_TUD_DFU)
30
31#include "device/usbd.h"
32#include "device/usbd_pvt.h"
33
34#include "dfu_device.h"
35
36//--------------------------------------------------------------------+
37// MACRO CONSTANT TYPEDEF
38//--------------------------------------------------------------------+
39
40// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
41#ifndef CFG_TUD_DFU_LOG_LEVEL
42 #define CFG_TUD_DFU_LOG_LEVEL CFG_TUD_LOG_LEVEL
43#endif
44
45#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_DFU_LOG_LEVEL, __VA_ARGS__)
46
47//--------------------------------------------------------------------+
48// INTERNAL OBJECT & FUNCTION DECLARATION
49//--------------------------------------------------------------------+
50typedef struct
51{
52 uint8_t attrs;
53 uint8_t alt;
54
57
59 uint16_t block;
60 uint16_t length;
61
62 CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE];
64
65// Only a single dfu state is allowed
66CFG_TUD_MEM_SECTION tu_static dfu_state_ctx_t _dfu_ctx;
67
68static void reset_state(void)
69{
73}
74
75static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
76static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
77static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
78
79//--------------------------------------------------------------------+
80// Debug
81//--------------------------------------------------------------------+
82#if CFG_TUSB_DEBUG >= 2
83
85{
86 { .key = DFU_REQUEST_DETACH , .data = "DETACH" },
87 { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" },
88 { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" },
89 { .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" },
90 { .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" },
91 { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" },
92 { .key = DFU_REQUEST_ABORT , .data = "ABORT" },
93};
94
96{
97 .count = TU_ARRAY_SIZE(_dfu_request_lookup),
98 .items = _dfu_request_lookup
99};
100
102{
103 { .key = APP_IDLE , .data = "APP_IDLE" },
104 { .key = APP_DETACH , .data = "APP_DETACH" },
105 { .key = DFU_IDLE , .data = "IDLE" },
106 { .key = DFU_DNLOAD_SYNC , .data = "DNLOAD_SYNC" },
107 { .key = DFU_DNBUSY , .data = "DNBUSY" },
108 { .key = DFU_DNLOAD_IDLE , .data = "DNLOAD_IDLE" },
109 { .key = DFU_MANIFEST_SYNC , .data = "MANIFEST_SYNC" },
110 { .key = DFU_MANIFEST , .data = "MANIFEST" },
111 { .key = DFU_MANIFEST_WAIT_RESET , .data = "MANIFEST_WAIT_RESET" },
112 { .key = DFU_UPLOAD_IDLE , .data = "UPLOAD_IDLE" },
113 { .key = DFU_ERROR , .data = "ERROR" },
114};
115
117{
118 .count = TU_ARRAY_SIZE(_dfu_state_lookup),
119 .items = _dfu_state_lookup
120};
121
123{
124 { .key = DFU_STATUS_OK , .data = "OK" },
125 { .key = DFU_STATUS_ERR_TARGET , .data = "errTARGET" },
126 { .key = DFU_STATUS_ERR_FILE , .data = "errFILE" },
127 { .key = DFU_STATUS_ERR_WRITE , .data = "errWRITE" },
128 { .key = DFU_STATUS_ERR_ERASE , .data = "errERASE" },
129 { .key = DFU_STATUS_ERR_CHECK_ERASED , .data = "errCHECK_ERASED" },
130 { .key = DFU_STATUS_ERR_PROG , .data = "errPROG" },
131 { .key = DFU_STATUS_ERR_VERIFY , .data = "errVERIFY" },
132 { .key = DFU_STATUS_ERR_ADDRESS , .data = "errADDRESS" },
133 { .key = DFU_STATUS_ERR_NOTDONE , .data = "errNOTDONE" },
134 { .key = DFU_STATUS_ERR_FIRMWARE , .data = "errFIRMWARE" },
135 { .key = DFU_STATUS_ERR_VENDOR , .data = "errVENDOR" },
136 { .key = DFU_STATUS_ERR_USBR , .data = "errUSBR" },
137 { .key = DFU_STATUS_ERR_POR , .data = "errPOR" },
138 { .key = DFU_STATUS_ERR_UNKNOWN , .data = "errUNKNOWN" },
139 { .key = DFU_STATUS_ERR_STALLEDPKT , .data = "errSTALLEDPKT" },
140};
141
143{
144 .count = TU_ARRAY_SIZE(_dfu_status_lookup),
145 .items = _dfu_status_lookup
146};
147
148#endif
149
150//--------------------------------------------------------------------+
151// USBD Driver API
152//--------------------------------------------------------------------+
153void dfu_moded_reset(uint8_t rhport)
154{
155 (void) rhport;
156
157 _dfu_ctx.attrs = 0;
158 _dfu_ctx.alt = 0;
159
160 reset_state();
161}
162
163void dfu_moded_init(void) {
165}
166
168 return true;
169}
170
171uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
172{
173 (void) rhport;
174
175 //------------- Interface (with Alt) descriptor -------------//
176 uint8_t const itf_num = itf_desc->bInterfaceNumber;
177 uint8_t alt_count = 0;
178
179 uint16_t drv_len = 0;
180 TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU, 0);
181
182 while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU)
183 {
184 TU_ASSERT(max_len > drv_len, 0);
185
186 // Alternate must have the same interface number
187 TU_ASSERT(itf_desc->bInterfaceNumber == itf_num, 0);
188
189 // Alt should increase by one every time
190 TU_ASSERT(itf_desc->bAlternateSetting == alt_count, 0);
191 alt_count++;
192
193 drv_len += tu_desc_len(itf_desc);
194 itf_desc = (tusb_desc_interface_t const *) tu_desc_next(itf_desc);
195 }
196
197 //------------- DFU Functional descriptor -------------//
198 tusb_desc_dfu_functional_t const *func_desc = (tusb_desc_dfu_functional_t const *) itf_desc;
199 TU_ASSERT(tu_desc_type(func_desc) == TUSB_DESC_FUNCTIONAL, 0);
200 drv_len += sizeof(tusb_desc_dfu_functional_t);
201
202 _dfu_ctx.attrs = func_desc->bAttributes;
203
204 // CFG_TUD_DFU_XFER_BUFSIZE has to be set to the buffer size used in TUD_DFU_DESCRIPTOR
205 uint16_t const transfer_size = tu_le16toh( tu_unaligned_read16((uint8_t const*) func_desc + offsetof(tusb_desc_dfu_functional_t, wTransferSize)) );
206 TU_ASSERT(transfer_size <= CFG_TUD_DFU_XFER_BUFSIZE, drv_len);
207
208 return drv_len;
209}
210
211// Invoked when a control transfer occurred on an interface of this class
212// Driver response accordingly to the request and the transfer stage (setup/data/ack)
213// return false to stall control endpoint (e.g unsupported request)
214bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
215{
216 TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
217
218 TU_LOG_DRV(" DFU State : %s, Status: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_ctx.state), tu_lookup_find(&_dfu_status_table, _dfu_ctx.status));
219
221 {
222 // Standard request include GET/SET_INTERFACE
223 switch ( request->bRequest )
224 {
226 if ( stage == CONTROL_STAGE_SETUP )
227 {
228 // Switch Alt interface and reset state machine
229 _dfu_ctx.alt = (uint8_t) request->wValue;
230 reset_state();
231 return tud_control_status(rhport, request);
232 }
233 break;
234
237 {
238 return tud_control_xfer(rhport, request, &_dfu_ctx.alt, 1);
239 }
240 break;
241
242 // unsupported request
243 default: return false;
244 }
245 }
247 {
248 TU_LOG_DRV(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest));
249
250 // Class request
251 switch ( request->bRequest )
252 {
254 if ( stage == CONTROL_STAGE_SETUP )
255 {
257 }
258 else if ( stage == CONTROL_STAGE_ACK )
259 {
261 }
262 break;
263
265 if ( stage == CONTROL_STAGE_SETUP )
266 {
267 reset_state();
269 }
270 break;
271
273 if ( stage == CONTROL_STAGE_SETUP )
274 {
276 }
277 break;
278
280 if ( stage == CONTROL_STAGE_SETUP )
281 {
282 reset_state();
284 }
285 else if ( stage == CONTROL_STAGE_ACK )
286 {
288 }
289 break;
290
292 if ( stage == CONTROL_STAGE_SETUP )
293 {
294 TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD);
295 TU_VERIFY(tud_dfu_upload_cb);
296 TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
297
299
300 return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, xfer_len);
301 }
302 break;
303
305 if ( stage == CONTROL_STAGE_SETUP )
306 {
307 TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_DOWNLOAD);
309 TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
310
311 // set to true for both download and manifest
313
314 // save block and length for flashing
317
318 if ( request->wLength )
319 {
320 // Download with payload -> transition to DOWNLOAD SYNC
323 }
324 else
325 {
326 // Download is complete -> transition to MANIFEST SYNC
328 return tud_control_status(rhport, request);
329 }
330 }
331 break;
332
334 switch ( _dfu_ctx.state )
335 {
336 case DFU_DNLOAD_SYNC:
338 break;
339
342 break;
343
344 default:
346 break;
347 }
348 break;
349
350 default: return false; // stall unsupported request
351 }
352 }else
353 {
354 return false; // unsupported request
355 }
356
357 return true;
358}
359
360void tud_dfu_finish_flashing(uint8_t status)
361{
363
364 if ( status == DFU_STATUS_OK )
365 {
367 {
369 }
370 else if (_dfu_ctx.state == DFU_MANIFEST)
371 {
372 _dfu_ctx.state = (_dfu_ctx.attrs & DFU_ATTR_MANIFESTATION_TOLERANT)
374 }
375 }
376 else
377 {
378 // failed while flashing, move to dfuError
380 _dfu_ctx.status = (dfu_status_t)status;
381 }
382}
383
384static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
385{
386 if ( stage == CONTROL_STAGE_SETUP )
387 {
388 // only transition to next state on CONTROL_STAGE_ACK
389 dfu_state_t next_state;
390 uint32_t timeout;
391
393 {
394 next_state = DFU_DNBUSY;
395 timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, (uint8_t) next_state);
396 }
397 else
398 {
399 next_state = DFU_DNLOAD_IDLE;
400 timeout = 0;
401 }
402
403 return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
404 }
405 else if ( stage == CONTROL_STAGE_ACK )
406 {
408 {
411 }else
412 {
414 }
415 }
416
417 return true;
418}
419
420static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
421{
422 if ( stage == CONTROL_STAGE_SETUP )
423 {
424 // only transition to next state on CONTROL_STAGE_ACK
425 dfu_state_t next_state;
426 uint32_t timeout;
427
429 {
430 next_state = DFU_MANIFEST;
431 timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, next_state);
432 }
433 else
434 {
435 next_state = DFU_IDLE;
436 timeout = 0;
437 }
438
439 return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
440 }
441 else if ( stage == CONTROL_STAGE_ACK )
442 {
444 {
447 }
448 else
449 {
451 }
452 }
453
454 return true;
455}
456
457static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout)
458{
460 resp.bStatus = (uint8_t) status;
461 resp.bwPollTimeout[0] = TU_U32_BYTE0(timeout);
462 resp.bwPollTimeout[1] = TU_U32_BYTE1(timeout);
463 resp.bwPollTimeout[2] = TU_U32_BYTE2(timeout);
464 resp.bState = (uint8_t) state;
465 resp.iString = 0;
466
467 return tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
468}
469
470#endif
dfu_state_t
Definition: dfu.h:65
@ APP_DETACH
Definition: dfu.h:67
@ DFU_IDLE
Definition: dfu.h:68
@ DFU_DNBUSY
Definition: dfu.h:70
@ DFU_MANIFEST
Definition: dfu.h:73
@ APP_IDLE
Definition: dfu.h:66
@ DFU_DNLOAD_SYNC
Definition: dfu.h:69
@ DFU_MANIFEST_SYNC
Definition: dfu.h:72
@ DFU_MANIFEST_WAIT_RESET
Definition: dfu.h:74
@ DFU_UPLOAD_IDLE
Definition: dfu.h:75
@ DFU_DNLOAD_IDLE
Definition: dfu.h:71
@ DFU_ERROR
Definition: dfu.h:76
@ DFU_REQUEST_UPLOAD
Definition: dfu.h:57
@ DFU_REQUEST_GETSTATUS
Definition: dfu.h:58
@ DFU_REQUEST_ABORT
Definition: dfu.h:61
@ DFU_REQUEST_GETSTATE
Definition: dfu.h:60
@ DFU_REQUEST_CLRSTATUS
Definition: dfu.h:59
@ DFU_REQUEST_DETACH
Definition: dfu.h:55
@ DFU_REQUEST_DNLOAD
Definition: dfu.h:56
dfu_status_t
Definition: dfu.h:80
@ DFU_STATUS_ERR_CHECK_ERASED
Definition: dfu.h:86
@ DFU_STATUS_ERR_PROG
Definition: dfu.h:87
@ DFU_STATUS_OK
Definition: dfu.h:81
@ DFU_STATUS_ERR_POR
Definition: dfu.h:94
@ DFU_STATUS_ERR_FILE
Definition: dfu.h:83
@ DFU_STATUS_ERR_ERASE
Definition: dfu.h:85
@ DFU_STATUS_ERR_WRITE
Definition: dfu.h:84
@ DFU_STATUS_ERR_STALLEDPKT
Definition: dfu.h:96
@ DFU_STATUS_ERR_TARGET
Definition: dfu.h:82
@ DFU_STATUS_ERR_NOTDONE
Definition: dfu.h:90
@ DFU_STATUS_ERR_FIRMWARE
Definition: dfu.h:91
@ DFU_STATUS_ERR_VERIFY
Definition: dfu.h:88
@ DFU_STATUS_ERR_ADDRESS
Definition: dfu.h:89
@ DFU_STATUS_ERR_USBR
Definition: dfu.h:93
@ DFU_STATUS_ERR_VENDOR
Definition: dfu.h:92
@ DFU_STATUS_ERR_UNKNOWN
Definition: dfu.h:95
tu_static tu_lookup_entry_t const _dfu_state_lookup[]
Definition: dfu_device.c:101
void dfu_moded_init(void)
Definition: dfu_device.c:163
tu_static tu_lookup_table_t const _dfu_state_table
Definition: dfu_device.c:116
tu_static tu_lookup_entry_t const _dfu_status_lookup[]
Definition: dfu_device.c:122
bool dfu_moded_deinit(void)
Definition: dfu_device.c:167
tu_static tu_lookup_table_t const _dfu_status_table
Definition: dfu_device.c:142
uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
Definition: dfu_device.c:171
void tud_dfu_finish_flashing(uint8_t status)
Definition: dfu_device.c:360
bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
Definition: dfu_device.c:214
static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const *request, dfu_state_t state, dfu_status_t status, uint32_t timeout)
Definition: dfu_device.c:457
static void reset_state(void)
Definition: dfu_device.c:68
void dfu_moded_reset(uint8_t rhport)
Definition: dfu_device.c:153
tu_static tu_lookup_table_t const _dfu_request_table
Definition: dfu_device.c:95
CFG_TUD_MEM_SECTION tu_static dfu_state_ctx_t _dfu_ctx
Definition: dfu_device.c:66
static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
Definition: dfu_device.c:420
static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
Definition: dfu_device.c:384
tu_static tu_lookup_entry_t const _dfu_request_lookup[]
Definition: dfu_device.c:84
void tud_dfu_manifest_cb(uint8_t alt)
TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt)
TU_ATTR_WEAK void tud_dfu_detach_cb(void)
uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state)
TU_ATTR_WEAK uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t *data, uint16_t length)
void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length)
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
uint8_t iString
Definition: dfu.h:110
uint16_t wValue
Definition: audio.h:934
struct TU_ATTR_PACKED::@16::TU_ATTR_PACKED bmRequestType_bit
uint16_t wLength
Definition: audio.h:840
uint8_t bwPollTimeout[3]
Definition: dfu.h:108
uint8_t bInterfaceSubClass
Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bInterfaceCla...
Definition: tusb_types.h:350
uint8_t bState
Definition: dfu.h:109
uint8_t bRequest
Request type audio_cs_req_t.
Definition: audio.h:831
uint8_t bAttributes
Definition: tusb_types.h:463
uint8_t bInterfaceProtocol
Protocol code (assigned by the USB). These codes are qualified by the value of the bInterfaceClass ...
Definition: tusb_types.h:351
uint8_t bInterfaceNumber
Number of this interface. Zero-based value identifying the index in the array of concurrent interface...
Definition: tusb_types.h:346
uint8_t bAlternateSetting
Value used to select this alternate setting for the interface identified in the prior field.
Definition: tusb_types.h:347
uint8_t bStatus
Definition: dfu.h:107
uint16_t length
Definition: dfu_device.c:60
uint16_t block
Definition: dfu_device.c:59
dfu_state_t state
Definition: dfu_device.c:55
CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE]
Definition: dfu_device.c:62
bool flashing_in_progress
Definition: dfu_device.c:58
dfu_status_t status
Definition: dfu_device.c:56
uint8_t attrs
Definition: dfu_device.c:52
Definition: tusb_debug.h:100
uint32_t key
Definition: tusb_debug.h:101
static TU_ATTR_ALWAYS_INLINE uint16_t tu_unaligned_read16(const void *mem)
Definition: tusb_common.h:219
static const char * tu_lookup_find(tu_lookup_table_t const *p_table, uint32_t key)
Definition: tusb_debug.h:110
@ TUSB_REQ_SET_INTERFACE
Definition: tusb_types.h:133
@ TUSB_REQ_GET_INTERFACE
Definition: tusb_types.h:132
static TU_ATTR_ALWAYS_INLINE uint8_t tu_desc_len(void const *desc)
Definition: tusb_types.h:542
@ TUSB_REQ_RCPT_INTERFACE
Definition: tusb_types.h:152
@ CONTROL_STAGE_ACK
Definition: tusb_types.h:270
@ CONTROL_STAGE_SETUP
Definition: tusb_types.h:268
static TU_ATTR_ALWAYS_INLINE uint8_t tu_desc_type(void const *desc)
Definition: tusb_types.h:537
struct TU_ATTR_PACKED tusb_desc_dfu_functional_t
@ TUSB_REQ_TYPE_STANDARD
Definition: tusb_types.h:144
@ TUSB_REQ_TYPE_CLASS
Definition: tusb_types.h:145
static TU_ATTR_ALWAYS_INLINE uint8_t const * tu_desc_next(void const *desc)
Definition: tusb_types.h:531
@ TUSB_DESC_FUNCTIONAL
Definition: tusb_types.h:108
bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const *request, void *buffer, uint16_t len)
Definition: usbd_control.c:111
bool tud_control_status(uint8_t rhport, tusb_control_request_t const *request)
Definition: usbd_control.c:81
CFG_TUH_MEM_ALIGN tusb_control_request_t request
Definition: usbh.c:259
volatile uint8_t stage
Definition: usbh.c:265