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