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