446 lines
15 KiB
C
446 lines
15 KiB
C
/* *****************************************************************************
|
|
* The MIT License
|
|
*
|
|
* Copyright (c) 2010 LeafLabs LLC.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
* ****************************************************************************/
|
|
|
|
/**
|
|
* @file dfu.c
|
|
*
|
|
* @brief The principle dfu state machine as well as the data
|
|
* transfer callbacks accessed by the usb library
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include "dfu.h"
|
|
#include "usb.h"
|
|
|
|
/* DFU globals */
|
|
static volatile u32 userAppAddr = USER_CODE_RAM; /* default RAM user code location */
|
|
static volatile u32 userAppEnd = RAM_END;
|
|
static volatile DFUStatus dfuAppStatus; /* includes state */
|
|
volatile dfuUploadTypes_t userUploadType = DFU_UPLOAD_NONE;
|
|
volatile bool dfuBusy = FALSE;
|
|
|
|
static volatile u8 recvBuffer[wTransferSize] __attribute__((aligned(4)));
|
|
static volatile u32 userFirmwareLen = 0;
|
|
static volatile u16 thisBlockLen = 0;
|
|
static volatile u16 uploadBlockLen = 0;
|
|
|
|
|
|
volatile PLOT code_copy_lock;
|
|
|
|
/* todo: force dfu globals to be singleton to avoid re-inits? */
|
|
void dfuInit(void) {
|
|
dfuAppStatus.bStatus = OK;
|
|
dfuAppStatus.bwPollTimeout0 = 0x00;
|
|
dfuAppStatus.bwPollTimeout1 = 0x00;
|
|
dfuAppStatus.bwPollTimeout2 = 0x00;
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
dfuAppStatus.iString = 0x00; /* all strings must be 0x00 until we make them! */
|
|
userFirmwareLen = 0;
|
|
thisBlockLen = 0;;
|
|
userAppAddr = USER_CODE_RAM; /* default RAM user code location */
|
|
userAppEnd = RAM_END;
|
|
userUploadType=DFU_UPLOAD_NONE;
|
|
code_copy_lock = WAIT;
|
|
dfuBusy = FALSE;
|
|
}
|
|
|
|
bool dfuUpdateByRequest(void) {
|
|
/* were using the global pInformation struct from usb_lib here,
|
|
see comment in maple_dfu.h around DFUEvent struct */
|
|
dfuBusy = TRUE;
|
|
|
|
u8 startState = dfuAppStatus.bState;
|
|
dfuAppStatus.bStatus = OK;
|
|
/* often leaner to nest if's then embed a switch/case */
|
|
if (startState == dfuIDLE) {
|
|
/* device running inside DFU mode */
|
|
dfuBusy = TRUE; // signals the main loop to defer to the dfu write-loop
|
|
|
|
if (pInformation->USBbRequest == DFU_DNLOAD) {
|
|
|
|
if (pInformation->USBwLengths.w > 0) {
|
|
userFirmwareLen = 0;
|
|
dfuAppStatus.bState = dfuDNLOAD_SYNC;
|
|
switch(pInformation->Current_AlternateSetting)
|
|
{
|
|
case 0:
|
|
userAppAddr = USER_CODE_RAM;
|
|
userUploadType = DFU_UPLOAD_RAM;
|
|
break;
|
|
case 1:
|
|
userAppAddr = USER_CODE_FLASH0X8005000;
|
|
userUploadType = DFU_UPLOAD_FLASH_0X8005000;
|
|
|
|
/* make sure the flash is setup properly, unlock it */
|
|
setupFLASH();
|
|
flashUnlock();
|
|
// Clear lower memory so that we can check on cold boot, whether the last upload was to 0x8002000 or 0x8005000
|
|
flashErasePage((u32)USER_CODE_FLASH0X8002000);
|
|
break;
|
|
case 2:
|
|
userUploadType = DFU_UPLOAD_FLASH_0X8002000;
|
|
userAppAddr = USER_CODE_FLASH0X8002000;
|
|
/* make sure the flash is setup properly, unlock it */
|
|
setupFLASH();
|
|
flashUnlock();
|
|
break;
|
|
default:
|
|
userAppAddr = USER_CODE_RAM;
|
|
userUploadType = DFU_UPLOAD_RAM;
|
|
break;
|
|
}
|
|
} else {
|
|
dfuAppStatus.bState = dfuERROR;
|
|
dfuAppStatus.bStatus = errNOTDONE;
|
|
}
|
|
} else if (pInformation->USBbRequest == DFU_UPLOAD) {
|
|
dfuAppStatus.bState = dfuUPLOAD_IDLE;
|
|
/* record length of first block for calculating target
|
|
address from wValue in consecutive blocks */
|
|
uploadBlockLen = pInformation->USBwLengths.w;
|
|
thisBlockLen = uploadBlockLen; /* for this first block as well */
|
|
/* calculate where the data should be copied from */
|
|
userFirmwareLen = uploadBlockLen * pInformation->USBwValue;
|
|
switch(pInformation->Current_AlternateSetting)
|
|
{
|
|
case 0:
|
|
userAppAddr = USER_CODE_RAM;
|
|
userAppEnd = RAM_END;
|
|
case 1:
|
|
userAppAddr = USER_CODE_FLASH0X8005000;
|
|
userAppEnd = FLASH_END;
|
|
break;
|
|
case 2:
|
|
userAppAddr = USER_CODE_FLASH0X8002000;
|
|
userAppEnd = FLASH_END;
|
|
break;
|
|
default:
|
|
userAppAddr = USER_CODE_RAM;
|
|
userAppEnd = RAM_END;
|
|
break;
|
|
}
|
|
} else if (pInformation->USBbRequest == DFU_ABORT) {
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
dfuAppStatus.bStatus = OK; /* are we really ok? we were just aborted */
|
|
} else if (pInformation->USBbRequest == DFU_GETSTATUS) {
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
} else if (pInformation->USBbRequest == DFU_GETSTATE) {
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
} else {
|
|
dfuAppStatus.bState = dfuERROR;
|
|
dfuAppStatus.bStatus = errSTALLEDPKT;
|
|
}
|
|
|
|
} else if (startState == dfuDNLOAD_SYNC) {
|
|
/* device received block, waiting for DFU_GETSTATUS request */
|
|
|
|
if (pInformation->USBbRequest == DFU_GETSTATUS) {
|
|
/* todo, add routine to wait for last block write to finish */
|
|
if (userUploadType == DFU_UPLOAD_RAM) {
|
|
if (code_copy_lock == WAIT) {
|
|
code_copy_lock = BEGINNING;
|
|
dfuAppStatus.bwPollTimeout0 = 0x20; /* 32 ms */
|
|
dfuAppStatus.bwPollTimeout1 = 0x00;
|
|
dfuAppStatus.bState = dfuDNBUSY;
|
|
|
|
} else if (code_copy_lock == BEGINNING) {
|
|
dfuAppStatus.bState = dfuDNLOAD_SYNC;
|
|
|
|
} else if (code_copy_lock == MIDDLE) {
|
|
dfuAppStatus.bState = dfuDNLOAD_SYNC;
|
|
|
|
} else if (code_copy_lock == END) {
|
|
dfuAppStatus.bwPollTimeout0 = 0x00;
|
|
code_copy_lock = WAIT;
|
|
dfuAppStatus.bState = dfuDNLOAD_IDLE;
|
|
}
|
|
|
|
} else {
|
|
dfuAppStatus.bState = dfuDNLOAD_IDLE;
|
|
dfuCopyBufferToExec();
|
|
}
|
|
|
|
} else if (pInformation->USBbRequest == DFU_GETSTATE) {
|
|
dfuAppStatus.bState = dfuDNLOAD_SYNC;
|
|
} else {
|
|
dfuAppStatus.bState = dfuERROR;
|
|
dfuAppStatus.bStatus = errSTALLEDPKT;
|
|
}
|
|
|
|
} else if (startState == dfuDNBUSY) {
|
|
/* if were actually done writing, goto sync, else stay busy */
|
|
if (code_copy_lock == END) {
|
|
dfuAppStatus.bwPollTimeout0 = 0x00;
|
|
code_copy_lock = WAIT;
|
|
dfuAppStatus.bState = dfuDNLOAD_IDLE;
|
|
} else {
|
|
dfuAppStatus.bState = dfuDNBUSY;
|
|
}
|
|
|
|
} else if (startState == dfuDNLOAD_IDLE) {
|
|
/* device is expecting dfu_dnload requests */
|
|
if (pInformation->USBbRequest == DFU_DNLOAD) {
|
|
if (pInformation->USBwLengths.w > 0) {
|
|
dfuAppStatus.bState = dfuDNLOAD_SYNC;
|
|
} else {
|
|
/* todo, support "disagreement" if device expects more data than this */
|
|
dfuAppStatus.bState = dfuMANIFEST_SYNC;
|
|
|
|
/* relock the flash */
|
|
flashLock();
|
|
}
|
|
} else if (pInformation->USBbRequest == DFU_ABORT) {
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
} else if (pInformation->USBbRequest == DFU_GETSTATUS) {
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
} else if (pInformation->USBbRequest == DFU_GETSTATE) {
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
} else {
|
|
dfuAppStatus.bState = dfuERROR;
|
|
dfuAppStatus.bStatus = errSTALLEDPKT;
|
|
}
|
|
|
|
} else if (startState == dfuMANIFEST_SYNC) {
|
|
/* device has received last block, waiting DFU_GETSTATUS request */
|
|
|
|
if (pInformation->USBbRequest == DFU_GETSTATUS) {
|
|
dfuAppStatus.bState = dfuMANIFEST_WAIT_RESET;
|
|
dfuAppStatus.bStatus = OK;
|
|
} else if (pInformation->USBbRequest == DFU_GETSTATE) {
|
|
dfuAppStatus.bState = dfuMANIFEST_SYNC;
|
|
} else {
|
|
dfuAppStatus.bState = dfuERROR;
|
|
dfuAppStatus.bStatus = errSTALLEDPKT;
|
|
}
|
|
|
|
} else if (startState == dfuMANIFEST) {
|
|
/* device is in manifestation phase */
|
|
|
|
/* should never receive request while in manifest! */
|
|
dfuAppStatus.bState = dfuMANIFEST_WAIT_RESET;
|
|
dfuAppStatus.bStatus = OK;
|
|
|
|
} else if (startState == dfuMANIFEST_WAIT_RESET) {
|
|
/* device has programmed new firmware but needs external
|
|
usb reset or power on reset to run the new code */
|
|
|
|
/* consider timing out and self-resetting */
|
|
dfuAppStatus.bState = dfuMANIFEST_WAIT_RESET;
|
|
|
|
} else if (startState == dfuUPLOAD_IDLE) {
|
|
/* device expecting further dfu_upload requests */
|
|
|
|
if (pInformation->USBbRequest == DFU_UPLOAD) {
|
|
if (pInformation->USBwLengths.w > 0) {
|
|
/* check that this is not the last possible block */
|
|
userFirmwareLen = uploadBlockLen * pInformation->USBwValue;
|
|
if (userAppAddr + userFirmwareLen + uploadBlockLen <= userAppEnd) {
|
|
thisBlockLen = uploadBlockLen;
|
|
dfuAppStatus.bState = dfuUPLOAD_IDLE;
|
|
} else {
|
|
/* if above comparison was just equal, thisBlockLen becomes zero
|
|
next time when USBWValue has been increased by one */
|
|
thisBlockLen = userAppEnd - userAppAddr - userFirmwareLen;
|
|
/* check for overflow due to USBwValue out of range */
|
|
if (thisBlockLen >= pInformation->USBwLengths.w) {
|
|
thisBlockLen = 0;
|
|
}
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
}
|
|
} else {
|
|
dfuAppStatus.bState = dfuERROR;
|
|
dfuAppStatus.bStatus = errNOTDONE;
|
|
}
|
|
} else if (pInformation->USBbRequest == DFU_ABORT) {
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
} else if (pInformation->USBbRequest == DFU_GETSTATUS) {
|
|
dfuAppStatus.bState = dfuUPLOAD_IDLE;
|
|
} else if (pInformation->USBbRequest == DFU_GETSTATE) {
|
|
dfuAppStatus.bState = dfuUPLOAD_IDLE;
|
|
} else {
|
|
dfuAppStatus.bState = dfuERROR;
|
|
dfuAppStatus.bStatus = errSTALLEDPKT;
|
|
}
|
|
|
|
|
|
} else if (startState == dfuERROR) {
|
|
/* status is in error, awaiting DFU_CLRSTATUS request */
|
|
|
|
if (pInformation->USBbRequest == DFU_GETSTATUS) {
|
|
/* todo, add routine to wait for last block write to finish */
|
|
dfuAppStatus.bState = dfuERROR;
|
|
} else if (pInformation->USBbRequest == DFU_GETSTATE) {
|
|
dfuAppStatus.bState = dfuERROR;
|
|
} else if (pInformation->USBbRequest == DFU_CLRSTATUS) {
|
|
/* todo handle any cleanup we need here */
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
dfuAppStatus.bStatus = OK;
|
|
} else {
|
|
dfuAppStatus.bState = dfuERROR;
|
|
dfuAppStatus.bStatus = errSTALLEDPKT;
|
|
}
|
|
|
|
} else {
|
|
/* some kind of error... */
|
|
dfuAppStatus.bState = dfuERROR;
|
|
dfuAppStatus.bStatus = errSTALLEDPKT;
|
|
}
|
|
|
|
if (dfuAppStatus.bStatus == OK) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void dfuUpdateByReset(void) {
|
|
u8 startState = dfuAppStatus.bState;
|
|
userFirmwareLen = 0;
|
|
|
|
if (startState == appDETACH) {
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
dfuAppStatus.bStatus = OK;
|
|
|
|
nvicDisableInterrupts();
|
|
usbEnbISR();
|
|
|
|
} else if (startState == appIDLE || startState == dfuIDLE) {
|
|
/* do nothing...might be normal usb bus activity */
|
|
} else {
|
|
/* we reset from the dfu, reset everything and startover,
|
|
which is the correct operation if this is an erroneous
|
|
event or properly following a MANIFEST */
|
|
dfuAppStatus.bState = dfuIDLE;
|
|
dfuAppStatus.bStatus = OK;
|
|
|
|
systemHardReset();
|
|
}
|
|
}
|
|
|
|
void dfuUpdateByTimeout(void) {
|
|
}
|
|
|
|
u8 *dfuCopyState(u16 length) {
|
|
if (length == 0) {
|
|
pInformation->Ctrl_Info.Usb_wLength = 1;
|
|
return NULL;
|
|
} else {
|
|
return (&(dfuAppStatus.bState));
|
|
}
|
|
}
|
|
|
|
u8 *dfuCopyStatus(u16 length) {
|
|
if (length == 0) {
|
|
pInformation->Ctrl_Info.Usb_wLength = 6;
|
|
return NULL;
|
|
} else {
|
|
return (u8*)(&dfuAppStatus);
|
|
}
|
|
}
|
|
|
|
|
|
u8 *dfuCopyDNLOAD(u16 length) {
|
|
if (length == 0) {
|
|
pInformation->Ctrl_Info.Usb_wLength = pInformation->USBwLengths.w - pInformation->Ctrl_Info.Usb_wOffset;
|
|
thisBlockLen = pInformation->USBwLengths.w;
|
|
return NULL;
|
|
} else {
|
|
return ((u8 *)recvBuffer + pInformation->Ctrl_Info.Usb_wOffset);
|
|
}
|
|
}
|
|
|
|
u8 *dfuCopyUPLOAD(u16 length) {
|
|
if (length == 0) {
|
|
pInformation->Ctrl_Info.Usb_wLength = thisBlockLen - pInformation->Ctrl_Info.Usb_wOffset;
|
|
return NULL;
|
|
} else {
|
|
return((u8*) userAppAddr + userFirmwareLen + pInformation->Ctrl_Info.Usb_wOffset);
|
|
}
|
|
}
|
|
|
|
void dfuCopyBufferToExec() {
|
|
int i;
|
|
u32 *userSpace;
|
|
|
|
if (userUploadType == DFU_UPLOAD_RAM)
|
|
{
|
|
userSpace = (u32 *)(USER_CODE_RAM + userFirmwareLen);
|
|
/* we dont need to handle when thisBlock len is not divisible by 4,
|
|
since the linker will align everything to 4B anyway */
|
|
for (i = 0; i < thisBlockLen; i = i + 4) {
|
|
*userSpace++ = *(u32 *)(recvBuffer + i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (userUploadType == DFU_UPLOAD_FLASH_0X8005000)
|
|
{
|
|
userSpace = (u32 *)(USER_CODE_FLASH0X8005000 + userFirmwareLen);
|
|
}
|
|
else
|
|
{
|
|
userSpace = (u32 *)(USER_CODE_FLASH0X8002000 + userFirmwareLen);
|
|
}
|
|
|
|
flashErasePage((u32)(userSpace));
|
|
|
|
for (i = 0; i < thisBlockLen; i = i + 4) {
|
|
flashWriteWord((u32)(userSpace++), *(u32 *)(recvBuffer +i));
|
|
}
|
|
|
|
}
|
|
userFirmwareLen += thisBlockLen;
|
|
|
|
thisBlockLen = 0;
|
|
}
|
|
|
|
u8 dfuGetState(void) {
|
|
return dfuAppStatus.bState;
|
|
}
|
|
|
|
void dfuSetState(u8 newState) {
|
|
dfuAppStatus.bState = newState;
|
|
}
|
|
|
|
bool dfuUploadStarted() {
|
|
return dfuBusy;
|
|
}
|
|
|
|
void dfuFinishUpload() {
|
|
while (1) {
|
|
if (userUploadType==DFU_UPLOAD_RAM) {
|
|
if (code_copy_lock == BEGINNING) {
|
|
code_copy_lock = MIDDLE;
|
|
strobePin(LED_BANK, LED, 2, 0x1000);
|
|
dfuCopyBufferToExec();
|
|
strobePin(LED_BANK, LED, 2, 0x500);
|
|
code_copy_lock = END;
|
|
}
|
|
}
|
|
/* otherwise do nothing, dfu state machine resets itself */
|
|
}
|
|
}
|
|
|