#include "usbd_dfu_bootloader.h" #include "stm32f1xx_hal.h" #include "usbd_ctlreq.h" #define DFU_DETACH 0x00U #define DFU_DNLOAD 0x01U #define DFU_UPLOAD 0x02U #define DFU_GETSTATUS 0x03U #define DFU_CLRSTATUS 0x04U #define DFU_GETSTATE 0x05U #define DFU_ABORT 0x06U #define DFU_STATUS_OK 0x00U #define DFU_STATUS_ERR_WRITE 0x03U #define DFU_STATUS_ERR_ERASE 0x04U #define DFU_STATUS_ERR_ADDRESS 0x08U #define DFU_STATUS_ERR_STALLEDPKT 0x0FU #define DFU_STATE_DFU_IDLE 0x02U #define DFU_STATE_DFU_DNLOAD_SYNC 0x03U #define DFU_STATE_DFU_DNLOAD_IDLE 0x05U #define DFU_STATE_DFU_MANIFEST_SYNC 0x06U #define DFU_STATE_DFU_MANIFEST_WAIT_RESET 0x08U #define DFU_STATE_DFU_ERROR 0x0AU #define DFU_DESCRIPTOR_TYPE 0x21U #define STM32F103_FLASH_PAGE_SIZE 1024U typedef struct { uint8_t state; uint8_t status; uint8_t altSetting; uint8_t resetPending; uint16_t blockNumber; uint16_t downloadLength; uint32_t resetAt; } USBD_DFU_BootloaderHandleTypeDef; static uint8_t DFU_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx); static uint8_t DFU_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx); static uint8_t DFU_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static uint8_t DFU_EP0RxReady(USBD_HandleTypeDef *pdev); static uint8_t DFU_SOF(USBD_HandleTypeDef *pdev); static uint8_t *DFU_GetCfgDesc(uint16_t *length); static uint8_t *DFU_GetDeviceQualifierDesc(uint16_t *length); static uint8_t DFU_WriteBlock(USBD_DFU_BootloaderHandleTypeDef *handle); static uint32_t DFU_GetSlotBase(uint8_t altSetting); __ALIGN_BEGIN static uint32_t dfuDownloadWords[DFU_TRANSFER_SIZE / 4U] __ALIGN_END; USBD_ClassTypeDef USBD_DFU_BOOTLOADER = { DFU_Init, DFU_DeInit, DFU_Setup, NULL, DFU_EP0RxReady, NULL, NULL, DFU_SOF, NULL, NULL, DFU_GetCfgDesc, DFU_GetCfgDesc, DFU_GetCfgDesc, DFU_GetDeviceQualifierDesc, }; /* One DFU interface with two alternate settings: App1 and App2. */ __ALIGN_BEGIN static uint8_t dfuConfigDescriptor[USB_DFU_CONFIG_DESC_SIZ] __ALIGN_END = { 0x09, USB_DESC_TYPE_CONFIGURATION, LOBYTE(USB_DFU_CONFIG_DESC_SIZ), HIBYTE(USB_DFU_CONFIG_DESC_SIZ), 0x01, 0x01, 0x00, 0x80, 0x32, 0x09, USB_DESC_TYPE_INTERFACE, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x02, 0x00, /* DFU 1.1 functional descriptor: download only, 1024-byte transfer. */ 0x09, DFU_DESCRIPTOR_TYPE, 0x01, 0xE8, 0x03, LOBYTE(DFU_TRANSFER_SIZE), HIBYTE(DFU_TRANSFER_SIZE), 0x10, 0x01, 0x09, USB_DESC_TYPE_INTERFACE, 0x00, 0x01, 0x00, 0xFE, 0x01, 0x02, 0x00, }; __ALIGN_BEGIN static uint8_t dfuDeviceQualifierDescriptor[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END = { USB_LEN_DEV_QUALIFIER_DESC, USB_DESC_TYPE_DEVICE_QUALIFIER, 0x00, 0x02, 0x00, 0x00, 0x00, USB_MAX_EP0_SIZE, 0x01, 0x00, }; static uint8_t DFU_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { USBD_DFU_BootloaderHandleTypeDef *handle; UNUSED(cfgidx); pdev->pClassData = USBD_malloc(sizeof(USBD_DFU_BootloaderHandleTypeDef)); if (pdev->pClassData == NULL) { return USBD_FAIL; } handle = (USBD_DFU_BootloaderHandleTypeDef *)pdev->pClassData; handle->state = DFU_STATE_DFU_IDLE; handle->status = DFU_STATUS_OK; handle->altSetting = 0U; handle->resetPending = 0U; handle->blockNumber = 0U; handle->downloadLength = 0U; handle->resetAt = 0U; return USBD_OK; } static uint8_t DFU_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { UNUSED(cfgidx); if (pdev->pClassData != NULL) { USBD_free(pdev->pClassData); pdev->pClassData = NULL; } return USBD_OK; } static uint8_t DFU_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { USBD_DFU_BootloaderHandleTypeDef *handle = (USBD_DFU_BootloaderHandleTypeDef *)pdev->pClassData; static uint8_t statusResponse[6]; uint16_t statusInfo = 0U; if (handle == NULL) { USBD_CtlError(pdev, req); return USBD_FAIL; } if ((req->bmRequest & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS) { switch (req->bRequest) { case DFU_DNLOAD: if ((handle->state != DFU_STATE_DFU_IDLE) && (handle->state != DFU_STATE_DFU_DNLOAD_IDLE)) { break; } if (req->wLength == 0U) { handle->state = DFU_STATE_DFU_MANIFEST_SYNC; return USBD_OK; } if (req->wLength > DFU_TRANSFER_SIZE) { handle->status = DFU_STATUS_ERR_STALLEDPKT; handle->state = DFU_STATE_DFU_ERROR; break; } handle->blockNumber = req->wValue; handle->downloadLength = req->wLength; USBD_CtlPrepareRx(pdev, (uint8_t *)dfuDownloadWords, req->wLength); return USBD_OK; case DFU_UPLOAD: break; case DFU_GETSTATUS: if (handle->state == DFU_STATE_DFU_DNLOAD_SYNC) { handle->state = DFU_STATE_DFU_DNLOAD_IDLE; } else if (handle->state == DFU_STATE_DFU_MANIFEST_SYNC) { handle->state = DFU_STATE_DFU_MANIFEST_WAIT_RESET; handle->resetPending = 1U; handle->resetAt = HAL_GetTick() + 100U; } statusResponse[0] = handle->status; statusResponse[1] = 0U; statusResponse[2] = 0U; statusResponse[3] = 0U; statusResponse[4] = handle->state; statusResponse[5] = 0U; USBD_CtlSendData(pdev, statusResponse, sizeof(statusResponse)); return USBD_OK; case DFU_CLRSTATUS: handle->status = DFU_STATUS_OK; handle->state = DFU_STATE_DFU_IDLE; return USBD_OK; case DFU_GETSTATE: USBD_CtlSendData(pdev, &handle->state, 1U); return USBD_OK; case DFU_ABORT: handle->status = DFU_STATUS_OK; handle->state = DFU_STATE_DFU_IDLE; handle->downloadLength = 0U; return USBD_OK; case DFU_DETACH: handle->resetPending = 1U; handle->resetAt = HAL_GetTick() + 100U; return USBD_OK; default: break; } } else if ((req->bmRequest & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) { switch (req->bRequest) { case USB_REQ_GET_STATUS: USBD_CtlSendData(pdev, (uint8_t *)&statusInfo, sizeof(statusInfo)); return USBD_OK; case USB_REQ_GET_INTERFACE: USBD_CtlSendData(pdev, &handle->altSetting, 1U); return USBD_OK; case USB_REQ_SET_INTERFACE: if (req->wValue <= 1U) { handle->altSetting = (uint8_t)req->wValue; handle->state = DFU_STATE_DFU_IDLE; handle->status = DFU_STATUS_OK; return USBD_OK; } break; case USB_REQ_GET_DESCRIPTOR: if ((req->wValue >> 8) == DFU_DESCRIPTOR_TYPE) { USBD_CtlSendData(pdev, &dfuConfigDescriptor[18], MIN(9U, req->wLength)); return USBD_OK; } break; default: break; } } USBD_CtlError(pdev, req); return USBD_FAIL; } static uint8_t DFU_EP0RxReady(USBD_HandleTypeDef *pdev) { USBD_DFU_BootloaderHandleTypeDef *handle = (USBD_DFU_BootloaderHandleTypeDef *)pdev->pClassData; if ((handle == NULL) || (handle->downloadLength == 0U)) { return USBD_OK; } if (DFU_WriteBlock(handle) == USBD_OK) { handle->status = DFU_STATUS_OK; handle->state = DFU_STATE_DFU_DNLOAD_SYNC; } else { handle->state = DFU_STATE_DFU_ERROR; } handle->downloadLength = 0U; return USBD_OK; } static uint8_t DFU_SOF(USBD_HandleTypeDef *pdev) { USBD_DFU_BootloaderHandleTypeDef *handle = (USBD_DFU_BootloaderHandleTypeDef *)pdev->pClassData; if ((handle != NULL) && (handle->resetPending != 0U) && ((int32_t)(HAL_GetTick() - handle->resetAt) >= 0)) { NVIC_SystemReset(); } return USBD_OK; } static uint8_t DFU_WriteBlock(USBD_DFU_BootloaderHandleTypeDef *handle) { FLASH_EraseInitTypeDef erase = {0}; uint32_t eraseError = 0U; uint32_t slotBase = DFU_GetSlotBase(handle->altSetting); uint32_t address = slotBase + ((uint32_t)handle->blockNumber * DFU_TRANSFER_SIZE); uint32_t slotEnd = slotBase + DFU_APP_SLOT_SIZE; uint32_t index; if ((address < slotBase) || (address >= slotEnd) || (handle->downloadLength > (slotEnd - address))) { handle->status = DFU_STATUS_ERR_ADDRESS; return USBD_FAIL; } HAL_FLASH_Unlock(); __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR); erase.TypeErase = FLASH_TYPEERASE_PAGES; erase.PageAddress = address; erase.NbPages = 1U; if (HAL_FLASHEx_Erase(&erase, &eraseError) != HAL_OK) { handle->status = DFU_STATUS_ERR_ERASE; HAL_FLASH_Lock(); return USBD_FAIL; } for (index = 0U; index < handle->downloadLength; index += 2U) { uint8_t *bytes = (uint8_t *)dfuDownloadWords; uint16_t halfword = bytes[index]; if ((index + 1U) < handle->downloadLength) { halfword |= (uint16_t)((uint16_t)bytes[index + 1U] << 8U); } else { halfword |= 0xFF00U; } if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, address + index, halfword) != HAL_OK) { handle->status = DFU_STATUS_ERR_WRITE; HAL_FLASH_Lock(); return USBD_FAIL; } } HAL_FLASH_Lock(); return USBD_OK; } static uint32_t DFU_GetSlotBase(uint8_t altSetting) { return (altSetting == 0U) ? DFU_APP1_ADDRESS : DFU_APP2_ADDRESS; } static uint8_t *DFU_GetCfgDesc(uint16_t *length) { *length = sizeof(dfuConfigDescriptor); return dfuConfigDescriptor; } static uint8_t *DFU_GetDeviceQualifierDesc(uint16_t *length) { *length = sizeof(dfuDeviceQualifierDescriptor); return dfuDeviceQualifierDescriptor; }