mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-29 09:32:34 +00:00
1763 lines
49 KiB
C++
Executable File
1763 lines
49 KiB
C++
Executable File
/******************************************************************************
|
|
* Filename: sbl_device_cc2538.cpp
|
|
* Revised: $Date$
|
|
* Revision: $Revision$
|
|
*
|
|
* Description: Serial Bootloader device file for CC2538
|
|
*
|
|
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
|
|
*
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* Neither the name of Texas Instruments Incorporated nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include <sbllib.h>
|
|
#include "sbl_device.h"
|
|
#include "sbl_device_cc2538.h"
|
|
#include "serialib.h"
|
|
//#include <ComPort.h>
|
|
|
|
#include <vector>
|
|
|
|
|
|
/// Struct used when splitting long transfers
|
|
typedef struct {
|
|
uint32_t startAddr;
|
|
uint32_t byteCount;
|
|
uint32_t startOffset;
|
|
bool bExpectAck;
|
|
} tTransfer;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief Constructor
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
SblDeviceCC2538::SblDeviceCC2538()
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
m_pageEraseSize = SBL_CC2538_PAGE_ERASE_SIZE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief Destructor
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
SblDeviceCC2538::~SblDeviceCC2538()
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function sends ping command to device.
|
|
*
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::ping()
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
int retCode = SBL_SUCCESS;
|
|
bool bResponse = false;
|
|
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_PING)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Get response
|
|
//
|
|
if((retCode = getCmdResponse(bResponse)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
return (bResponse) ? SBL_SUCCESS : SBL_ERROR;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function gets status from device.
|
|
*
|
|
* \param[out] pStatus
|
|
* Pointer to where status is stored.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::readStatus(uint32_t *pui32Status)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
uint32_t retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_GET_STATUS)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
if(!bSuccess)
|
|
{
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Receive command response data
|
|
//
|
|
char status = 0;
|
|
uint32_t ui32NumBytes = 1;
|
|
if((retCode = getResponseData(&status, ui32NumBytes)) != SBL_SUCCESS)
|
|
{
|
|
//
|
|
// Respond with NAK
|
|
//
|
|
sendCmdResponse(false);
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Respond with ACK
|
|
//
|
|
sendCmdResponse(true);
|
|
|
|
m_lastDeviceStatus = status;
|
|
*pui32Status = status;
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function reads device ID.
|
|
*
|
|
* \param[out] pui32DeviceId
|
|
* Pointer to where device ID is stored.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::readDeviceId(uint32_t *pui32DeviceId)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
int retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_GET_CHIP_ID)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Receive response data
|
|
//
|
|
char pId[4];
|
|
memset(pId, 0, 4);
|
|
uint32_t numBytes = 4;
|
|
if((retCode = getResponseData(pId, numBytes)) != SBL_SUCCESS)
|
|
{
|
|
//
|
|
// Respond with NAK
|
|
//
|
|
sendCmdResponse(false);
|
|
return retCode;
|
|
}
|
|
|
|
if(numBytes != 4)
|
|
{
|
|
//
|
|
// Respond with NAK
|
|
//
|
|
sendCmdResponse(false);
|
|
setState(SBL_ERROR, "Didn't receive 4 B.\n");
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Respond with ACK
|
|
//
|
|
sendCmdResponse(true);
|
|
|
|
//
|
|
// Store retrieved ID and report success
|
|
//
|
|
*pui32DeviceId = charArrayToUL(pId);
|
|
m_deviceId = *pui32DeviceId;
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function reads device FLASH size in bytes.
|
|
*
|
|
* \param[out] pui32FlashSize
|
|
* Pointer to where FLASH size is stored.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::readFlashSize(uint32_t *pui32FlashSize)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
uint32_t retCode = SBL_SUCCESS;
|
|
|
|
//
|
|
// Read CC2538 DIECFG0 (contains FLASH size information)
|
|
//
|
|
uint32_t addr = SBL_CC2538_DIECFG0;
|
|
uint32_t value;
|
|
if((retCode = readMemory32(addr, 1, &value)) != SBL_SUCCESS)
|
|
{
|
|
setState((tSblStatus)retCode, "Failed to read device FLASH size: %s", getLastError().c_str());
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Calculate FLASH size (FLASH size bits are at bits [6:4])
|
|
//
|
|
value = ((value >> 4) & 0x07);
|
|
switch(value)
|
|
{
|
|
case 1: *pui32FlashSize = 0x20000; break; // 128 KB
|
|
case 2: *pui32FlashSize = 0x40000; break; // 256 KB
|
|
case 3: *pui32FlashSize = 0x60000; break; // 384 KB
|
|
case 4: *pui32FlashSize = 0x80000; break; // 512 KB
|
|
case 0: // 64 KB
|
|
default:*pui32FlashSize = 0x10000; break; // All invalid values are interpreted as 64 KB
|
|
}
|
|
|
|
m_flashSize = *pui32FlashSize;
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function reads device RAM size in bytes.
|
|
*
|
|
* \param[out] pui32RamSize
|
|
* Pointer to where RAM size is stored.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::readRamSize(uint32_t *pui32RamSize)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
int retCode = SBL_SUCCESS;
|
|
|
|
//
|
|
// Read CC2538 DIECFG0 (contains RAM size information
|
|
//
|
|
uint32_t addr = SBL_CC2538_DIECFG0;
|
|
uint32_t value;
|
|
if((retCode = readMemory32(addr, 1, &value)) != SBL_SUCCESS)
|
|
{
|
|
setState(retCode, "Failed to read device RAM size: %s", getLastError().c_str());
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Calculate RAM size in bytes (Ram size bits are at bits [9:7])
|
|
//
|
|
value = ((value >> 7) & 0x07);
|
|
switch(value)
|
|
{
|
|
case 4: *pui32RamSize = 0x8000; break; // 32 KB
|
|
case 0: *pui32RamSize = 0x4000; break; // 16 KB
|
|
case 1: *pui32RamSize = 0x2000; break; // 8 KB
|
|
default:*pui32RamSize = 0x2000; break; // All invalid values are interpreted as 8 KB
|
|
}
|
|
|
|
m_ramSize = *pui32RamSize;
|
|
|
|
return retCode;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function makes the device run from the address given by
|
|
* \u ui32Address, transferring execution control away from the
|
|
* bootloader. No further bootloader access will be possible.
|
|
*
|
|
* \parameter[in] ui32Address
|
|
* The device address where execution will be transferred to.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::run(uint32_t ui32Address)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
int retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Generate payload
|
|
// - 4B address
|
|
//
|
|
char pcPayload[4];
|
|
ulToCharArray(ui32Address, &pcPayload[0]);
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_RUN, pcPayload, 4)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
setState(SBL_ERROR, "Run command NAKed by device.\n");
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
m_bCommInitialized = false;
|
|
return SBL_SUCCESS;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function reset the device. Communication to the device must be
|
|
* reinitialized after calling this function.
|
|
*
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::reset()
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
int retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_RESET)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
setState(SBL_ERROR, "Reset command NAKed by device.\n");
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
m_bCommInitialized = false;
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function erases device flash pages. Starting page is the page
|
|
* that includes the address in \e startAddress. Ending page is the page
|
|
* that includes the address <startAddress + byteCount>. CC2538 erase
|
|
* size is 2KB.
|
|
*
|
|
* \param[in] ui32StartAddress
|
|
* The start address in flash.
|
|
* \param[in] ui32ByteCount
|
|
* The number of bytes to erase.
|
|
*
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::eraseFlashRange(uint32_t ui32StartAddress,
|
|
uint32_t ui32ByteCount)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
uint32_t retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
char pcPayload[8];
|
|
uint32_t devStatus;
|
|
|
|
//
|
|
// Initial check
|
|
//
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Calculate retry count
|
|
//
|
|
uint32_t ui32PageCount = ui32ByteCount / SBL_CC2538_PAGE_ERASE_SIZE;
|
|
if( ui32ByteCount % SBL_CC2538_PAGE_ERASE_SIZE) ui32PageCount ++;
|
|
uint32_t ui32TryCount = (((ui32PageCount * SBL_CC2538_PAGE_ERASE_TIME_MS) / \
|
|
SBL_DEFAULT_READ_TIMEOUT) + 1);
|
|
|
|
//
|
|
// Build payload
|
|
// - 4B address (MSB first)
|
|
// - 4B byte count (MSB first)
|
|
//
|
|
ulToCharArray(ui32StartAddress, &pcPayload[0]);
|
|
ulToCharArray(ui32ByteCount, &pcPayload[4]);
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(0);
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_ERASE, pcPayload, 8)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess, ui32TryCount)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Check device status (Flash failed if page(s) locked)
|
|
//
|
|
readStatus(&devStatus);
|
|
if(devStatus != SblDeviceCC2538::CMD_RET_SUCCESS)
|
|
{
|
|
setState(SBL_ERROR, "Flash erase failed. (Status 0x%02X = '%s'). Flash pages may be locked.\n", devStatus, getCmdStatusString(devStatus).c_str());
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(100);
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function reads \e ui32UnitCount (32 bit) words of data from
|
|
* device. Destination array is 32 bit wide. The start address must be 4
|
|
* byte aligned.
|
|
*
|
|
* \param[in] ui32StartAddress
|
|
* Start address in device (must be 4 byte aligned).
|
|
* \param[in] ui32UnitCount
|
|
* Number of data words to read.
|
|
* \param[out] pcData
|
|
* Pointer to where read data is stored.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::readMemory32(uint32_t ui32StartAddress, uint32_t ui32UnitCount,
|
|
uint32_t *pui32Data)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
int retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
|
|
//
|
|
// Check input arguments
|
|
//
|
|
if((ui32StartAddress & 0x03))
|
|
{
|
|
setState(SBL_ARGUMENT_ERROR, "readMemory32(): Start address (0x%08X) must be a multiple of 4.\n", ui32StartAddress);
|
|
return SBL_ARGUMENT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(0);
|
|
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
char pcPayload[5];
|
|
uint32_t recvCount = 0;
|
|
for(uint32_t i = 0; i < ui32UnitCount; i++)
|
|
{
|
|
//
|
|
// Build payload
|
|
// - 4B address (MSB first)
|
|
// - 1B access width
|
|
//
|
|
ulToCharArray((ui32StartAddress + (i*4)), &pcPayload[0]);
|
|
pcPayload[4] = SBL_CC2538_ACCESS_WIDTH_4B;
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress( ((100 * i)/ui32UnitCount) );
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_MEMORY_READ, pcPayload, 5)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Receive 4B response
|
|
//
|
|
recvCount = 4;
|
|
if((retCode = getResponseData(pcPayload, recvCount)) != SBL_SUCCESS)
|
|
{
|
|
//
|
|
// Respond with NAK
|
|
//
|
|
sendCmdResponse(false);
|
|
return retCode;
|
|
}
|
|
if(recvCount != 4)
|
|
{
|
|
//
|
|
// Respond with NAK
|
|
//
|
|
sendCmdResponse(false);
|
|
setState(SBL_ERROR, "Didn't receive 4 B.\n");
|
|
return SBL_ERROR;
|
|
}
|
|
pui32Data[i] = charArrayToUL(pcPayload);
|
|
|
|
//
|
|
// Respond with ACK
|
|
//
|
|
sendCmdResponse(true);
|
|
}
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(100);
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function reads \e unitCount bytes of data from device.
|
|
* Destination array is 8 bit wide.
|
|
*
|
|
* \param[in] ui32StartAddress
|
|
* Start address in device.
|
|
* \param[in] ui32UnitCount
|
|
* Number of bytes to read.
|
|
* \param[out] pcData
|
|
* Pointer to where read data is stored.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::readMemory8(uint32_t ui32StartAddress, uint32_t ui32UnitCount,
|
|
char *pcData)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
int retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
|
|
//
|
|
// Using 4B access width. Expanding byte count to include bytes at start
|
|
// and end of sequence.
|
|
//
|
|
uint32_t begCnt = ui32StartAddress % 4;
|
|
uint32_t endCnt = (ui32StartAddress + ui32UnitCount) % 4;
|
|
if(endCnt) { endCnt = 4 - endCnt; }
|
|
uint32_t totByteCnt = begCnt + ui32UnitCount + endCnt;
|
|
|
|
|
|
//
|
|
// Word align start address
|
|
//
|
|
ui32StartAddress -= begCnt;
|
|
|
|
//
|
|
// Create temporary vector with enough space.
|
|
//
|
|
std::vector<char> tmpBuf (totByteCnt);
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(0);
|
|
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
char pcPayload[5];
|
|
uint32_t recvCount = 0;
|
|
for(uint32_t i = 0; i < totByteCnt/4; i++)
|
|
{
|
|
//
|
|
// Build payload
|
|
// - 4B address (MSB first)
|
|
// - 1B access width
|
|
//
|
|
ulToCharArray((ui32StartAddress + (i*4)), &pcPayload[0]);
|
|
pcPayload[4] = SBL_CC2538_ACCESS_WIDTH_4B;
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress( ((100 * i)/(totByteCnt/4)) );
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_MEMORY_READ, pcPayload, 5)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Receive 4B response
|
|
//
|
|
recvCount = 4;
|
|
if((retCode = getResponseData(pcPayload, recvCount)) != SBL_SUCCESS)
|
|
{
|
|
//
|
|
// Respond with NAK
|
|
//
|
|
sendCmdResponse(false);
|
|
return retCode;
|
|
}
|
|
if(recvCount != 4)
|
|
{
|
|
//
|
|
// Respond with NAK
|
|
//
|
|
sendCmdResponse(false);
|
|
setState(SBL_ERROR, "Didn't receive 4 B.\n");
|
|
return SBL_ERROR;
|
|
}
|
|
byteSwap(pcPayload);
|
|
memcpy(&tmpBuf[4*i], pcPayload, 4);
|
|
|
|
//
|
|
// Respond with ACK
|
|
//
|
|
sendCmdResponse(true);
|
|
}
|
|
|
|
//
|
|
// Copy wanted data to pcData
|
|
//
|
|
for(uint32_t i = 0; i < ui32UnitCount; i++)
|
|
{
|
|
pcData[i] = tmpBuf.at(i + begCnt);
|
|
}
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(100);
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function writes \e unitCount words of data to device SRAM.
|
|
* Source array is 32 bit wide. \e ui32StartAddress must be 4 byte
|
|
* aligned.
|
|
*
|
|
* \param[in] ui32StartAddress
|
|
* Start address in device.
|
|
* \param[in] ui32UnitCount
|
|
* Number of data words (32bit) to write.
|
|
* \param[in] pui32Data
|
|
* Pointer to source data.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::writeMemory32(uint32_t ui32StartAddress,
|
|
uint32_t ui32UnitCount,
|
|
const uint32_t *pui32Data)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
uint32_t retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
char pcPayload[9];
|
|
uint32_t recvCount = 0;
|
|
uint32_t currAddr;
|
|
|
|
//
|
|
// Check input arguments
|
|
//
|
|
if(!addressInRam(ui32StartAddress, ui32UnitCount*4))
|
|
{
|
|
setState(SBL_ARGUMENT_ERROR, "writeMemory32(): Address range (0x%08X + %d bytes) is not in device RAM.\n", ui32StartAddress, ui32UnitCount*4);
|
|
return SBL_ARGUMENT_ERROR;
|
|
}
|
|
if((ui32StartAddress & 0x03))
|
|
{
|
|
setState(SBL_ARGUMENT_ERROR, "writeMemory32(): Start address (0x%08X) must be a multiple of 4.\n", ui32StartAddress);
|
|
return SBL_ARGUMENT_ERROR;
|
|
}
|
|
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(0);
|
|
|
|
for(uint32_t i = 0; i < ui32UnitCount; i++)
|
|
{
|
|
currAddr = ui32StartAddress + (i * 4);
|
|
//
|
|
// Build payload
|
|
// - 4B address (MSB first)
|
|
// - 4B data value (MSB first)
|
|
// - 1B access width
|
|
//
|
|
ulToCharArray(currAddr, &pcPayload[0]);
|
|
ulToCharArray(pui32Data[i], &pcPayload[4]);
|
|
pcPayload[8] = SBL_CC2538_ACCESS_WIDTH_4B;
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress( ((100 * i)/ui32UnitCount) );
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_MEMORY_WRITE, pcPayload, 9)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess, 5)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
setState(SBL_ERROR, "Device NAKed read command for address 0x%08X.\n", currAddr);
|
|
return SBL_ERROR;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(100);
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function writes \e unitCount words of data to device SRAM.
|
|
* Source array is 8 bit wide. Parameters \e startAddress and \e unitCount
|
|
* must be a a multiple of 4.
|
|
*
|
|
* \param[in] ui32StartAddress
|
|
* Start address in device.
|
|
* \param[in] ui32UnitCount
|
|
* Number of bytes to write.
|
|
* \param[in] pcData
|
|
* Pointer to source data.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::writeMemory8(uint32_t ui32StartAddress,
|
|
uint32_t ui32UnitCount,
|
|
const char *pcData)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
uint32_t retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
char pcPayload[9];
|
|
uint32_t recvCount = 0;
|
|
uint32_t currAddr;
|
|
|
|
//
|
|
// Check input arguments
|
|
//
|
|
if(!addressInRam(ui32StartAddress, ui32UnitCount))
|
|
{
|
|
setState(SBL_ARGUMENT_ERROR, "writeMemory8(): Address range (0x%08X + %d bytes) is not in device RAM.\n", ui32StartAddress, ui32UnitCount*4);
|
|
return SBL_ARGUMENT_ERROR;
|
|
}
|
|
if((ui32StartAddress & 0x03) || (ui32UnitCount & 0x03))
|
|
{
|
|
setState(SBL_ARGUMENT_ERROR, "writeMemory8(): Start address (0x%08X) and byte count (%d) must be a multiple of 4.\n", ui32StartAddress, ui32UnitCount);
|
|
return SBL_ARGUMENT_ERROR;
|
|
}
|
|
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(0);
|
|
|
|
for(uint32_t i = 0; i < ui32UnitCount/4; i++)
|
|
{
|
|
currAddr = ui32StartAddress + (i * 4);
|
|
//
|
|
// Build payload
|
|
// - 4B address (MSB first)
|
|
// - 4B data value (MSB first)
|
|
// - 1B access width
|
|
//
|
|
ulToCharArray(currAddr, &pcPayload[0]);
|
|
memcpy(&pcPayload[4], &pcData[(i * 4)], 4);
|
|
pcPayload[8] = SBL_CC2538_ACCESS_WIDTH_4B;
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress( ((100 * i) / (ui32UnitCount / 4)) );
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_MEMORY_WRITE, pcPayload, 9)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess, 5)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
setState(SBL_ERROR, "Device NAKed read command for address 0x%08X.\n", currAddr);
|
|
return SBL_ERROR;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(100);
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function calculates CRC over \e byteCount bytes on the device,
|
|
* starting at address \e startAddress.
|
|
*
|
|
* \param[in] ui32StartAddress
|
|
* Start address in device.
|
|
* \param[in] ui32ByteCount
|
|
* Number of bytes to calculate CRC32 over.
|
|
* \param[out] pui32Crc
|
|
* Pointer to where checksum from device is stored.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::calculateCrc32(uint32_t ui32StartAddress,
|
|
uint32_t ui32ByteCount, uint32_t *pui32Crc)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
uint32_t retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
char pcPayload[8];
|
|
uint32_t ui32RecvCount = 0;
|
|
|
|
//
|
|
// Check input arguments
|
|
//
|
|
if(!addressInFlash(ui32StartAddress, ui32ByteCount) &&
|
|
!addressInRam(ui32StartAddress, ui32ByteCount))
|
|
{
|
|
setState(SBL_ARGUMENT_ERROR, "Specified address range (0x%08X + %d bytes) is not in device FLASH nor RAM.\n", ui32StartAddress, ui32ByteCount);
|
|
return SBL_ARGUMENT_ERROR;
|
|
}
|
|
|
|
if(!isConnected())
|
|
{
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(0);
|
|
|
|
//
|
|
// Build payload
|
|
// - 4B address (MSB first)
|
|
// - 4B byte count(MSB first)
|
|
//
|
|
ulToCharArray(ui32StartAddress, &pcPayload[0]);
|
|
ulToCharArray(ui32ByteCount, &pcPayload[4]);
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_CRC32, pcPayload, 8)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess, 5)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
setState(SBL_ERROR, "Device NAKed CRC32 command.\n");
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Get data response
|
|
//
|
|
ui32RecvCount = 4;
|
|
if((retCode = getResponseData(pcPayload, ui32RecvCount)) != SBL_SUCCESS)
|
|
{
|
|
sendCmdResponse(false);
|
|
return retCode;
|
|
}
|
|
*pui32Crc = charArrayToUL(pcPayload);
|
|
|
|
//
|
|
// Send ACK/NAK to command
|
|
//
|
|
bool bAck = (ui32RecvCount == 4) ? true : false;
|
|
sendCmdResponse(bAck);
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
setProgress(100);
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function writes \e unitCount words of data to device FLASH.
|
|
* Source array is 8 bit wide. Parameters \e startAddress and \e unitCount
|
|
* must be a a multiple of 4. This function does not erase the flash
|
|
* before writing data, this must be done using e.g. eraseFlashRange().
|
|
*
|
|
* \param[in] ui32StartAddress
|
|
* Start address in device. Must be a multiple of 4.
|
|
* \param[in] ui32ByteCount
|
|
* Number of bytes to program. Must be a multiple of 4.
|
|
* \param[in] pcData
|
|
* Pointer to source data.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::writeFlashRange(uint32_t ui32StartAddress,
|
|
uint32_t ui32ByteCount, const char *pcData)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
uint32_t devStatus = SblDeviceCC2538::CMD_RET_UNKNOWN_CMD;
|
|
uint32_t retCode = SBL_SUCCESS;
|
|
uint32_t bytesLeft, dataIdx, bytesInTransfer;
|
|
uint32_t transferNumber = 1;
|
|
bool bIsRetry = false;
|
|
bool bBlToBeDisabled = false;
|
|
std::vector<tTransfer> pvTransfer;
|
|
uint32_t ui32TotChunks = (ui32ByteCount / SBL_CC2538_MAX_BYTES_PER_TRANSFER);
|
|
if(ui32ByteCount % SBL_CC2538_MAX_BYTES_PER_TRANSFER) ui32TotChunks++;
|
|
uint32_t ui32CurrChunk = 0;
|
|
|
|
//
|
|
// Calculate BL configuration address (depends on flash size)
|
|
//
|
|
uint32_t ui32BlCfgAddr = getBootloaderEnableAddress();
|
|
|
|
//
|
|
// Calculate BL configuration buffer index
|
|
//
|
|
uint32_t ui32BlCfgDataIdx = ui32BlCfgAddr - ui32StartAddress;
|
|
|
|
//
|
|
// Is BL configuration part of buffer?
|
|
//
|
|
if(ui32BlCfgDataIdx <= ui32ByteCount)
|
|
{
|
|
if((pcData[ui32BlCfgDataIdx] & SBL_CC2538_BL_CONFIG_ENABLED_BM) == 0)
|
|
{
|
|
bBlToBeDisabled = true;
|
|
setState(SBL_SUCCESS, "Warning: Bootloader will be disabled.\n");
|
|
}
|
|
}
|
|
|
|
if(bBlToBeDisabled)
|
|
{
|
|
//
|
|
// Split into two transfers
|
|
//
|
|
pvTransfer.resize(2);
|
|
|
|
//
|
|
// Main transfer (before lock bit)
|
|
//
|
|
pvTransfer[0].bExpectAck = true;
|
|
pvTransfer[0].startAddr = ui32StartAddress;
|
|
pvTransfer[0].byteCount = (ui32BlCfgAddr - ui32StartAddress) & (~0x03);
|
|
pvTransfer[0].startOffset = 0;
|
|
|
|
//
|
|
// The transfer locking the backdoor
|
|
//
|
|
pvTransfer[1].bExpectAck = false;
|
|
pvTransfer[1].startAddr = ui32BlCfgAddr - (ui32BlCfgAddr % 4);
|
|
pvTransfer[1].byteCount = ui32ByteCount - pvTransfer[0].byteCount;
|
|
pvTransfer[1].startOffset = ui32BlCfgDataIdx - (ui32BlCfgDataIdx % 4);
|
|
|
|
}
|
|
else
|
|
{
|
|
pvTransfer.resize(1);
|
|
pvTransfer[0].bExpectAck = true;
|
|
pvTransfer[0].byteCount = ui32ByteCount;
|
|
pvTransfer[0].startAddr = ui32StartAddress;
|
|
pvTransfer[0].startOffset = 0;
|
|
}
|
|
|
|
//
|
|
// For each transfer
|
|
//
|
|
for(uint32_t i = 0; i < pvTransfer.size(); i++)
|
|
{
|
|
//
|
|
// Sanity check
|
|
//
|
|
if(pvTransfer[i].byteCount == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Set progress
|
|
//
|
|
//setProgress(addressToPage(pvTransfer[i].startAddr));
|
|
|
|
//
|
|
// Send download command
|
|
//
|
|
if((retCode = cmdDownload(pvTransfer[i].startAddr,
|
|
pvTransfer[i].byteCount)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Check status after download command
|
|
//
|
|
retCode = readStatus(&devStatus);
|
|
if(retCode != SBL_SUCCESS)
|
|
{
|
|
setState(retCode, "Error during download initialization. Failed to read device status after sending download command.\n");
|
|
return retCode;
|
|
}
|
|
if(devStatus != SblDeviceCC2538::CMD_RET_SUCCESS)
|
|
{
|
|
setState(SBL_ERROR, "Error during download initialization. Device returned status %d (%s).\n", devStatus, getCmdStatusString(devStatus).c_str());
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Send data in chunks
|
|
//
|
|
bytesLeft = pvTransfer[i].byteCount;
|
|
dataIdx = pvTransfer[i].startOffset;
|
|
while(bytesLeft)
|
|
{
|
|
//
|
|
// Set progress
|
|
//
|
|
//setProgress(addressToPage(ui32StartAddress + dataIdx));
|
|
setProgress( ((100*(++ui32CurrChunk))/ui32TotChunks) );
|
|
|
|
//
|
|
// Limit transfer count
|
|
//
|
|
#if 0
|
|
bytesInTransfer = min(SBL_CC2538_MAX_BYTES_PER_TRANSFER, bytesLeft);
|
|
#else
|
|
if(bytesLeft > SBL_CC2538_MAX_BYTES_PER_TRANSFER)
|
|
{
|
|
bytesInTransfer = SBL_CC2538_MAX_BYTES_PER_TRANSFER;
|
|
} else {
|
|
bytesInTransfer = bytesLeft;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Send Data command
|
|
//
|
|
if(retCode = cmdSendData(&pcData[dataIdx], bytesInTransfer) != SBL_SUCCESS)
|
|
{
|
|
setState(retCode, "Error during flash download. \n- Start address 0x%08X (page %d). \n- Tried to transfer %d bytes. \n- This was transfer %d.\n",
|
|
(ui32StartAddress+dataIdx),
|
|
addressToPage(ui32StartAddress+dataIdx),
|
|
bytesInTransfer,
|
|
(transferNumber));
|
|
return retCode;
|
|
}
|
|
|
|
if(pvTransfer[i].bExpectAck)
|
|
{
|
|
//
|
|
// Check status after send data command
|
|
//
|
|
devStatus = 0;
|
|
retCode = readStatus(&devStatus);
|
|
if(retCode != SBL_SUCCESS)
|
|
{
|
|
setState(retCode, "Error during flash download. Failed to read device status.\n- Start address 0x%08X (page %d). \n- Tried to transfer %d bytes. \n- This was transfer %d in chunk %d.\n",
|
|
(ui32StartAddress+dataIdx),
|
|
addressToPage(ui32StartAddress + dataIdx),
|
|
(bytesInTransfer), (transferNumber),
|
|
(i));
|
|
return retCode;
|
|
}
|
|
if(devStatus != SblDeviceCC2538::CMD_RET_SUCCESS)
|
|
{
|
|
setState(SBL_SUCCESS, "Device returned status %s\n", getCmdStatusString(devStatus).c_str());
|
|
if(bIsRetry)
|
|
{
|
|
//
|
|
// We have failed a second time. Aborting.
|
|
setState(SBL_ERROR, "Error retrying flash download.\n- Start address 0x%08X (page %d). \n- Tried to transfer %d bytes. \n- This was transfer %d in chunk %d.\n",
|
|
(ui32StartAddress+dataIdx),
|
|
addressToPage(ui32StartAddress + dataIdx),
|
|
(bytesInTransfer), (transferNumber),
|
|
(i));
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Retry to send data one more time.
|
|
//
|
|
bIsRetry = true;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We're locking device and will lose access
|
|
//
|
|
m_bCommInitialized = false;
|
|
|
|
}
|
|
|
|
//
|
|
// Update index and bytesLeft
|
|
//
|
|
bytesLeft -= bytesInTransfer;
|
|
dataIdx += bytesInTransfer;
|
|
transferNumber++;
|
|
bIsRetry = false;
|
|
}
|
|
}
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function sends the specified bootloader command.
|
|
*
|
|
* \param[in] ui32Cmd
|
|
* The command to send.
|
|
* \param[in] pcSendData
|
|
* Pointer to the data to send with the command.
|
|
* \param[in] ui32SendLen
|
|
* The number of bytes to send from \e pcSendData.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::sendCmd(uint32_t ui32Cmd, const char *pcSendData/* = NULL*/,
|
|
uint32_t ui32SendLen/* = 0*/)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
unsigned char pktLen = ui32SendLen + 3; // +3 => <1b Length>, <1B checksum>, <1B cmd>
|
|
std::vector<char> pvPkt((pktLen));
|
|
unsigned char pktSum = generateCheckSum(ui32Cmd, pcSendData, ui32SendLen);
|
|
|
|
//
|
|
// Build packet
|
|
//
|
|
pvPkt.at(0) = pktLen;
|
|
pvPkt.at(1) = pktSum;
|
|
pvPkt.at(2) = (unsigned char)ui32Cmd;
|
|
if(ui32SendLen)
|
|
{
|
|
memcpy(&pvPkt[3], pcSendData, ui32SendLen);
|
|
}
|
|
|
|
//
|
|
// Send packet
|
|
//
|
|
if(m_pCom->writeBytes(&pvPkt[0], pvPkt.size()) < 1)
|
|
{
|
|
setState(SBL_PORT_ERROR, "Writing to device failed (Command '%s').\n", getCmdString(ui32Cmd).c_str());
|
|
return SBL_PORT_ERROR;
|
|
}
|
|
|
|
|
|
//
|
|
// Empty and deallocate vector
|
|
//
|
|
pvPkt.clear();
|
|
std::vector<char>().swap(pvPkt);
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function initializes connection to the CC2538 device.
|
|
*
|
|
* \param[in] bSetXosc
|
|
* If true, try to enable device XOSC.
|
|
* \return
|
|
* Returns SBL_SUCCESS, ...
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::initCommunication(bool bSetXosc)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
bool bSuccess, bBaudSetOk;
|
|
int retCode = SBL_ERROR;
|
|
|
|
//
|
|
// Send dummy command to see if device is already initialized at
|
|
// this baud rate.
|
|
//
|
|
if(sendCmd(0) != SBL_SUCCESS)
|
|
{
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Do we get a response (ACK/NAK)?
|
|
//
|
|
bSuccess = false;
|
|
if(getCmdResponse(bSuccess, SBL_DEFAULT_RETRY_COUNT, true) != SBL_SUCCESS)
|
|
{
|
|
//
|
|
// No response received. Try auto baud
|
|
//
|
|
if(retCode = sendAutoBaud(bBaudSetOk) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
}
|
|
|
|
if(bSetXosc)
|
|
{
|
|
setState(SBL_SUCCESS, "Trying to set device XOSC.\n");
|
|
//
|
|
// Try to enable XOSC
|
|
//
|
|
if((retCode = setXosc()) != SBL_SUCCESS)
|
|
{
|
|
//
|
|
// setXosc returned error
|
|
//
|
|
setState(retCode, "Failed to activate device XOSC.\n");
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Send dummy command
|
|
//
|
|
bSuccess = false;
|
|
sendCmd(0);
|
|
if(retCode = getCmdResponse(bSuccess, SBL_DEFAULT_RETRY_COUNT, true) != SBL_SUCCESS)
|
|
{
|
|
//
|
|
// Send auto baud again
|
|
//
|
|
if((retCode = sendAutoBaud(bBaudSetOk)) != SBL_SUCCESS)
|
|
{
|
|
setState((tSblStatus)retCode, "Auto baud detection failed after starting XOSC.\n");
|
|
return retCode;
|
|
}
|
|
setState(SBL_SUCCESS, "Device XOSC activated.\n");
|
|
}
|
|
}
|
|
|
|
m_bCommInitialized = true;
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function returns a string representation of the \e ui32Cmd
|
|
* command.
|
|
*
|
|
* \param[out] ui32Cmd
|
|
* The serial bootloader command.
|
|
* \return
|
|
* Returns std::string with name of device command.
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
std::string
|
|
SblDeviceCC2538::getCmdString(uint32_t ui32Cmd)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
switch(ui32Cmd)
|
|
{
|
|
case SblDeviceCC2538::CMD_PING: return "CMD_PING"; break;
|
|
case SblDeviceCC2538::CMD_CRC32: return "CMD_CRC32"; break;
|
|
case SblDeviceCC2538::CMD_DOWNLOAD: return "CMD_DOWNLOAD"; break;
|
|
case SblDeviceCC2538::CMD_ERASE: return "CMD_ERASE"; break;
|
|
case SblDeviceCC2538::CMD_GET_CHIP_ID: return "CMD_GET_CHIP_ID"; break;
|
|
case SblDeviceCC2538::CMD_GET_STATUS: return "CMD_GET_STATUS"; break;
|
|
case SblDeviceCC2538::CMD_MEMORY_READ: return "CMD_MEMORY_READ"; break;
|
|
case SblDeviceCC2538::CMD_MEMORY_WRITE: return "CMD_MEMORY_WRITE"; break;
|
|
case SblDeviceCC2538::CMD_RESET: return "CMD_RESET"; break;
|
|
default: return "Unknown command"; break;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function returns a string representation of the
|
|
* \e ui32Status serial bootloader status value.
|
|
*
|
|
* \param[out] ui32Status
|
|
* The serial bootloader status value.
|
|
* \return
|
|
* Returns std::string of device status.
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
std::string
|
|
SblDeviceCC2538::getCmdStatusString(uint32_t ui32Status)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
switch(ui32Status)
|
|
{
|
|
case SblDeviceCC2538::CMD_RET_FLASH_FAIL: return "FLASH_FAIL"; break;
|
|
case SblDeviceCC2538::CMD_RET_INVALID_ADR: return "INVALID_ADR"; break;
|
|
case SblDeviceCC2538::CMD_RET_INVALID_CMD: return "INVALID_CMD"; break;
|
|
case SblDeviceCC2538::CMD_RET_SUCCESS: return "SUCCESS"; break;
|
|
case SblDeviceCC2538::CMD_RET_UNKNOWN_CMD: return "UNKNOWN_CMD"; break;
|
|
default: return "Unknown status"; break;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function sends the CC2538 download command and handles the
|
|
* device response. \e ui32ByteCount must be a multiple of 4.
|
|
*
|
|
* \param[in] ui32Address
|
|
* The start address in CC2538 flash.
|
|
* \param[in] ui32ByteCount
|
|
* The total number of bytes to program on the device.
|
|
*
|
|
* \return
|
|
* Returns SBL_SUCCESS if command and response was successful.
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::cmdDownload(uint32_t ui32Address, uint32_t ui32Size)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
int retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
|
|
|
|
//
|
|
// Check input arguments
|
|
//
|
|
if(!addressInFlash(ui32Address, ui32Size))
|
|
{
|
|
setState(SBL_ARGUMENT_ERROR, "Flash download: Address range (0x%08X + %d bytes) is not in device FLASH nor RAM.\n", ui32Address, ui32Size);
|
|
return SBL_ARGUMENT_ERROR;
|
|
}
|
|
if(ui32Size & 0x03)
|
|
{
|
|
setState(SBL_ARGUMENT_ERROR, "Flash download: Byte count must be a multiple of 4\n");
|
|
return SBL_ARGUMENT_ERROR;
|
|
}
|
|
|
|
//
|
|
// Generate payload
|
|
// - 4B Program address
|
|
// - 4B Program size
|
|
//
|
|
char pcPayload[8];
|
|
ulToCharArray(ui32Address, &pcPayload[0]);
|
|
ulToCharArray(ui32Size, &pcPayload[4]);
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_DOWNLOAD, pcPayload, 8)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Return command response
|
|
//
|
|
return (bSuccess) ? SBL_SUCCESS : SBL_ERROR;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function sends the CC2538 SendData command and handles the
|
|
* device response. \e ui32ByteCount is limited by
|
|
* SBL_CC2538_MAX_BYTES_PER_TRANSFER.
|
|
*
|
|
* \param[in] pcData
|
|
* Pointer to the data to send.
|
|
* \param[in] ui32ByteCount
|
|
* The number of bytes to send.
|
|
*
|
|
* \return
|
|
* Returns SBL_SUCCESS if command and response was successful.
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::cmdSendData(const char *pcData, uint32_t ui32ByteCount)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
uint32_t retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
|
|
//
|
|
// Check input arguments
|
|
//
|
|
if(ui32ByteCount > SBL_CC2538_MAX_BYTES_PER_TRANSFER)
|
|
{
|
|
setState(SBL_ERROR, "Error: Byte count (%d) exceeds maximum transfer size %d.\n", ui32ByteCount, SBL_CC2538_MAX_BYTES_PER_TRANSFER);
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_SEND_DATA, pcData, ui32ByteCount)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess, 3)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function returns the FLASH address of the bootloader enable
|
|
* configuration.
|
|
*
|
|
* \return
|
|
* Returns true if the address/range is within the device RAM.
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t SblDeviceCC2538::getBootloaderEnableAddress()
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
return (SBL_CC2538_FLASH_START_ADDRESS + getFlashSize() - getPageEraseSize() + SBL_CC2538_BL_CONFIG_PAGE_OFFSET);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function sends the CC2538 SetXosc command and handles the
|
|
* response from the device.
|
|
*
|
|
* \return
|
|
* Returns SBL_SUCCESS if command and response was successful.
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::setXosc()
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
int retCode = SBL_SUCCESS;
|
|
bool bSuccess = false;
|
|
|
|
//
|
|
// Send command
|
|
//
|
|
if((retCode = sendCmd(SblDeviceCC2538::CMD_SET_XOSC)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
//
|
|
// Receive command response (ACK/NAK)
|
|
//
|
|
if((retCode = getCmdResponse(bSuccess, 5, true)) != SBL_SUCCESS)
|
|
{
|
|
return retCode;
|
|
}
|
|
if(!bSuccess)
|
|
{
|
|
return SBL_ERROR;
|
|
}
|
|
|
|
return SBL_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function returns the address within which the specified
|
|
* \e ui32Address is located.
|
|
*
|
|
* \return
|
|
* Returns the flash page within which an address is located.
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
uint32_t
|
|
SblDeviceCC2538::addressToPage(uint32_t ui32Address)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
return ((ui32Address - SBL_CC2538_FLASH_START_ADDRESS) / \
|
|
SBL_CC2538_PAGE_ERASE_SIZE);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function checks if the specified \e ui32StartAddress (or range)
|
|
* is located within the device RAM area.
|
|
*
|
|
* \return
|
|
* Returns true if the address/range is within the device RAM.
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
bool
|
|
SblDeviceCC2538::addressInRam(uint32_t ui32StartAddress,
|
|
uint32_t ui32ByteCount/* = 1*/)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
uint32_t ui32EndAddr = ui32StartAddress + ui32ByteCount;
|
|
|
|
if(ui32StartAddress < SBL_CC2538_RAM_START_ADDRESS)
|
|
{
|
|
return false;
|
|
}
|
|
if(ui32EndAddr > (SBL_CC2538_RAM_START_ADDRESS + getRamSize()))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** \brief This function checks if the specified \e ui32StartAddress (or range)
|
|
* is located within the device FLASH area.
|
|
*
|
|
* \return
|
|
* Returns true if the address/range is within the device FLASH.
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
bool
|
|
SblDeviceCC2538::addressInFlash(uint32_t ui32StartAddress,
|
|
uint32_t ui32ByteCount/* = 1*/)
|
|
{
|
|
DEBUG_PRINT("\n");
|
|
uint32_t ui32EndAddr = ui32StartAddress + ui32ByteCount;
|
|
|
|
if(ui32StartAddress < SBL_CC2538_FLASH_START_ADDRESS)
|
|
{
|
|
return false;
|
|
}
|
|
if(ui32EndAddr > (SBL_CC2538_FLASH_START_ADDRESS + getFlashSize()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|