1 /*
2 * Copyright (c) 2022, sakumisu
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "usbd_core.h"
7 #include "usbd_dfu.h"
8
9 /** Modify the following three parameters according to different platforms */
10 #ifndef USBD_DFU_XFER_SIZE
11 #define USBD_DFU_XFER_SIZE 1024
12 #endif
13
14 #ifndef USBD_DFU_APP_DEFAULT_ADD
15 #define USBD_DFU_APP_DEFAULT_ADD 0x8004000
16 #endif
17
18 #ifndef FLASH_PROGRAM_TIME
19 #define FLASH_PROGRAM_TIME 50
20 #endif
21
22 #ifndef FLASH_ERASE_TIME
23 #define FLASH_ERASE_TIME 50
24 #endif
25
26 struct usbd_dfu_priv {
27 struct dfu_info info;
28 union {
29 uint32_t d32[USBD_DFU_XFER_SIZE / 4U];
30 uint8_t d8[USBD_DFU_XFER_SIZE];
31 } buffer;
32
33 uint32_t wblock_num;
34 uint32_t wlength;
35 uint32_t data_ptr;
36 uint32_t alt_setting;
37
38 uint8_t dev_status[6];
39 uint8_t ReservedForAlign[2];
40 uint8_t dev_state;
41 uint8_t manif_state;
42 uint8_t firmwar_flag;
43 } g_usbd_dfu;
44
dfu_reset(void)45 static void dfu_reset(void)
46 {
47 memset(&g_usbd_dfu, 0, sizeof(g_usbd_dfu));
48
49 g_usbd_dfu.alt_setting = 0U;
50 g_usbd_dfu.data_ptr = USBD_DFU_APP_DEFAULT_ADD;
51 g_usbd_dfu.wblock_num = 0U;
52 g_usbd_dfu.wlength = 0U;
53
54 g_usbd_dfu.manif_state = DFU_MANIFEST_COMPLETE;
55 g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
56
57 g_usbd_dfu.dev_status[0] = DFU_STATUS_OK;
58 g_usbd_dfu.dev_status[1] = 0U;
59 g_usbd_dfu.dev_status[2] = 0U;
60 g_usbd_dfu.dev_status[3] = 0U;
61 g_usbd_dfu.dev_status[4] = DFU_STATE_DFU_IDLE;
62 g_usbd_dfu.dev_status[5] = 0U;
63 }
64
dfu_getstatus(uint32_t add,uint8_t cmd,uint8_t * buffer)65 static uint16_t dfu_getstatus(uint32_t add, uint8_t cmd, uint8_t *buffer)
66 {
67 switch (cmd) {
68 case DFU_MEDIA_PROGRAM:
69 buffer[1] = (uint8_t)FLASH_PROGRAM_TIME;
70 buffer[2] = (uint8_t)(FLASH_PROGRAM_TIME << 8);
71 buffer[3] = 0;
72 break;
73
74 case DFU_MEDIA_ERASE:
75 buffer[1] = (uint8_t)FLASH_ERASE_TIME;
76 buffer[2] = (uint8_t)(FLASH_ERASE_TIME << 8);
77 buffer[3] = 0;
78 default:
79
80 break;
81 }
82 return (0);
83 }
84
dfu_request_detach(void)85 static void dfu_request_detach(void)
86 {
87 if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) ||
88 (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_SYNC) ||
89 (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE) ||
90 (g_usbd_dfu.dev_state == DFU_STATE_DFU_MANIFEST_SYNC) ||
91 (g_usbd_dfu.dev_state == DFU_STATE_DFU_UPLOAD_IDLE)) {
92 /* Update the state machine */
93 g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
94 g_usbd_dfu.dev_status[0] = DFU_STATUS_OK;
95 g_usbd_dfu.dev_status[1] = 0U;
96 g_usbd_dfu.dev_status[2] = 0U;
97 g_usbd_dfu.dev_status[3] = 0U; /*bwPollTimeout=0ms*/
98 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
99 g_usbd_dfu.dev_status[5] = 0U; /*iString*/
100 g_usbd_dfu.wblock_num = 0U;
101 g_usbd_dfu.wlength = 0U;
102 }
103 }
104
dfu_request_upload(struct usb_setup_packet * setup,uint8_t ** data,uint32_t * len)105 static void dfu_request_upload(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
106 {
107 struct usb_setup_packet *req = setup;
108 uint32_t addr;
109 uint8_t *phaddr;
110 /* Data setup request */
111 if (req->wLength > 0U) {
112 if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) || (g_usbd_dfu.dev_state == DFU_STATE_DFU_UPLOAD_IDLE)) {
113 /* Update the global length and block number */
114 g_usbd_dfu.wblock_num = req->wValue;
115 g_usbd_dfu.wlength = MIN(req->wLength, USBD_DFU_XFER_SIZE);
116
117 /* DFU Get Command */
118 if (g_usbd_dfu.wblock_num == 0U) {
119 /* Update the state machine */
120 g_usbd_dfu.dev_state = (g_usbd_dfu.wlength > 3U) ? DFU_STATE_DFU_IDLE : DFU_STATE_DFU_UPLOAD_IDLE;
121
122 g_usbd_dfu.dev_status[1] = 0U;
123 g_usbd_dfu.dev_status[2] = 0U;
124 g_usbd_dfu.dev_status[3] = 0U;
125 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
126
127 /* Store the values of all supported commands */
128 g_usbd_dfu.buffer.d8[0] = DFU_CMD_GETCOMMANDS;
129 g_usbd_dfu.buffer.d8[1] = DFU_CMD_SETADDRESSPOINTER;
130 g_usbd_dfu.buffer.d8[2] = DFU_CMD_ERASE;
131
132 /* Send the status data over EP0 */
133 memcpy(*data, g_usbd_dfu.buffer.d8, 3);
134 *len = 3;
135 } else if (g_usbd_dfu.wblock_num > 1U) {
136 g_usbd_dfu.dev_state = DFU_STATE_DFU_UPLOAD_IDLE;
137
138 g_usbd_dfu.dev_status[1] = 0U;
139 g_usbd_dfu.dev_status[2] = 0U;
140 g_usbd_dfu.dev_status[3] = 0U;
141 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
142
143 addr = ((g_usbd_dfu.wblock_num - 2U) * USBD_DFU_XFER_SIZE) + g_usbd_dfu.data_ptr;
144
145 /* Return the physical address where data are stored */
146 phaddr = dfu_read_flash((uint8_t *)addr, g_usbd_dfu.buffer.d8, g_usbd_dfu.wlength);
147
148 /* Send the status data over EP0 */
149 memcpy(*data, g_usbd_dfu.buffer.d8, g_usbd_dfu.wlength);
150 *len = g_usbd_dfu.wlength;
151 } else /* unsupported g_usbd_dfu.wblock_num */
152 {
153 g_usbd_dfu.dev_state = DFU_STATUS_ERR_STALLEDPKT;
154
155 g_usbd_dfu.dev_status[1] = 0U;
156 g_usbd_dfu.dev_status[2] = 0U;
157 g_usbd_dfu.dev_status[3] = 0U;
158 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
159
160 /* Call the error management function (command will be NAKed */
161 USB_LOG_ERR("Dfu_request_upload unsupported g_usbd_dfu.wblock_num\r\n");
162 }
163 }
164 /* Unsupported state */
165 else {
166 g_usbd_dfu.wlength = 0U;
167 g_usbd_dfu.wblock_num = 0U;
168
169 /* Call the error management function (command will be NAKed */
170 USB_LOG_ERR("Dfu_request_upload unsupported state\r\n");
171 }
172 }
173 /* No Data setup request */
174 else {
175 g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
176
177 g_usbd_dfu.dev_status[1] = 0U;
178 g_usbd_dfu.dev_status[2] = 0U;
179 g_usbd_dfu.dev_status[3] = 0U;
180 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
181 }
182 }
183
dfu_request_dnload(struct usb_setup_packet * setup,uint8_t ** data,uint32_t * len)184 static void dfu_request_dnload(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
185 {
186 /* Data setup request */
187 struct usb_setup_packet *req = setup;
188 if (req->wLength > 0U) {
189 if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) || (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE)) {
190 /* Update the global length and block number */
191 g_usbd_dfu.wblock_num = req->wValue;
192 g_usbd_dfu.wlength = MIN(req->wLength, USBD_DFU_XFER_SIZE);
193
194 /* Update the state machine */
195 g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_SYNC;
196 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
197
198 /*!< Data has received complete */
199 memcpy((uint8_t *)g_usbd_dfu.buffer.d8, (uint8_t *)*data, g_usbd_dfu.wlength);
200 /*!< Set flag = 1 Write the firmware to the flash in the next dfu_request_getstatus */
201 g_usbd_dfu.firmwar_flag = 1;
202 }
203 /* Unsupported state */
204 else {
205 USB_LOG_ERR("Dfu_request_dnload unsupported state\r\n");
206 }
207 }
208 /* 0 Data DNLOAD request */
209 else {
210 /* End of DNLOAD operation*/
211 if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE) || (g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE)) {
212 g_usbd_dfu.manif_state = DFU_MANIFEST_IN_PROGRESS;
213 g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST_SYNC;
214 g_usbd_dfu.dev_status[1] = 0U;
215 g_usbd_dfu.dev_status[2] = 0U;
216 g_usbd_dfu.dev_status[3] = 0U;
217 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
218 } else {
219 /* Call the error management function (command will be NAKed */
220 USB_LOG_ERR("Dfu_request_dnload End of DNLOAD operation but dev_state %02x \r\n", g_usbd_dfu.dev_state);
221 }
222 }
223 }
224
dfu_getstatus_special_handler(void)225 static int8_t dfu_getstatus_special_handler(void)
226 {
227 uint32_t addr;
228 if (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_BUSY) {
229 /* Decode the Special Command */
230 if (g_usbd_dfu.wblock_num == 0U) {
231 if (g_usbd_dfu.wlength == 1U) {
232 if (g_usbd_dfu.buffer.d8[0] == DFU_CMD_GETCOMMANDS) {
233 /* Nothing to do */
234 }
235 } else if (g_usbd_dfu.wlength == 5U) {
236 if (g_usbd_dfu.buffer.d8[0] == DFU_CMD_SETADDRESSPOINTER) {
237 g_usbd_dfu.data_ptr = g_usbd_dfu.buffer.d8[1];
238 g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[2] << 8;
239 g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[3] << 16;
240 g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[4] << 24;
241 } else if (g_usbd_dfu.buffer.d8[0] == DFU_CMD_ERASE) {
242 g_usbd_dfu.data_ptr = g_usbd_dfu.buffer.d8[1];
243 g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[2] << 8;
244 g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[3] << 16;
245 g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[4] << 24;
246
247 USB_LOG_DBG("Erase start add %08x \r\n", g_usbd_dfu.data_ptr);
248 /*!< Erase */
249 dfu_erase_flash(g_usbd_dfu.data_ptr);
250 } else {
251 return -1;
252 }
253 } else {
254 /* Reset the global length and block number */
255 g_usbd_dfu.wlength = 0U;
256 g_usbd_dfu.wblock_num = 0U;
257 /* Call the error management function (command will be NAKed) */
258 USB_LOG_ERR("Reset the global length and block number\r\n");
259 }
260 }
261 /* Regular Download Command */
262 else {
263 if (g_usbd_dfu.wblock_num > 1U) {
264 /* Decode the required address */
265 addr = ((g_usbd_dfu.wblock_num - 2U) * USBD_DFU_XFER_SIZE) + g_usbd_dfu.data_ptr;
266
267 /* Perform the write operation */
268 /* Write flash */
269 USB_LOG_DBG("Write start add %08x length %d\r\n", addr, g_usbd_dfu.wlength);
270 dfu_write_flash(g_usbd_dfu.buffer.d8, (uint8_t *)addr, g_usbd_dfu.wlength);
271 }
272 }
273
274 /* Reset the global length and block number */
275 g_usbd_dfu.wlength = 0U;
276 g_usbd_dfu.wblock_num = 0U;
277
278 /* Update the state machine */
279 g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_SYNC;
280
281 g_usbd_dfu.dev_status[1] = 0U;
282 g_usbd_dfu.dev_status[2] = 0U;
283 g_usbd_dfu.dev_status[3] = 0U;
284 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
285 }
286 return 0;
287 }
288
dfu_request_getstatus(struct usb_setup_packet * setup,uint8_t ** data,uint32_t * len)289 static void dfu_request_getstatus(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
290 {
291 /*!< Determine whether to leave DFU mode */
292 if (g_usbd_dfu.manif_state == DFU_MANIFEST_IN_PROGRESS &&
293 g_usbd_dfu.dev_state == DFU_STATE_DFU_MANIFEST_SYNC &&
294 g_usbd_dfu.dev_status[1] == 0U &&
295 g_usbd_dfu.dev_status[2] == 0U &&
296 g_usbd_dfu.dev_status[3] == 0U &&
297 g_usbd_dfu.dev_status[4] == g_usbd_dfu.dev_state) {
298 g_usbd_dfu.manif_state = DFU_MANIFEST_COMPLETE;
299
300 if ((0x0B & DFU_MANIFEST_MASK) != 0U) {
301 g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST_SYNC;
302
303 g_usbd_dfu.dev_status[1] = 0U;
304 g_usbd_dfu.dev_status[2] = 0U;
305 g_usbd_dfu.dev_status[3] = 0U;
306 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
307 return;
308 } else {
309 g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST_WAIT_RESET;
310
311 g_usbd_dfu.dev_status[1] = 0U;
312 g_usbd_dfu.dev_status[2] = 0U;
313 g_usbd_dfu.dev_status[3] = 0U;
314 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
315 /* Generate system reset to allow jumping to the user code */
316 dfu_leave();
317 }
318 }
319
320 switch (g_usbd_dfu.dev_state) {
321 case DFU_STATE_DFU_DNLOAD_SYNC:
322 if (g_usbd_dfu.wlength != 0U) {
323 g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_BUSY;
324
325 g_usbd_dfu.dev_status[1] = 0U;
326 g_usbd_dfu.dev_status[2] = 0U;
327 g_usbd_dfu.dev_status[3] = 0U;
328 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
329
330 if ((g_usbd_dfu.wblock_num == 0U) && (g_usbd_dfu.buffer.d8[0] == DFU_CMD_ERASE)) {
331 dfu_getstatus(g_usbd_dfu.data_ptr, DFU_MEDIA_ERASE, g_usbd_dfu.dev_status);
332 } else {
333 dfu_getstatus(g_usbd_dfu.data_ptr, DFU_MEDIA_PROGRAM, g_usbd_dfu.dev_status);
334 }
335 } else /* (g_usbd_dfu.wlength==0)*/
336 {
337 g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_IDLE;
338
339 g_usbd_dfu.dev_status[1] = 0U;
340 g_usbd_dfu.dev_status[2] = 0U;
341 g_usbd_dfu.dev_status[3] = 0U;
342 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
343 }
344 break;
345
346 case DFU_STATE_DFU_MANIFEST_SYNC:
347 if (g_usbd_dfu.manif_state == DFU_MANIFEST_IN_PROGRESS) {
348 g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST;
349
350 g_usbd_dfu.dev_status[1] = 1U; /*bwPollTimeout = 1ms*/
351 g_usbd_dfu.dev_status[2] = 0U;
352 g_usbd_dfu.dev_status[3] = 0U;
353 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
354 } else {
355 if ((g_usbd_dfu.manif_state == DFU_MANIFEST_COMPLETE) &&
356 ((0x0B & DFU_MANIFEST_MASK) != 0U)) {
357 g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
358
359 g_usbd_dfu.dev_status[1] = 0U;
360 g_usbd_dfu.dev_status[2] = 0U;
361 g_usbd_dfu.dev_status[3] = 0U;
362 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
363 }
364 }
365 break;
366
367 default:
368 break;
369 }
370
371 /* Send the status data over EP0 */
372 memcpy(*data, g_usbd_dfu.dev_status, 6);
373 *len = 6;
374
375 if (g_usbd_dfu.firmwar_flag == 1) {
376 if (dfu_getstatus_special_handler() != 0) {
377 USB_LOG_ERR("dfu_getstatus_special_handler error \r\n");
378 }
379 g_usbd_dfu.firmwar_flag = 0;
380 }
381 }
382
dfu_request_clrstatus(void)383 static void dfu_request_clrstatus(void)
384 {
385 if (g_usbd_dfu.dev_state == DFU_STATE_DFU_ERROR) {
386 g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
387 g_usbd_dfu.dev_status[0] = DFU_STATUS_OK; /* bStatus */
388 g_usbd_dfu.dev_status[1] = 0U;
389 g_usbd_dfu.dev_status[2] = 0U;
390 g_usbd_dfu.dev_status[3] = 0U; /* bwPollTimeout=0ms */
391 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state; /* bState */
392 g_usbd_dfu.dev_status[5] = 0U; /* iString */
393 } else {
394 /* State Error */
395 g_usbd_dfu.dev_state = DFU_STATE_DFU_ERROR;
396 g_usbd_dfu.dev_status[0] = DFU_STATUS_ERR_UNKNOWN; /* bStatus */
397 g_usbd_dfu.dev_status[1] = 0U;
398 g_usbd_dfu.dev_status[2] = 0U;
399 g_usbd_dfu.dev_status[3] = 0U; /* bwPollTimeout=0ms */
400 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state; /* bState */
401 g_usbd_dfu.dev_status[5] = 0U; /* iString */
402 }
403 }
404
dfu_request_getstate(struct usb_setup_packet * setup,uint8_t ** data,uint32_t * len)405 static void dfu_request_getstate(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
406 {
407 /* Return the current state of the DFU interface */
408 (*data)[0] = g_usbd_dfu.dev_state;
409 *len = 1;
410 }
411
dfu_request_abort(void)412 void dfu_request_abort(void)
413 {
414 if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) ||
415 (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_SYNC) ||
416 (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE) ||
417 (g_usbd_dfu.dev_state == DFU_STATE_DFU_MANIFEST_SYNC) ||
418 (g_usbd_dfu.dev_state == DFU_STATE_DFU_UPLOAD_IDLE)) {
419 g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
420 g_usbd_dfu.dev_status[0] = DFU_STATUS_OK;
421 g_usbd_dfu.dev_status[1] = 0U;
422 g_usbd_dfu.dev_status[2] = 0U;
423 g_usbd_dfu.dev_status[3] = 0U; /* bwPollTimeout=0ms */
424 g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
425 g_usbd_dfu.dev_status[5] = 0U; /* iString */
426 g_usbd_dfu.wblock_num = 0U;
427 g_usbd_dfu.wlength = 0U;
428 }
429 }
430
dfu_class_interface_request_handler(uint8_t busid,struct usb_setup_packet * setup,uint8_t ** data,uint32_t * len)431 static int dfu_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
432 {
433 USB_LOG_DBG("DFU Class request: "
434 "bRequest 0x%02x\r\n",
435 setup->bRequest);
436
437 switch (setup->bRequest) {
438 case DFU_REQUEST_DETACH:
439 dfu_request_detach();
440 break;
441 case DFU_REQUEST_DNLOAD:
442 dfu_request_dnload(setup, data, len);
443 break;
444 case DFU_REQUEST_UPLOAD:
445 dfu_request_upload(setup, data, len);
446 break;
447 case DFU_REQUEST_GETSTATUS:
448 dfu_request_getstatus(setup, data, len);
449 break;
450 case DFU_REQUEST_CLRSTATUS:
451 dfu_request_clrstatus();
452 break;
453 case DFU_REQUEST_GETSTATE:
454 dfu_request_getstate(setup, data, len);
455 break;
456 case DFU_REQUEST_ABORT:
457 dfu_request_abort();
458 break;
459 default:
460 USB_LOG_WRN("Unhandled DFU Class bRequest 0x%02x\r\n", setup->bRequest);
461 return -1;
462 }
463
464 return 0;
465 }
466
dfu_notify_handler(uint8_t busid,uint8_t event,void * arg)467 static void dfu_notify_handler(uint8_t busid, uint8_t event, void *arg)
468 {
469 switch (event) {
470 case USBD_EVENT_RESET:
471 dfu_reset();
472 break;
473 default:
474 break;
475 }
476 }
477
usbd_dfu_init_intf(struct usbd_interface * intf)478 struct usbd_interface *usbd_dfu_init_intf(struct usbd_interface *intf)
479 {
480 intf->class_interface_handler = dfu_class_interface_request_handler;
481 intf->class_endpoint_handler = NULL;
482 intf->vendor_handler = NULL;
483 intf->notify_handler = dfu_notify_handler;
484
485 return intf;
486 }
487
dfu_read_flash(uint8_t * src,uint8_t * dest,uint32_t len)488 __WEAK uint8_t *dfu_read_flash(uint8_t *src, uint8_t *dest, uint32_t len)
489 {
490 return dest;
491 }
492
dfu_write_flash(uint8_t * src,uint8_t * dest,uint32_t len)493 __WEAK uint16_t dfu_write_flash(uint8_t *src, uint8_t *dest, uint32_t len)
494 {
495 return 0;
496 }
497
dfu_erase_flash(uint32_t add)498 __WEAK uint16_t dfu_erase_flash(uint32_t add)
499 {
500 return 0;
501 }
502
dfu_leave(void)503 __WEAK void dfu_leave(void)
504 {
505 }
506