866 lines
21 KiB
C
866 lines
21 KiB
C
/*
|
|
* Copyright (c) 2015-2019, STMicroelectronics - All Rights Reserved
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <platform_def.h>
|
|
|
|
#include <common/debug.h>
|
|
#include <lib/usb/usb_st_dfu.h>
|
|
|
|
static uintptr_t usbd_dfu_download_address;
|
|
static uint32_t usbd_dfu_phase_id;
|
|
static uint32_t usbd_dfu_operation_complete;
|
|
static uint32_t usbd_dfu_current_req;
|
|
static uint32_t usbd_detach_req;
|
|
|
|
/*
|
|
* @brief USBD_DFU_Init
|
|
* Initialize the DFU interface
|
|
* @param pdev: device instance
|
|
* @param cfgidx: Configuration index
|
|
* @retval status
|
|
*/
|
|
static uint8_t usb_dfu_init(usb_handle_t *pdev, uint8_t cfgidx)
|
|
{
|
|
/* Nothing to do in this stage */
|
|
return USBD_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief USBD_DFU_Init
|
|
* De-Initialize the DFU layer
|
|
* @param pdev: device instance
|
|
* @param cfgidx: Configuration index
|
|
* @retval status
|
|
*/
|
|
static uint8_t usb_dfu_de_init(usb_handle_t *pdev, uint8_t cfgidx)
|
|
{
|
|
/* Nothing to do in this stage */
|
|
return USBD_OK;
|
|
}
|
|
|
|
/*
|
|
* @brief USBD_DFU_DataIn
|
|
* handle data IN Stage
|
|
* @param pdev: device instance
|
|
* @param epnum: endpoint index
|
|
* @retval status
|
|
*/
|
|
static uint8_t usb_dfu_data_in(usb_handle_t *pdev, uint8_t epnum)
|
|
{
|
|
(void)pdev;
|
|
(void)epnum;
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
/*
|
|
* @brief DFU_Leave
|
|
* Handles the sub-protocol DFU leave DFU mode request (leaves DFU mode
|
|
* and resets device to jump to user loaded code).
|
|
* @param pdev: device instance
|
|
* @retval None
|
|
*/
|
|
static void usb_dfu_leave(usb_handle_t *pdev)
|
|
{
|
|
usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data;
|
|
|
|
hdfu->manif_state = DFU_MANIFEST_COMPLETE;
|
|
|
|
if (DFU_BM_ATTRIBUTE & 0x04) {
|
|
hdfu->dev_state = DFU_STATE_MANIFEST_SYNC;
|
|
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
} else {
|
|
hdfu->dev_state = DFU_STATE_MANIFEST_WAIT_RESET;
|
|
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
|
|
/* Disconnect the USB device */
|
|
usb_core_stop(pdev);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @brief USBD_DFU_EP0_RxReady
|
|
* handle EP0 Rx Ready event
|
|
* @param pdev: device instance
|
|
* @retval status
|
|
*/
|
|
static uint8_t usb_dfu_ep0_rx_ready(usb_handle_t *pdev)
|
|
{
|
|
(void)pdev;
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
/*
|
|
* @brief USBD_DFU_EP0_TxReady
|
|
* handle EP0 TRx Ready event
|
|
* @param pdev: device instance
|
|
* @retval status
|
|
*/
|
|
static uint8_t usb_dfu_ep0_tx_ready(usb_handle_t *pdev)
|
|
{
|
|
usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data;
|
|
uint16_t len, dfu_version = 0;
|
|
uint8_t *serial = pdev->desc->get_dfu_desc(&len);
|
|
|
|
dfu_version = serial[len - 1] << 8 | serial[len - 2];
|
|
|
|
if (hdfu->dev_state == DFU_STATE_DNLOAD_BUSY) {
|
|
if (dfu_version == 0x011a) {
|
|
/* Decode the Special Command*/
|
|
if (hdfu->wblock_num == 0) {
|
|
if (hdfu->buffer[0] ==
|
|
DFU_CMD_SETADDRESSPOINTER &&
|
|
hdfu->wlength == 5) {
|
|
hdfu->data_ptr = hdfu->buffer[1];
|
|
hdfu->data_ptr +=
|
|
hdfu->buffer[2] << 8;
|
|
hdfu->data_ptr +=
|
|
hdfu->buffer[3] << 16;
|
|
hdfu->data_ptr +=
|
|
hdfu->buffer[4] << 24;
|
|
} else if (hdfu->buffer[0] ==
|
|
DFU_CMD_ERASE &&
|
|
hdfu->wlength == 5) {
|
|
hdfu->data_ptr = hdfu->buffer[1];
|
|
hdfu->data_ptr +=
|
|
hdfu->buffer[2] << 8;
|
|
hdfu->data_ptr +=
|
|
hdfu->buffer[3] << 16;
|
|
hdfu->data_ptr +=
|
|
hdfu->buffer[4] << 24;
|
|
} else {
|
|
/* Reset the global length and block number */
|
|
hdfu->wlength = 0;
|
|
hdfu->wblock_num = 0;
|
|
/* Call the error management function
|
|
* (command will be nacked)
|
|
*/
|
|
usb_core_ctl_error(pdev);
|
|
}
|
|
}
|
|
}
|
|
if ((hdfu->wblock_num > 1 && dfu_version == 0x011a) ||
|
|
dfu_version != 0x011a) {
|
|
/* Perform the write operation */
|
|
if (((usb_dfu_media_t *)
|
|
pdev->user_data)->write_done((uint32_t *)
|
|
hdfu->data_ptr,
|
|
hdfu->wlength)
|
|
!= USBD_OK)
|
|
return USBD_FAIL;
|
|
}
|
|
|
|
/* Reset the global length and block number */
|
|
hdfu->wlength = 0;
|
|
hdfu->wblock_num = 0;
|
|
|
|
/* Update the state machine */
|
|
hdfu->dev_state = DFU_STATE_DNLOAD_SYNC;
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
return USBD_OK;
|
|
} else if (hdfu->dev_state == DFU_STATE_MANIFEST) {
|
|
/* Manifestation in progress*/
|
|
/* Start leaving DFU mode */
|
|
usb_dfu_leave(pdev);
|
|
}
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
/*
|
|
* @brief USBD_DFU_SOF
|
|
* handle SOF event
|
|
* @param pdev: device instance
|
|
* @retval status
|
|
*/
|
|
static uint8_t usb_dfu_sof(usb_handle_t *pdev)
|
|
{
|
|
(void)pdev;
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
/*
|
|
* @brief USBD_DFU_IsoINIncomplete
|
|
* handle data ISO IN Incomplete event
|
|
* @param pdev: device instance
|
|
* @param epnum: endpoint index
|
|
* @retval status
|
|
*/
|
|
static uint8_t usb_dfu_iso_in_incomplete(usb_handle_t *pdev, uint8_t epnum)
|
|
{
|
|
(void)pdev;
|
|
(void)epnum;
|
|
return USBD_OK;
|
|
}
|
|
|
|
/*
|
|
* @brief USBD_DFU_IsoOutIncomplete
|
|
* handle data ISO OUT Incomplete event
|
|
* @param pdev: device instance
|
|
* @param epnum: endpoint index
|
|
* @retval status
|
|
*/
|
|
static uint8_t usb_dfu_iso_out_incomplete(usb_handle_t *pdev, uint8_t epnum)
|
|
{
|
|
(void)pdev;
|
|
(void)epnum;
|
|
return USBD_OK;
|
|
}
|
|
|
|
/*
|
|
* @brief USBD_DFU_DataOut
|
|
* handle data OUT Stage
|
|
* @param pdev: device instance
|
|
* @param epnum: endpoint index
|
|
* @retval status
|
|
*/
|
|
static uint8_t usb_dfu_data_out(usb_handle_t *pdev, uint8_t epnum)
|
|
{
|
|
(void)pdev;
|
|
(void)epnum;
|
|
return USBD_OK;
|
|
}
|
|
|
|
/*
|
|
* @brief DFU_Detach
|
|
* Handles the DFU DETACH request.
|
|
* @param pdev: device instance
|
|
* @param req: pointer to the request structure.
|
|
* @retval None.
|
|
*/
|
|
static void usb_dfu_detach(usb_handle_t *pdev, usb_setup_req_t *req)
|
|
{
|
|
usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data;
|
|
|
|
INFO("Receive Detach\n");
|
|
|
|
if (hdfu->dev_state == DFU_STATE_IDLE ||
|
|
hdfu->dev_state == DFU_STATE_DNLOAD_SYNC ||
|
|
hdfu->dev_state == DFU_STATE_DNLOAD_IDLE ||
|
|
hdfu->dev_state == DFU_STATE_MANIFEST_SYNC ||
|
|
hdfu->dev_state == DFU_STATE_UPLOAD_IDLE) {
|
|
/* Update the state machine */
|
|
hdfu->dev_state = DFU_STATE_IDLE;
|
|
hdfu->dev_status[0] = DFU_ERROR_NONE;
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0; /*bwPollTimeout=0ms*/
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
hdfu->dev_status[5] = 0; /*iString*/
|
|
hdfu->wblock_num = 0;
|
|
}
|
|
hdfu->wlength = 0;
|
|
|
|
usbd_detach_req = 0;
|
|
}
|
|
|
|
/*
|
|
* @brief DFU_Download
|
|
* Handles the DFU DNLOAD request.
|
|
* @param pdev: device instance
|
|
* @param req: pointer to the request structure
|
|
* @retval None
|
|
*/
|
|
static void usb_dfu_download(usb_handle_t *pdev, usb_setup_req_t *req)
|
|
{
|
|
usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data;
|
|
|
|
/* Data setup request */
|
|
if (req->length > 0) {
|
|
if ((hdfu->dev_state == DFU_STATE_IDLE) ||
|
|
(hdfu->dev_state == DFU_STATE_DNLOAD_IDLE)) {
|
|
/* Update the global length and block number */
|
|
hdfu->wblock_num = req->value;
|
|
hdfu->wlength = req->length;
|
|
|
|
/* Update the data address */
|
|
hdfu->data_ptr = usbd_dfu_download_address;
|
|
|
|
/* Update the state machine */
|
|
hdfu->dev_state = DFU_STATE_DNLOAD_SYNC;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
|
|
/* Prepare the reception of the buffer over EP0 */
|
|
/* Set EP0 State */
|
|
pdev->ep0_state = USBD_EP0_DATA_OUT;
|
|
pdev->ep_out[0].total_length = hdfu->wlength;
|
|
pdev->ep_out[0].rem_length = hdfu->wlength;
|
|
|
|
/* Start the transfer */
|
|
usb_core_receive(pdev,
|
|
0,
|
|
(uint8_t *)usbd_dfu_download_address,
|
|
hdfu->wlength);
|
|
|
|
usbd_dfu_download_address += hdfu->wlength;
|
|
} else {
|
|
/* Unsupported state */
|
|
/* Call the error management function
|
|
* (command will be nacked)
|
|
*/
|
|
usb_core_ctl_error(pdev);
|
|
}
|
|
} else {
|
|
/* End of DNLOAD operation*/
|
|
if (hdfu->dev_state == DFU_STATE_DNLOAD_IDLE ||
|
|
hdfu->dev_state == DFU_STATE_IDLE) {
|
|
hdfu->manif_state = DFU_MANIFEST_IN_PROGRESS;
|
|
hdfu->dev_state = DFU_STATE_MANIFEST_SYNC;
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
} else {
|
|
/* Call the error management function
|
|
* (command will be nacked)
|
|
*/
|
|
usb_core_ctl_error(pdev);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @brief DFU_Upload
|
|
* Handles the DFU UPLOAD request.
|
|
* @param pdev: instance
|
|
* @param req: pointer to the request structure
|
|
* @retval status
|
|
*/
|
|
static void usb_dfu_upload(usb_handle_t *pdev, usb_setup_req_t *req)
|
|
{
|
|
usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data;
|
|
|
|
/* Data setup request */
|
|
if (req->length > 0) {
|
|
if ((hdfu->dev_state == DFU_STATE_IDLE) ||
|
|
(hdfu->dev_state == DFU_STATE_UPLOAD_IDLE)) {
|
|
/* Update the global length and block number */
|
|
hdfu->wblock_num = req->value;
|
|
hdfu->wlength = req->length;
|
|
|
|
/* DFU GetPhase Command */
|
|
if (hdfu->wblock_num == 0) {
|
|
/* Update the state machine */
|
|
hdfu->dev_state = (hdfu->wlength > 3) ?
|
|
DFU_STATE_IDLE :
|
|
DFU_STATE_UPLOAD_IDLE;
|
|
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
|
|
INFO("UPLOAD :\n");
|
|
INFO("\t\tPhase ID : %i\n", usbd_dfu_phase_id);
|
|
INFO("\t\taddress 0x%lx\n",
|
|
usbd_dfu_download_address);
|
|
|
|
hdfu->buffer[0] = usbd_dfu_phase_id;
|
|
hdfu->buffer[1] = (uint8_t)
|
|
(usbd_dfu_download_address);
|
|
hdfu->buffer[2] = (uint8_t)
|
|
(usbd_dfu_download_address >>
|
|
8);
|
|
hdfu->buffer[3] = (uint8_t)
|
|
(usbd_dfu_download_address >>
|
|
16);
|
|
hdfu->buffer[4] = (uint8_t)
|
|
(usbd_dfu_download_address >>
|
|
24);
|
|
|
|
hdfu->buffer[5] = 0x00;
|
|
hdfu->buffer[6] = 0x00;
|
|
hdfu->buffer[7] = 0x00;
|
|
hdfu->buffer[8] = 0x00;
|
|
|
|
if ((usbd_dfu_download_address ==
|
|
UNDEFINE_DOWN_ADDR) &&
|
|
(usbd_detach_req)) {
|
|
INFO("Send detach request\n");
|
|
hdfu->buffer[9] = 0x01;
|
|
pdev->ep_in[0].total_length = 10;
|
|
pdev->ep_in[0].rem_length = 10;
|
|
} else {
|
|
pdev->ep_in[0].total_length = 9;
|
|
pdev->ep_in[0].rem_length = 9;
|
|
}
|
|
|
|
/* Send the status data over EP0 */
|
|
pdev->ep0_state = USBD_EP0_DATA_IN;
|
|
/* Start the transfer */
|
|
usb_core_transmit(pdev, 0x00,
|
|
(uint8_t *)&hdfu->buffer[0],
|
|
pdev->ep_in[0].total_length);
|
|
} else {
|
|
/* unsupported hdfu->wblock_num */
|
|
ERROR("UPLOAD : Unsupported block : %i\n",
|
|
hdfu->wblock_num);
|
|
|
|
hdfu->dev_state = DFU_ERROR_STALLEDPKT;
|
|
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
|
|
/* Call the error management function
|
|
* (command will be nacked
|
|
*/
|
|
usb_core_ctl_error(pdev);
|
|
}
|
|
} else {
|
|
/* Unsupported state */
|
|
ERROR("UPLOAD : Unsupported State\n");
|
|
|
|
hdfu->wlength = 0;
|
|
hdfu->wblock_num = 0;
|
|
/* Call the error management function
|
|
* (command will be nacked
|
|
*/
|
|
usb_core_ctl_error(pdev);
|
|
}
|
|
} else {
|
|
/* No Data setup request */
|
|
INFO("USB : DFU : Nothing to do\n");
|
|
hdfu->dev_state = DFU_STATE_IDLE;
|
|
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @brief DFU_GetStatus
|
|
* Handles the DFU GETSTATUS request.
|
|
* @param pdev: instance
|
|
* @retval status
|
|
*/
|
|
static void usb_dfu_get_status(usb_handle_t *pdev)
|
|
{
|
|
usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data;
|
|
uint16_t status;
|
|
uint8_t dfu_bm_attribute = DFU_BM_ATTRIBUTE;
|
|
|
|
switch (hdfu->dev_state) {
|
|
case DFU_STATE_DNLOAD_SYNC:
|
|
status = ((usb_dfu_media_t *)pdev->user_data)->get_status();
|
|
|
|
switch (status) {
|
|
case DFU_MEDIA_STATE_WRITTEN:
|
|
/* SRAM block writing is finished, checks if checksum
|
|
* error has been detected
|
|
*/
|
|
hdfu->dev_state = DFU_STATE_DNLOAD_IDLE;
|
|
break;
|
|
|
|
case DFU_MEDIA_STATE_ERROR:
|
|
hdfu->dev_state = DFU_STATE_ERROR;
|
|
break;
|
|
|
|
case DFU_MEDIA_STATE_READY:
|
|
default:
|
|
/* SRAM is ready to be written */
|
|
hdfu->dev_state = DFU_STATE_DNLOAD_BUSY;
|
|
break;
|
|
}
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
break;
|
|
|
|
case DFU_STATE_MANIFEST_SYNC:
|
|
if (hdfu->manif_state == DFU_MANIFEST_IN_PROGRESS) {
|
|
hdfu->dev_state = DFU_STATE_MANIFEST;
|
|
|
|
hdfu->dev_status[1] = 1;/*bwPollTimeout = 1ms*/
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
} else if ((hdfu->manif_state == DFU_MANIFEST_COMPLETE) &&
|
|
(dfu_bm_attribute & 0x04)) {
|
|
INFO("USB : DFU : end of download partition : %i\n",
|
|
hdfu->alt_setting);
|
|
hdfu->dev_state = DFU_STATE_IDLE;
|
|
usbd_dfu_operation_complete = 1;
|
|
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0;
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Send the status data over EP0 */
|
|
pdev->ep0_state = USBD_EP0_DATA_IN;
|
|
pdev->ep_in[0].total_length = 6;
|
|
pdev->ep_in[0].rem_length = 6;
|
|
/* Start the transfer */
|
|
usb_core_transmit(pdev, 0x00, (uint8_t *)&hdfu->dev_status[0], 6);
|
|
}
|
|
|
|
/*
|
|
* @brief DFU_ClearStatus
|
|
* Handles the DFU CLRSTATUS request.
|
|
* @param pdev: device instance
|
|
* @retval status
|
|
*/
|
|
static void usb_dfu_clear_status(usb_handle_t *pdev)
|
|
{
|
|
usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data;
|
|
|
|
if (hdfu->dev_state == DFU_STATE_ERROR) {
|
|
hdfu->dev_state = DFU_STATE_IDLE;
|
|
hdfu->dev_status[0] = DFU_ERROR_NONE;/*bStatus*/
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0; /*bwPollTimeout=0ms*/
|
|
hdfu->dev_status[4] = hdfu->dev_state;/*bState*/
|
|
hdfu->dev_status[5] = 0;/*iString*/
|
|
} else {
|
|
/*State Error*/
|
|
hdfu->dev_state = DFU_STATE_ERROR;
|
|
hdfu->dev_status[0] = DFU_ERROR_UNKNOWN;/*bStatus*/
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0; /*bwPollTimeout=0ms*/
|
|
hdfu->dev_status[4] = hdfu->dev_state;/*bState*/
|
|
hdfu->dev_status[5] = 0;/*iString*/
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @brief DFU_GetState
|
|
* Handles the DFU GETSTATE request.
|
|
* @param pdev: device instance
|
|
* @retval None
|
|
*/
|
|
static void usb_dfu_get_state(usb_handle_t *pdev)
|
|
{
|
|
usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data;
|
|
|
|
/* Return the current state of the DFU interface */
|
|
/* Send the status data over EP0 */
|
|
pdev->ep0_state = USBD_EP0_DATA_IN;
|
|
pdev->ep_in[0].total_length = 1;
|
|
pdev->ep_in[0].rem_length = 1;
|
|
|
|
/* Start the transfer */
|
|
usb_core_transmit(pdev, 0x00, &hdfu->dev_state, 1);
|
|
}
|
|
|
|
/*
|
|
* @brief DFU_Abort
|
|
* Handles the DFU ABORT request.
|
|
* @param pdev: device instance
|
|
* @retval None
|
|
*/
|
|
static void usb_dfu_abort(usb_handle_t *pdev)
|
|
{
|
|
usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data;
|
|
|
|
if (hdfu->dev_state == DFU_STATE_IDLE ||
|
|
hdfu->dev_state == DFU_STATE_DNLOAD_SYNC ||
|
|
hdfu->dev_state == DFU_STATE_DNLOAD_IDLE ||
|
|
hdfu->dev_state == DFU_STATE_MANIFEST_SYNC ||
|
|
hdfu->dev_state == DFU_STATE_UPLOAD_IDLE) {
|
|
hdfu->dev_state = DFU_STATE_IDLE;
|
|
hdfu->dev_status[0] = DFU_ERROR_NONE;
|
|
hdfu->dev_status[1] = 0;
|
|
hdfu->dev_status[2] = 0;
|
|
hdfu->dev_status[3] = 0; /*bwPollTimeout=0ms*/
|
|
hdfu->dev_status[4] = hdfu->dev_state;
|
|
hdfu->dev_status[5] = 0; /*iString*/
|
|
hdfu->wblock_num = 0;
|
|
hdfu->wlength = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @brief USBD_DFU_Setup
|
|
* Handle the DFU specific requests
|
|
* @param pdev: instance
|
|
* @param req: usb requests
|
|
* @retval status
|
|
*/
|
|
static uint8_t usb_dfu_setup(usb_handle_t *pdev, usb_setup_req_t *req)
|
|
{
|
|
uint8_t *pbuf = NULL;
|
|
uint16_t len = 0;
|
|
uint8_t ret = USBD_OK;
|
|
usb_dfu_handle_t *hdfu = (usb_dfu_handle_t *)pdev->class_data;
|
|
|
|
VERBOSE("alt_setting %i, bmRequest : 0x%x, brequest : 0x%x\n",
|
|
hdfu->alt_setting, req->bm_request & USB_REQ_TYPE_MASK,
|
|
req->b_request);
|
|
switch (req->bm_request & USB_REQ_TYPE_MASK) {
|
|
case USB_REQ_TYPE_CLASS:
|
|
usbd_dfu_current_req = req->b_request;
|
|
if (hdfu->alt_setting == usbd_dfu_phase_id) {
|
|
switch (req->b_request) {
|
|
case DFU_DNLOAD:
|
|
usb_dfu_download(pdev, req);
|
|
break;
|
|
|
|
case DFU_UPLOAD:
|
|
usb_dfu_upload(pdev, req);
|
|
break;
|
|
|
|
case DFU_GETSTATUS:
|
|
usb_dfu_get_status(pdev);
|
|
break;
|
|
|
|
case DFU_CLRSTATUS:
|
|
usb_dfu_clear_status(pdev);
|
|
break;
|
|
|
|
case DFU_GETSTATE:
|
|
usb_dfu_get_state(pdev);
|
|
break;
|
|
|
|
case DFU_ABORT:
|
|
usb_dfu_abort(pdev);
|
|
break;
|
|
|
|
case DFU_DETACH:
|
|
usb_dfu_detach(pdev, req);
|
|
break;
|
|
|
|
default:
|
|
ERROR("phase ID :%i\n", usbd_dfu_phase_id);
|
|
usb_core_ctl_error(pdev);
|
|
ret = USBD_FAIL;
|
|
break;
|
|
}
|
|
} else if (hdfu->alt_setting == DFU_GET_PHASE) {
|
|
switch (req->b_request) {
|
|
case DFU_UPLOAD:
|
|
usb_dfu_upload(pdev, req);
|
|
break;
|
|
|
|
case DFU_GETSTATUS:
|
|
INFO("GETSTATUS :\n");
|
|
usb_dfu_get_status(pdev);
|
|
|
|
switch (hdfu->dev_state) {
|
|
case APP_STATE_IDLE:
|
|
INFO("\t\tAPP_STATE_IDLE\n");
|
|
break;
|
|
case APP_STATE_DETACH:
|
|
INFO("\t\tAPP_STATE_DETACH\n");
|
|
break;
|
|
case DFU_STATE_IDLE:
|
|
INFO("\t\tDFU_STATE_IDLE\n");
|
|
break;
|
|
case DFU_STATE_DNLOAD_SYNC:
|
|
INFO("\t\tDFU_STATE_DNLOAD_SYNC\n");
|
|
break;
|
|
case DFU_STATE_DNLOAD_BUSY:
|
|
INFO("\t\tDFU_STATE_DNLOAD_BUSY\n");
|
|
break;
|
|
case DFU_STATE_DNLOAD_IDLE:
|
|
INFO("\t\tDFU_STATE_DNLOAD_IDLE\n");
|
|
break;
|
|
case DFU_STATE_MANIFEST_SYNC:
|
|
INFO("\t\tDFU_STATE_MANIFEST_SYNC\n");
|
|
break;
|
|
case DFU_STATE_MANIFEST:
|
|
INFO("\t\tDFU_STATE_MANIFEST\n");
|
|
break;
|
|
case DFU_STATE_MANIFEST_WAIT_RESET:
|
|
INFO("\t\tDFU_STATE_MANIFEST_WAIT_RESET\n");
|
|
break;
|
|
case DFU_STATE_UPLOAD_IDLE:
|
|
INFO("\t\tDFU_STATE_UPLOAD_IDLE\n");
|
|
break;
|
|
case DFU_STATE_ERROR:
|
|
ERROR("\t\tDFU_STATE_ERROR\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case DFU_CLRSTATUS:
|
|
INFO("Receive DFU clear status\n");
|
|
usb_dfu_clear_status(pdev);
|
|
break;
|
|
|
|
case DFU_GETSTATE:
|
|
INFO("GETSTATE :\n");
|
|
usb_dfu_get_state(pdev);
|
|
|
|
switch (hdfu->dev_state) {
|
|
case APP_STATE_IDLE:
|
|
INFO("\t\tAPP_STATE_IDLE\n");
|
|
break;
|
|
case APP_STATE_DETACH:
|
|
INFO("\t\tAPP_STATE_DETACH\n");
|
|
break;
|
|
case DFU_STATE_IDLE:
|
|
INFO("\t\tDFU_STATE_IDLE\n");
|
|
break;
|
|
case DFU_STATE_DNLOAD_SYNC:
|
|
INFO("\t\tDFU_STATE_DNLOAD_SYNC\n");
|
|
break;
|
|
case DFU_STATE_DNLOAD_BUSY:
|
|
INFO("\t\tDFU_STATE_DNLOAD_BUSY\n");
|
|
break;
|
|
case DFU_STATE_DNLOAD_IDLE:
|
|
INFO("\t\tDFU_STATE_DNLOAD_IDLE\n");
|
|
break;
|
|
case DFU_STATE_MANIFEST_SYNC:
|
|
INFO("\t\tDFU_STATE_MANIFEST_SYNC\n");
|
|
break;
|
|
case DFU_STATE_MANIFEST:
|
|
INFO("\t\tDFU_STATE_MANIFEST\n");
|
|
break;
|
|
case DFU_STATE_MANIFEST_WAIT_RESET:
|
|
INFO("\t\tDFU_STATE_MANIFEST_WAIT_RESET\n");
|
|
break;
|
|
case DFU_STATE_UPLOAD_IDLE:
|
|
INFO("\t\tDFU_STATE_UPLOAD_IDLE\n");
|
|
break;
|
|
case DFU_STATE_ERROR:
|
|
ERROR("\t\tDFU_STATE_ERROR\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case DFU_ABORT:
|
|
INFO("Receive DFU abort\n");
|
|
usb_dfu_abort(pdev);
|
|
break;
|
|
|
|
case DFU_DETACH:
|
|
usb_dfu_detach(pdev, req);
|
|
break;
|
|
|
|
default:
|
|
ERROR("phase ID :%i\n", DFU_GET_PHASE);
|
|
usb_core_ctl_error(pdev);
|
|
ret = USBD_FAIL;
|
|
break;
|
|
}
|
|
} else {
|
|
ERROR("Unknown alternate : %i\n", hdfu->alt_setting);
|
|
ret = USBD_FAIL;
|
|
}
|
|
break;
|
|
case USB_REQ_TYPE_STANDARD:
|
|
switch (req->b_request) {
|
|
case USB_REQ_GET_DESCRIPTOR:
|
|
if ((req->value >> 8) == DFU_DESCRIPTOR_TYPE) {
|
|
pbuf = pdev->desc->get_dfu_desc(&len);
|
|
len = MIN(len, req->length);
|
|
}
|
|
|
|
pdev->ep0_state = USBD_EP0_DATA_IN;
|
|
pdev->ep_in[0].total_length = len;
|
|
pdev->ep_in[0].rem_length = len;
|
|
/* Start the transfer */
|
|
usb_core_transmit(pdev, 0x00, pbuf, len);
|
|
|
|
break;
|
|
|
|
case USB_REQ_GET_INTERFACE:
|
|
pdev->ep0_state = USBD_EP0_DATA_IN;
|
|
pdev->ep_in[0].total_length = 1;
|
|
pdev->ep_in[0].rem_length = 1;
|
|
/* Start the transfer */
|
|
usb_core_transmit(pdev, 0x00,
|
|
(uint8_t *)&hdfu->alt_setting, 1);
|
|
break;
|
|
|
|
case USB_REQ_SET_INTERFACE:
|
|
hdfu->alt_setting = (uint8_t)(req->value);
|
|
break;
|
|
|
|
default:
|
|
usb_core_ctl_error(pdev);
|
|
ret = USBD_FAIL;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const usb_class_t USBD_DFU_initvalue = {
|
|
usb_dfu_init,
|
|
usb_dfu_de_init,
|
|
usb_dfu_setup,
|
|
usb_dfu_ep0_tx_ready,
|
|
usb_dfu_ep0_rx_ready,
|
|
usb_dfu_data_in,
|
|
usb_dfu_data_out,
|
|
usb_dfu_sof,
|
|
usb_dfu_iso_in_incomplete,
|
|
usb_dfu_iso_out_incomplete,
|
|
0
|
|
};
|
|
|
|
void usb_dfu_register_callback(usb_handle_t *pdev)
|
|
{
|
|
pdev->class = (usb_class_t *)&USBD_DFU_initvalue;
|
|
}
|
|
|
|
void usb_dfu_set_phase_id(uint32_t phase_id)
|
|
{
|
|
usbd_dfu_phase_id = phase_id;
|
|
usbd_dfu_operation_complete = 0;
|
|
}
|
|
|
|
void usb_dfu_set_download_addr(uintptr_t addr)
|
|
{
|
|
usbd_dfu_download_address = addr;
|
|
}
|
|
|
|
uint32_t usb_dfu_download_is_completed(void)
|
|
{
|
|
return usbd_dfu_operation_complete;
|
|
}
|
|
|
|
uint32_t usb_dfu_get_current_req(void)
|
|
{
|
|
return usbd_dfu_current_req;
|
|
}
|
|
|
|
uint32_t usb_dfu_detach_req(void)
|
|
{
|
|
return usbd_detach_req;
|
|
}
|
|
|
|
void usb_dfu_request_detach(void)
|
|
{
|
|
usbd_detach_req = 1;
|
|
}
|