1 /*
2 * Copyright (c) 2021-2025, STMicroelectronics - All Rights Reserved
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <errno.h>
8 #include <string.h>
9
10 #include <common/debug.h>
11 #include <drivers/delay_timer.h>
12
13 #include <platform_def.h>
14 #include <usb_dfu.h>
15
16 /* Device states as defined in DFU spec */
17 #define STATE_APP_IDLE 0
18 #define STATE_APP_DETACH 1
19 #define STATE_DFU_IDLE 2
20 #define STATE_DFU_DNLOAD_SYNC 3
21 #define STATE_DFU_DNLOAD_BUSY 4
22 #define STATE_DFU_DNLOAD_IDLE 5
23 #define STATE_DFU_MANIFEST_SYNC 6
24 #define STATE_DFU_MANIFEST 7
25 #define STATE_DFU_MANIFEST_WAIT_RESET 8
26 #define STATE_DFU_UPLOAD_IDLE 9
27 #define STATE_DFU_ERROR 10
28
29 /* DFU errors */
30 #define DFU_ERROR_NONE 0x00
31 #define DFU_ERROR_TARGET 0x01
32 #define DFU_ERROR_FILE 0x02
33 #define DFU_ERROR_WRITE 0x03
34 #define DFU_ERROR_ERASE 0x04
35 #define DFU_ERROR_CHECK_ERASED 0x05
36 #define DFU_ERROR_PROG 0x06
37 #define DFU_ERROR_VERIFY 0x07
38 #define DFU_ERROR_ADDRESS 0x08
39 #define DFU_ERROR_NOTDONE 0x09
40 #define DFU_ERROR_FIRMWARE 0x0A
41 #define DFU_ERROR_VENDOR 0x0B
42 #define DFU_ERROR_USB 0x0C
43 #define DFU_ERROR_POR 0x0D
44 #define DFU_ERROR_UNKNOWN 0x0E
45 #define DFU_ERROR_STALLEDPKT 0x0F
46
47 /* DFU request */
48 #define DFU_DETACH 0
49 #define DFU_DNLOAD 1
50 #define DFU_UPLOAD 2
51 #define DFU_GETSTATUS 3
52 #define DFU_CLRSTATUS 4
53 #define DFU_GETSTATE 5
54 #define DFU_ABORT 6
55
56 static bool usb_dfu_detach_req;
57 static uint64_t detach_timeout;
58
59 /*
60 * usb_dfu_init
61 * Initialize the DFU interface
62 * pdev: device instance
63 * cfgidx: Configuration index
64 * return: status
65 */
usb_dfu_init(struct usb_handle * pdev,uint8_t cfgidx)66 static uint8_t usb_dfu_init(struct usb_handle *pdev, uint8_t cfgidx)
67 {
68 (void)pdev;
69 (void)cfgidx;
70
71 /* Nothing to do in this stage */
72 return USBD_OK;
73 }
74
75 /*
76 * usb_dfu_de_init
77 * De-Initialize the DFU layer
78 * pdev: device instance
79 * cfgidx: Configuration index
80 * return: status
81 */
usb_dfu_de_init(struct usb_handle * pdev,uint8_t cfgidx)82 static uint8_t usb_dfu_de_init(struct usb_handle *pdev, uint8_t cfgidx)
83 {
84 (void)pdev;
85 (void)cfgidx;
86
87 /* Nothing to do in this stage */
88 return USBD_OK;
89 }
90
91 /*
92 * usb_dfu_data_in
93 * handle data IN Stage
94 * pdev: device instance
95 * epnum: endpoint index
96 * return: status
97 */
usb_dfu_data_in(struct usb_handle * pdev,uint8_t epnum)98 static uint8_t usb_dfu_data_in(struct usb_handle *pdev, uint8_t epnum)
99 {
100 (void)pdev;
101 (void)epnum;
102
103 return USBD_OK;
104 }
105
106 /*
107 * usb_dfu_ep0_rx_ready
108 * handle EP0 Rx Ready event
109 * pdev: device
110 * return: status
111 */
usb_dfu_ep0_rx_ready(struct usb_handle * pdev)112 static uint8_t usb_dfu_ep0_rx_ready(struct usb_handle *pdev)
113 {
114 (void)pdev;
115
116 return USBD_OK;
117 }
118
119 /*
120 * usb_dfu_ep0_tx_ready
121 * handle EP0 TRx Ready event
122 * pdev: device instance
123 * return: status
124 */
usb_dfu_ep0_tx_ready(struct usb_handle * pdev)125 static uint8_t usb_dfu_ep0_tx_ready(struct usb_handle *pdev)
126 {
127 (void)pdev;
128
129 return USBD_OK;
130 }
131
132 /*
133 * usb_dfu_sof
134 * handle SOF event
135 * pdev: device instance
136 * return: status
137 */
usb_dfu_sof(struct usb_handle * pdev)138 static uint8_t usb_dfu_sof(struct usb_handle *pdev)
139 {
140 (void)pdev;
141
142 return USBD_OK;
143 }
144
145 /*
146 * usb_dfu_iso_in_incomplete
147 * handle data ISO IN Incomplete event
148 * pdev: device instance
149 * epnum: endpoint index
150 * return: status
151 */
usb_dfu_iso_in_incomplete(struct usb_handle * pdev,uint8_t epnum)152 static uint8_t usb_dfu_iso_in_incomplete(struct usb_handle *pdev, uint8_t epnum)
153 {
154 (void)pdev;
155 (void)epnum;
156
157 return USBD_OK;
158 }
159
160 /*
161 * usb_dfu_iso_out_incomplete
162 * handle data ISO OUT Incomplete event
163 * pdev: device instance
164 * epnum: endpoint index
165 * return: status
166 */
usb_dfu_iso_out_incomplete(struct usb_handle * pdev,uint8_t epnum)167 static uint8_t usb_dfu_iso_out_incomplete(struct usb_handle *pdev,
168 uint8_t epnum)
169 {
170 (void)pdev;
171 (void)epnum;
172
173 return USBD_OK;
174 }
175
176 /*
177 * usb_dfu_data_out
178 * handle data OUT Stage
179 * pdev: device instance
180 * epnum: endpoint index
181 * return: status
182 */
usb_dfu_data_out(struct usb_handle * pdev,uint8_t epnum)183 static uint8_t usb_dfu_data_out(struct usb_handle *pdev, uint8_t epnum)
184 {
185 (void)pdev;
186 (void)epnum;
187
188 return USBD_OK;
189 }
190
191 /*
192 * usb_dfu_detach
193 * Handles the DFU DETACH request.
194 * pdev: device instance
195 * req: pointer to the request structure.
196 */
usb_dfu_detach(struct usb_handle * pdev,struct usb_setup_req * req)197 static void usb_dfu_detach(struct usb_handle *pdev, struct usb_setup_req *req)
198 {
199 struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
200
201 INFO("Receive DFU Detach\n");
202
203 if ((hdfu->dev_state == STATE_DFU_IDLE) ||
204 (hdfu->dev_state == STATE_DFU_DNLOAD_SYNC) ||
205 (hdfu->dev_state == STATE_DFU_DNLOAD_IDLE) ||
206 (hdfu->dev_state == STATE_DFU_MANIFEST_SYNC) ||
207 (hdfu->dev_state == STATE_DFU_UPLOAD_IDLE)) {
208 /* Update the state machine */
209 hdfu->dev_state = STATE_DFU_IDLE;
210 hdfu->dev_status = DFU_ERROR_NONE;
211 }
212
213 /* Timeout that lets the core handle interrupts before detach */
214 detach_timeout = timeout_init_us(100); /* usec */
215 usb_dfu_detach_req = true;
216 }
217
218 /*
219 * usb_dfu_download
220 * Handles the DFU DNLOAD request.
221 * pdev: device instance
222 * req: pointer to the request structure
223 */
usb_dfu_download(struct usb_handle * pdev,struct usb_setup_req * req)224 static void usb_dfu_download(struct usb_handle *pdev, struct usb_setup_req *req)
225 {
226 struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
227 uintptr_t data_ptr;
228 uint32_t length;
229 int ret;
230
231 /* Data setup request */
232 if (req->length > 0) {
233 /* Unsupported state */
234 if ((hdfu->dev_state != STATE_DFU_IDLE) &&
235 (hdfu->dev_state != STATE_DFU_DNLOAD_IDLE)) {
236 /* Call the error management function (command will be nacked) */
237 usb_core_ctl_error(pdev);
238 return;
239 }
240
241 /* Get the data address */
242 length = req->length;
243 ret = hdfu->callback->download(hdfu->alt_setting, &data_ptr,
244 &length, pdev->user_data);
245 if (ret == 0U) {
246 /* Update the state machine */
247 hdfu->dev_state = STATE_DFU_DNLOAD_SYNC;
248 /* Start the transfer */
249 usb_core_receive_ep0(pdev, (uint8_t *)data_ptr, length);
250 } else {
251 usb_core_ctl_error(pdev);
252 }
253 } else {
254 /* End of DNLOAD operation*/
255 if (hdfu->dev_state != STATE_DFU_DNLOAD_IDLE) {
256 /* Call the error management function (command will be nacked) */
257 usb_core_ctl_error(pdev);
258 return;
259 }
260 /* End of DNLOAD operation*/
261 hdfu->dev_state = STATE_DFU_MANIFEST_SYNC;
262 ret = hdfu->callback->manifestation(hdfu->alt_setting, pdev->user_data);
263 if (ret == 0U) {
264 hdfu->dev_state = STATE_DFU_MANIFEST_SYNC;
265 } else {
266 usb_core_ctl_error(pdev);
267 }
268 }
269 }
270
271 /*
272 * usb_dfu_upload
273 * Handles the DFU UPLOAD request.
274 * pdev: instance
275 * req: pointer to the request structure
276 */
usb_dfu_upload(struct usb_handle * pdev,struct usb_setup_req * req)277 static void usb_dfu_upload(struct usb_handle *pdev, struct usb_setup_req *req)
278 {
279 struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
280 uintptr_t data_ptr;
281 uint32_t length;
282 int ret;
283
284 /* Data setup request */
285 if (req->length == 0) {
286 /* No Data setup request */
287 hdfu->dev_state = STATE_DFU_IDLE;
288 return;
289 }
290
291 /* Unsupported state */
292 if ((hdfu->dev_state != STATE_DFU_IDLE) && (hdfu->dev_state != STATE_DFU_UPLOAD_IDLE)) {
293 ERROR("UPLOAD : Unsupported State\n");
294 /* Call the error management function (command will be nacked) */
295 usb_core_ctl_error(pdev);
296 return;
297 }
298
299 /* Update the data address */
300 length = req->length;
301 ret = hdfu->callback->upload(hdfu->alt_setting, &data_ptr, &length, pdev->user_data);
302 if (ret == 0U) {
303 /* Short frame */
304 hdfu->dev_state = (req->length > length) ? STATE_DFU_IDLE : STATE_DFU_UPLOAD_IDLE;
305
306 /* Start the transfer */
307 usb_core_transmit_ep0(pdev, (uint8_t *)data_ptr, length);
308 } else {
309 ERROR("UPLOAD : bad block %i on alt %i\n", req->value, req->index);
310 hdfu->dev_state = STATE_DFU_ERROR;
311 hdfu->dev_status = DFU_ERROR_STALLEDPKT;
312
313 /* Call the error management function (command will be nacked) */
314 usb_core_ctl_error(pdev);
315 }
316 }
317
318 /*
319 * usb_dfu_get_status
320 * Handles the DFU GETSTATUS request.
321 * pdev: instance
322 */
usb_dfu_get_status(struct usb_handle * pdev)323 static void usb_dfu_get_status(struct usb_handle *pdev)
324 {
325 struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
326
327 hdfu->status[0] = hdfu->dev_status; /* bStatus */
328 hdfu->status[1] = 0; /* bwPollTimeout[3] */
329 hdfu->status[2] = 0;
330 hdfu->status[3] = 0;
331 hdfu->status[4] = hdfu->dev_state; /* bState */
332 hdfu->status[5] = 0; /* iString */
333
334 /* next step */
335 switch (hdfu->dev_state) {
336 case STATE_DFU_DNLOAD_SYNC:
337 hdfu->dev_state = STATE_DFU_DNLOAD_IDLE;
338 break;
339 case STATE_DFU_MANIFEST_SYNC:
340 /* the device is 'ManifestationTolerant' */
341 hdfu->status[4] = STATE_DFU_MANIFEST;
342 hdfu->status[1] = 1U; /* bwPollTimeout = 1ms */
343 hdfu->dev_state = STATE_DFU_IDLE;
344 break;
345
346 default:
347 break;
348 }
349
350 /* Start the transfer */
351 usb_core_transmit_ep0(pdev, (uint8_t *)&hdfu->status[0], sizeof(hdfu->status));
352 }
353
354 /*
355 * usb_dfu_clear_status
356 * Handles the DFU CLRSTATUS request.
357 * pdev: device instance
358 */
usb_dfu_clear_status(struct usb_handle * pdev)359 static void usb_dfu_clear_status(struct usb_handle *pdev)
360 {
361 struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
362
363 if (hdfu->dev_state == STATE_DFU_ERROR) {
364 hdfu->dev_state = STATE_DFU_IDLE;
365 hdfu->dev_status = DFU_ERROR_NONE;
366 } else {
367 /* State Error */
368 hdfu->dev_state = STATE_DFU_ERROR;
369 hdfu->dev_status = DFU_ERROR_UNKNOWN;
370 }
371 }
372
373 /*
374 * usb_dfu_get_state
375 * Handles the DFU GETSTATE request.
376 * pdev: device instance
377 */
usb_dfu_get_state(struct usb_handle * pdev)378 static void usb_dfu_get_state(struct usb_handle *pdev)
379 {
380 struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
381
382 /* Return the current state of the DFU interface */
383 usb_core_transmit_ep0(pdev, &hdfu->dev_state, 1);
384 }
385
386 /*
387 * usb_dfu_abort
388 * Handles the DFU ABORT request.
389 * pdev: device instance
390 */
usb_dfu_abort(struct usb_handle * pdev)391 static void usb_dfu_abort(struct usb_handle *pdev)
392 {
393 struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
394
395 if ((hdfu->dev_state == STATE_DFU_IDLE) ||
396 (hdfu->dev_state == STATE_DFU_DNLOAD_SYNC) ||
397 (hdfu->dev_state == STATE_DFU_DNLOAD_IDLE) ||
398 (hdfu->dev_state == STATE_DFU_MANIFEST_SYNC) ||
399 (hdfu->dev_state == STATE_DFU_UPLOAD_IDLE)) {
400 hdfu->dev_state = STATE_DFU_IDLE;
401 hdfu->dev_status = DFU_ERROR_NONE;
402 }
403 }
404
405 /*
406 * usb_dfu_setup
407 * Handle the DFU specific requests
408 * pdev: instance
409 * req: usb requests
410 * return: status
411 */
usb_dfu_setup(struct usb_handle * pdev,struct usb_setup_req * req)412 static uint8_t usb_dfu_setup(struct usb_handle *pdev, struct usb_setup_req *req)
413 {
414 uint8_t *pbuf = NULL;
415 uint16_t len = 0U;
416 uint8_t ret = USBD_OK;
417 struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
418
419 switch (req->bm_request & USB_REQ_TYPE_MASK) {
420 case USB_REQ_TYPE_CLASS:
421 switch (req->b_request) {
422 case DFU_DNLOAD:
423 usb_dfu_download(pdev, req);
424 break;
425
426 case DFU_UPLOAD:
427 usb_dfu_upload(pdev, req);
428 break;
429
430 case DFU_GETSTATUS:
431 usb_dfu_get_status(pdev);
432 break;
433
434 case DFU_CLRSTATUS:
435 usb_dfu_clear_status(pdev);
436 break;
437
438 case DFU_GETSTATE:
439 usb_dfu_get_state(pdev);
440 break;
441
442 case DFU_ABORT:
443 usb_dfu_abort(pdev);
444 break;
445
446 case DFU_DETACH:
447 usb_dfu_detach(pdev, req);
448 break;
449
450 default:
451 ERROR("unknown request %x on alternate %i\n",
452 req->b_request, hdfu->alt_setting);
453 usb_core_ctl_error(pdev);
454 ret = USBD_FAIL;
455 break;
456 }
457 break;
458 case USB_REQ_TYPE_STANDARD:
459 switch (req->b_request) {
460 case USB_REQ_GET_DESCRIPTOR:
461 if (HIBYTE(req->value) == DFU_DESCRIPTOR_TYPE) {
462 pbuf = pdev->desc->get_config_desc(&len);
463 /* DFU descriptor at the end of the USB */
464 pbuf += len - 9U;
465 len = 9U;
466 len = MIN(len, req->length);
467 }
468
469 /* Start the transfer */
470 usb_core_transmit_ep0(pdev, pbuf, len);
471
472 break;
473
474 case USB_REQ_GET_INTERFACE:
475 /* Start the transfer */
476 usb_core_transmit_ep0(pdev, (uint8_t *)&hdfu->alt_setting, 1U);
477 break;
478
479 case USB_REQ_SET_INTERFACE:
480 hdfu->alt_setting = LOBYTE(req->value);
481 break;
482
483 default:
484 usb_core_ctl_error(pdev);
485 ret = USBD_FAIL;
486 break;
487 }
488 default:
489 break;
490 }
491
492 return ret;
493 }
494
495 static const struct usb_class usb_dfu = {
496 .init = usb_dfu_init,
497 .de_init = usb_dfu_de_init,
498 .setup = usb_dfu_setup,
499 .ep0_tx_sent = usb_dfu_ep0_tx_ready,
500 .ep0_rx_ready = usb_dfu_ep0_rx_ready,
501 .data_in = usb_dfu_data_in,
502 .data_out = usb_dfu_data_out,
503 .sof = usb_dfu_sof,
504 .iso_in_incomplete = usb_dfu_iso_in_incomplete,
505 .iso_out_incomplete = usb_dfu_iso_out_incomplete,
506 };
507
usb_dfu_register(struct usb_handle * pdev,struct usb_dfu_handle * phandle)508 void usb_dfu_register(struct usb_handle *pdev, struct usb_dfu_handle *phandle)
509 {
510 pdev->class = (struct usb_class *)&usb_dfu;
511 pdev->class_data = phandle;
512
513 phandle->dev_state = STATE_DFU_IDLE;
514 phandle->dev_status = DFU_ERROR_NONE;
515 }
516
usb_dfu_loop(struct usb_handle * pdev,const struct usb_dfu_media * pmedia)517 int usb_dfu_loop(struct usb_handle *pdev, const struct usb_dfu_media *pmedia)
518 {
519 enum usb_status ret;
520 struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
521
522 hdfu->callback = pmedia;
523 usb_dfu_detach_req = false;
524
525 /* DFU infinite loop until DETACH_REQ */
526 for (;;) {
527 ret = usb_core_handle_it(pdev);
528 if (ret != USBD_OK) {
529 return -EIO;
530 }
531
532 /*
533 * Detach request received, continue to handle USB core IT
534 * to assure complete data transmission
535 */
536 if (usb_dfu_detach_req && timeout_elapsed(detach_timeout)) {
537 break;
538 }
539 }
540
541 return 0;
542 }
543