openblt/Host/Source/LibOpenBLT/xcploader.c

1403 lines
50 KiB
C

/************************************************************************************//**
* \file xcploader.c
* \brief XCP Loader module source file.
* \ingroup XcpLoader
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include <assert.h> /* for assertions */
#include <stdint.h> /* for standard integer types */
#include <stddef.h> /* for NULL declaration */
#include <stdbool.h> /* for boolean type */
#include <stdlib.h> /* for standard library */
#include <string.h> /* for string library */
#include "session.h" /* Communication session module */
#include "xcploader.h" /* XCP loader module */
#include "xcpprotect.h" /* XCP protection module */
/****************************************************************************************
* Macro definitions
****************************************************************************************/
/* XCP command codes as defined by the protocol currently supported by this module */
#define XCPLOADER_CMD_CONNECT (0xFFu) /**< XCP connect command code. */
#define XCPLOADER_CMD_GET_STATUS (0xFDu) /**< XCP get status command code. */
#define XCPLOADER_CMD_GET_SEED (0xF8u) /**< XCP get seed command code. */
#define XCPLOADER_CMD_UNLOCK (0xF7u) /**< XCP unlock command code. */
#define XCPLOADER_CMD_SET_MTA (0xF6u) /**< XCP set mta command code. */
#define XCPLOADER_CMD_UPLOAD (0xF5u) /**< XCP upload command code. */
#define XCPLOADER_CMD_PROGRAM_START (0xD2u) /**< XCP program start command code. */
#define XCPLOADER_CMD_PROGRAM_CLEAR (0xD1u) /**< XCP program clear command code. */
#define XCPLOADER_CMD_PROGRAM (0xD0u) /**< XCP program command code. */
#define XCPLOADER_CMD_PROGRAM_RESET (0xCFu) /**< XCP program reset command code. */
#define XCPLOADER_CMD_PROGRAM_MAX (0xC9u) /**< XCP program max command code. */
/* XCP response packet IDs as defined by the protocol. */
#define XCPLOADER_CMD_PID_RES (0xFFu) /**< positive response */
/** \brief Number of retries to connect to the XCP slave. */
#define XCPLOADER_CONNECT_RETRIES (5u)
/****************************************************************************************
* Function prototypes
****************************************************************************************/
/* Protocol functions for linking to the session module. */
static void XcpLoaderInit(void const * settings);
static void XcpLoaderTerminate(void);
static bool XcpLoaderStart(void);
static void XcpLoaderStop(void);
static bool XcpLoaderClearMemory(uint32_t address, uint32_t len);
static bool XcpLoaderWriteData(uint32_t address, uint32_t len, uint8_t const * data);
static bool XcpLoaderReadData(uint32_t address, uint32_t len, uint8_t * data);
/* General module specific utility functions. */
static void XcpLoaderSetOrderedLong(uint32_t value, uint8_t *data);
static uint16_t XcpLoaderGetOrderedWord(uint8_t const * data);
/* XCP command functions. */
static bool XcpLoaderSendCmdConnect(void);
static bool XcpLoaderSendCmdGetStatus(uint8_t * session, uint8_t * protectedResources,
uint16_t * configId);
static bool XcpLoaderSendCmdGetSeed(uint8_t resource, uint8_t mode, uint8_t * seed, uint8_t * seedLen);
static bool XcpLoaderSendCmdUnlock(uint8_t const * key, uint8_t keyLen,
uint8_t * protectedResources);
static bool XcpLoaderSendCmdSetMta(uint32_t address);
static bool XcpLoaderSendCmdUpload(uint8_t * data, uint8_t length);
static bool XcpLoaderSendCmdProgramStart(void);
static bool XcpLoaderSendCmdProgramReset(void);
static bool XcpLoaderSendCmdProgram(uint8_t length, uint8_t const * data);
static bool XcpLoaderSendCmdProgramMax(uint8_t const * data);
static bool XcpLoaderSendCmdProgramClear(uint32_t length);
/****************************************************************************************
* Local constant declarations
****************************************************************************************/
/** \brief Protocol structure filled with XCP loader specifics. */
static const tSessionProtocol xcpLoader =
{
.Init = XcpLoaderInit,
.Terminate = XcpLoaderTerminate,
.Start = XcpLoaderStart,
.Stop = XcpLoaderStop,
.ClearMemory = XcpLoaderClearMemory,
.WriteData = XcpLoaderWriteData,
.ReadData = XcpLoaderReadData
};
/****************************************************************************************
* Local data declarations
****************************************************************************************/
/** \brief The settings that should be used by the XCP loader. */
static tXcpLoaderSettings xcpSettings;
/** \brief Flag to keep track of the connection status. */
static bool xcpConnected;
/** \brief Store the byte ordering of the XCP slave. */
static bool xcpSlaveIsIntel;
/** \brief The max number of bytes in the command transmit object (master->slave). */
static uint8_t xcpMaxCto;
/** \brief The max number of bytes in the command transmit object (master->slave) during
* a programming session.
*/
static uint8_t xcpMaxProgCto;
/** \brief The max number of bytes in the data transmit object (slave->master). */
static uint16_t xcpMaxDto;
/***********************************************************************************//**
** \brief Obtains a pointer to the protocol structure, so that it can be linked to
** the communication session module.
** \return Pointer to protocol structure.
**
****************************************************************************************/
tSessionProtocol const * XcpLoaderGetProtocol(void)
{
return &xcpLoader;
} /*** end of XcpLoaderGetProtocol ***/
/************************************************************************************//**
** \brief Initializes the protocol module.
** \param settings Pointer to the structure with protocol settings.
**
****************************************************************************************/
static void XcpLoaderInit(void const * settings)
{
char * seedNKeyFileName;
/* Initialize locals. */
xcpConnected = false;
xcpSlaveIsIntel = false;
xcpMaxCto = 0;
xcpMaxProgCto = 0;
xcpMaxDto = 0;
/* Reset the XCP session layer settings. */
xcpSettings.timeoutT1 = 1000;
xcpSettings.timeoutT3 = 2000;
xcpSettings.timeoutT4 = 10000;
xcpSettings.timeoutT5 = 1000;
xcpSettings.timeoutT6 = 50;
xcpSettings.timeoutT7 = 2000;
xcpSettings.connectMode = 0;
xcpSettings.seedKeyFile = NULL;
xcpSettings.transport = NULL;
xcpSettings.transportSettings = NULL;
/* Check parameter. */
assert(settings != NULL);
/* Only continue with valid parameter. */
if (settings != NULL) /*lint !e774 */
{
/* shallow copy the XCP settings for later usage */
xcpSettings = *(tXcpLoaderSettings *)settings;
/* The seedKeyFile is a pointer and it is not guaranteed that it stays valid so we
* need to deep copy this one. note the +1 for '\0' in malloc. Note that it is okay
* for this value to be NULL.
*/
if (((tXcpLoaderSettings *)settings)->seedKeyFile != NULL) /*lint !e774 */
{
seedNKeyFileName = malloc(strlen(((tXcpLoaderSettings *)settings)->seedKeyFile) + 1);
assert(seedNKeyFileName != NULL);
if (seedNKeyFileName != NULL) /*lint !e774 */
{
strcpy(seedNKeyFileName, ((tXcpLoaderSettings *)settings)->seedKeyFile);
xcpSettings.seedKeyFile = seedNKeyFileName;
}
}
/* Check that a valid transport layer was specified. */
assert(xcpSettings.transport != NULL);
/* Only access the transport layer if it is valid. */
if (xcpSettings.transport != NULL) /*lint !e774 */
{
/* Initialize the transport layer. */
xcpSettings.transport->Init(xcpSettings.transportSettings);
}
/* Invalidate the transportSettings as it is probably no longer valid outside this
* function scope and should also not be used anymore.
*/
xcpSettings.transportSettings = NULL;
}
/* Initialize the XCP protection module. */
XcpProtectInit(xcpSettings.seedKeyFile);
} /*** end of XcpLoaderInit ***/
/************************************************************************************//**
** \brief Terminates the protocol module.
**
****************************************************************************************/
static void XcpLoaderTerminate(void)
{
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Terminate the XCP protection module. */
XcpProtectTerminate();
/* Only continue with a valid transport layer. */
if (xcpSettings.transport != NULL) /*lint !e774 */
{
/* Terminate the transport layer. */
xcpSettings.transport->Terminate();
/* Unlink the transport layer. */
xcpSettings.transport = NULL;
}
/* Release memory that was allocated for storing the seedKeyFile. */
if (xcpSettings.seedKeyFile != NULL)
{
free((char *)xcpSettings.seedKeyFile);
}
/* Reset the XCP session layer settings. */
xcpSettings.timeoutT1 = 1000;
xcpSettings.timeoutT3 = 2000;
xcpSettings.timeoutT4 = 10000;
xcpSettings.timeoutT5 = 1000;
xcpSettings.timeoutT6 = 50;
xcpSettings.timeoutT7 = 2000;
xcpSettings.connectMode = 0;
xcpSettings.seedKeyFile = NULL;
xcpSettings.transport = NULL;
xcpSettings.transportSettings = NULL;
} /*** end of XcpLoaderTerminate ***/
/************************************************************************************//**
** \brief Starts the firmware update session. This is where the connection with the
** target is made and the bootloader on the target is activated.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderStart(void)
{
bool result = false;
uint8_t retryCnt;
uint8_t protectedResources = 0;
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue with a valid transport layer. */
if (xcpSettings.transport != NULL) /*lint !e774 */
{
/* Make sure the session is stopped before starting a new one. */
XcpLoaderStop();
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* Connect the transport layer. */
if (!xcpSettings.transport->Connect())
{
/* Could not connect the transport layer. */
result = false;
}
/* Connect to the target with a finite amount of retries. */
if (result)
{
for (retryCnt=0; retryCnt<XCPLOADER_CONNECT_RETRIES; retryCnt++)
{
/* Send the connect command. */
if (XcpLoaderSendCmdConnect())
{
/* Update connection state. */
xcpConnected = true;
/* Connected so no need to retry. */
break;
}
}
/* Check if a connection with the target could be made within the finite amount
* of retries.
*/
if (!xcpConnected)
{
/* Disconnect the transport layer again. */
xcpSettings.transport->Disconnect();
/* Update the result. */
result = false;
}
}
/* Obtain the current resource protection status. */
if (result)
{
if (!XcpLoaderSendCmdGetStatus(NULL, &protectedResources, NULL))
{
result = false;
}
}
/* Check if the programming resource needs to be unlocked. */
if ( (protectedResources & XCPPROTECT_RESOURCE_PGM) != 0)
{
uint8_t availableResources = 0;
uint8_t seed[256] = { 0 };
uint8_t *seedPtr = &seed[0];
uint8_t seedTotalLen = 0;
uint8_t seedRemainingLen = 0;
uint8_t key[256] = { 0 };
uint8_t keyTotalLen = 0;
uint8_t keyRemainingLen = 0;
uint8_t keyCurrentLen = 0;
uint8_t *keyPtr = &key[0];
/* Make sure the XCP protection module contains an unlock algorithm for the
* programming resource.
*/
if (result)
{
if (!XcpProtectGetPrivileges(&availableResources))
{
/* Could not obtain the supported resource privileges from the XCP protection
* module.
*/
result = false;
}
else if ((availableResources & XCPPROTECT_RESOURCE_PGM) == 0)
{
/* No unlock algorithm available for the programming resource in the XCP
* protection module.
*/
result = false;
}
}
/* Request (first part of) the seed for unlocking the programming resources. */
if (result)
{
if (!XcpLoaderSendCmdGetSeed(XCPPROTECT_RESOURCE_PGM, 0, seedPtr, &seedRemainingLen))
{
result = false;
}
else
{
/* store the total seed length */
seedTotalLen = seedRemainingLen;
}
}
/* Check if more parts of the seed need to be requested. */
if (result)
{
while (seedRemainingLen > (xcpMaxDto - 2))
{
/* update the seed pointer for the next part */
seedPtr += (xcpMaxDto - 2);
if (!XcpLoaderSendCmdGetSeed(XCPPROTECT_RESOURCE_PGM, 1, seedPtr, &seedRemainingLen))
{
result = false;
break;
}
}
}
/* Only continue with resource unlock operation if not already unlocked, which
* is indicated by a seed length of 0.
*/
if ( (result) && (seedTotalLen > 0) )
{
/* Compute the key using the XCP protection module. */
if (!XCPProtectComputeKeyFromSeed(XCPPROTECT_RESOURCE_PGM, seedTotalLen, seed,
&keyTotalLen, key))
{
result = false;
}
/* Unlock the resource now that the key is available. */
if (result)
{
uint8_t currentlyProtectedResources = 0;
/* Initialize remaining length */
keyRemainingLen = keyTotalLen;
/* Send the key to unlock the resource */
while (keyRemainingLen > 0)
{
/* Determine how many key bytes are about to be sent. */
keyCurrentLen = keyRemainingLen;
if (keyCurrentLen > (xcpMaxCto - 2))
{
keyCurrentLen = (xcpMaxCto - 2);
}
/* The the (possible partial) unlock command. */
if (!XcpLoaderSendCmdUnlock(keyPtr, keyRemainingLen, &currentlyProtectedResources))
{
result = false;
break;
}
/* Update key pointer and the remaining length */
keyRemainingLen -= keyCurrentLen;
keyPtr += keyCurrentLen;
/* Check if the key was now completely sent. */
if (keyRemainingLen == 0)
{
/* Double-check that the programming resource is now unlocked. */
if ((currentlyProtectedResources & XCPPROTECT_RESOURCE_PGM) != 0)
{
/* Programming resource unlock operation failed. */
result = false;
}
}
}
}
}
}
/* Place the target in programming mode if connected. */
if (result)
{
if (!XcpLoaderSendCmdProgramStart())
{
result = false;
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderStart ***/
/************************************************************************************//**
** \brief Stops the firmware update. This is where the bootloader starts the user
** program on the target if a valid one is present. After this the connection
** with the target is severed.
**
****************************************************************************************/
static void XcpLoaderStop(void)
{
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue with a valid transport layer and if actually connected. */
if ( (xcpSettings.transport != NULL) && (xcpConnected) ) /*lint !e774 */
{
/* End the programming session by sending the program command with size 0. */
if (XcpLoaderSendCmdProgram(0, NULL))
{
/* Disconnect the target. Here the reset command is used instead of the disconnect
* command, because the bootloader should start the user program on the target.
*/
(void)XcpLoaderSendCmdProgramReset();
}
/* Disconnect the transport layer. */
xcpSettings.transport->Disconnect();
/* Reset connection status. */
xcpConnected = false;
}
} /*** end of XcpLoaderStop ***/
/************************************************************************************//**
** \brief Requests the bootloader to erase the specified range of memory on the
** target. The bootloader aligns this range to hardware specified erase
** blocks.
** \param address The starting memory address for the erase operation.
** \param len The total number of bytes to erase from memory.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderClearMemory(uint32_t address, uint32_t len)
{
bool result = false;
/* Check parameters. */
assert(len > 0);
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue if a transport layer is linked and the parameters are valid. */
if ( (len > 0) && (xcpSettings.transport != NULL) && (xcpConnected) )
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* First set the MTA pointer. */
if (!XcpLoaderSendCmdSetMta(address))
{
result = false;
}
/* Now perform the erase operation. */
if (result)
{
if (!XcpLoaderSendCmdProgramClear(len))
{
result = false;
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderClearMemory ***/
/************************************************************************************//**
** \brief Requests the bootloader to program the specified data to memory. In case of
** non-volatile memory, the application needs to make sure the memory range
** was erased beforehand.
** \param address The starting memory address for the write operation.
** \param len The number of bytes in the data buffer that should be written.
** \param data Pointer to the byte array with data to write.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderWriteData(uint32_t address, uint32_t len, uint8_t const * data)
{
bool result = false;
uint8_t currentWriteCnt;
uint32_t bufferOffset = 0;
/* Check parameters. */
assert(data != NULL);
assert(len > 0);
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue if the parameters are valid. */
if ( (data != NULL) && (len > 0) && (xcpSettings.transport != NULL) && /*lint !e774 */
(xcpConnected) )
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* First set the MTA pointer. */
if (!XcpLoaderSendCmdSetMta(address))
{
result = false;
}
/* Perform segmented programming of the data. */
if (result)
{
while (len > 0)
{
/* Set the current write length to make optimal use of the available packet
* data.
*/
currentWriteCnt = len % (xcpMaxProgCto - 1u);
if (currentWriteCnt == 0)
{
currentWriteCnt = (xcpMaxProgCto - 1u);
}
/* Prepare the packed data for the program command. */
if (currentWriteCnt < (xcpMaxProgCto - 1u))
{
/* Program data. */
if (!XcpLoaderSendCmdProgram(currentWriteCnt, &data[bufferOffset]))
{
result = false;
break;
}
}
else
{
/* Program max data. */
if (!XcpLoaderSendCmdProgramMax(&data[bufferOffset]))
{
result = false;
break;
}
}
/* Update loop variables. */
len -= currentWriteCnt;
bufferOffset += currentWriteCnt;
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderWriteData ***/
/************************************************************************************//**
** \brief Request the bootloader to upload the specified range of memory. The data is
** stored in the data byte array to which the pointer was specified.
** \param address The starting memory address for the read operation.
** \param len The number of bytes to upload from the target and store in the data
** buffer.
** \param data Pointer to the byte array where the uploaded data should be stored.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderReadData(uint32_t address, uint32_t len, uint8_t * data)
{
bool result = false;
uint8_t currentReadCnt;
uint32_t bufferOffset = 0;
/* Check parameters. */
assert(data != NULL);
assert(len > 0);
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue if the parameters are valid. */
if ( (data != NULL) && (len > 0) && (xcpSettings.transport != NULL) && /*lint !e774 */
(xcpConnected) )
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* First set the MTA pointer. */
if (!XcpLoaderSendCmdSetMta(address))
{
result = false;
}
/* Perform segmented upload of the data. */
if (result)
{
while (len > 0)
{
/* Set the current read length to make optimal use of the available packet
* data.
*/
currentReadCnt = len % (uint8_t)(xcpMaxDto - 1u);
if (currentReadCnt == 0)
{
currentReadCnt = (uint8_t)(xcpMaxDto - 1u);
}
/* Upload some data */
if (!XcpLoaderSendCmdUpload(&data[bufferOffset], currentReadCnt))
{
result = false;
break;
}
/* Update loop variables. */
len -= currentReadCnt;
bufferOffset += currentReadCnt;
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderReadData ***/
/************************************************************************************//**
** \brief Stores a 32-bit value into a byte buffer taking into account Intel
** or Motorola byte ordering.
** \param value The 32-bit value to store in the buffer.
** \param data Array to the buffer for storage.
**
****************************************************************************************/
static void XcpLoaderSetOrderedLong(uint32_t value, uint8_t *data)
{
/* Check parameters. */
assert(data != NULL);
/* Only continue with valid parameters. */
if (data != NULL) /*lint !e774 */
{
if (xcpSlaveIsIntel)
{
data[3] = (uint8_t)(value >> 24);
data[2] = (uint8_t)(value >> 16);
data[1] = (uint8_t)(value >> 8);
data[0] = (uint8_t)value;
}
else
{
data[0] = (uint8_t)(value >> 24);
data[1] = (uint8_t)(value >> 16);
data[2] = (uint8_t)(value >> 8);
data[3] = (uint8_t)value;
}
}
} /*** end of XcpLoaderSetOrderedLong ***/
/************************************************************************************//**
** \brief Obtains a 16-bit value from a byte buffer taking into account Intel
** or Motorola byte ordering.
** \param data Array to the buffer with the word value stored as bytes.
** \return The 16-bit value.
**
****************************************************************************************/
static uint16_t XcpLoaderGetOrderedWord(uint8_t const * data)
{
uint16_t result = 0;
/* Check parameters. */
assert(data != NULL);
/* Only continue with valid parameters. */
if (data != NULL) /*lint !e774 */
{
if (xcpSlaveIsIntel)
{
result |= (uint16_t)data[0];
result |= (uint16_t)(data[1] << 8);
}
else
{
result |= (uint16_t)data[1];
result |= (uint16_t)(data[0] << 8);
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderGetOrderedWord ***/
/************************************************************************************//**
** \brief Sends the XCP Connect command.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdConnect(void)
{
bool result = false;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue with a valid transport layer. */
if (xcpSettings.transport != NULL) /*lint !e774 */
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* Prepare the command packet. */
cmdPacket.data[0] = XCPLOADER_CMD_CONNECT;
cmdPacket.data[1] = xcpSettings.connectMode;
cmdPacket.len = 2;
/* Send the packet. */
if (!xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT6))
{
/* Could not send packet or receive response within the specified timeout. */
result = false;
}
/* Only continue if a response was received. */
if (result)
{
/* Check if the response was valid. */
if ( (resPacket.len != 8) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
/* Process the response data. */
if (result)
{
/* Store slave's byte ordering information. */
if ((resPacket.data[2] & 0x01) == 0)
{
xcpSlaveIsIntel = true;
}
else
{
xcpSlaveIsIntel = false;
}
/* Store max number of bytes the slave allows for master->slave packets. */
xcpMaxCto = resPacket.data[3];
xcpMaxProgCto = xcpMaxCto;
/* Store max number of bytes the slave allows for slave->master packets. */
if (xcpSlaveIsIntel)
{
xcpMaxDto = resPacket.data[4] + (resPacket.data[5] << 8);
}
else
{
xcpMaxDto = resPacket.data[5] + (resPacket.data[4] << 8);
}
/* Double check size configuration. CTO length can be adjusted. DTO not. */
if (xcpMaxCto > XCPLOADER_PACKET_SIZE_MAX) /*lint !e685 */
{
xcpMaxCto = XCPLOADER_PACKET_SIZE_MAX;
}
if (xcpMaxDto > XCPLOADER_PACKET_SIZE_MAX)
{
result = false;
}
if ( (xcpMaxCto == 0) || (xcpMaxDto == 0) )
{
result = false;
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdConnect ***/
/************************************************************************************//**
** \brief Sends the XCP Get Status command. Note that it is okay to specify a NULL
** value for the parameters if you are not interested in a particular one.
** \param session Current session status.
** \param protectedResources Current resource protection status.
** \param configId Session configuration identifier.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdGetStatus(uint8_t * session, uint8_t * protectedResources,
uint16_t * configId)
{
bool result = false;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue with a valid transport layer. */
if (xcpSettings.transport != NULL) /*lint !e774 */
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* Prepare the command packet. */
cmdPacket.data[0] = XCPLOADER_CMD_GET_STATUS;
cmdPacket.len = 1;
/* Send the packet. */
if (!xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT1))
{
/* Could not send packet or receive response within the specified timeout. */
result = false;
}
/* Only continue if a response was received. */
if (result)
{
/* Check if the response was valid. */
if ( (resPacket.len != 6) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
/* Extract and store the received status information. */
if (result)
{
/* Store the current session status. */
if (session != NULL)
{
*session = resPacket.data[1];
}
/* Store the current resource protection status. */
if (protectedResources != NULL)
{
*protectedResources = resPacket.data[2];
}
/* Store the session configuration id. */
if (configId != NULL)
{
*configId = XcpLoaderGetOrderedWord(&resPacket.data[4]);
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdGetStatus ***/
/************************************************************************************//**
** \brief Sends the XCP Get Seed command.
** \param resource The resource to unlock (XCPPROTECT_RESOURCE_xxx).
** \param mode 0 for the first part of the seed, 1 for the remaining part.
** \param seed Pointer to byte array where the received seed is stored.
** \param seedLen Length of the seed in bytes.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdGetSeed(uint8_t resource, uint8_t mode, uint8_t * seed,
uint8_t * seedLen)
{
bool result = false;
uint8_t currentSeedLen;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
/* Make sure a valid transport layer is linked and that the parameters are valid. */
assert(xcpSettings.transport != NULL);
assert(seed != NULL);
assert(seedLen != NULL);
/* Only continue with a valid transport layer and parameters. */
if ( (xcpSettings.transport != NULL) && (seed != NULL) &&
(seedLen != NULL) ) /*lint !e774 */
{
/* Only continue with a valid resource value. */
if ( (resource == XCPPROTECT_RESOURCE_PGM) ||
(resource == XCPPROTECT_RESOURCE_STIM)||
(resource == XCPPROTECT_RESOURCE_DAQ) ||
(resource == XCPPROTECT_RESOURCE_CALPAG))
{
/* Init the result value to okay and only set it to error when a problem
* occurred.
*/
result = true;
/* Prepare the command packet. */
cmdPacket.data[0] = XCPLOADER_CMD_GET_SEED;
cmdPacket.data[1] = mode;
cmdPacket.data[2] = resource;
cmdPacket.len = 3;
/* Send the packet. */
if (!xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT1))
{
/* Could not send packet or receive response within the specified timeout. */
result = false;
}
/* Only continue if a response was received. */
if (result)
{
/* Check if the response was valid. */
if ( (resPacket.len <= 2) || (resPacket.len > xcpMaxCto) ||
(resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
/* Extract and store the seed. */
if (result)
{
/* Store the seed length. */
*seedLen = resPacket.data[1];
/* Determine the number of seed bytes in the current response */
currentSeedLen = *seedLen;
if (currentSeedLen > (xcpMaxCto - 2))
{
currentSeedLen = (xcpMaxCto - 2);
}
/* Store the seed bytes. */
for (uint8_t idx = 0; idx < currentSeedLen; idx++)
{
seed[idx] = resPacket.data[idx + 2];
}
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdGetSeed ***/
/************************************************************************************//**
** \brief Sends the XCP Unlock command.
** \param key Pointer to a byte array containing the key.
** \param keyLen The length of the key in bytes.
** \param protectedResources Current resource protection status.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdUnlock(uint8_t const * key, uint8_t keyLen,
uint8_t * protectedResources)
{
bool result = false;
uint8_t keyCurrentLen;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
/* Make sure a valid transport layer is linked and that the parameters are valid. */
assert(xcpSettings.transport != NULL);
assert(key != NULL);
assert(keyLen > 0);
assert(protectedResources != NULL);
/* Only continue with a valid transport layer and parameters. */
if ( (xcpSettings.transport != NULL) && (key != NULL) && (keyLen > 0) &&
(protectedResources != NULL) ) /*lint !e774 */
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* Prepare the command packet. */
cmdPacket.data[0] = XCPLOADER_CMD_UNLOCK;
cmdPacket.data[1] = keyLen;
/* Determine number of key bytes for the packet. */
keyCurrentLen = keyLen;
if (keyCurrentLen > (xcpMaxCto - 2))
{
keyCurrentLen = xcpMaxCto - 2;
}
/* Copy key bytes. */
for (uint8_t idx = 0; idx < keyCurrentLen; idx++)
{
cmdPacket.data[idx + 2] = key[idx];
}
cmdPacket.len = keyCurrentLen + 2;
/* Send the packet. */
if (!xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT1))
{
/* Could not send packet or receive response within the specified timeout. */
result = false;
}
/* Only continue if a response was received. */
if (result)
{
/* Check if the response was valid. */
if ( (resPacket.len != 2) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
/* Store the current resource protection status. */
if (result)
{
*protectedResources = resPacket.data[1];
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdUnlock ***/
/************************************************************************************//**
** \brief Sends the XCP Set MTA command.
** \param address New MTA address for the slave.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdSetMta(uint32_t address)
{
bool result = false;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue with a valid transport layer. */
if (xcpSettings.transport != NULL) /*lint !e774 */
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* Prepare the command packet. */
cmdPacket.data[0] = XCPLOADER_CMD_SET_MTA;
cmdPacket.data[1] = 0; /* Reserved. */
cmdPacket.data[2] = 0; /* Reserved. */
cmdPacket.data[3] = 0; /* Address extension not supported. */
/* Set the address taking into account byte ordering. */
XcpLoaderSetOrderedLong(address, &cmdPacket.data[4]);
cmdPacket.len = 8;
/* Send the packet. */
if (!xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT1))
{
/* Could not send packet or receive response within the specified timeout. */
result = false;
}
/* Only continue if a response was received. */
if (result)
{
/* Check if the response was valid. */
if ( (resPacket.len != 1) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdSetMta ***/
/************************************************************************************//**
** \brief Sends the XCP UPLOAD command.
** \param data Destination data buffer.
** \param length Number of bytes to upload.
** \return SB_TRUE is successfull, SB_FALSE otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdUpload(uint8_t * data, uint8_t length)
{
bool result = false;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
uint8_t dataIdx;
/* Cannot request more data then the max rx data - 1. */
assert(length < xcpMaxDto);
assert(data != NULL);
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue with a valid transport layer and valid parameters. */
if ( (length < xcpMaxDto) && (data != NULL) &&
(xcpSettings.transport != NULL) ) /*lint !e774 */
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* Prepare the command packet. */
cmdPacket.data[0] = XCPLOADER_CMD_UPLOAD;
cmdPacket.data[1] = length;
cmdPacket.len = 2;
/* Send the packet. */
if (!xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT1))
{
/* Could not send packet or receive response within the specified timeout. */
result = false;
}
/* Only continue if a response was received. */
if (result)
{
/* Check if the response was valid. */
if ( (resPacket.len == 0) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
/* Now store the uploaded data. */
if (result)
{
for (dataIdx = 0; dataIdx < length; dataIdx++)
{
data[dataIdx] = resPacket.data[dataIdx + 1];
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdUpload ***/
/************************************************************************************//**
** \brief Sends the XCP PROGRAM START command.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdProgramStart(void)
{
bool result = false;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue with a valid transport layer. */
if (xcpSettings.transport != NULL) /*lint !e774 */
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* Prepare the command packet. */
cmdPacket.data[0] = XCPLOADER_CMD_PROGRAM_START;
cmdPacket.len = 1;
/* Send the packet. */
if (!xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT3))
{
/* Could not send packet or receive response within the specified timeout. */
result = false;
}
/* Only continue if a response was received. */
if (result)
{
/* Check if the response was valid. */
if ( (resPacket.len != 7) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
/* Store max number of bytes the slave allows for master->slave packets during the
* programming session.
*/
xcpMaxProgCto = resPacket.data[3];
if (xcpMaxProgCto > XCPLOADER_PACKET_SIZE_MAX) /*lint !e685 */
{
xcpMaxProgCto = XCPLOADER_PACKET_SIZE_MAX;
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdProgramStart ***/
/************************************************************************************//**
** \brief Sends the XCP PROGRAM RESET command. Note that this command is a bit
** different as in it does not require a response.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdProgramReset(void)
{
bool result = false;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue with a valid transport layer. */
if (xcpSettings.transport != NULL) /*lint !e774 */
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* Prepare the command packet. */
cmdPacket.data[0] = XCPLOADER_CMD_PROGRAM_RESET;
cmdPacket.len = 1;
/* Send the packet. Note that it is okay if no response is received as this is
* allowed for the program reset command. Just make sure to only process the
* response if one was actually received.
*/
if (xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT5))
{
/* Still here so a response was received. check if the reponse was valid. */
if ( (resPacket.len != 1) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdProgramReset ***/
/************************************************************************************//**
** \brief Sends the XCP PROGRAM command.
** \param length Number of bytes in the data array to program.
** \param data Array with data bytes to program.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdProgram(uint8_t length, uint8_t const * data)
{
bool result = false;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
uint8_t cnt;
/* Verify that this number of bytes actually fits in this command. */
assert((length <= (xcpMaxProgCto-2)) &&
(xcpMaxProgCto <= XCPLOADER_PACKET_SIZE_MAX)); /*lint !e685 */
if (length > 0)
{
assert(data != NULL);
}
/* Make sure a valid transport layer is linked. */
assert(xcpSettings.transport != NULL);
/* Only continue with a valid transport layer and valid parameters. */
if ( (length <= (xcpMaxProgCto-2)) &&
(xcpMaxProgCto <= XCPLOADER_PACKET_SIZE_MAX) && /*lint !e685 */
(xcpSettings.transport != NULL) )/*lint !e774 */
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* Prepare the command packet */
cmdPacket.data[0] = XCPLOADER_CMD_PROGRAM;
cmdPacket.data[1] = length;
/* Only access data if it is not a NULL pointer. */
if (data != NULL)
{
for (cnt=0; cnt<length; cnt++)
{
cmdPacket.data[cnt+2] = data[cnt];
}
}
cmdPacket.len = length + 2;
/* Send the packet. */
if (!xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT5))
{
/* Could not send packet or receive response within the specified timeout. */
result = false;
}
/* Only continue if a response was received. */
if (result)
{
/* Check if the response was valid. */
if ( (resPacket.len != 1) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdProgram ***/
/************************************************************************************//**
** \brief Sends the XCP PROGRAM MAX command.
** \param data Array with data bytes to program.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdProgramMax(uint8_t const * data)
{
bool result = false;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
uint8_t cnt;
/* verify that this number of bytes actually fits in this command */
assert(xcpMaxProgCto <= XCPLOADER_PACKET_SIZE_MAX); /*lint !e685 */
assert(data != NULL);
/* Only continue with a valid transport layer and valid parameters. */
if ( (xcpMaxProgCto <= XCPLOADER_PACKET_SIZE_MAX) && (data != NULL) && /*lint !e685 */
(xcpSettings.transport != NULL) )/*lint !e774 */
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/* Prepare the command packet. */
cmdPacket.data[0] = XCPLOADER_CMD_PROGRAM_MAX;
for (cnt = 0; cnt < (xcpMaxProgCto-1); cnt++)
{
cmdPacket.data[cnt+1] = data[cnt];
}
cmdPacket.len = xcpMaxProgCto;
/* Send the packet. */
if (!xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT5))
{
/* Could not send packet or receive response within the specified timeout. */
result = false;
}
/* Only continue if a response was received. */
if (result)
{
/* Check if the response was valid. */
if ( (resPacket.len != 1) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdProgramMax ***/
/************************************************************************************//**
** \brief Sends the XCP PROGRAM CLEAR command.
** \param length Number of elements to clear starting at the MTA address.
** \return True if successful, false otherwise.
**
****************************************************************************************/
static bool XcpLoaderSendCmdProgramClear(uint32_t length)
{
bool result = false;
tXcpTransportPacket cmdPacket;
tXcpTransportPacket resPacket;
/* Only continue with a valid transport layer. */
if (xcpSettings.transport != NULL) /*lint !e774 */
{
/* Init the result value to okay and only set it to error when a problem occurred. */
result = true;
/*Pprepare the command packet. */
cmdPacket.data[0] = XCPLOADER_CMD_PROGRAM_CLEAR;
cmdPacket.data[1] = 0; /* Use absolute mode. */
cmdPacket.data[2] = 0; /* Reserved. */
cmdPacket.data[3] = 0; /* Reserved. */
/* Set the erase length taking into account byte ordering. */
XcpLoaderSetOrderedLong(length, &cmdPacket.data[4]);
cmdPacket.len = 8;
/* Send the packet. */
if (!xcpSettings.transport->SendPacket(&cmdPacket, &resPacket,
xcpSettings.timeoutT4))
{
/* Could not send packet or receive response within the specified timeout. */
result = false;
}
/* Only continue if a response was received. */
if (result)
{
/* Check if the response was valid. */
if ( (resPacket.len != 1) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
{
/* Not a valid or positive response. */
result = false;
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpLoaderSendCmdProgramClear ***/
/*********************************** end of xcploader.c ********************************/