mirror of https://github.com/rusefi/openblt.git
Refs #154. Redesigned SerialBoot for improved run-time performance and modularity.
git-svn-id: https://svn.code.sf.net/p/openblt/code/trunk@208 5dc33758-31d5-4daf-9ae8-b24bf3d40d73
This commit is contained in:
parent
109dba61ea
commit
36a87ec580
Binary file not shown.
|
@ -6,7 +6,7 @@
|
||||||
#----------------------------------------------------------------------------------------
|
#----------------------------------------------------------------------------------------
|
||||||
# C O P Y R I G H T
|
# C O P Y R I G H T
|
||||||
#----------------------------------------------------------------------------------------
|
#----------------------------------------------------------------------------------------
|
||||||
# Copyright (c) 2014 by Feaser http://www.feaser.com All rights reserved
|
# Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved
|
||||||
#
|
#
|
||||||
#----------------------------------------------------------------------------------------
|
#----------------------------------------------------------------------------------------
|
||||||
# L I C E N S E
|
# L I C E N S E
|
||||||
|
@ -34,7 +34,7 @@ project(SerialBoot)
|
||||||
|
|
||||||
# Set the port directory, which is platform specific
|
# Set the port directory, which is platform specific
|
||||||
IF(WIN32)
|
IF(WIN32)
|
||||||
set(PROJECT_PORT_DIR ${PROJECT_SOURCE_DIR}/port/win32)
|
set(PROJECT_PORT_DIR ${PROJECT_SOURCE_DIR}/port/windows)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPLATFORM_WIN32 -D_CRT_SECURE_NO_WARNINGS")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPLATFORM_WIN32 -D_CRT_SECURE_NO_WARNINGS")
|
||||||
ELSEIF(UNIX)
|
ELSEIF(UNIX)
|
||||||
set(PROJECT_PORT_DIR ${PROJECT_SOURCE_DIR}/port/linux)
|
set(PROJECT_PORT_DIR ${PROJECT_SOURCE_DIR}/port/linux)
|
||||||
|
@ -45,7 +45,22 @@ ENDIF(WIN32)
|
||||||
set(CMAKE_BUILD_TYPE "Debug")
|
set(CMAKE_BUILD_TYPE "Debug")
|
||||||
|
|
||||||
# Set include directories
|
# Set include directories
|
||||||
include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_PORT_DIR}" "${PROJECT_SOURCE_DIR}/port")
|
include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_PORT_DIR}")
|
||||||
|
|
||||||
|
# Set the output directory
|
||||||
|
set (PROJECT_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../../..)
|
||||||
|
|
||||||
|
# Set the output directory for the generic no-config case (e.g. with mingw)
|
||||||
|
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_OUTPUT_DIRECTORY} )
|
||||||
|
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_OUTPUT_DIRECTORY} )
|
||||||
|
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_OUTPUT_DIRECTORY} )
|
||||||
|
# Set the output directory for multi-config builds (e.g. msvc)
|
||||||
|
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
|
||||||
|
string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
|
||||||
|
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_OUTPUT_DIRECTORY} )
|
||||||
|
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_OUTPUT_DIRECTORY} )
|
||||||
|
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_OUTPUT_DIRECTORY} )
|
||||||
|
endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES )
|
||||||
|
|
||||||
# Get header files
|
# Get header files
|
||||||
file(GLOB_RECURSE INCS "*.h")
|
file(GLOB_RECURSE INCS "*.h")
|
||||||
|
@ -53,10 +68,12 @@ file(GLOB_RECURSE INCS "*.h")
|
||||||
# Add sources
|
# Add sources
|
||||||
add_executable(
|
add_executable(
|
||||||
SerialBoot
|
SerialBoot
|
||||||
|
firmware.c
|
||||||
main.c
|
main.c
|
||||||
xcpmaster.c
|
srecparser.c
|
||||||
srecord.c
|
xcploader.c
|
||||||
${PROJECT_PORT_DIR}/xcptransport.c
|
xcptpuart.c
|
||||||
|
${PROJECT_PORT_DIR}/serialport.c
|
||||||
${PROJECT_PORT_DIR}/timeutil.c
|
${PROJECT_PORT_DIR}/timeutil.c
|
||||||
${INCS}
|
${INCS}
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
/************************************************************************************//**
|
||||||
|
* \file firmware.c
|
||||||
|
* \brief Firmware module source file.
|
||||||
|
* \ingroup SerialBoot
|
||||||
|
* \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 <stddef.h> /* for NULL declaration */
|
||||||
|
#include <assert.h> /* for assertions */
|
||||||
|
#include "firmware.h" /* firmware module */
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Local data declarations
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief Pointer to the firmware parser that is linked. */
|
||||||
|
static tFirmwareParser const * parserPtr = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Initializes the firmware module.
|
||||||
|
** \param parser Pointer to the firmware parser to link.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
void FirmwareInit(tFirmwareParser const * const parser)
|
||||||
|
{
|
||||||
|
/* verify parameters */
|
||||||
|
assert(parser != NULL);
|
||||||
|
|
||||||
|
/* link the firmware parser */
|
||||||
|
parserPtr = parser;
|
||||||
|
/* initialize the firmware parser */
|
||||||
|
parserPtr->Init();
|
||||||
|
} /*** end of FirmwareInit ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Uninitializes the firmware module.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
void FirmwareDeinit(void)
|
||||||
|
{
|
||||||
|
/* make sure the parser is linked */
|
||||||
|
assert(parserPtr != NULL);
|
||||||
|
|
||||||
|
/* uninitialize the parser */
|
||||||
|
parserPtr->Deinit();
|
||||||
|
/* unlink the parser */
|
||||||
|
parserPtr = NULL;
|
||||||
|
} /*** end of FirmwareDeinit ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Loads the firmware data from the specified firmware file, using the linked
|
||||||
|
** parser.
|
||||||
|
** \return True is successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool FirmwareLoadFromFile(char *firmwareFile)
|
||||||
|
{
|
||||||
|
/* make sure the parser is linked */
|
||||||
|
assert(parserPtr != NULL);
|
||||||
|
/* make sure the filename is valid */
|
||||||
|
assert(firmwareFile != NULL);
|
||||||
|
|
||||||
|
return parserPtr->LoadFromFile(firmwareFile);
|
||||||
|
} /*** end of FirmwareLoadFromFile ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Returns the number of firmware segments that were loaded by the parser.
|
||||||
|
** \return Number of firmware segments.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
uint32_t FirmwareGetSegmentCount(void)
|
||||||
|
{
|
||||||
|
/* make sure the parser is linked */
|
||||||
|
assert(parserPtr != NULL);
|
||||||
|
|
||||||
|
return parserPtr->GetSegmentCount();
|
||||||
|
} /*** end of FirmwareGetSegmentCount ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Obtains a pointer to the firmware segment at the specified index.
|
||||||
|
** \return Pointer to firmware segment if successful, NULL otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
const tFirmwareSegment *FirmwareGetSegment(uint32_t segmentIdx)
|
||||||
|
{
|
||||||
|
/* make sure the parser is linked */
|
||||||
|
assert(parserPtr != NULL);
|
||||||
|
|
||||||
|
return parserPtr->GetSegment(segmentIdx);
|
||||||
|
} /*** end of FirmwareGetSegment ***/
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** end of firmware.c *********************************/
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
/************************************************************************************//**
|
/************************************************************************************//**
|
||||||
* \file srecord.h
|
* \file firmware.h
|
||||||
* \brief Motorola S-record library header file.
|
* \brief Firmware module header file.
|
||||||
* \ingroup SerialBoot
|
* \ingroup SerialBoot
|
||||||
* \internal
|
* \internal
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* C O P Y R I G H T
|
* C O P Y R I G H T
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* Copyright (c) 2014 by Feaser http://www.feaser.com All rights reserved
|
* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved
|
||||||
*
|
*
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* L I C E N S E
|
* L I C E N S E
|
||||||
|
@ -25,51 +25,60 @@
|
||||||
*
|
*
|
||||||
* \endinternal
|
* \endinternal
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
#ifndef SRECORD_H
|
#ifndef FIRMWARE_H
|
||||||
#define SRECORD_H
|
#define FIRMWARE_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Macro definitions
|
* Include files
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
/** \brief Maximum number of characters that can be on a line in the firmware file. */
|
#include <stdint.h> /* for standard integer types */
|
||||||
#define SRECORD_MAX_CHARS_PER_LINE (512)
|
#include <stdbool.h> /* for boolean type */
|
||||||
|
|
||||||
/** \brief Maximum number of data bytes that can be on a line in the firmware file
|
|
||||||
* (S-record).
|
|
||||||
*/
|
|
||||||
#define SRECORD_MAX_DATA_BYTES_PER_LINE (SRECORD_MAX_CHARS_PER_LINE/2)
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Type definitions
|
* Type definitions
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
/** \brief Structure type for grouping the parsing results of an S-record file. */
|
/** \brief Groups information together of a firmware segments. */
|
||||||
typedef struct
|
typedef struct t_firmware_segment
|
||||||
{
|
{
|
||||||
sb_uint32 address_low; /**< lowest memory address */
|
uint32_t base; /**< Start memory address of the segment. */
|
||||||
sb_uint32 address_high; /**< lowest memory address */
|
uint32_t length; /**< Number of data bytes in the segment. */
|
||||||
sb_uint32 data_bytes_total; /**< total number of data bytes */
|
uint8_t *data; /**< Pointer to array with the segment's data bytes. */
|
||||||
} tSrecordParseResults;
|
} tFirmwareSegment;
|
||||||
|
|
||||||
/** \brief Structure type for grouping the parsing results of an S-record line. */
|
/** \brief Firmware file parser. */
|
||||||
typedef struct
|
typedef struct t_firmware_parser
|
||||||
{
|
{
|
||||||
sb_uint8 data[SRECORD_MAX_DATA_BYTES_PER_LINE]; /**< array for S1,S2 or S3 data bytes*/
|
/** \brief Initialization of the file parser. */
|
||||||
sb_uint32 address; /**< address on S1,S2 or S3 line */
|
void (*Init) (void);
|
||||||
sb_uint16 length; /**< number of bytes written to array */
|
/** \brief Uninitializes the file parser. */
|
||||||
} tSrecordLineParseResults;
|
void (*Deinit) (void);
|
||||||
|
/** \brief Extract the firmware segments from the firmware file. */
|
||||||
|
bool (*LoadFromFile) (char *firmwareFile);
|
||||||
|
/** \brief Obtains the number of segments. */
|
||||||
|
uint32_t (*GetSegmentCount) (void);
|
||||||
|
/** \brief Obtains a segment. */
|
||||||
|
const tFirmwareSegment * (*GetSegment) (uint32_t segmentIdx);
|
||||||
|
} tFirmwareParser;
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Function prototypes
|
* Function prototypes
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
sb_uint8 SrecordIsValid(const sb_char *srecordFile);
|
void FirmwareInit(tFirmwareParser const * const parser);
|
||||||
sb_file SrecordOpen(const sb_char *srecordFile);
|
void FirmwareDeinit(void);
|
||||||
void SrecordParse(sb_file srecordHandle, tSrecordParseResults *parseResults);
|
bool FirmwareLoadFromFile(char *firmwareFile);
|
||||||
void SrecordClose(sb_file srecordHandle);
|
uint32_t FirmwareGetSegmentCount(void);
|
||||||
sb_uint8 SrecordParseNextDataLine(sb_file srecordHandle, tSrecordLineParseResults *parseResults);
|
const tFirmwareSegment *FirmwareGetSegment(uint32_t segmentIdx);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* FIRMWARE_H */
|
||||||
|
/********************************* end of firmware.h ***********************************/
|
||||||
|
|
||||||
#endif /* SRECORD_H */
|
|
||||||
/*********************************** end of srecord.h **********************************/
|
|
|
@ -1,12 +1,12 @@
|
||||||
/************************************************************************************//**
|
/************************************************************************************//**
|
||||||
* \file main.c
|
* \file main.c
|
||||||
* \brief SerialBoot command line demonstration program for OpenBLT.
|
* \brief SerialBoot program source file.
|
||||||
* \ingroup SerialBoot
|
* \ingroup SerialBoot
|
||||||
* \internal
|
* \internal
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* C O P Y R I G H T
|
* C O P Y R I G H T
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* Copyright (c) 2014 by Feaser http://www.feaser.com All rights reserved
|
* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved
|
||||||
*
|
*
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* L I C E N S E
|
* L I C E N S E
|
||||||
|
@ -26,17 +26,53 @@
|
||||||
* \endinternal
|
* \endinternal
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Include files
|
* Include files
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
#include <assert.h> /* assertion module */
|
#include <stdio.h> /* standard I/O functions */
|
||||||
#include <sb_types.h> /* C types */
|
#include <string.h> /* for string library */
|
||||||
#include <stdio.h> /* standard I/O library */
|
#include "xcploader.h" /* XCP loader module */
|
||||||
#include <string.h> /* string library */
|
#include "xcptpuart.h" /* XCP transport layer for UART */
|
||||||
#include "xcpmaster.h" /* XCP master protocol module */
|
#include "firmware.h" /* Firmware module */
|
||||||
#include "srecord.h" /* S-record file handling */
|
#include "srecparser.h" /* S-record parser */
|
||||||
#include "timeutil.h" /* time utility module */
|
#include "timeutil.h" /* for time utilities module */
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Macro definitions
|
||||||
|
****************************************************************************************/
|
||||||
|
/* Program return codes. */
|
||||||
|
#define RESULT_OK (0)
|
||||||
|
#define RESULT_COMMANDLINE_ERROR (1)
|
||||||
|
#define RESULT_FIRMWARE_LOAD_ERROR (2)
|
||||||
|
#define RESULT_PROGRAM_START_ERROR (3)
|
||||||
|
#define RESULT_MEMORY_ERASE_ERROR (4)
|
||||||
|
#define RESULT_PROGRAM_STOP_ERROR (5)
|
||||||
|
#define RESULT_MEMORY_PROGRAM_ERROR (6)
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Local data declarations
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief The firmware filename that is specified at the command line. */
|
||||||
|
static char *firmwareFilename;
|
||||||
|
|
||||||
|
/** \brief XCP loader settings. */
|
||||||
|
static tXcpSettings xcpSettings =
|
||||||
|
{
|
||||||
|
.timeoutT1 = 1000,
|
||||||
|
.timeoutT3 = 2000,
|
||||||
|
.timeoutT4 = 10000,
|
||||||
|
.timeoutT5 = 1000,
|
||||||
|
.timeoutT7 = 2000
|
||||||
|
};
|
||||||
|
|
||||||
|
/** \brief XCP UART transport layer settings. */
|
||||||
|
static tXcpTpUartSettings xcpTpUartSettings =
|
||||||
|
{
|
||||||
|
.baudrate = SERIALPORT_BR57600,
|
||||||
|
.portname = "/dev/ttyS0"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
|
@ -44,189 +80,163 @@
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
static void DisplayProgramInfo(void);
|
static void DisplayProgramInfo(void);
|
||||||
static void DisplayProgramUsage(void);
|
static void DisplayProgramUsage(void);
|
||||||
static sb_uint8 ParseCommandLine(sb_int32 argc, sb_char *argv[]);
|
static bool ParseCommandLine(int argc, char *argv[]);
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Macro definitions
|
|
||||||
****************************************************************************************/
|
|
||||||
/** \brief Program return code if all went ok. */
|
|
||||||
#define PROG_RESULT_OK (0)
|
|
||||||
|
|
||||||
/** \brief Program return code if an error occurred. */
|
|
||||||
#define PROG_RESULT_ERROR (1)
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Local data declarations
|
|
||||||
****************************************************************************************/
|
|
||||||
/** \brief Name of the serial device, such as COM4 or /dev/ttyUSB0. */
|
|
||||||
static sb_char serialDeviceName[32];
|
|
||||||
|
|
||||||
/** \brief Serial communication speed in bits per second. */
|
|
||||||
static sb_uint32 serialBaudrate;
|
|
||||||
|
|
||||||
/** \brief Name of the S-record file. */
|
|
||||||
static sb_char srecordFileName[128];
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
/************************************************************************************//**
|
||||||
** \brief Program entry point.
|
** \brief This is the program entry point.
|
||||||
** \param argc Number of program parameters.
|
** \param argc Number of program arguments.
|
||||||
** \param argv array to program parameter strings.
|
** \param argv Array with program arguments.
|
||||||
** \return 0 on success, > 0 on error.
|
** \return Program return code. 0 for success, error code otherwise.
|
||||||
**
|
**
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
sb_int32 main(sb_int32 argc, sb_char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
sb_file hSrecord;
|
int result = RESULT_OK;
|
||||||
tSrecordParseResults fileParseResults;
|
uint32_t fwBaseAddress;
|
||||||
tSrecordLineParseResults lineParseResults;
|
uint32_t fwTotalSize;
|
||||||
|
uint32_t segmentIdx;
|
||||||
/* disable buffering for the standard output to make sure printf does not wait until
|
const tFirmwareSegment *segment;
|
||||||
* a newline character is detected before outputting text on the console.
|
|
||||||
*/
|
|
||||||
setbuf(stdout, SB_NULL);
|
|
||||||
|
|
||||||
|
/* -------------------- Display info ----------------------------------------------- */
|
||||||
/* inform user about the program */
|
/* inform user about the program */
|
||||||
DisplayProgramInfo();
|
DisplayProgramInfo();
|
||||||
|
|
||||||
|
/* -------------------- Process command line --------------------------------------- */
|
||||||
/* start out by making sure program was started with the correct parameters */
|
/* start out by making sure program was started with the correct parameters */
|
||||||
if (ParseCommandLine(argc, argv) == SB_FALSE)
|
if (!ParseCommandLine(argc, argv))
|
||||||
{
|
{
|
||||||
/* parameters invalid. inform user about how this program works */
|
/* parameters invalid. inform user about how this program works */
|
||||||
DisplayProgramUsage();
|
DisplayProgramUsage();
|
||||||
return PROG_RESULT_ERROR;
|
return RESULT_COMMANDLINE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------- start the firmware update procedure ------------------------ */
|
/* -------------------- Initialization --------------------------------------------- */
|
||||||
printf("Starting firmware update for \"%s\" using %s @ %u bits/s\n", srecordFileName, serialDeviceName, serialBaudrate);
|
/* initialize the XCP loader module using the UART transport layer. */
|
||||||
|
XcpLoaderInit(&xcpSettings, XcpTpUartGetTransport(), &xcpTpUartSettings);
|
||||||
|
/* initialize the firmware module and link the S-recorder parser */
|
||||||
|
FirmwareInit(SRecParserGetParser());
|
||||||
|
|
||||||
/* -------------------- validating the S-record file ------------------------------- */
|
/* -------------------- Parse the firmware file ------------------------------------ */
|
||||||
printf("Checking formatting of S-record file \"%s\"...", srecordFileName);
|
/* attempt to load the firmware file */
|
||||||
if (SrecordIsValid(srecordFileName) == SB_FALSE)
|
printf("Loading firmware file..."); fflush(stdout);
|
||||||
|
if (!FirmwareLoadFromFile(firmwareFilename))
|
||||||
{
|
{
|
||||||
|
/* set error code and abort */
|
||||||
printf("ERROR\n");
|
printf("ERROR\n");
|
||||||
return PROG_RESULT_ERROR;
|
result = RESULT_FIRMWARE_LOAD_ERROR;
|
||||||
|
goto finish;
|
||||||
}
|
}
|
||||||
printf("OK\n");
|
printf("OK\n");
|
||||||
|
/* determine firmware base address and total size */
|
||||||
/* -------------------- opening the S-record file ---------------------------------- */
|
for (segmentIdx=0; segmentIdx<FirmwareGetSegmentCount(); segmentIdx++)
|
||||||
printf("Opening S-record file \"%s\"...", srecordFileName);
|
|
||||||
if ((hSrecord = SrecordOpen(srecordFileName)) == SB_NULL)
|
|
||||||
{
|
{
|
||||||
printf("ERROR\n");
|
segment = FirmwareGetSegment(segmentIdx);
|
||||||
return PROG_RESULT_ERROR;
|
/* is this the first segment? */
|
||||||
}
|
if (segmentIdx == 0)
|
||||||
printf("OK\n");
|
|
||||||
|
|
||||||
/* -------------------- parsing the S-record file ---------------------------------- */
|
|
||||||
printf("Parsing S-record file \"%s\"...", srecordFileName);
|
|
||||||
SrecordParse(hSrecord, &fileParseResults);
|
|
||||||
printf("OK\n");
|
|
||||||
printf("-> Lowest memory address: 0x%08x\n", fileParseResults.address_low);
|
|
||||||
printf("-> Highest memory address: 0x%08x\n", fileParseResults.address_high);
|
|
||||||
printf("-> Total data bytes: %u\n", fileParseResults.data_bytes_total);
|
|
||||||
|
|
||||||
/* -------------------- Open the serial port --------------------------------------- */
|
|
||||||
printf("Opening serial port %s...", serialDeviceName);
|
|
||||||
if (XcpMasterInit(serialDeviceName, serialBaudrate) == SB_FALSE)
|
|
||||||
{
|
{
|
||||||
printf("ERROR\n");
|
/* initialize */
|
||||||
SrecordClose(hSrecord);
|
fwBaseAddress = segment->base;
|
||||||
return PROG_RESULT_ERROR;
|
fwTotalSize = segment->length;
|
||||||
}
|
}
|
||||||
printf("OK\n");
|
else
|
||||||
|
{
|
||||||
|
/* update */
|
||||||
|
if (segment->base < fwBaseAddress)
|
||||||
|
{
|
||||||
|
fwBaseAddress = segment->base;
|
||||||
|
}
|
||||||
|
fwTotalSize += segment->length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* display some firmware statistics */
|
||||||
|
printf("-> Number of segments: %u\n", FirmwareGetSegmentCount());
|
||||||
|
printf("-> Base memory address: 0x%08x\n", fwBaseAddress);
|
||||||
|
printf("-> Total data bytes: %u\n", fwTotalSize);
|
||||||
|
|
||||||
/* -------------------- Connect to XCP slave --------------------------------------- */
|
/* -------------------- Connect to target ------------------------------------------ */
|
||||||
printf("Connecting to bootloader...");
|
printf("Connecting to bootloader..."); fflush(stdout);
|
||||||
if (XcpMasterConnect() == SB_FALSE)
|
if (!XcpLoaderConnect())
|
||||||
{
|
{
|
||||||
/* no response. prompt the user to reset the system */
|
/* no response. prompt the user to reset the system */
|
||||||
printf("TIMEOUT\nReset your microcontroller...");
|
printf("TIMEOUT\nReset your microcontroller..."); fflush(stdout);
|
||||||
}
|
|
||||||
/* now keep retrying until we get a response */
|
/* now keep retrying until we get a response */
|
||||||
while (XcpMasterConnect() == SB_FALSE)
|
while (!XcpLoaderConnect())
|
||||||
{
|
{
|
||||||
/* delay a bit to not pump up the CPU load */
|
/* delay a bit to not pump up the CPU load */
|
||||||
TimeUtilDelayMs(20);
|
TimeUtilDelayMs(20);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
printf("OK\n");
|
printf("OK\n");
|
||||||
|
|
||||||
/* -------------------- Prepare the programming session ---------------------------- */
|
/* -------------------- Start the programming session ------------------------------ */
|
||||||
printf("Initializing programming session...");
|
/* attempt to start the programming session */
|
||||||
if (XcpMasterStartProgrammingSession() == SB_FALSE)
|
printf("Starting programming session..."); fflush(stdout);
|
||||||
|
if (!XcpLoaderStartProgrammingSession())
|
||||||
{
|
{
|
||||||
|
/* set error code and abort */
|
||||||
printf("ERROR\n");
|
printf("ERROR\n");
|
||||||
XcpMasterDisconnect();
|
result = RESULT_PROGRAM_START_ERROR;
|
||||||
XcpMasterDeinit();
|
goto finish;
|
||||||
SrecordClose(hSrecord);
|
|
||||||
return PROG_RESULT_ERROR;
|
|
||||||
}
|
}
|
||||||
printf("OK\n");
|
printf("OK\n");
|
||||||
|
|
||||||
/* -------------------- Erase memory ----------------------------------------------- */
|
/* -------------------- Erase memory ----------------------------------------------- */
|
||||||
printf("Erasing %u bytes starting at 0x%08x...", fileParseResults.data_bytes_total, fileParseResults.address_low);
|
/* erase each segment one at a time */
|
||||||
if (XcpMasterClearMemory(fileParseResults.address_low, (fileParseResults.address_high - fileParseResults.address_low)) == SB_FALSE)
|
for (segmentIdx=0; segmentIdx<FirmwareGetSegmentCount(); segmentIdx++)
|
||||||
{
|
{
|
||||||
|
segment = FirmwareGetSegment(segmentIdx);
|
||||||
|
/* attempt to erase memory */
|
||||||
|
printf("Erasing %u bytes starting at 0x%08x...", segment->length, segment->base); fflush(stdout);
|
||||||
|
if (!XcpLoaderClearMemory(segment->base, segment->length))
|
||||||
|
{
|
||||||
|
/* set error code and abort */
|
||||||
printf("ERROR\n");
|
printf("ERROR\n");
|
||||||
XcpMasterDisconnect();
|
result = RESULT_MEMORY_ERASE_ERROR;
|
||||||
XcpMasterDeinit();
|
goto finish;
|
||||||
SrecordClose(hSrecord);
|
|
||||||
return PROG_RESULT_ERROR;
|
|
||||||
}
|
}
|
||||||
printf("OK\n");
|
printf("OK\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------- Program data ----------------------------------------------- */
|
/* -------------------- Program data ----------------------------------------------- */
|
||||||
printf("Programming data. Please wait...");
|
/* program each segment one at a time */
|
||||||
/* loop through all S-records with program data */
|
for (segmentIdx=0; segmentIdx<FirmwareGetSegmentCount(); segmentIdx++)
|
||||||
while (SrecordParseNextDataLine(hSrecord, &lineParseResults) == SB_TRUE)
|
|
||||||
{
|
{
|
||||||
if (XcpMasterProgramData(lineParseResults.address, lineParseResults.length, lineParseResults.data) == SB_FALSE)
|
segment = FirmwareGetSegment(segmentIdx);
|
||||||
|
/* attempt to program memory */
|
||||||
|
printf("Programming %u bytes starting at 0x%08x...", segment->length, segment->base); fflush(stdout);
|
||||||
|
if (!XcpLoaderProgramData(segment->base, segment->length, segment->data))
|
||||||
{
|
{
|
||||||
|
/* set error code and abort */
|
||||||
printf("ERROR\n");
|
printf("ERROR\n");
|
||||||
XcpMasterDisconnect();
|
result = RESULT_MEMORY_PROGRAM_ERROR;
|
||||||
XcpMasterDeinit();
|
goto finish;
|
||||||
SrecordClose(hSrecord);
|
|
||||||
return PROG_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
printf("OK\n");
|
printf("OK\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------- Stop the programming session ------------------------------- */
|
/* -------------------- Stop the programming session ------------------------------- */
|
||||||
printf("Finishing programming session...");
|
/* attempt to stop the programming session */
|
||||||
if (XcpMasterStopProgrammingSession() == SB_FALSE)
|
printf("Finishing programming session..."); fflush(stdout);
|
||||||
|
if (!XcpLoaderStopProgrammingSession())
|
||||||
{
|
{
|
||||||
|
/* set error code and abort */
|
||||||
printf("ERROR\n");
|
printf("ERROR\n");
|
||||||
XcpMasterDisconnect();
|
result = RESULT_PROGRAM_STOP_ERROR;
|
||||||
XcpMasterDeinit();
|
goto finish;
|
||||||
SrecordClose(hSrecord);
|
|
||||||
return PROG_RESULT_ERROR;
|
|
||||||
}
|
}
|
||||||
printf("OK\n");
|
printf("OK\n");
|
||||||
|
|
||||||
/* -------------------- Disconnect from XCP slave and perform software reset ------- */
|
/* -------------------- Cleanup ---------------------------------------------------- */
|
||||||
printf("Performing software reset...");
|
finish:
|
||||||
if (XcpMasterDisconnect() == SB_FALSE)
|
/* uninitialize the firmware module */
|
||||||
{
|
FirmwareDeinit();
|
||||||
printf("ERROR\n");
|
/* uninitialize the XCP loader module. note that this automatically disconnects the
|
||||||
XcpMasterDeinit();
|
* slave, if connected, by requesting it to perform a reset.
|
||||||
SrecordClose(hSrecord);
|
*/
|
||||||
return PROG_RESULT_ERROR;
|
XcpLoaderDeinit();
|
||||||
}
|
/* give result back */
|
||||||
printf("OK\n");
|
return result;
|
||||||
|
|
||||||
/* -------------------- close the serial port -------------------------------------- */
|
|
||||||
XcpMasterDeinit();
|
|
||||||
printf("Closed serial port %s\n", serialDeviceName);
|
|
||||||
|
|
||||||
/* -------------------- close the S-record file ------------------------------------ */
|
|
||||||
SrecordClose(hSrecord);
|
|
||||||
printf("Closed S-record file \"%s\"\n", srecordFileName);
|
|
||||||
|
|
||||||
/* all done */
|
|
||||||
printf("Firmware successfully updated!\n");
|
|
||||||
return PROG_RESULT_OK;
|
|
||||||
} /*** end of main ***/
|
} /*** end of main ***/
|
||||||
|
|
||||||
|
|
||||||
|
@ -238,7 +248,7 @@ sb_int32 main(sb_int32 argc, sb_char *argv[])
|
||||||
static void DisplayProgramInfo(void)
|
static void DisplayProgramInfo(void)
|
||||||
{
|
{
|
||||||
printf("-------------------------------------------------------------------------\n");
|
printf("-------------------------------------------------------------------------\n");
|
||||||
printf("SerialBoot version 1.00. Performs firmware updates via the serial port\n");
|
printf("SerialBoot version 2.00. Performs firmware updates via the serial port\n");
|
||||||
printf("for a microcontroller based system that runs the OpenBLT bootloader.\n\n");
|
printf("for a microcontroller based system that runs the OpenBLT bootloader.\n\n");
|
||||||
printf("Copyright (c) by Feaser http://www.feaser.com\n");
|
printf("Copyright (c) by Feaser http://www.feaser.com\n");
|
||||||
printf("-------------------------------------------------------------------------\n");
|
printf("-------------------------------------------------------------------------\n");
|
||||||
|
@ -262,6 +272,8 @@ static void DisplayProgramUsage(void)
|
||||||
#endif
|
#endif
|
||||||
printf(" bits/second and programs the myfirmware.s19 file in non-\n");
|
printf(" bits/second and programs the myfirmware.s19 file in non-\n");
|
||||||
printf(" volatile memory of the microcontroller using OpenBLT.\n");
|
printf(" volatile memory of the microcontroller using OpenBLT.\n");
|
||||||
|
printf(" Supported baudrates are: 9600, 19200, 38400, 57600 and\n");
|
||||||
|
printf(" 115200 bits/second.\n");
|
||||||
printf("-------------------------------------------------------------------------\n");
|
printf("-------------------------------------------------------------------------\n");
|
||||||
} /*** end of DisplayProgramUsage ***/
|
} /*** end of DisplayProgramUsage ***/
|
||||||
|
|
||||||
|
@ -272,59 +284,82 @@ static void DisplayProgramUsage(void)
|
||||||
** SerialBoot -d[device] -b[baudrate] [s-record file]
|
** SerialBoot -d[device] -b[baudrate] [s-record file]
|
||||||
** \param argc Number of program parameters.
|
** \param argc Number of program parameters.
|
||||||
** \param argv array to program parameter strings.
|
** \param argv array to program parameter strings.
|
||||||
** \return SB_TRUE on success, SB_FALSE otherwise.
|
** \return True if successful, false otherwise.
|
||||||
**
|
**
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
static sb_uint8 ParseCommandLine(sb_int32 argc, sb_char *argv[])
|
static bool ParseCommandLine(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
sb_uint8 paramIdx;
|
uint8_t paramIdx;
|
||||||
sb_uint8 paramDfound = SB_FALSE;
|
bool firmwareFileFound = false;
|
||||||
sb_uint8 paramBfound = SB_FALSE;
|
uint32_t baudrateValue;
|
||||||
sb_uint8 srecordfound = SB_FALSE;
|
|
||||||
|
|
||||||
/* make sure the right amount of arguments are given */
|
/* make sure that enough arguments were specified. this program needs at least 2. the
|
||||||
if (argc != 4)
|
* first one is always the program name and the second one is the s-record file.
|
||||||
|
*/
|
||||||
|
if (argc < 2)
|
||||||
{
|
{
|
||||||
return SB_FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* loop through all the command lina parameters, just skip the 1st one because this
|
/* loop through all the command line parameters, just skip the 1st one because this
|
||||||
* is the name of the program, which we are not interested in.
|
* is the name of the program, which we are not interested in.
|
||||||
*/
|
*/
|
||||||
for (paramIdx=1; paramIdx<argc; paramIdx++)
|
for (paramIdx=1; paramIdx<argc; paramIdx++)
|
||||||
{
|
{
|
||||||
/* is this the device name? */
|
/* is this the device name? */
|
||||||
if ( (argv[paramIdx][0] == '-') && (argv[paramIdx][1] == 'd') && (paramDfound == SB_FALSE) )
|
if ( (argv[paramIdx][0] == '-') && (argv[paramIdx][1] == 'd') )
|
||||||
{
|
{
|
||||||
/* copy the device name and set flag that this parameter was found */
|
/* set the device name */
|
||||||
strcpy(serialDeviceName, &argv[paramIdx][2]);
|
xcpTpUartSettings.portname = &argv[paramIdx][2];
|
||||||
paramDfound = SB_TRUE;
|
continue;
|
||||||
}
|
}
|
||||||
/* is this the device name? */
|
/* is this the baudrate? */
|
||||||
else if ( (argv[paramIdx][0] == '-') && (argv[paramIdx][1] == 'b') && (paramBfound == SB_FALSE) )
|
if ( (argv[paramIdx][0] == '-') && (argv[paramIdx][1] == 'b') )
|
||||||
{
|
{
|
||||||
/* extract the baudrate and set flag that this parameter was found */
|
/* extract the baudrate */
|
||||||
sscanf(&argv[paramIdx][2], "%u", &serialBaudrate);
|
sscanf(&argv[paramIdx][2], "%u", &baudrateValue);
|
||||||
paramBfound = SB_TRUE;
|
/* convert to the baudrate type */
|
||||||
|
switch (baudrateValue)
|
||||||
|
{
|
||||||
|
case 115200:
|
||||||
|
xcpTpUartSettings.baudrate = SERIALPORT_BR115200;
|
||||||
|
break;
|
||||||
|
case 57600:
|
||||||
|
xcpTpUartSettings.baudrate = SERIALPORT_BR57600;
|
||||||
|
break;
|
||||||
|
case 38400:
|
||||||
|
xcpTpUartSettings.baudrate = SERIALPORT_BR38400;
|
||||||
|
break;
|
||||||
|
case 19200:
|
||||||
|
xcpTpUartSettings.baudrate = SERIALPORT_BR19200;
|
||||||
|
break;
|
||||||
|
case 9600:
|
||||||
|
xcpTpUartSettings.baudrate = SERIALPORT_BR9600;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unsupported baudrate specified */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
/* still here so it must be the filename */
|
/* still here so it must be the filename */
|
||||||
else if (srecordfound == SB_FALSE)
|
else
|
||||||
{
|
{
|
||||||
/* copy the file name and set flag that this parameter was found */
|
/* set the file name and set flag that this parameter was found */
|
||||||
strcpy(srecordFileName, &argv[paramIdx][0]);
|
firmwareFilename = &argv[paramIdx][0];
|
||||||
srecordfound = SB_TRUE;
|
firmwareFileFound = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* verify if all parameters were found */
|
/* verify if all required parameters were found */
|
||||||
if ( (paramDfound == SB_FALSE) || (paramBfound == SB_FALSE) || (srecordfound == SB_FALSE) )
|
if (!firmwareFileFound)
|
||||||
{
|
{
|
||||||
return SB_FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* still here so the parsing was successful */
|
/* still here so the parsing was successful */
|
||||||
return SB_TRUE;
|
return true;
|
||||||
} /*** end of ParseCommandLine ***/
|
} /*** end of ParseCommandLine ***/
|
||||||
|
|
||||||
|
|
||||||
/*********************************** end of main.c *************************************/
|
/*********************************** end of main.c *************************************/
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
/************************************************************************************//**
|
||||||
|
* \file port\linux\serialport.c
|
||||||
|
* \brief Serial port source file.
|
||||||
|
* \ingroup SerialBoot
|
||||||
|
* \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 <stddef.h> /* for NULL declaration */
|
||||||
|
#include <assert.h> /* for assertions */
|
||||||
|
#include <unistd.h> /* UNIX standard functions */
|
||||||
|
#include <termios.h> /* POSIX terminal control */
|
||||||
|
#include <fcntl.h> /* file control definitions */
|
||||||
|
#include <sys/ioctl.h> /* system I/O control */
|
||||||
|
#include "serialport.h" /* serial port module */
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Macro definitions
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief Invalid serial port device handle. */
|
||||||
|
#define SERIALPORT_INVALID_HANDLE (-1)
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Local data declarations
|
||||||
|
****************************************************************************************/
|
||||||
|
static int32_t portHandle = SERIALPORT_INVALID_HANDLE;
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Local constant declarations
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief Lookup table for converting this module's generic baudrate value to a value
|
||||||
|
* supported by the low level interface.
|
||||||
|
*/
|
||||||
|
static const speed_t baudrateLookup[] =
|
||||||
|
{
|
||||||
|
B9600, /**< Index 0 = SERIALPORT_BR9600 */
|
||||||
|
B19200, /**< Index 1 = SERIALPORT_BR19200 */
|
||||||
|
B38400, /**< Index 2 = SERIALPORT_BR38400 */
|
||||||
|
B57600, /**< Index 3 = SERIALPORT_BR57600 */
|
||||||
|
B115200 /**< Index 4 = SERIALPORT_BR115200 */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Opens the connection with the serial port configured as 8,N,1 and no flow
|
||||||
|
** control.
|
||||||
|
** \param portname The name of the serial port to open, i.e. /dev/ttyUSB0.
|
||||||
|
** \param baudrate The desired communication speed.
|
||||||
|
** \return True if successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool SerialPortOpen(char *portname, tSerialPortBaudrate baudrate)
|
||||||
|
{
|
||||||
|
struct termios options;
|
||||||
|
int32_t iFlags;
|
||||||
|
|
||||||
|
/* check parameters */
|
||||||
|
assert(portname != NULL);
|
||||||
|
|
||||||
|
/* open the port */
|
||||||
|
portHandle = open(portname, O_RDWR | O_NOCTTY | O_NDELAY);
|
||||||
|
/* check the result */
|
||||||
|
if (portHandle == SERIALPORT_INVALID_HANDLE)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* configure the device to block during read operations */
|
||||||
|
if (fcntl(portHandle, F_SETFL, 0) == -1)
|
||||||
|
{
|
||||||
|
SerialPortClose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* get the current options for the port */
|
||||||
|
if (tcgetattr(portHandle, &options) == -1)
|
||||||
|
{
|
||||||
|
SerialPortClose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* configure the baudrate */
|
||||||
|
if (cfsetispeed(&options, baudrateLookup[baudrate]) == -1)
|
||||||
|
{
|
||||||
|
SerialPortClose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cfsetospeed(&options, baudrateLookup[baudrate]) == -1)
|
||||||
|
{
|
||||||
|
SerialPortClose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* input modes - clear indicated ones giving: no break, no CR to NL,
|
||||||
|
* no parity check, no strip char, no start/stop output (sic) control
|
||||||
|
*/
|
||||||
|
options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
||||||
|
/* output modes - clear giving: no post processing such as NL to CR+NL */
|
||||||
|
options.c_oflag &= ~(OPOST);
|
||||||
|
/* control modes - set 8 bit chars */
|
||||||
|
options.c_cflag |= (CS8);
|
||||||
|
/* local modes - clear giving: echoing off, canonical off (no erase with
|
||||||
|
* backspace, ^U,...), no extended functions, no signal chars (^Z,^C)
|
||||||
|
*/
|
||||||
|
options.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
|
||||||
|
/* configure timeouts */
|
||||||
|
options.c_cc[VMIN] = 0;
|
||||||
|
options.c_cc[VTIME] = 1; /* in units of 1/10th of a second */
|
||||||
|
/* set the new options for the port */
|
||||||
|
if (tcsetattr(portHandle, TCSAFLUSH, &options) == -1)
|
||||||
|
{
|
||||||
|
SerialPortClose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* turn on DTR */
|
||||||
|
iFlags = TIOCM_DTR;
|
||||||
|
ioctl(portHandle, TIOCMBIS, &iFlags);
|
||||||
|
/* success */
|
||||||
|
return true;
|
||||||
|
} /*** end of SerialPortOpen ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Closes the connection with the serial port.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
void SerialPortClose(void)
|
||||||
|
{
|
||||||
|
/* close the port handle if valid */
|
||||||
|
if (portHandle != SERIALPORT_INVALID_HANDLE)
|
||||||
|
{
|
||||||
|
close(portHandle);
|
||||||
|
}
|
||||||
|
/* invalidate handle */
|
||||||
|
portHandle = SERIALPORT_INVALID_HANDLE;
|
||||||
|
} /*** end of SerialPortClose ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Writes data to the serial port.
|
||||||
|
** \param data Pointer to byte array with data to write.
|
||||||
|
** \param length Number of bytes to write.
|
||||||
|
** \return True if successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool SerialPortWrite(uint8_t *data, uint32_t length)
|
||||||
|
{
|
||||||
|
size_t bytesWritten;
|
||||||
|
|
||||||
|
/* check parameters */
|
||||||
|
assert(data != NULL);
|
||||||
|
assert(length > 0);
|
||||||
|
|
||||||
|
/* submit the data for sending */
|
||||||
|
bytesWritten = write(portHandle, data, length);
|
||||||
|
/* check and return the result */
|
||||||
|
return (bytesWritten == length);
|
||||||
|
} /*** end of SerialPortWrite ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Reads data from the serial port in a blocking manner.
|
||||||
|
** \param data Pointer to byte array to store read data.
|
||||||
|
** \param length Number of bytes to read.
|
||||||
|
** \return True if successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool SerialPortRead(uint8_t *data, uint32_t length)
|
||||||
|
{
|
||||||
|
size_t bytesRead;
|
||||||
|
|
||||||
|
/* check parameters */
|
||||||
|
assert(data != NULL);
|
||||||
|
assert(length > 0);
|
||||||
|
|
||||||
|
/* attempt to read the requested data */
|
||||||
|
bytesRead = read(portHandle, data, length);
|
||||||
|
/* check and return the result */
|
||||||
|
return (bytesRead == length);
|
||||||
|
} /*** end of SerialPortRead ***/
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** end of serialport.c *******************************/
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* C O P Y R I G H T
|
* C O P Y R I G H T
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* Copyright (c) 2014 by Feaser http://www.feaser.com All rights reserved
|
* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved
|
||||||
*
|
*
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* L I C E N S E
|
* L I C E N S E
|
||||||
|
@ -29,11 +29,10 @@
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Include files
|
* Include files
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
#include <assert.h> /* assertion module */
|
#include <stddef.h> /* for NULL declaration */
|
||||||
#include <sb_types.h> /* C types */
|
|
||||||
#include <unistd.h> /* UNIX standard functions */
|
#include <unistd.h> /* UNIX standard functions */
|
||||||
#include <fcntl.h> /* file control definitions */
|
|
||||||
#include <sys/time.h> /* time definitions */
|
#include <sys/time.h> /* time definitions */
|
||||||
|
#include "timeutil.h" /* for time utilities module */
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
/************************************************************************************//**
|
||||||
|
@ -41,16 +40,16 @@
|
||||||
** \return Time in milliseconds.
|
** \return Time in milliseconds.
|
||||||
**
|
**
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
sb_uint32 TimeUtilGetSystemTimeMs(void)
|
uint32_t TimeUtilGetSystemTimeMs(void)
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
if (gettimeofday(&tv, SB_NULL) != 0)
|
if (gettimeofday(&tv, NULL) != 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (sb_uint32)((tv.tv_sec * 1000ul) + (tv.tv_usec / 1000ul));
|
return (uint32_t)((tv.tv_sec * 1000ul) + (tv.tv_usec / 1000ul));
|
||||||
} /*** end of XcpTransportClose ***/
|
} /*** end of XcpTransportClose ***/
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,10 +59,11 @@ sb_uint32 TimeUtilGetSystemTimeMs(void)
|
||||||
** \return none.
|
** \return none.
|
||||||
**
|
**
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
void TimeUtilDelayMs(sb_uint16 delay)
|
void TimeUtilDelayMs(uint16_t delay)
|
||||||
{
|
{
|
||||||
usleep(1000 * delay);
|
usleep(1000 * delay);
|
||||||
} /*** end of TimeUtilDelayMs **/
|
} /*** end of TimeUtilDelayMs **/
|
||||||
|
|
||||||
|
|
||||||
/*********************************** end of xcptransport.c *****************************/
|
/*********************************** end of timeutil.c *********************************/
|
||||||
|
|
||||||
|
|
|
@ -1,304 +0,0 @@
|
||||||
/************************************************************************************//**
|
|
||||||
* \file port\linux\xcptransport.c
|
|
||||||
* \brief XCP transport layer interface source file.
|
|
||||||
* \ingroup SerialBoot
|
|
||||||
* \internal
|
|
||||||
*----------------------------------------------------------------------------------------
|
|
||||||
* C O P Y R I G H T
|
|
||||||
*----------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) 2014 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> /* assertion module */
|
|
||||||
#include <sb_types.h> /* C types */
|
|
||||||
#include <stdio.h> /* standard I/O library */
|
|
||||||
#include <string.h> /* string function definitions */
|
|
||||||
#include <unistd.h> /* UNIX standard functions */
|
|
||||||
#include <fcntl.h> /* file control definitions */
|
|
||||||
#include <errno.h> /* error number definitions */
|
|
||||||
#include <termios.h> /* POSIX terminal control */
|
|
||||||
#include <sys/ioctl.h> /* system I/O control */
|
|
||||||
#include "xcpmaster.h" /* XCP master protocol module */
|
|
||||||
#include "timeutil.h" /* time utility module */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Macro definitions
|
|
||||||
****************************************************************************************/
|
|
||||||
/** \brief Invalid UART device/file handle. */
|
|
||||||
#define UART_INVALID_HANDLE (-1)
|
|
||||||
|
|
||||||
/** \brief maximum number of bytes in a transmit/receive XCP packet in UART. */
|
|
||||||
#define XCP_MASTER_UART_MAX_DATA ((XCP_MASTER_TX_MAX_DATA>XCP_MASTER_RX_MAX_DATA) ? \
|
|
||||||
(XCP_MASTER_TX_MAX_DATA+1) : (XCP_MASTER_RX_MAX_DATA+1))
|
|
||||||
|
|
||||||
/** \brief The smallest time in millisecond that the UART is configured for. */
|
|
||||||
#define UART_RX_TIMEOUT_MIN_MS (100)
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Function prototypes
|
|
||||||
****************************************************************************************/
|
|
||||||
static speed_t XcpTransportGetBaudrateMask(sb_uint32 baudrate);
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Local data declarations
|
|
||||||
****************************************************************************************/
|
|
||||||
static tXcpTransportResponsePacket responsePacket;
|
|
||||||
static sb_int32 hUart = UART_INVALID_HANDLE;
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Initializes the communication interface used by this transport layer.
|
|
||||||
** \param device Serial communication device name. For example "COM4".
|
|
||||||
** \param baudrate Communication speed in bits/sec.
|
|
||||||
** \return SB_TRUE if successful, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpTransportInit(sb_char *device, sb_uint32 baudrate)
|
|
||||||
{
|
|
||||||
struct termios options;
|
|
||||||
int iFlags;
|
|
||||||
|
|
||||||
/* open the port */
|
|
||||||
hUart = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
|
|
||||||
/* verify the result */
|
|
||||||
if (hUart == UART_INVALID_HANDLE)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* configure the device to block during read operations */
|
|
||||||
if (fcntl(hUart, F_SETFL, 0) == -1)
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* get the current options for the port */
|
|
||||||
if (tcgetattr(hUart, &options) == -1)
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* configure the baudrate */
|
|
||||||
if (cfsetispeed(&options, XcpTransportGetBaudrateMask(baudrate)) == -1)
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
if (cfsetospeed(&options, XcpTransportGetBaudrateMask(baudrate)) == -1)
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* enable the receiver and set local mode */
|
|
||||||
options.c_cflag |= (CLOCAL | CREAD);
|
|
||||||
/* configure 8-n-1 */
|
|
||||||
options.c_cflag &= ~PARENB;
|
|
||||||
options.c_cflag &= ~CSTOPB;
|
|
||||||
options.c_cflag &= ~CSIZE;
|
|
||||||
options.c_cflag |= CS8;
|
|
||||||
/* disable hardware flow control */
|
|
||||||
options.c_cflag &= ~CRTSCTS;
|
|
||||||
/* configure raw input */
|
|
||||||
options.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOE);
|
|
||||||
/* configure raw output */
|
|
||||||
options.c_oflag &= ~OPOST;
|
|
||||||
/* configure timeouts */
|
|
||||||
options.c_cc[VMIN] = 0;
|
|
||||||
options.c_cc[VTIME] = UART_RX_TIMEOUT_MIN_MS/100; /* 1/10th of a second */
|
|
||||||
/* set the new options for the port */
|
|
||||||
if (tcsetattr(hUart, TCSAFLUSH, &options) == -1)
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* turn on DTR */
|
|
||||||
iFlags = TIOCM_DTR;
|
|
||||||
ioctl(hUart, TIOCMBIS, &iFlags);
|
|
||||||
/* success */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpTransportInit ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Transmits an XCP packet on the transport layer and attemps to receive the
|
|
||||||
** response within the given timeout. The data in the response packet is
|
|
||||||
** stored in an internal data buffer that can be obtained through function
|
|
||||||
** XcpTransportReadResponsePacket().
|
|
||||||
** \return SB_TRUE is the response packet was successfully received and stored,
|
|
||||||
** SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpTransportSendPacket(sb_uint8 *data, sb_uint8 len, sb_uint16 timeOutMs)
|
|
||||||
{
|
|
||||||
sb_uint16 cnt;
|
|
||||||
static sb_uint8 xcpUartBuffer[XCP_MASTER_UART_MAX_DATA]; /* static to lower stack load */
|
|
||||||
sb_uint16 xcpUartLen;
|
|
||||||
sb_int32 bytesSent;
|
|
||||||
sb_int32 bytesToRead;
|
|
||||||
sb_int32 bytesRead;
|
|
||||||
sb_uint8 *uartReadDataPtr;
|
|
||||||
sb_uint32 timeoutTime;
|
|
||||||
sb_uint32 nowTime;
|
|
||||||
ssize_t result;
|
|
||||||
|
|
||||||
/* ------------------------ XCP packet transmission -------------------------------- */
|
|
||||||
/* prepare the XCP packet for transmission on UART. this is basically the same as the
|
|
||||||
* xcp packet data but just the length of the packet is added to the first byte.
|
|
||||||
*/
|
|
||||||
xcpUartLen = len+1;
|
|
||||||
xcpUartBuffer[0] = len;
|
|
||||||
for (cnt=0; cnt<len; cnt++)
|
|
||||||
{
|
|
||||||
xcpUartBuffer[cnt+1] = data[cnt];
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesSent = write(hUart, xcpUartBuffer, xcpUartLen);
|
|
||||||
|
|
||||||
if (bytesSent != xcpUartLen)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------ XCP packet reception ----------------------------------- */
|
|
||||||
/* determine timeout time */
|
|
||||||
timeoutTime = TimeUtilGetSystemTimeMs() + timeOutMs + UART_RX_TIMEOUT_MIN_MS;
|
|
||||||
|
|
||||||
/* read the first byte, which contains the length of the xcp packet that follows */
|
|
||||||
bytesToRead = 1;
|
|
||||||
responsePacket.len = 0;
|
|
||||||
while(bytesToRead > 0)
|
|
||||||
{
|
|
||||||
result = read(hUart, &responsePacket.len, bytesToRead);
|
|
||||||
if (result != -1)
|
|
||||||
{
|
|
||||||
bytesRead = result;
|
|
||||||
/* one byte should be read and it should contain the packet length, which cannot be 0 */
|
|
||||||
if ((bytesRead == bytesToRead) && (responsePacket.len > 0))
|
|
||||||
{
|
|
||||||
/* valid packet length received so stop this loop to continue with the reception
|
|
||||||
* remaining packet bytes
|
|
||||||
*/
|
|
||||||
bytesToRead = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* check for timeout if not yet done */
|
|
||||||
if ( (bytesToRead > 0) && (TimeUtilGetSystemTimeMs() >= timeoutTime) )
|
|
||||||
{
|
|
||||||
/* timeout occurred */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read the rest of the packet */
|
|
||||||
bytesToRead = responsePacket.len;
|
|
||||||
uartReadDataPtr = &responsePacket.data[0];
|
|
||||||
while(bytesToRead > 0)
|
|
||||||
{
|
|
||||||
result = read(hUart, uartReadDataPtr, bytesToRead);
|
|
||||||
if (result != -1)
|
|
||||||
{
|
|
||||||
bytesRead = result;
|
|
||||||
/* update the bytes that were already read */
|
|
||||||
uartReadDataPtr += bytesRead;
|
|
||||||
bytesToRead -= bytesRead;
|
|
||||||
}
|
|
||||||
/* check for timeout if not yet done */
|
|
||||||
if ( (bytesToRead > 0) && (TimeUtilGetSystemTimeMs() >= timeoutTime) )
|
|
||||||
{
|
|
||||||
/* timeout occurred */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* still here so the complete packet was received */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterTpSendPacket ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Reads the data from the response packet. Make sure to not call this
|
|
||||||
** function while XcpTransportSendPacket() is active, because the data won't be
|
|
||||||
** valid then.
|
|
||||||
** \return Pointer to the response packet data.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
tXcpTransportResponsePacket *XcpTransportReadResponsePacket(void)
|
|
||||||
{
|
|
||||||
return &responsePacket;
|
|
||||||
} /*** end of XcpTransportReadResponsePacket ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Closes the communication channel.
|
|
||||||
** \return none.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
void XcpTransportClose(void)
|
|
||||||
{
|
|
||||||
/* close the COM port handle if valid */
|
|
||||||
if (hUart != UART_INVALID_HANDLE)
|
|
||||||
{
|
|
||||||
close(hUart);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set handles to invalid */
|
|
||||||
hUart = UART_INVALID_HANDLE;
|
|
||||||
} /*** end of XcpTransportClose ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Converts the baudrate value to a bitmask value used by termios. Currently
|
|
||||||
** supports the most commonly used baudrates.
|
|
||||||
** \return none.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static speed_t XcpTransportGetBaudrateMask(sb_uint32 baudrate)
|
|
||||||
{
|
|
||||||
speed_t result;
|
|
||||||
|
|
||||||
switch (baudrate)
|
|
||||||
{
|
|
||||||
case 115200:
|
|
||||||
result = B115200;
|
|
||||||
break;
|
|
||||||
case 57600:
|
|
||||||
result = B57600;
|
|
||||||
break;
|
|
||||||
case 38400:
|
|
||||||
result = B38400;
|
|
||||||
break;
|
|
||||||
case 19200:
|
|
||||||
result = B19200;
|
|
||||||
break;
|
|
||||||
case 9600:
|
|
||||||
default:
|
|
||||||
result = B9600;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} /*** end of XcpTransportGetBaudrateMask ***/
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************** end of xcptransport.c *****************************/
|
|
|
@ -1,265 +0,0 @@
|
||||||
/************************************************************************************//**
|
|
||||||
* \file port\win32\xcptransport.c
|
|
||||||
* \brief XCP transport layer interface source file.
|
|
||||||
* \ingroup SerialBoot
|
|
||||||
* \internal
|
|
||||||
*----------------------------------------------------------------------------------------
|
|
||||||
* C O P Y R I G H T
|
|
||||||
*----------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) 2014 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> /* assertion module */
|
|
||||||
#include <sb_types.h> /* C types */
|
|
||||||
#include <windows.h> /* for WIN32 library */
|
|
||||||
#include <string.h> /* string library */
|
|
||||||
#include "xcpmaster.h" /* XCP master protocol module */
|
|
||||||
#include "timeutil.h" /* time utility module */
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Macro definitions
|
|
||||||
****************************************************************************************/
|
|
||||||
#define UART_TX_BUFFER_SIZE (1024) /**< transmission buffer size */
|
|
||||||
#define UART_RX_BUFFER_SIZE (1024) /**< reception buffer size */
|
|
||||||
|
|
||||||
/** \brief maximum number of bytes in a transmit/receive XCP packet in UART. */
|
|
||||||
#define XCP_MASTER_UART_MAX_DATA ((XCP_MASTER_TX_MAX_DATA>XCP_MASTER_RX_MAX_DATA) ? \
|
|
||||||
(XCP_MASTER_TX_MAX_DATA+1) : (XCP_MASTER_RX_MAX_DATA+1))
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Local data declarations
|
|
||||||
****************************************************************************************/
|
|
||||||
static tXcpTransportResponsePacket responsePacket;
|
|
||||||
static HANDLE hUart = INVALID_HANDLE_VALUE;
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Initializes the communication interface used by this transport layer.
|
|
||||||
** \param device Serial communication device name. For example "COM4".
|
|
||||||
** \param baudrate Communication speed in bits/sec.
|
|
||||||
** \return SB_TRUE if successful, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpTransportInit(sb_char *device, sb_uint32 baudrate)
|
|
||||||
{
|
|
||||||
COMMTIMEOUTS timeouts = { 0 };
|
|
||||||
DCB dcbSerialParams = { 0 };
|
|
||||||
char portStr[64] = "\\\\.\\\0";
|
|
||||||
|
|
||||||
/* construct the COM port name as a string */
|
|
||||||
strcat_s(portStr, 59, device);
|
|
||||||
|
|
||||||
/* obtain access to the COM port */
|
|
||||||
hUart = CreateFile(portStr, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
|
||||||
FILE_ATTRIBUTE_NORMAL, 0);
|
|
||||||
|
|
||||||
/* validate COM port handle */
|
|
||||||
if (hUart == INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get current COM port configuration */
|
|
||||||
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
|
|
||||||
if (!GetCommState(hUart, &dcbSerialParams))
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* configure the baudrate and 8,n,1 */
|
|
||||||
dcbSerialParams.BaudRate = baudrate;
|
|
||||||
dcbSerialParams.ByteSize = 8;
|
|
||||||
dcbSerialParams.StopBits = ONESTOPBIT;
|
|
||||||
dcbSerialParams.Parity = NOPARITY;
|
|
||||||
dcbSerialParams.fOutX = FALSE;
|
|
||||||
dcbSerialParams.fInX = FALSE;
|
|
||||||
dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
|
|
||||||
dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;
|
|
||||||
|
|
||||||
if (!SetCommState(hUart, &dcbSerialParams))
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set communication timeout parameters */
|
|
||||||
timeouts.ReadIntervalTimeout = 0;
|
|
||||||
timeouts.ReadTotalTimeoutConstant = 0;
|
|
||||||
timeouts.ReadTotalTimeoutMultiplier = 100;
|
|
||||||
timeouts.WriteTotalTimeoutConstant = 0;
|
|
||||||
timeouts.WriteTotalTimeoutMultiplier = 100;
|
|
||||||
|
|
||||||
if (!SetCommTimeouts(hUart, &timeouts))
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set transmit and receive buffer sizes */
|
|
||||||
if(!SetupComm(hUart, UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE))
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* empty the transmit and receive buffers */
|
|
||||||
if (!FlushFileBuffers(hUart))
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* successfully connected to the serial device */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpTransportInit ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Transmits an XCP packet on the transport layer and attemps to receive the
|
|
||||||
** response within the given timeout. The data in the response packet is
|
|
||||||
** stored in an internal data buffer that can be obtained through function
|
|
||||||
** XcpTransportReadResponsePacket().
|
|
||||||
** \return SB_TRUE is the response packet was successfully received and stored,
|
|
||||||
** SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpTransportSendPacket(sb_uint8 *data, sb_uint8 len, sb_uint16 timeOutMs)
|
|
||||||
{
|
|
||||||
sb_uint32 dwWritten = 0;
|
|
||||||
sb_uint32 dwRead = 0;
|
|
||||||
sb_uint32 dwToRead;
|
|
||||||
sb_uint16 cnt;
|
|
||||||
static sb_uint8 xcpUartBuffer[XCP_MASTER_UART_MAX_DATA]; /* static to lower stack load */
|
|
||||||
sb_uint16 xcpUartLen;
|
|
||||||
sb_uint8 *uartReadDataPtr;
|
|
||||||
sb_uint32 timeoutTime;
|
|
||||||
|
|
||||||
/* ------------------------ XCP packet transmission -------------------------------- */
|
|
||||||
/* prepare the XCP packet for transmission on UART. this is basically the same as the
|
|
||||||
* xcp packet data but just the length of the packet is added to the first byte.
|
|
||||||
*/
|
|
||||||
xcpUartLen = len+1;
|
|
||||||
xcpUartBuffer[0] = len;
|
|
||||||
for (cnt=0; cnt<len; cnt++)
|
|
||||||
{
|
|
||||||
xcpUartBuffer[cnt+1] = data[cnt];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* first submit the XCP packet for transmission */
|
|
||||||
if (!WriteFile(hUart, xcpUartBuffer, xcpUartLen, &dwWritten, SB_NULL))
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* double check that all bytes were actually transmitted */
|
|
||||||
if (dwWritten != xcpUartLen)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------ XCP packet reception ----------------------------------- */
|
|
||||||
/* determine timeout time */
|
|
||||||
timeoutTime = TimeUtilGetSystemTimeMs() + timeOutMs + 100;
|
|
||||||
|
|
||||||
/* read the first byte, which contains the length of the xcp packet that follows */
|
|
||||||
dwToRead = 1;
|
|
||||||
responsePacket.len = 0;
|
|
||||||
while(dwToRead > 0)
|
|
||||||
{
|
|
||||||
dwRead = 0;
|
|
||||||
if (ReadFile(hUart, &responsePacket.len, dwToRead, &dwRead, NULL))
|
|
||||||
{
|
|
||||||
/* one byte should be read and it should contain the packet length, which cannot be 0 */
|
|
||||||
if ((dwRead == dwToRead) && (responsePacket.len > 0))
|
|
||||||
{
|
|
||||||
/* valid packet length received so stop this loop to continue with the reception
|
|
||||||
* remaining packet bytes
|
|
||||||
*/
|
|
||||||
dwToRead = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* check for timeout if not yet done */
|
|
||||||
if ( (dwToRead > 0) && (TimeUtilGetSystemTimeMs() >= timeoutTime) )
|
|
||||||
{
|
|
||||||
/* timeout occurred */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read the rest of the packet */
|
|
||||||
dwToRead = responsePacket.len;
|
|
||||||
uartReadDataPtr = &responsePacket.data[0];
|
|
||||||
while(dwToRead > 0)
|
|
||||||
{
|
|
||||||
dwRead = 0;
|
|
||||||
if (ReadFile(hUart, uartReadDataPtr, dwToRead, &dwRead, NULL))
|
|
||||||
{
|
|
||||||
/* update the bytes that were already read */
|
|
||||||
uartReadDataPtr += dwRead;
|
|
||||||
dwToRead -= dwRead;
|
|
||||||
}
|
|
||||||
/* check for timeout if not yet done */
|
|
||||||
if ( (dwToRead > 0) && (TimeUtilGetSystemTimeMs() >= timeoutTime) )
|
|
||||||
{
|
|
||||||
/* timeout occurred */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* still here so the complete packet was received */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterTpSendPacket ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Reads the data from the response packet. Make sure to not call this
|
|
||||||
** function while XcpTransportSendPacket() is active, because the data won't be
|
|
||||||
** valid then.
|
|
||||||
** \return Pointer to the response packet data.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
tXcpTransportResponsePacket *XcpTransportReadResponsePacket(void)
|
|
||||||
{
|
|
||||||
return &responsePacket;
|
|
||||||
} /*** end of XcpTransportReadResponsePacket ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Closes the communication channel.
|
|
||||||
** \return none.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
void XcpTransportClose(void)
|
|
||||||
{
|
|
||||||
/* close the COM port handle if valid */
|
|
||||||
if (hUart != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
CloseHandle(hUart);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set handles to invalid */
|
|
||||||
hUart = INVALID_HANDLE_VALUE;
|
|
||||||
} /*** end of XcpTransportClose ***/
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************** end of xcptransport.c *****************************/
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
/************************************************************************************//**
|
||||||
|
* \file port\windows\serialport.c
|
||||||
|
* \brief Serial port source file.
|
||||||
|
* \ingroup SerialBoot
|
||||||
|
* \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 <stddef.h> /* for NULL declaration */
|
||||||
|
#include <windows.h> /* for windows library */
|
||||||
|
#include <assert.h> /* for assertions */
|
||||||
|
#include "serialport.h" /* serial port module */
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Macro definitions
|
||||||
|
****************************************************************************************/
|
||||||
|
#define UART_TX_BUFFER_SIZE (1024) /**< transmission buffer size */
|
||||||
|
#define UART_RX_BUFFER_SIZE (1024) /**< reception buffer size */
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Local data declarations
|
||||||
|
****************************************************************************************/
|
||||||
|
static HANDLE hUart = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Function prototypes
|
||||||
|
****************************************************************************************/
|
||||||
|
static uint32_t SerialConvertBaudrate(tSerialPortBaudrate baudrate);
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Opens the connection with the serial port configured as 8,N,1 and no flow
|
||||||
|
** control.
|
||||||
|
** \param portname The name of the serial port to open, i.e. COM4.
|
||||||
|
** \param baudrate The desired communication speed.
|
||||||
|
** \return True if successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool SerialPortOpen(char *portname, tSerialPortBaudrate baudrate)
|
||||||
|
{
|
||||||
|
COMMTIMEOUTS timeouts = { 0 };
|
||||||
|
DCB dcbSerialParams = { 0 };
|
||||||
|
char portStr[64] = "\\\\.\\\0";
|
||||||
|
|
||||||
|
/* check parameters */
|
||||||
|
assert(portname != NULL);
|
||||||
|
|
||||||
|
/* construct the COM port name as a string */
|
||||||
|
strcat_s(portStr, 59, portname);
|
||||||
|
|
||||||
|
/* obtain access to the COM port */
|
||||||
|
hUart = CreateFile(portStr, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
|
||||||
|
/* validate COM port handle */
|
||||||
|
if (hUart == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get current COM port configuration */
|
||||||
|
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
|
||||||
|
if (!GetCommState(hUart, &dcbSerialParams))
|
||||||
|
{
|
||||||
|
CloseHandle(hUart);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* configure the baudrate and 8,n,1 */
|
||||||
|
dcbSerialParams.BaudRate = SerialConvertBaudrate(baudrate);
|
||||||
|
dcbSerialParams.ByteSize = 8;
|
||||||
|
dcbSerialParams.StopBits = ONESTOPBIT;
|
||||||
|
dcbSerialParams.Parity = NOPARITY;
|
||||||
|
dcbSerialParams.fOutX = FALSE;
|
||||||
|
dcbSerialParams.fInX = FALSE;
|
||||||
|
dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
|
||||||
|
dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;
|
||||||
|
|
||||||
|
if (!SetCommState(hUart, &dcbSerialParams))
|
||||||
|
{
|
||||||
|
CloseHandle(hUart);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set communication timeout parameters */
|
||||||
|
timeouts.ReadIntervalTimeout = 0;
|
||||||
|
timeouts.ReadTotalTimeoutConstant = 0;
|
||||||
|
timeouts.ReadTotalTimeoutMultiplier = 100;
|
||||||
|
timeouts.WriteTotalTimeoutConstant = 0;
|
||||||
|
timeouts.WriteTotalTimeoutMultiplier = 100;
|
||||||
|
|
||||||
|
if (!SetCommTimeouts(hUart, &timeouts))
|
||||||
|
{
|
||||||
|
CloseHandle(hUart);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set transmit and receive buffer sizes */
|
||||||
|
if (!SetupComm(hUart, UART_RX_BUFFER_SIZE, UART_TX_BUFFER_SIZE))
|
||||||
|
{
|
||||||
|
CloseHandle(hUart);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* empty the transmit and receive buffers */
|
||||||
|
if (!FlushFileBuffers(hUart))
|
||||||
|
{
|
||||||
|
CloseHandle(hUart);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* successfully connected to the serial device */
|
||||||
|
return true;
|
||||||
|
} /*** end of SerialPortOpen ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Closes the connection with the serial port.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
void SerialPortClose(void)
|
||||||
|
{
|
||||||
|
/* close the COM port handle if valid */
|
||||||
|
if (hUart != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
CloseHandle(hUart);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set handles to invalid */
|
||||||
|
hUart = INVALID_HANDLE_VALUE;
|
||||||
|
} /*** end of SerialPortClose ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Writes data to the serial port.
|
||||||
|
** \param data Pointer to byte array with data to write.
|
||||||
|
** \param length Number of bytes to write.
|
||||||
|
** \return True if successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool SerialPortWrite(uint8_t *data, uint32_t length)
|
||||||
|
{
|
||||||
|
uint32_t dwWritten = 0;
|
||||||
|
|
||||||
|
/* check parameters */
|
||||||
|
assert(data != NULL);
|
||||||
|
assert(length > 0);
|
||||||
|
|
||||||
|
/* submit the data for transmission with the serial port */
|
||||||
|
if (!WriteFile(hUart, data, length, &dwWritten, NULL))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* double check that all bytes were actually transmitted */
|
||||||
|
if (dwWritten != length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return true;
|
||||||
|
} /*** end of SerialPortWrite ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Reads data from the serial port in a blocking manner.
|
||||||
|
** \param data Pointer to byte array to store read data.
|
||||||
|
** \param length Number of bytes to read.
|
||||||
|
** \return True if successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool SerialPortRead(uint8_t *data, uint32_t length)
|
||||||
|
{
|
||||||
|
uint32_t dwRead = 0;
|
||||||
|
|
||||||
|
/* check parameters */
|
||||||
|
assert(data != NULL);
|
||||||
|
assert(length > 0);
|
||||||
|
|
||||||
|
/* attempt to read data from the serial port */
|
||||||
|
if (!ReadFile(hUart, data, length, &dwRead, NULL))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* double check that all bytes were actually read */
|
||||||
|
if (dwRead != length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return true;
|
||||||
|
} /*** end of SerialPortRead ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Opens the connection with the serial port configured as 8,N,1 and no flow
|
||||||
|
** control.
|
||||||
|
** \param portname The name of the serial port to open, i.e. COM4.
|
||||||
|
** \param baudrate The desired communication speed.
|
||||||
|
** \return True if successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static uint32_t SerialConvertBaudrate(tSerialPortBaudrate baudrate)
|
||||||
|
{
|
||||||
|
uint32_t result;
|
||||||
|
|
||||||
|
switch (baudrate)
|
||||||
|
{
|
||||||
|
case SERIALPORT_BR9600:
|
||||||
|
result = CBR_9600;
|
||||||
|
break;
|
||||||
|
case SERIALPORT_BR19200:
|
||||||
|
result = CBR_19200;
|
||||||
|
break;
|
||||||
|
case SERIALPORT_BR38400:
|
||||||
|
result = CBR_38400;
|
||||||
|
break;
|
||||||
|
case SERIALPORT_BR57600:
|
||||||
|
result = CBR_57600;
|
||||||
|
break;
|
||||||
|
case SERIALPORT_BR115200:
|
||||||
|
result = CBR_115200;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = CBR_9600;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} /*** end of SerialConvertBaudrate ***/
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** end of serialport.c *******************************/
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
/************************************************************************************//**
|
/************************************************************************************//**
|
||||||
* \file port\win32\timeutil.c
|
* \file port\windows\timeutil.c
|
||||||
* \brief Time utility source file.
|
* \brief Time utility source file.
|
||||||
* \ingroup SerialBoot
|
* \ingroup SerialBoot
|
||||||
* \internal
|
* \internal
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* C O P Y R I G H T
|
* C O P Y R I G H T
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* Copyright (c) 2014 by Feaser http://www.feaser.com All rights reserved
|
* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved
|
||||||
*
|
*
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* L I C E N S E
|
* L I C E N S E
|
||||||
|
@ -29,9 +29,8 @@
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Include files
|
* Include files
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
#include <assert.h> /* assertion module */
|
#include <windows.h> /* for windows library */
|
||||||
#include <sb_types.h> /* C types */
|
#include "timeutil.h" /* for time utilities module */
|
||||||
#include <windows.h> /* for WIN32 library */
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
/************************************************************************************//**
|
||||||
|
@ -39,7 +38,7 @@
|
||||||
** \return Time in milliseconds.
|
** \return Time in milliseconds.
|
||||||
**
|
**
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
sb_uint32 TimeUtilGetSystemTimeMs(void)
|
uint32_t TimeUtilGetSystemTimeMs(void)
|
||||||
{
|
{
|
||||||
return GetTickCount();
|
return GetTickCount();
|
||||||
} /*** end of XcpTransportClose ***/
|
} /*** end of XcpTransportClose ***/
|
||||||
|
@ -51,10 +50,11 @@ sb_uint32 TimeUtilGetSystemTimeMs(void)
|
||||||
** \return none.
|
** \return none.
|
||||||
**
|
**
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
void TimeUtilDelayMs(sb_uint16 delay)
|
void TimeUtilDelayMs(uint16_t delay)
|
||||||
{
|
{
|
||||||
Sleep(delay);
|
Sleep(delay);
|
||||||
} /*** end of TimeUtilDelayMs **/
|
} /*** end of TimeUtilDelayMs **/
|
||||||
|
|
||||||
|
|
||||||
/*********************************** end of timeutil.c *********************************/
|
/*********************************** end of timeutil.c *********************************/
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
/************************************************************************************//**
|
/************************************************************************************//**
|
||||||
* \file xcpmaster.h
|
* \file serialport.h
|
||||||
* \brief XCP Master header file.
|
* \brief Serial port header file.
|
||||||
* \ingroup SerialBoot
|
* \ingroup SerialBoot
|
||||||
* \internal
|
* \internal
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* C O P Y R I G H T
|
* C O P Y R I G H T
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* Copyright (c) 2014 by Feaser http://www.feaser.com All rights reserved
|
* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved
|
||||||
*
|
*
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* L I C E N S E
|
* L I C E N S E
|
||||||
|
@ -25,42 +25,46 @@
|
||||||
*
|
*
|
||||||
* \endinternal
|
* \endinternal
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
#ifndef XCPMASTER_H
|
#ifndef SERIALPORT_H
|
||||||
#define XCPMASTER_H
|
#define SERIALPORT_H
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Macro definitions
|
|
||||||
****************************************************************************************/
|
|
||||||
/** \brief Configure number of bytes in the master->slave data packet. Should be at least
|
|
||||||
* equal or larger than that configured on the slave.
|
|
||||||
*/
|
|
||||||
#define XCP_MASTER_TX_MAX_DATA (255)
|
|
||||||
|
|
||||||
/** \brief Configure number of bytes in the slave->master data packet. Should be at least
|
|
||||||
* equal or larger than that configured on the slave.
|
|
||||||
*/
|
|
||||||
#define XCP_MASTER_RX_MAX_DATA (255)
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Include files
|
* Include files
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
#include "xcptransport.h" /* XCP transport layer */
|
#include <stdint.h> /* for standard integer types */
|
||||||
|
#include <stdbool.h> /* for boolean type */
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Typde definitions
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief Enumaration of the supported baudrates. */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SERIALPORT_BR9600 = 0, /**< 9600 bits/sec */
|
||||||
|
SERIALPORT_BR19200 = 1, /**< 19200 bits/sec */
|
||||||
|
SERIALPORT_BR38400 = 2, /**< 38400 bits/sec */
|
||||||
|
SERIALPORT_BR57600 = 3, /**< 57600 bits/sec */
|
||||||
|
SERIALPORT_BR115200 = 4 /**< 115200 bits/sec */
|
||||||
|
} tSerialPortBaudrate;
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Function prototypes
|
* Function prototypes
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
sb_uint8 XcpMasterInit(sb_char *device, sb_uint32 baudrate);
|
bool SerialPortOpen(char *portname, tSerialPortBaudrate baudrate);
|
||||||
void XcpMasterDeinit(void);
|
void SerialPortClose(void);
|
||||||
sb_uint8 XcpMasterConnect(void);
|
bool SerialPortWrite(uint8_t *data, uint32_t length);
|
||||||
sb_uint8 XcpMasterDisconnect(void);
|
bool SerialPortRead(uint8_t *data, uint32_t length);
|
||||||
sb_uint8 XcpMasterStartProgrammingSession(void);
|
|
||||||
sb_uint8 XcpMasterStopProgrammingSession(void);
|
|
||||||
sb_uint8 XcpMasterClearMemory(sb_uint32 addr, sb_uint32 len);
|
|
||||||
sb_uint8 XcpMasterReadData(sb_uint32 addr, sb_uint32 len, sb_uint8 data[]);
|
|
||||||
sb_uint8 XcpMasterProgramData(sb_uint32 addr, sb_uint32 len, sb_uint8 data[]);
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SERIALPORT_H */
|
||||||
|
/********************************* end of serialport.h *********************************/
|
||||||
|
|
||||||
#endif /* XCPMASTER_H */
|
|
||||||
/*********************************** end of xcpmaster.h ********************************/
|
|
|
@ -1,448 +0,0 @@
|
||||||
/************************************************************************************//**
|
|
||||||
* \file srecord.c
|
|
||||||
* \brief Motorola S-record library header file.
|
|
||||||
* \ingroup SerialBoot
|
|
||||||
* \internal
|
|
||||||
*----------------------------------------------------------------------------------------
|
|
||||||
* C O P Y R I G H T
|
|
||||||
*----------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) 2014 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> /* assertion module */
|
|
||||||
#include <sb_types.h> /* C types */
|
|
||||||
#include <string.h> /* for strcpy etc. */
|
|
||||||
#include <ctype.h> /* for toupper() etc. */
|
|
||||||
#include "srecord.h" /* S-record library */
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Type definitions
|
|
||||||
****************************************************************************************/
|
|
||||||
/** \brief Enumeration for the different S-record line types. */
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
LINE_TYPE_S1, /**< 16-bit address line */
|
|
||||||
LINE_TYPE_S2, /**< 24-bit address line */
|
|
||||||
LINE_TYPE_S3, /**< 32-bit address line */
|
|
||||||
LINE_TYPE_UNSUPPORTED /**< unsupported line */
|
|
||||||
} tSrecordLineType;
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Function prototypes
|
|
||||||
****************************************************************************************/
|
|
||||||
static tSrecordLineType SrecordGetLineType(const sb_char *line);
|
|
||||||
static sb_uint8 SrecordVerifyChecksum(const sb_char *line);
|
|
||||||
static sb_uint8 SrecordHexStringToByte(const sb_char *hexstring);
|
|
||||||
static sb_uint8 SrecordReadLine(sb_file srecordHandle, sb_char *line);
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Checks if the specified srecordFile exists and contains s-records.
|
|
||||||
** \param srecordFile The S-record file with full path if applicable.
|
|
||||||
** \return SB_TRUE on the S-record is valid, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 SrecordIsValid(const sb_char *srecordFile)
|
|
||||||
{
|
|
||||||
sb_file tempHandle;
|
|
||||||
sb_char line[SRECORD_MAX_CHARS_PER_LINE];
|
|
||||||
|
|
||||||
/* attempt to open the file */
|
|
||||||
tempHandle = SrecordOpen(srecordFile);
|
|
||||||
/* is the file available? */
|
|
||||||
if (tempHandle == SB_NULL)
|
|
||||||
{
|
|
||||||
/* cannot open the file */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* all lines should be formatted as S-records. read the first one to check this */
|
|
||||||
if (SrecordReadLine(tempHandle, line) == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* could not read a line. file must be empty */
|
|
||||||
SrecordClose(tempHandle);
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* check if the line starts with the 'S' character, followed by a digit */
|
|
||||||
if ( (toupper(line[0]) != 'S') || (isdigit(line[1]) == 0) )
|
|
||||||
{
|
|
||||||
SrecordClose(tempHandle);
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* still here so it is a valid s-record */
|
|
||||||
SrecordClose(tempHandle);
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of SrecordIsValid ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Opens the S-record file for reading.
|
|
||||||
** \param srecordFile The S-record file with full path if applicable.
|
|
||||||
** \return The filehandle if successful, SB_NULL otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_file SrecordOpen(const sb_char *srecordFile)
|
|
||||||
{
|
|
||||||
/* open the file for reading */
|
|
||||||
return fopen(srecordFile, "r");
|
|
||||||
} /*** end of SrecordOpen ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Parse the S-record file to obtain information about its contents.
|
|
||||||
** \param srecordHandle The S-record file handle. It is returned by SrecordOpen.
|
|
||||||
** \param parseResults Pointer to where the parse results should be stored.
|
|
||||||
** \return none.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
void SrecordParse(sb_file srecordHandle, tSrecordParseResults *parseResults)
|
|
||||||
{
|
|
||||||
tSrecordLineParseResults lineResults;
|
|
||||||
|
|
||||||
/* start at the beginning of the file */
|
|
||||||
rewind(srecordHandle);
|
|
||||||
|
|
||||||
/* init data structure */
|
|
||||||
parseResults->address_high = 0;
|
|
||||||
parseResults->address_low = 0xffffffff;
|
|
||||||
parseResults->data_bytes_total = 0;
|
|
||||||
|
|
||||||
/* loop through all S-records with program data */
|
|
||||||
while (SrecordParseNextDataLine(srecordHandle, &lineResults) == SB_TRUE)
|
|
||||||
{
|
|
||||||
/* update byte total */
|
|
||||||
parseResults->data_bytes_total += lineResults.length;
|
|
||||||
/* is this a new lowest address? */
|
|
||||||
if (lineResults.address < parseResults->address_low)
|
|
||||||
{
|
|
||||||
parseResults->address_low = lineResults.address;
|
|
||||||
}
|
|
||||||
/* is this a new highest address? */
|
|
||||||
if ((lineResults.address + lineResults.length - 1) > parseResults->address_high)
|
|
||||||
{
|
|
||||||
parseResults->address_high = (lineResults.address + lineResults.length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* reset to the beginning of the file again */
|
|
||||||
rewind(srecordHandle);
|
|
||||||
} /*** end of SrecordParse ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Closes the S-record file.
|
|
||||||
** \param srecordHandle The S-record file handle. It is returned by SrecordOpen.
|
|
||||||
** \return none.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
void SrecordClose(sb_file srecordHandle)
|
|
||||||
{
|
|
||||||
/* close the file handle if valid */
|
|
||||||
if (srecordHandle != SB_NULL)
|
|
||||||
{
|
|
||||||
fclose(srecordHandle);
|
|
||||||
}
|
|
||||||
} /*** end of SrecordClose ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Reads the next S-record with program data, parses it and returns the
|
|
||||||
** results.
|
|
||||||
** \param srecordHandle The S-record file handle. It is returned by SrecordOpen.
|
|
||||||
** \param parseResults Pointer to where the parse results should be stored.
|
|
||||||
** \return SB_TRUE is valid parse results were stored. SB_FALSE in case of end-of-
|
|
||||||
** file.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 SrecordParseNextDataLine(sb_file srecordHandle, tSrecordLineParseResults *parseResults)
|
|
||||||
{
|
|
||||||
sb_char line[SRECORD_MAX_CHARS_PER_LINE];
|
|
||||||
sb_uint8 data_line_found = SB_FALSE;
|
|
||||||
tSrecordLineType lineType;
|
|
||||||
sb_uint16 bytes_on_line;
|
|
||||||
sb_uint16 i;
|
|
||||||
sb_char *linePtr;
|
|
||||||
|
|
||||||
/* first set the length paramter to 0 */
|
|
||||||
parseResults->length = 0;
|
|
||||||
|
|
||||||
while (data_line_found == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* read the next line from the file */
|
|
||||||
if (SrecordReadLine(srecordHandle, line) == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* end-of-file encountered */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* we now have a line. check if it is a S-record data line */
|
|
||||||
lineType = SrecordGetLineType(line);
|
|
||||||
if (lineType != LINE_TYPE_UNSUPPORTED)
|
|
||||||
{
|
|
||||||
/* check if the checksum on the line is correct */
|
|
||||||
if (SrecordVerifyChecksum(line) == SB_TRUE)
|
|
||||||
{
|
|
||||||
/* found a valid line that can be parsed. loop will stop */
|
|
||||||
data_line_found = SB_TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* still here so we have a valid S-record data line. start parsing */
|
|
||||||
linePtr = &line[0];
|
|
||||||
/* all good so far, now read out the address and databytes for the line */
|
|
||||||
switch (lineType)
|
|
||||||
{
|
|
||||||
/* ---------------------------- S1 line type ------------------------------------- */
|
|
||||||
case LINE_TYPE_S1:
|
|
||||||
/* adjust pointer to point to byte count value */
|
|
||||||
linePtr += 2;
|
|
||||||
/* read out the number of byte values that follow on the line */
|
|
||||||
bytes_on_line = SrecordHexStringToByte(linePtr);
|
|
||||||
/* read out the 16-bit address */
|
|
||||||
linePtr += 2;
|
|
||||||
parseResults->address = SrecordHexStringToByte(linePtr) << 8;
|
|
||||||
linePtr += 2;
|
|
||||||
parseResults->address += SrecordHexStringToByte(linePtr);
|
|
||||||
/* adjust pointer to point to the first data byte after the address */
|
|
||||||
linePtr += 2;
|
|
||||||
/* determine how many data bytes are on the line */
|
|
||||||
parseResults->length = bytes_on_line - 3; /* -2 bytes address, -1 byte checksum */
|
|
||||||
/* read and store data bytes if requested */
|
|
||||||
for (i=0; i<parseResults->length; i++)
|
|
||||||
{
|
|
||||||
parseResults->data[i] = SrecordHexStringToByte(linePtr);
|
|
||||||
linePtr += 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* ---------------------------- S2 line type ------------------------------------- */
|
|
||||||
case LINE_TYPE_S2:
|
|
||||||
/* adjust pointer to point to byte count value */
|
|
||||||
linePtr += 2;
|
|
||||||
/* read out the number of byte values that follow on the line */
|
|
||||||
bytes_on_line = SrecordHexStringToByte(linePtr);
|
|
||||||
/* read out the 32-bit address */
|
|
||||||
linePtr += 2;
|
|
||||||
parseResults->address = SrecordHexStringToByte(linePtr) << 16;
|
|
||||||
linePtr += 2;
|
|
||||||
parseResults->address += SrecordHexStringToByte(linePtr) << 8;
|
|
||||||
linePtr += 2;
|
|
||||||
parseResults->address += SrecordHexStringToByte(linePtr);
|
|
||||||
/* adjust pointer to point to the first data byte after the address */
|
|
||||||
linePtr += 2;
|
|
||||||
/* determine how many data bytes are on the line */
|
|
||||||
parseResults->length = bytes_on_line - 4; /* -3 bytes address, -1 byte checksum */
|
|
||||||
/* read and store data bytes if requested */
|
|
||||||
for (i=0; i<parseResults->length; i++)
|
|
||||||
{
|
|
||||||
parseResults->data[i] = SrecordHexStringToByte(linePtr);
|
|
||||||
linePtr += 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* ---------------------------- S3 line type ------------------------------------- */
|
|
||||||
case LINE_TYPE_S3:
|
|
||||||
/* adjust pointer to point to byte count value */
|
|
||||||
linePtr += 2;
|
|
||||||
/* read out the number of byte values that follow on the line */
|
|
||||||
bytes_on_line = SrecordHexStringToByte(linePtr);
|
|
||||||
/* read out the 32-bit address */
|
|
||||||
linePtr += 2;
|
|
||||||
parseResults->address = SrecordHexStringToByte(linePtr) << 24;
|
|
||||||
linePtr += 2;
|
|
||||||
parseResults->address += SrecordHexStringToByte(linePtr) << 16;
|
|
||||||
linePtr += 2;
|
|
||||||
parseResults->address += SrecordHexStringToByte(linePtr) << 8;
|
|
||||||
linePtr += 2;
|
|
||||||
parseResults->address += SrecordHexStringToByte(linePtr);
|
|
||||||
/* adjust pointer to point to the first data byte after the address */
|
|
||||||
linePtr += 2;
|
|
||||||
/* determine how many data bytes are on the line */
|
|
||||||
parseResults->length = bytes_on_line - 5; /* -4 bytes address, -1 byte checksum */
|
|
||||||
/* read and store data bytes if requested */
|
|
||||||
for (i=0; i<parseResults->length; i++)
|
|
||||||
{
|
|
||||||
parseResults->data[i] = SrecordHexStringToByte(linePtr);
|
|
||||||
linePtr += 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* will not happen */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parsing all done */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of SrecordParseNextDataLine ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Inspects a line from a Motorola S-Record file to determine its type.
|
|
||||||
** \param line A line from the S-Record.
|
|
||||||
** \return the S-Record line type.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static tSrecordLineType SrecordGetLineType(const sb_char *line)
|
|
||||||
{
|
|
||||||
/* check if the line starts with the 'S' character, followed by a digit */
|
|
||||||
if ( (toupper(line[0]) != 'S') || (isdigit(line[1]) == 0) )
|
|
||||||
{
|
|
||||||
/* not a valid S-Record line type */
|
|
||||||
return LINE_TYPE_UNSUPPORTED;
|
|
||||||
}
|
|
||||||
/* determine the line type */
|
|
||||||
if (line[1] == '1')
|
|
||||||
{
|
|
||||||
return LINE_TYPE_S1;
|
|
||||||
}
|
|
||||||
if (line[1] == '2')
|
|
||||||
{
|
|
||||||
return LINE_TYPE_S2;
|
|
||||||
}
|
|
||||||
if (line[1] == '3')
|
|
||||||
{
|
|
||||||
return LINE_TYPE_S3;
|
|
||||||
}
|
|
||||||
/* still here so not a supported line type found */
|
|
||||||
return LINE_TYPE_UNSUPPORTED;
|
|
||||||
} /*** end of SrecordGetLineType ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Inspects an S1, S2 or S3 line from a Motorola S-Record file to
|
|
||||||
** determine if the checksum at the end is corrrect.
|
|
||||||
** \param line An S1, S2 or S3 line from the S-Record.
|
|
||||||
** \return SB_TRUE if the checksum is correct, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 SrecordVerifyChecksum(const sb_char *line)
|
|
||||||
{
|
|
||||||
sb_uint16 bytes_on_line;
|
|
||||||
sb_uint8 checksum = 0;
|
|
||||||
|
|
||||||
/* adjust pointer to point to byte count value */
|
|
||||||
line += 2;
|
|
||||||
/* read out the number of byte values that follow on the line */
|
|
||||||
bytes_on_line = SrecordHexStringToByte(line);
|
|
||||||
/* byte count is part of checksum */
|
|
||||||
checksum += bytes_on_line;
|
|
||||||
/* adjust pointer to the first byte of the address */
|
|
||||||
line += 2;
|
|
||||||
/* add byte values of address and data, but not the final checksum */
|
|
||||||
do
|
|
||||||
{
|
|
||||||
/* add the next byte value to the checksum */
|
|
||||||
checksum += SrecordHexStringToByte(line);
|
|
||||||
/* update counter */
|
|
||||||
bytes_on_line--;
|
|
||||||
/* point to next hex string in the line */
|
|
||||||
line += 2;
|
|
||||||
}
|
|
||||||
while (bytes_on_line > 1);
|
|
||||||
/* the checksum is calculated by summing up the values of the byte count, address and
|
|
||||||
* databytes and then taking the 1-complement of the sum's least signigicant byte */
|
|
||||||
checksum = ~checksum;
|
|
||||||
/* finally verify the calculated checksum with the one at the end of the line */
|
|
||||||
if (checksum != SrecordHexStringToByte(line))
|
|
||||||
{
|
|
||||||
/* checksum incorrect */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* still here so the checksum was correct */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of SrecordVerifyChecksum ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Helper function to convert a sequence of 2 characters that represent
|
|
||||||
** a hexadecimal value to the actual byte value.
|
|
||||||
** Example: SrecordHexStringToByte("2f") --> returns 47.
|
|
||||||
** \param hexstring String beginning with 2 characters that represent a hexa-
|
|
||||||
** decimal value.
|
|
||||||
** \return The resulting byte value.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 SrecordHexStringToByte(const sb_char *hexstring)
|
|
||||||
{
|
|
||||||
sb_uint8 result = 0;
|
|
||||||
sb_char c;
|
|
||||||
sb_uint8 counter;
|
|
||||||
|
|
||||||
/* a hexadecimal character is 2 characters long (i.e 0x4F minus the 0x part) */
|
|
||||||
for (counter=0; counter < 2; counter++)
|
|
||||||
{
|
|
||||||
/* read out the character */
|
|
||||||
c = toupper(hexstring[counter]);
|
|
||||||
/* check that the character is 0..9 or A..F */
|
|
||||||
if ( (c < '0') || (c > 'F') || ( (c > '9') && (c < 'A') ) )
|
|
||||||
{
|
|
||||||
/* character not valid */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* convert character to 4-bit value (check ASCII table for more info) */
|
|
||||||
c -= '0';
|
|
||||||
if (c > 9)
|
|
||||||
{
|
|
||||||
c -= 7;
|
|
||||||
}
|
|
||||||
/* add it to the result */
|
|
||||||
result = (result << 4) + c;
|
|
||||||
}
|
|
||||||
/* return the results */
|
|
||||||
return result;
|
|
||||||
} /*** end of SrecordHexStringToByte ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Reads the next line from the S-record file handle.
|
|
||||||
** \param srecordHandle The S-record file handle. It is returned by SrecordOpen.
|
|
||||||
** \param line Destination buffer for the line characters. Should be of size
|
|
||||||
** SRECORD_MAX_CHARS_PER_LINE.
|
|
||||||
** \return SB_TRUE if successful, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 SrecordReadLine(sb_file srecordHandle, sb_char *line)
|
|
||||||
{
|
|
||||||
/* init the line as an empty line */
|
|
||||||
line[0] = '\0';
|
|
||||||
|
|
||||||
/* loop as long as we find a non-empty line or end-of-file */
|
|
||||||
while (line[0] == '\0')
|
|
||||||
{
|
|
||||||
if (fgets(line, SRECORD_MAX_CHARS_PER_LINE, srecordHandle) == SB_NULL)
|
|
||||||
{
|
|
||||||
/* no more lines available */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* replace the line termination with a string termination */
|
|
||||||
line[strcspn(line, "\n")] = '\0';
|
|
||||||
}
|
|
||||||
/* still here so not EOF and not and empty line, so success */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of SrecordReadLine ***/
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************** end of srecord.c **********************************/
|
|
|
@ -0,0 +1,751 @@
|
||||||
|
/************************************************************************************//**
|
||||||
|
* \file srecparser.c
|
||||||
|
* \brief S-record parser source file.
|
||||||
|
* \ingroup SerialBoot
|
||||||
|
* \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 <stddef.h> /* for NULL declaration */
|
||||||
|
#include <stdio.h> /* for standard I/O library */
|
||||||
|
#include <stdlib.h> /* for standard library */
|
||||||
|
#include <string.h> /* for string library */
|
||||||
|
#include <ctype.h> /* for character testing */
|
||||||
|
#include <assert.h> /* for assertions */
|
||||||
|
#include "srecparser.h" /* S-record parser */
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Macro definitions
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief Maximum number of characters that can be on a line in the firmware file. */
|
||||||
|
#define SRECORD_MAX_CHARS_PER_LINE (512)
|
||||||
|
|
||||||
|
/** \brief Maximum number of data bytes that can be on a line in the firmware file
|
||||||
|
* (S-record).
|
||||||
|
*/
|
||||||
|
#define SRECORD_MAX_DATA_BYTES_PER_LINE (SRECORD_MAX_CHARS_PER_LINE/2)
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Type definitions
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief Layout of a firmware segment node in the linked list. */
|
||||||
|
typedef struct t_segment_node
|
||||||
|
{
|
||||||
|
/** \brief The firmware segment stored in this node. */
|
||||||
|
tFirmwareSegment segment;
|
||||||
|
/** \brief Pointer to the next node, or NULL if it is the last one. */
|
||||||
|
struct t_segment_node *next;
|
||||||
|
} tSegmentNode;
|
||||||
|
|
||||||
|
/** \brief Enumeration for the different S-record line types. */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
LINE_TYPE_S1, /**< 16-bit address line */
|
||||||
|
LINE_TYPE_S2, /**< 24-bit address line */
|
||||||
|
LINE_TYPE_S3, /**< 32-bit address line */
|
||||||
|
LINE_TYPE_UNSUPPORTED /**< unsupported line */
|
||||||
|
} tSrecordLineType;
|
||||||
|
|
||||||
|
/** \brief Structure type for grouping the parsing results of an S-record line. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t data[SRECORD_MAX_DATA_BYTES_PER_LINE]; /**< array for S1,S2 or S3 data bytes */
|
||||||
|
uint32_t address; /**< address on S1,S2 or S3 line */
|
||||||
|
uint16_t length; /**< number of bytes written to array */
|
||||||
|
} tSrecordLineParseResults;
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Function prototypes
|
||||||
|
****************************************************************************************/
|
||||||
|
static void SRecParserInit(void);
|
||||||
|
static void SRecParserDeinit(void);
|
||||||
|
static bool SRecParserLoadFromFile(char *firmwareFile);
|
||||||
|
static uint32_t SRecParserGetSegmentCount(void);
|
||||||
|
static const tFirmwareSegment *SRecParserGetSegment(uint32_t segmentIdx);
|
||||||
|
static void SRecParserSegmentListInit(void);
|
||||||
|
static void SRecParserSegmentListDeinit(void);
|
||||||
|
static tSegmentNode *SRecParserSegmentListCreateNode(void);
|
||||||
|
static uint32_t SRecParserSegmentListGetNodeCount(void);
|
||||||
|
static tSegmentNode *SRecParserSegmentListGetNode(uint32_t nodeIdx);
|
||||||
|
/* S-record utility functions */
|
||||||
|
static bool SrecordParseNextDataLine(FILE* srecordHandle, tSrecordLineParseResults *parseResults);
|
||||||
|
static tSrecordLineType SrecordGetLineType(const char *line);
|
||||||
|
static bool SrecordVerifyChecksum(const char *line);
|
||||||
|
static uint8_t SrecordHexStringToByte(const char *hexstring);
|
||||||
|
static bool SrecordReadLine(FILE *srecordHandle, char *line);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Local constant declarations
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief XCP transport structure filled with CAN specifics. */
|
||||||
|
static const tFirmwareParser srecParser =
|
||||||
|
{
|
||||||
|
SRecParserInit,
|
||||||
|
SRecParserDeinit,
|
||||||
|
SRecParserLoadFromFile,
|
||||||
|
SRecParserGetSegmentCount,
|
||||||
|
SRecParserGetSegment
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Local data declarations
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief Linked list with firmware segments. */
|
||||||
|
static tSegmentNode *segmentList;
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************************************//**
|
||||||
|
** \brief Obtains a pointer to the parser structure, so that it can be linked to the
|
||||||
|
** firmware loader module.
|
||||||
|
** \return Pointer to firmware parser structure.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
tFirmwareParser const * const SRecParserGetParser(void)
|
||||||
|
{
|
||||||
|
return &srecParser;
|
||||||
|
} /*** end of SRecParserGetParser ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Initializes the parser.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static void SRecParserInit(void)
|
||||||
|
{
|
||||||
|
/* initialize the segment list */
|
||||||
|
SRecParserSegmentListInit();
|
||||||
|
} /*** end of SRecParserInit ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Uninitializes the parser.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static void SRecParserDeinit(void)
|
||||||
|
{
|
||||||
|
/* uninitialize the segment list */
|
||||||
|
SRecParserSegmentListDeinit();
|
||||||
|
} /*** end of SRecParserDeinit ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Parses the data in the specified firmware file into firmware segments that
|
||||||
|
** are stored internally in this module.
|
||||||
|
** \return True is successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static bool SRecParserLoadFromFile(char *firmwareFile)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char line[SRECORD_MAX_CHARS_PER_LINE];
|
||||||
|
tSrecordLineParseResults lineResults;
|
||||||
|
tSegmentNode *node;
|
||||||
|
uint32_t nodeIdx;
|
||||||
|
bool matchFound;
|
||||||
|
uint32_t byteIdx;
|
||||||
|
uint32_t byteOffset;
|
||||||
|
|
||||||
|
/* verify parameters */
|
||||||
|
assert(firmwareFile != NULL);
|
||||||
|
|
||||||
|
/* open the file for reading */
|
||||||
|
fp = fopen(firmwareFile, "r");
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
/* could not open the file */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* start at the beginning of the file */
|
||||||
|
rewind(fp);
|
||||||
|
|
||||||
|
/* -------------------------- file type validation --------------------------------- */
|
||||||
|
/* validate S-record file. all lines should be formatted as S-records. read the first
|
||||||
|
* one to check this.
|
||||||
|
*/
|
||||||
|
if (!SrecordReadLine(fp, line))
|
||||||
|
{
|
||||||
|
/* could not read a line. file must be empty */
|
||||||
|
fclose(fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* check if the line starts with the 'S' character, followed by a digit */
|
||||||
|
if ( (toupper(line[0]) != 'S') || (isdigit(line[1]) == 0) )
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------- extract segment info --------------------------------- */
|
||||||
|
/* start at the beginning of the file */
|
||||||
|
rewind(fp);
|
||||||
|
|
||||||
|
/* loop through all S-records with program data to obtain segment info */
|
||||||
|
while (SrecordParseNextDataLine(fp, &lineResults))
|
||||||
|
{
|
||||||
|
/* reset flag that indicates that the line data was matched to an existing segment */
|
||||||
|
matchFound = false;
|
||||||
|
/* loop through all segment nodes */
|
||||||
|
for (nodeIdx=0; nodeIdx < SRecParserSegmentListGetNodeCount(); nodeIdx++)
|
||||||
|
{
|
||||||
|
/* get node access */
|
||||||
|
node = SRecParserSegmentListGetNode(nodeIdx);
|
||||||
|
|
||||||
|
/* does the line data fit at the start of this node's segment? */
|
||||||
|
if ((lineResults.address + lineResults.length) == node->segment.base)
|
||||||
|
{
|
||||||
|
/* update the node's segment */
|
||||||
|
node->segment.base -= lineResults.length;
|
||||||
|
node->segment.length += lineResults.length;
|
||||||
|
/* match found so continue with the next line */
|
||||||
|
matchFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* does the line data fit at the end of this node's segment? */
|
||||||
|
else if (lineResults.address == (node->segment.base + node->segment.length))
|
||||||
|
{
|
||||||
|
/* update the node's segment */
|
||||||
|
node->segment.length += lineResults.length;
|
||||||
|
/* match found so continue with the next line */
|
||||||
|
matchFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* check if the line data was matched and added to an existing segment */
|
||||||
|
if (!matchFound)
|
||||||
|
{
|
||||||
|
/* create a new segment */
|
||||||
|
node = SRecParserSegmentListCreateNode();
|
||||||
|
node->segment.base = lineResults.address;
|
||||||
|
node->segment.length = lineResults.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------- allocate data memory --------------------------------- */
|
||||||
|
/* loop through all segment nodes */
|
||||||
|
for (nodeIdx=0; nodeIdx < SRecParserSegmentListGetNodeCount(); nodeIdx++)
|
||||||
|
{
|
||||||
|
/* get node access */
|
||||||
|
node = SRecParserSegmentListGetNode(nodeIdx);
|
||||||
|
/* sanity check */
|
||||||
|
assert(node->segment.length > 0);
|
||||||
|
/* allocate data */
|
||||||
|
node->segment.data = malloc(node->segment.length);
|
||||||
|
/* check results */
|
||||||
|
if (node->segment.data == NULL)
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------- extract segment data --------------------------------- */
|
||||||
|
/* start at the beginning of the file */
|
||||||
|
rewind(fp);
|
||||||
|
|
||||||
|
/* loop through all S-records with program data to obtain segment info */
|
||||||
|
while (SrecordParseNextDataLine(fp, &lineResults))
|
||||||
|
{
|
||||||
|
/* loop through all segment nodes */
|
||||||
|
for (nodeIdx=0; nodeIdx < SRecParserSegmentListGetNodeCount(); nodeIdx++)
|
||||||
|
{
|
||||||
|
/* get node access */
|
||||||
|
node = SRecParserSegmentListGetNode(nodeIdx);
|
||||||
|
/* does the line data belong in this segment? */
|
||||||
|
if ( (lineResults.address >= node->segment.base) && \
|
||||||
|
((lineResults.address+lineResults.length) <= (node->segment.base+node->segment.length)) )
|
||||||
|
{
|
||||||
|
/* determine offset of the line data into the segment */
|
||||||
|
byteOffset = lineResults.address - node->segment.base;
|
||||||
|
/* store bytes in this segment */
|
||||||
|
for (byteIdx=0; byteIdx<lineResults.length; byteIdx++)
|
||||||
|
{
|
||||||
|
node->segment.data[byteOffset + byteIdx] = lineResults.data[byteIdx];
|
||||||
|
}
|
||||||
|
/* line data stored, so continue with the next S-record */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* close the file */
|
||||||
|
fclose(fp);
|
||||||
|
/* s-record successfully loaded and parsed */
|
||||||
|
return true;
|
||||||
|
} /*** end of SRecParserLoadFromFile ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Returns the number of firmware segments that were loaded by this parser.
|
||||||
|
** \return Number of firmware segments.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static uint32_t SRecParserGetSegmentCount(void)
|
||||||
|
{
|
||||||
|
return SRecParserSegmentListGetNodeCount();
|
||||||
|
} /*** end of SRecParserGetSegmentCount ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Obtains a pointer to the firmware segment at the specified index.
|
||||||
|
** \return Pointer to firmware segment.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static const tFirmwareSegment *SRecParserGetSegment(uint32_t segmentIdx)
|
||||||
|
{
|
||||||
|
/* validate the parameter */
|
||||||
|
assert(segmentIdx < SRecParserSegmentListGetNodeCount());
|
||||||
|
|
||||||
|
return &(SRecParserSegmentListGetNode(segmentIdx)->segment);
|
||||||
|
} /*** end of SRecParserGetSegment ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Initializes the linked list with firmware segments.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static void SRecParserSegmentListInit(void)
|
||||||
|
{
|
||||||
|
segmentList = NULL;
|
||||||
|
} /*** end of SRecParserSegmentListInit ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Uninitializes the linked list with firmware segments.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static void SRecParserSegmentListDeinit(void)
|
||||||
|
{
|
||||||
|
tSegmentNode *currentNode;
|
||||||
|
tSegmentNode *nodeToFree;
|
||||||
|
|
||||||
|
/* free all nodes */
|
||||||
|
if (segmentList != NULL)
|
||||||
|
{
|
||||||
|
currentNode = segmentList;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* store pointer to the node that should be released for later usage */
|
||||||
|
nodeToFree = currentNode;
|
||||||
|
/* move to the next node before freeing it */
|
||||||
|
currentNode = currentNode->next;
|
||||||
|
/* sanity check */
|
||||||
|
assert(nodeToFree != NULL);
|
||||||
|
/* free the node */
|
||||||
|
if (nodeToFree->segment.data != NULL)
|
||||||
|
{
|
||||||
|
free(nodeToFree->segment.data);
|
||||||
|
}
|
||||||
|
free(nodeToFree);
|
||||||
|
}
|
||||||
|
while(currentNode != NULL);
|
||||||
|
}
|
||||||
|
} /*** end of SRecParserSegmentListDeinit ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Creates a new node in the linked list with firmware segments.
|
||||||
|
** \return Pointer to the new node.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static tSegmentNode *SRecParserSegmentListCreateNode(void)
|
||||||
|
{
|
||||||
|
tSegmentNode *newNode;
|
||||||
|
tSegmentNode *currentNode;
|
||||||
|
|
||||||
|
/* allocate memory for the node */
|
||||||
|
newNode = malloc(sizeof(tSegmentNode));
|
||||||
|
assert(newNode != NULL);
|
||||||
|
|
||||||
|
/* initialize the node */
|
||||||
|
newNode->next = NULL;
|
||||||
|
newNode->segment.base = 0x00000000;
|
||||||
|
newNode->segment.length = 0;
|
||||||
|
newNode->segment.data = NULL;
|
||||||
|
|
||||||
|
/* add the first node if the list is empty */
|
||||||
|
if (segmentList == NULL)
|
||||||
|
{
|
||||||
|
segmentList = newNode;
|
||||||
|
}
|
||||||
|
/* add the node to the end of the list */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* find last entry in to list */
|
||||||
|
currentNode = segmentList;
|
||||||
|
while(currentNode->next != NULL)
|
||||||
|
{
|
||||||
|
currentNode = currentNode->next;
|
||||||
|
}
|
||||||
|
/* add the now */
|
||||||
|
currentNode->next = newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
} /*** end of SRecParserSegmentListCreateNode ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Obtains the number of nodes in the linked list with firmware segments.
|
||||||
|
** \return Number of nodes.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static uint32_t SRecParserSegmentListGetNodeCount(void)
|
||||||
|
{
|
||||||
|
tSegmentNode *currentNode;
|
||||||
|
uint32_t nodeCount = 0;
|
||||||
|
|
||||||
|
/* iterate over all nodes to determine to total count */
|
||||||
|
if (segmentList != NULL)
|
||||||
|
{
|
||||||
|
currentNode = segmentList;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
nodeCount++;
|
||||||
|
currentNode = currentNode->next;
|
||||||
|
}
|
||||||
|
while(currentNode != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeCount;
|
||||||
|
} /*** end of SRecParserSegmentListGetNodeCount ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Obtains the node at the specified index from the linked list with firmware
|
||||||
|
** segments.
|
||||||
|
** \return Pointer to the node.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static tSegmentNode *SRecParserSegmentListGetNode(uint32_t nodeIdx)
|
||||||
|
{
|
||||||
|
tSegmentNode *currentNode = NULL;
|
||||||
|
uint32_t currentIdx = 0;
|
||||||
|
|
||||||
|
/* validate the parameter */
|
||||||
|
assert(nodeIdx < SRecParserSegmentListGetNodeCount());
|
||||||
|
|
||||||
|
/* iterate until the specified index is found */
|
||||||
|
currentNode = segmentList;
|
||||||
|
for (currentIdx=0; currentIdx<nodeIdx; currentIdx++)
|
||||||
|
{
|
||||||
|
currentNode = currentNode->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentNode;
|
||||||
|
} /*** end of SRecParserSegmentListGetNode ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Reads the next S-record with program data, parses it and returns the
|
||||||
|
** results.
|
||||||
|
** \param srecordHandle The S-record file handle. It is returned by SrecordOpen.
|
||||||
|
** \param parseResults Pointer to where the parse results should be stored.
|
||||||
|
** \return SB_TRUE is valid parse results were stored. SB_FALSE in case of end-of-
|
||||||
|
** file.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static bool SrecordParseNextDataLine(FILE* srecordHandle, tSrecordLineParseResults *parseResults)
|
||||||
|
{
|
||||||
|
char line[SRECORD_MAX_CHARS_PER_LINE];
|
||||||
|
bool data_line_found = false;
|
||||||
|
tSrecordLineType lineType;
|
||||||
|
uint16_t bytes_on_line;
|
||||||
|
uint16_t i;
|
||||||
|
char *linePtr;
|
||||||
|
|
||||||
|
/* first set the length paramter to 0 */
|
||||||
|
parseResults->length = 0;
|
||||||
|
|
||||||
|
while (!data_line_found)
|
||||||
|
{
|
||||||
|
/* read the next line from the file */
|
||||||
|
if (!SrecordReadLine(srecordHandle, line))
|
||||||
|
{
|
||||||
|
/* end-of-file encountered */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* we now have a line. check if it is a S-record data line */
|
||||||
|
lineType = SrecordGetLineType(line);
|
||||||
|
if (lineType != LINE_TYPE_UNSUPPORTED)
|
||||||
|
{
|
||||||
|
/* check if the checksum on the line is correct */
|
||||||
|
if (SrecordVerifyChecksum(line))
|
||||||
|
{
|
||||||
|
/* found a valid line that can be parsed. loop will stop */
|
||||||
|
data_line_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so we have a valid S-record data line. start parsing */
|
||||||
|
linePtr = &line[0];
|
||||||
|
/* all good so far, now read out the address and databytes for the line */
|
||||||
|
switch (lineType)
|
||||||
|
{
|
||||||
|
/* ---------------------------- S1 line type ------------------------------------- */
|
||||||
|
case LINE_TYPE_S1:
|
||||||
|
/* adjust pointer to point to byte count value */
|
||||||
|
linePtr += 2;
|
||||||
|
/* read out the number of byte values that follow on the line */
|
||||||
|
bytes_on_line = SrecordHexStringToByte(linePtr);
|
||||||
|
/* read out the 16-bit address */
|
||||||
|
linePtr += 2;
|
||||||
|
parseResults->address = SrecordHexStringToByte(linePtr) << 8;
|
||||||
|
linePtr += 2;
|
||||||
|
parseResults->address += SrecordHexStringToByte(linePtr);
|
||||||
|
/* adjust pointer to point to the first data byte after the address */
|
||||||
|
linePtr += 2;
|
||||||
|
/* determine how many data bytes are on the line */
|
||||||
|
parseResults->length = bytes_on_line - 3; /* -2 bytes address, -1 byte checksum */
|
||||||
|
/* read and store data bytes if requested */
|
||||||
|
for (i=0; i<parseResults->length; i++)
|
||||||
|
{
|
||||||
|
parseResults->data[i] = SrecordHexStringToByte(linePtr);
|
||||||
|
linePtr += 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---------------------------- S2 line type ------------------------------------- */
|
||||||
|
case LINE_TYPE_S2:
|
||||||
|
/* adjust pointer to point to byte count value */
|
||||||
|
linePtr += 2;
|
||||||
|
/* read out the number of byte values that follow on the line */
|
||||||
|
bytes_on_line = SrecordHexStringToByte(linePtr);
|
||||||
|
/* read out the 32-bit address */
|
||||||
|
linePtr += 2;
|
||||||
|
parseResults->address = SrecordHexStringToByte(linePtr) << 16;
|
||||||
|
linePtr += 2;
|
||||||
|
parseResults->address += SrecordHexStringToByte(linePtr) << 8;
|
||||||
|
linePtr += 2;
|
||||||
|
parseResults->address += SrecordHexStringToByte(linePtr);
|
||||||
|
/* adjust pointer to point to the first data byte after the address */
|
||||||
|
linePtr += 2;
|
||||||
|
/* determine how many data bytes are on the line */
|
||||||
|
parseResults->length = bytes_on_line - 4; /* -3 bytes address, -1 byte checksum */
|
||||||
|
/* read and store data bytes if requested */
|
||||||
|
for (i=0; i<parseResults->length; i++)
|
||||||
|
{
|
||||||
|
parseResults->data[i] = SrecordHexStringToByte(linePtr);
|
||||||
|
linePtr += 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* ---------------------------- S3 line type ------------------------------------- */
|
||||||
|
case LINE_TYPE_S3:
|
||||||
|
/* adjust pointer to point to byte count value */
|
||||||
|
linePtr += 2;
|
||||||
|
/* read out the number of byte values that follow on the line */
|
||||||
|
bytes_on_line = SrecordHexStringToByte(linePtr);
|
||||||
|
/* read out the 32-bit address */
|
||||||
|
linePtr += 2;
|
||||||
|
parseResults->address = SrecordHexStringToByte(linePtr) << 24;
|
||||||
|
linePtr += 2;
|
||||||
|
parseResults->address += SrecordHexStringToByte(linePtr) << 16;
|
||||||
|
linePtr += 2;
|
||||||
|
parseResults->address += SrecordHexStringToByte(linePtr) << 8;
|
||||||
|
linePtr += 2;
|
||||||
|
parseResults->address += SrecordHexStringToByte(linePtr);
|
||||||
|
/* adjust pointer to point to the first data byte after the address */
|
||||||
|
linePtr += 2;
|
||||||
|
/* determine how many data bytes are on the line */
|
||||||
|
parseResults->length = bytes_on_line - 5; /* -4 bytes address, -1 byte checksum */
|
||||||
|
/* read and store data bytes if requested */
|
||||||
|
for (i=0; i<parseResults->length; i++)
|
||||||
|
{
|
||||||
|
parseResults->data[i] = SrecordHexStringToByte(linePtr);
|
||||||
|
linePtr += 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* will not happen */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parsing all done */
|
||||||
|
return true;
|
||||||
|
} /*** end of SrecordParseNextDataLine ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Inspects a line from a Motorola S-Record file to determine its type.
|
||||||
|
** \param line A line from the S-Record.
|
||||||
|
** \return the S-Record line type.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static tSrecordLineType SrecordGetLineType(const char *line)
|
||||||
|
{
|
||||||
|
/* check if the line starts with the 'S' character, followed by a digit */
|
||||||
|
if ( (toupper(line[0]) != 'S') || (isdigit(line[1]) == 0) )
|
||||||
|
{
|
||||||
|
/* not a valid S-Record line type */
|
||||||
|
return LINE_TYPE_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
/* determine the line type */
|
||||||
|
if (line[1] == '1')
|
||||||
|
{
|
||||||
|
return LINE_TYPE_S1;
|
||||||
|
}
|
||||||
|
if (line[1] == '2')
|
||||||
|
{
|
||||||
|
return LINE_TYPE_S2;
|
||||||
|
}
|
||||||
|
if (line[1] == '3')
|
||||||
|
{
|
||||||
|
return LINE_TYPE_S3;
|
||||||
|
}
|
||||||
|
/* still here so not a supported line type found */
|
||||||
|
return LINE_TYPE_UNSUPPORTED;
|
||||||
|
} /*** end of SrecordGetLineType ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Inspects an S1, S2 or S3 line from a Motorola S-Record file to
|
||||||
|
** determine if the checksum at the end is corrrect.
|
||||||
|
** \param line An S1, S2 or S3 line from the S-Record.
|
||||||
|
** \return SB_TRUE if the checksum is correct, SB_FALSE otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static bool SrecordVerifyChecksum(const char *line)
|
||||||
|
{
|
||||||
|
uint16_t bytes_on_line;
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
|
||||||
|
/* adjust pointer to point to byte count value */
|
||||||
|
line += 2;
|
||||||
|
/* read out the number of byte values that follow on the line */
|
||||||
|
bytes_on_line = SrecordHexStringToByte(line);
|
||||||
|
/* byte count is part of checksum */
|
||||||
|
checksum += bytes_on_line;
|
||||||
|
/* adjust pointer to the first byte of the address */
|
||||||
|
line += 2;
|
||||||
|
/* add byte values of address and data, but not the final checksum */
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* add the next byte value to the checksum */
|
||||||
|
checksum += SrecordHexStringToByte(line);
|
||||||
|
/* update counter */
|
||||||
|
bytes_on_line--;
|
||||||
|
/* point to next hex string in the line */
|
||||||
|
line += 2;
|
||||||
|
}
|
||||||
|
while (bytes_on_line > 1);
|
||||||
|
/* the checksum is calculated by summing up the values of the byte count, address and
|
||||||
|
* databytes and then taking the 1-complement of the sum's least signigicant byte */
|
||||||
|
checksum = ~checksum;
|
||||||
|
/* finally verify the calculated checksum with the one at the end of the line */
|
||||||
|
if (checksum != SrecordHexStringToByte(line))
|
||||||
|
{
|
||||||
|
/* checksum incorrect */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* still here so the checksum was correct */
|
||||||
|
return true;
|
||||||
|
} /*** end of SrecordVerifyChecksum ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Helper function to convert a sequence of 2 characters that represent
|
||||||
|
** a hexadecimal value to the actual byte value.
|
||||||
|
** Example: SrecordHexStringToByte("2f") --> returns 47.
|
||||||
|
** \param hexstring String beginning with 2 characters that represent a hexa-
|
||||||
|
** decimal value.
|
||||||
|
** \return The resulting byte value.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static uint8_t SrecordHexStringToByte(const char *hexstring)
|
||||||
|
{
|
||||||
|
uint8_t result = 0;
|
||||||
|
char c;
|
||||||
|
uint8_t counter;
|
||||||
|
|
||||||
|
/* a hexadecimal character is 2 characters long (i.e 0x4F minus the 0x part) */
|
||||||
|
for (counter=0; counter < 2; counter++)
|
||||||
|
{
|
||||||
|
/* read out the character */
|
||||||
|
c = toupper(hexstring[counter]);
|
||||||
|
/* check that the character is 0..9 or A..F */
|
||||||
|
if ( (c < '0') || (c > 'F') || ( (c > '9') && (c < 'A') ) )
|
||||||
|
{
|
||||||
|
/* character not valid */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* convert character to 4-bit value (check ASCII table for more info) */
|
||||||
|
c -= '0';
|
||||||
|
if (c > 9)
|
||||||
|
{
|
||||||
|
c -= 7;
|
||||||
|
}
|
||||||
|
/* add it to the result */
|
||||||
|
result = (result << 4) + c;
|
||||||
|
}
|
||||||
|
/* return the results */
|
||||||
|
return result;
|
||||||
|
} /*** end of SrecordHexStringToByte ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Reads the next line from the S-record file handle.
|
||||||
|
** \param srecordHandle The S-record file handle. It is returned by SrecordOpen.
|
||||||
|
** \param line Destination buffer for the line characters. Should be of size
|
||||||
|
** SRECORD_MAX_CHARS_PER_LINE.
|
||||||
|
** \return SB_TRUE if successful, SB_FALSE otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static bool SrecordReadLine(FILE *srecordHandle, char *line)
|
||||||
|
{
|
||||||
|
/* init the line as an empty line */
|
||||||
|
line[0] = '\0';
|
||||||
|
|
||||||
|
/* loop as long as we find a non-empty line or end-of-file */
|
||||||
|
while (line[0] == '\0')
|
||||||
|
{
|
||||||
|
if (fgets(line, SRECORD_MAX_CHARS_PER_LINE, srecordHandle) == NULL)
|
||||||
|
{
|
||||||
|
/* no more lines available */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* replace the line termination with a string termination */
|
||||||
|
line[strcspn(line, "\n")] = '\0';
|
||||||
|
}
|
||||||
|
/* still here so not EOF and not and empty line, so success */
|
||||||
|
return true;
|
||||||
|
} /*** end of SrecordReadLine ***/
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** end of srecparser.c *******************************/
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
/************************************************************************************//**
|
/************************************************************************************//**
|
||||||
* \file port\xcptransport.h
|
* \file srecparser.h
|
||||||
* \brief XCP transport layer interface header file.
|
* \brief S-record parser header file.
|
||||||
* \ingroup SerialBoot
|
* \ingroup SerialBoot
|
||||||
* \internal
|
* \internal
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* C O P Y R I G H T
|
* C O P Y R I G H T
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* Copyright (c) 2014 by Feaser http://www.feaser.com All rights reserved
|
* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved
|
||||||
*
|
*
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* L I C E N S E
|
* L I C E N S E
|
||||||
|
@ -25,27 +25,28 @@
|
||||||
*
|
*
|
||||||
* \endinternal
|
* \endinternal
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
#ifndef XCPTRANSPORT_H
|
#ifndef SRECPARSER_H
|
||||||
#define XCPTRANSPORT_H
|
#define SRECPARSER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Type definitions
|
* Include files
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
typedef struct
|
#include "firmware.h" /* firmware module */
|
||||||
{
|
|
||||||
sb_uint8 data[XCP_MASTER_RX_MAX_DATA];
|
|
||||||
sb_uint8 len;
|
|
||||||
} tXcpTransportResponsePacket;
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* EFunction prototypes
|
* Function prototypes
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
sb_uint8 XcpTransportInit(sb_char *device, sb_uint32 baudrate);
|
tFirmwareParser const * const SRecParserGetParser(void);
|
||||||
sb_uint8 XcpTransportSendPacket(sb_uint8 *data, sb_uint8 len, sb_uint16 timeOutMs);
|
|
||||||
tXcpTransportResponsePacket *XcpTransportReadResponsePacket(void);
|
|
||||||
void XcpTransportClose(void);
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SRECPARSER_H */
|
||||||
|
/********************************* end of srecparser.h *********************************/
|
||||||
|
|
||||||
#endif /* XCPTRANSPORT_H */
|
|
||||||
/*********************************** end of xcptransport.h *****************************/
|
|
|
@ -1,12 +1,12 @@
|
||||||
/************************************************************************************//**
|
/************************************************************************************//**
|
||||||
* \file port\timeutil.h
|
* \file timeutil.h
|
||||||
* \brief Time utility header file.
|
* \brief Time utility header file.
|
||||||
* \ingroup SerialBoot
|
* \ingroup SerialBoot
|
||||||
* \internal
|
* \internal
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* C O P Y R I G H T
|
* C O P Y R I G H T
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* Copyright (c) 2014 by Feaser http://www.feaser.com All rights reserved
|
* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved
|
||||||
*
|
*
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* L I C E N S E
|
* L I C E N S E
|
||||||
|
@ -28,12 +28,25 @@
|
||||||
#ifndef TIMEUTIL_H
|
#ifndef TIMEUTIL_H
|
||||||
#define TIMEUTIL_H
|
#define TIMEUTIL_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Include files
|
||||||
|
****************************************************************************************/
|
||||||
|
#include <stdint.h> /* for standard integer types */
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Function prototypes
|
* Function prototypes
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
sb_uint32 TimeUtilGetSystemTimeMs(void);
|
uint32_t TimeUtilGetSystemTimeMs(void);
|
||||||
void TimeUtilDelayMs(sb_uint16 delay);
|
void TimeUtilDelayMs(uint16_t delay);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* TIMEUTIL_H */
|
#endif /* TIMEUTIL_H */
|
||||||
/*********************************** end of timeutil.h *********************************/
|
/*********************************** end of timeutil.h *********************************/
|
|
@ -0,0 +1,755 @@
|
||||||
|
/************************************************************************************//**
|
||||||
|
* \file xcploader.c
|
||||||
|
* \brief XCP Loader module source file.
|
||||||
|
* \ingroup SerialBoot
|
||||||
|
* \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 <stddef.h> /* for NULL declaration */
|
||||||
|
#include <assert.h> /* for assertions */
|
||||||
|
#include "xcploader.h" /* XCP loader module */
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Macro definitions
|
||||||
|
****************************************************************************************/
|
||||||
|
/* XCP command codes as defined by the protocol currently supported by this module */
|
||||||
|
#define XCPLOADER_CMD_CONNECT (0xFF) /**< XCP connect command code. */
|
||||||
|
#define XCPLOADER_CMD_DISCONNECT (0xFE) /**< XCP disconnect command code. */
|
||||||
|
#define XCPLOADER_CMD_SET_MTA (0xF6) /**< XCP set mta command code. */
|
||||||
|
#define XCPLOADER_CMD_UPLOAD (0xF5) /**< XCP upload command code. */
|
||||||
|
#define XCPLOADER_CMD_PROGRAM_START (0xD2) /**< XCP program start command code. */
|
||||||
|
#define XCPLOADER_CMD_PROGRAM_CLEAR (0xD1) /**< XCP program clear command code. */
|
||||||
|
#define XCPLOADER_CMD_PROGRAM (0xD0) /**< XCP program command code. */
|
||||||
|
#define XCPLOADER_CMD_PROGRAM_RESET (0xCF) /**< XCP program reset command code. */
|
||||||
|
#define XCPLOADER_CMD_PROGRAM_MAX (0xC9) /**< XCP program max command code. */
|
||||||
|
|
||||||
|
/* XCP response packet IDs as defined by the protocol. */
|
||||||
|
#define XCPLOADER_CMD_PID_RES (0xFF) /**< positive response */
|
||||||
|
|
||||||
|
/** \brief Maximum timeout for the XCP connect command. */
|
||||||
|
#define XCPLOADER_CONNECT_TIMEOUT_MS (20)
|
||||||
|
|
||||||
|
/** \brief Number of retries to connect to the XCP slave. */
|
||||||
|
#define XCPLOADER_CONNECT_RETRIES (5)
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Function prototypes
|
||||||
|
****************************************************************************************/
|
||||||
|
static bool XcpLoaderSendCmdConnect(void);
|
||||||
|
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 *data);
|
||||||
|
static bool XcpLoaderSendCmdProgramMax(uint8_t *data);
|
||||||
|
static bool XcpLoaderSendCmdProgramClear(uint32_t length);
|
||||||
|
static void XcpLoaderSetOrderedLong(uint32_t value, uint8_t *data);
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Local data declarations
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief Pointer to the XCP transport layer that is linked. */
|
||||||
|
static tXcpTransport const * transportPtr = NULL;
|
||||||
|
|
||||||
|
/** \brief The settings that should be used by the XCP loader. */
|
||||||
|
static tXcpSettings 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 Initializes the loader module.
|
||||||
|
** \param settings Pointer to settings structure.
|
||||||
|
** \param transport Pointer to the transport layer to link.
|
||||||
|
** \param tpsettings Pointer to transport layer settings structure.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
void XcpLoaderInit(tXcpSettings *settings, tXcpTransport const * const transport, void *tpsettings)
|
||||||
|
{
|
||||||
|
/* verify parameters */
|
||||||
|
assert(transport != NULL);
|
||||||
|
assert(tpsettings != NULL);
|
||||||
|
assert(settings != NULL);
|
||||||
|
|
||||||
|
/* shallow copy the XCP settings for later usage */
|
||||||
|
xcpSettings = *settings;
|
||||||
|
/* link the XCP transport layer */
|
||||||
|
transportPtr = transport;
|
||||||
|
/* initialize the transport layer */
|
||||||
|
transportPtr->Init(tpsettings);
|
||||||
|
/* init locals */
|
||||||
|
xcpConnected = false;
|
||||||
|
xcpSlaveIsIntel = false;
|
||||||
|
xcpMaxCto = 0;
|
||||||
|
xcpMaxProgCto = 0;
|
||||||
|
xcpMaxDto = 0;
|
||||||
|
} /*** end of XcpLoaderInit ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Uninitializes the loader module.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
void XcpLoaderDeinit(void)
|
||||||
|
{
|
||||||
|
/* make sure the XCP transport layer is linked */
|
||||||
|
assert(transportPtr != NULL);
|
||||||
|
|
||||||
|
/* disconnect */
|
||||||
|
XcpLoaderDisconnect();
|
||||||
|
/* uninitialize the transport layer */
|
||||||
|
transportPtr->Deinit();
|
||||||
|
/* unlink the transport layer */
|
||||||
|
transportPtr = NULL;
|
||||||
|
} /*** end of XcpLoaderDeinit ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Connect to the XCP slave.
|
||||||
|
** \return True is successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool XcpLoaderConnect(void)
|
||||||
|
{
|
||||||
|
uint8_t retryCnt;
|
||||||
|
|
||||||
|
/* make sure the XCP transport layer is linked */
|
||||||
|
assert(transportPtr != NULL);
|
||||||
|
|
||||||
|
/* make sure that we are disconnected before connecting */
|
||||||
|
XcpLoaderDisconnect();
|
||||||
|
|
||||||
|
/* connect the transport layer */
|
||||||
|
if (!transportPtr->Connect())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to connect with a finite amount of retries */
|
||||||
|
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 */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so could not connect to XCP slave. disconnect the transport layer */
|
||||||
|
transportPtr->Disconnect();
|
||||||
|
return false;
|
||||||
|
} /*** end of XcpLoaderConnect ***/
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************************************//**
|
||||||
|
** \brief Disconnect from the XCP slave.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
void XcpLoaderDisconnect(void)
|
||||||
|
{
|
||||||
|
/* make sure the XCP transport layer is linked */
|
||||||
|
assert(transportPtr != NULL);
|
||||||
|
|
||||||
|
/* only disconnect if actually connected */
|
||||||
|
if (xcpConnected)
|
||||||
|
{
|
||||||
|
/* send reset command instead of the disconnect. this causes the user program on the
|
||||||
|
* slave to automatically start again if present.
|
||||||
|
*/
|
||||||
|
XcpLoaderSendCmdProgramReset();
|
||||||
|
/* disconnect the transport layer */
|
||||||
|
transportPtr->Disconnect();
|
||||||
|
/* reset connection status */
|
||||||
|
xcpConnected = false;
|
||||||
|
}
|
||||||
|
} /*** end of XcpLoaderDisconnect ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Puts a connected slave in programming session.
|
||||||
|
** \return True is successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool XcpLoaderStartProgrammingSession(void)
|
||||||
|
{
|
||||||
|
/* make sure the XCP transport layer is linked */
|
||||||
|
assert(transportPtr != NULL);
|
||||||
|
|
||||||
|
/* place the slave in programming mode */
|
||||||
|
return XcpLoaderSendCmdProgramStart();
|
||||||
|
} /*** end of XcpLoaderStartProgrammingSession ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Stops the programming session by sending a program command with size 0 and
|
||||||
|
** then resetting the slave.
|
||||||
|
** \return True is successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool XcpLoaderStopProgrammingSession(void)
|
||||||
|
{
|
||||||
|
/* make sure the XCP transport layer is linked */
|
||||||
|
assert(transportPtr != NULL);
|
||||||
|
|
||||||
|
/* stop programming by sending the program command with size 0 */
|
||||||
|
return XcpLoaderSendCmdProgram(0, NULL);
|
||||||
|
} /*** end of XcpLoaderStopProgrammingSession ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Erases non volatile memory on the slave.
|
||||||
|
** \param addr Base memory address for the erase operation.
|
||||||
|
** \param len Number of bytes to erase.
|
||||||
|
** \return True is successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool XcpLoaderClearMemory(uint32_t addr, uint32_t len)
|
||||||
|
{
|
||||||
|
/* make sure the XCP transport layer is linked */
|
||||||
|
assert(transportPtr != NULL);
|
||||||
|
/* verify parameters */
|
||||||
|
assert(len > 0);
|
||||||
|
|
||||||
|
/* first set the MTA pointer */
|
||||||
|
if (!XcpLoaderSendCmdSetMta(addr))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* now perform the erase operation */
|
||||||
|
return XcpLoaderSendCmdProgramClear(len);
|
||||||
|
} /*** end of XcpLoaderClearMemory ***/
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************************************//**
|
||||||
|
** \brief Reads data from the slave's memory.
|
||||||
|
** \param addr Base memory address for the read operation
|
||||||
|
** \param len Number of bytes to read.
|
||||||
|
** \param data Destination buffer for storing the read data bytes.
|
||||||
|
** \return True is successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool XcpLoaderReadData(uint32_t addr, uint32_t len, uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t currentReadCnt;
|
||||||
|
uint32_t bufferOffset = 0;
|
||||||
|
|
||||||
|
/* make sure the XCP transport layer is linked */
|
||||||
|
assert(transportPtr != NULL);
|
||||||
|
/* verify parameters */
|
||||||
|
assert(data != NULL);
|
||||||
|
assert(len > 0);
|
||||||
|
|
||||||
|
/* first set the MTA pointer */
|
||||||
|
if (!XcpLoaderSendCmdSetMta(addr))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* perform segmented upload of the data */
|
||||||
|
while (len > 0)
|
||||||
|
{
|
||||||
|
/* set the current read length to make optimal use of the available packet data. */
|
||||||
|
currentReadCnt = len % (xcpMaxDto - 1);
|
||||||
|
if (currentReadCnt == 0)
|
||||||
|
{
|
||||||
|
currentReadCnt = (xcpMaxDto - 1);
|
||||||
|
}
|
||||||
|
/* upload some data */
|
||||||
|
if (!XcpLoaderSendCmdUpload(&data[bufferOffset], currentReadCnt))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* update loop variables */
|
||||||
|
len -= currentReadCnt;
|
||||||
|
bufferOffset += currentReadCnt;
|
||||||
|
}
|
||||||
|
/* still here so all data successfully read from the slave */
|
||||||
|
return true;
|
||||||
|
} /*** end of XcpLoaderReadData ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Programs data to the slave's non volatile memory. Note that it must be
|
||||||
|
** erased first.
|
||||||
|
** \param addr Base memory address for the program operation
|
||||||
|
** \param len Number of bytes to program.
|
||||||
|
** \param data Source buffer with the to be programmed bytes.
|
||||||
|
** \return True is successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
bool XcpLoaderProgramData(uint32_t addr, uint32_t len, uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t currentWriteCnt;
|
||||||
|
uint32_t bufferOffset = 0;
|
||||||
|
|
||||||
|
/* make sure the XCP transport layer is linked */
|
||||||
|
assert(transportPtr != NULL);
|
||||||
|
/* verify parameters */
|
||||||
|
assert(data != NULL);
|
||||||
|
assert(len > 0);
|
||||||
|
|
||||||
|
/* first set the MTA pointer */
|
||||||
|
if (!XcpLoaderSendCmdSetMta(addr))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* perform segmented programming of the data */
|
||||||
|
while (len > 0)
|
||||||
|
{
|
||||||
|
/* set the current read length to make optimal use of the available packet data. */
|
||||||
|
currentWriteCnt = len % (xcpMaxProgCto - 1);
|
||||||
|
if (currentWriteCnt == 0)
|
||||||
|
{
|
||||||
|
currentWriteCnt = (xcpMaxProgCto - 1);
|
||||||
|
}
|
||||||
|
/* prepare the packed data for the program command */
|
||||||
|
if (currentWriteCnt < (xcpMaxProgCto - 1))
|
||||||
|
{
|
||||||
|
/* program data */
|
||||||
|
if (!XcpLoaderSendCmdProgram(currentWriteCnt, &data[bufferOffset]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* program max data */
|
||||||
|
if (!XcpLoaderSendCmdProgramMax(&data[bufferOffset]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* update loop variables */
|
||||||
|
len -= currentWriteCnt;
|
||||||
|
bufferOffset += currentWriteCnt;
|
||||||
|
}
|
||||||
|
/* still here so all data successfully programmed */
|
||||||
|
return true;
|
||||||
|
} /*** end of XcpLoaderProgramData ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Sends the XCP Connect command.
|
||||||
|
** \return True if successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static bool XcpLoaderSendCmdConnect(void)
|
||||||
|
{
|
||||||
|
tXcpPacket cmdPacket;
|
||||||
|
tXcpPacket resPacket;
|
||||||
|
|
||||||
|
/* prepare the command packet */
|
||||||
|
cmdPacket.data[0] = XCPLOADER_CMD_CONNECT;
|
||||||
|
cmdPacket.data[1] = 0; /* normal mode */
|
||||||
|
cmdPacket.len = 2;
|
||||||
|
|
||||||
|
/* send the packet */
|
||||||
|
if (!transportPtr->SendPacket(&cmdPacket, &resPacket, XCPLOADER_CONNECT_TIMEOUT_MS))
|
||||||
|
{
|
||||||
|
/* could not send packet or receive response within the specified timeout */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so a response was received. check if the reponse was valid */
|
||||||
|
if ( (resPacket.len == 0) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
|
||||||
|
{
|
||||||
|
/* not a valid or positive response */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* process response data */
|
||||||
|
if ((resPacket.data[2] & 0x01) == 0)
|
||||||
|
{
|
||||||
|
/* store slave's byte ordering information */
|
||||||
|
xcpSlaveIsIntel = true;
|
||||||
|
}
|
||||||
|
/* 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 */
|
||||||
|
assert(XCPLOADER_PACKET_SIZE_MAX >= xcpMaxCto);
|
||||||
|
assert(XCPLOADER_PACKET_SIZE_MAX >= xcpMaxDto);
|
||||||
|
|
||||||
|
/* still here so all went well */
|
||||||
|
return true;
|
||||||
|
} /*** end of XcpLoaderSendCmdConnect ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \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)
|
||||||
|
{
|
||||||
|
tXcpPacket cmdPacket;
|
||||||
|
tXcpPacket resPacket;
|
||||||
|
|
||||||
|
/* 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 (!transportPtr->SendPacket(&cmdPacket, &resPacket, xcpSettings.timeoutT1))
|
||||||
|
{
|
||||||
|
/* could not send packet or receive response within the specified timeout */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so a response was received. check if the reponse was valid */
|
||||||
|
if ( (resPacket.len == 0) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
|
||||||
|
{
|
||||||
|
/* not a valid or positive response */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so all went well */
|
||||||
|
return true;
|
||||||
|
} /*** 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)
|
||||||
|
{
|
||||||
|
tXcpPacket cmdPacket;
|
||||||
|
tXcpPacket resPacket;
|
||||||
|
uint8_t data_index;
|
||||||
|
|
||||||
|
/* cannot request more data then the max rx data - 1 */
|
||||||
|
assert(length < XCPLOADER_PACKET_SIZE_MAX);
|
||||||
|
assert(data != NULL);
|
||||||
|
|
||||||
|
/* prepare the command packet */
|
||||||
|
cmdPacket.data[0] = XCPLOADER_CMD_UPLOAD;
|
||||||
|
cmdPacket.data[1] = length;
|
||||||
|
cmdPacket.len = 2;
|
||||||
|
|
||||||
|
/* send the packet */
|
||||||
|
if (!transportPtr->SendPacket(&cmdPacket, &resPacket, xcpSettings.timeoutT1))
|
||||||
|
{
|
||||||
|
/* could not send packet or receive response within the specified timeout */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so a response was received. check if the reponse was valid */
|
||||||
|
if ( (resPacket.len == 0) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
|
||||||
|
{
|
||||||
|
/* not a valid or positive response */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now store the uploaded data */
|
||||||
|
for (data_index=0; data_index<length; data_index++)
|
||||||
|
{
|
||||||
|
data[data_index] = resPacket.data[data_index+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so all went well */
|
||||||
|
return true;
|
||||||
|
} /*** end of XcpLoaderSendCmdUpload ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Sends the XCP PROGRAM START command.
|
||||||
|
** \return True if successful, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static bool XcpLoaderSendCmdProgramStart(void)
|
||||||
|
{
|
||||||
|
tXcpPacket cmdPacket;
|
||||||
|
tXcpPacket resPacket;
|
||||||
|
|
||||||
|
/* prepare the command packet */
|
||||||
|
cmdPacket.data[0] = XCPLOADER_CMD_PROGRAM_START;
|
||||||
|
cmdPacket.len = 1;
|
||||||
|
|
||||||
|
/* send the packet */
|
||||||
|
if (!transportPtr->SendPacket(&cmdPacket, &resPacket, xcpSettings.timeoutT3))
|
||||||
|
{
|
||||||
|
/* could not send packet or receive response within the specified timeout */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so a response was received. check if the reponse was valid */
|
||||||
|
if ( (resPacket.len == 0) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
|
||||||
|
{
|
||||||
|
/* not a valid or positive response */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store max number of bytes the slave allows for master->slave packets during the
|
||||||
|
* programming session
|
||||||
|
*/
|
||||||
|
xcpMaxProgCto = resPacket.data[3];
|
||||||
|
|
||||||
|
/* still here so all went well */
|
||||||
|
return true;
|
||||||
|
} /*** 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)
|
||||||
|
{
|
||||||
|
tXcpPacket cmdPacket;
|
||||||
|
tXcpPacket resPacket;
|
||||||
|
|
||||||
|
/* prepare the command packet */
|
||||||
|
cmdPacket.data[0] = XCPLOADER_CMD_PROGRAM_RESET;
|
||||||
|
cmdPacket.len = 1;
|
||||||
|
|
||||||
|
/* send the packet, assume the sending itself is ok and check if a response was
|
||||||
|
* received.
|
||||||
|
*/
|
||||||
|
if (!transportPtr->SendPacket(&cmdPacket, &resPacket, xcpSettings.timeoutT5))
|
||||||
|
{
|
||||||
|
/* probably no response received within the specified timeout, but that is allowed
|
||||||
|
* for the reset command.
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so a response was received. check if the reponse was valid */
|
||||||
|
if ( (resPacket.len == 0) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
|
||||||
|
{
|
||||||
|
/* not a valid or positive response */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so all went well */
|
||||||
|
return true;
|
||||||
|
} /*** 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 *data)
|
||||||
|
{
|
||||||
|
tXcpPacket cmdPacket;
|
||||||
|
tXcpPacket resPacket;
|
||||||
|
uint8_t cnt;
|
||||||
|
|
||||||
|
/* verify that this number of bytes actually first in this command */
|
||||||
|
assert(length <= (xcpMaxProgCto-2) && (xcpMaxProgCto <= XCPLOADER_PACKET_SIZE_MAX));
|
||||||
|
if (length > 0)
|
||||||
|
{
|
||||||
|
assert(data != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prepare the command packet */
|
||||||
|
cmdPacket.data[0] = XCPLOADER_CMD_PROGRAM;
|
||||||
|
cmdPacket.data[1] = length;
|
||||||
|
for (cnt=0; cnt<length; cnt++)
|
||||||
|
{
|
||||||
|
cmdPacket.data[cnt+2] = data[cnt];
|
||||||
|
}
|
||||||
|
cmdPacket.len = length + 2;
|
||||||
|
|
||||||
|
/* send the packet */
|
||||||
|
if (!transportPtr->SendPacket(&cmdPacket, &resPacket, xcpSettings.timeoutT5))
|
||||||
|
{
|
||||||
|
/* could not send packet or receive response within the specified timeout */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so a response was received. check if the reponse was valid */
|
||||||
|
if ( (resPacket.len == 0) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
|
||||||
|
{
|
||||||
|
/* not a valid or positive response */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so all went well */
|
||||||
|
return true;
|
||||||
|
} /*** 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 *data)
|
||||||
|
{
|
||||||
|
tXcpPacket cmdPacket;
|
||||||
|
tXcpPacket resPacket;
|
||||||
|
uint8_t cnt;
|
||||||
|
|
||||||
|
/* verify that this number of bytes actually first in this command */
|
||||||
|
assert(xcpMaxProgCto <= XCPLOADER_PACKET_SIZE_MAX);
|
||||||
|
assert(data != NULL);
|
||||||
|
|
||||||
|
/* 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 (!transportPtr->SendPacket(&cmdPacket, &resPacket, xcpSettings.timeoutT5))
|
||||||
|
{
|
||||||
|
/* could not send packet or receive response within the specified timeout */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so a response was received. check if the reponse was valid */
|
||||||
|
if ( (resPacket.len == 0) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
|
||||||
|
{
|
||||||
|
/* not a valid or positive response */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so all went well */
|
||||||
|
return true;
|
||||||
|
} /*** 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)
|
||||||
|
{
|
||||||
|
tXcpPacket cmdPacket;
|
||||||
|
tXcpPacket resPacket;
|
||||||
|
|
||||||
|
/* prepare 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 (!transportPtr->SendPacket(&cmdPacket, &resPacket, xcpSettings.timeoutT4))
|
||||||
|
{
|
||||||
|
/* could not send packet or receive response within the specified timeout */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so a response was received. check if the reponse was valid */
|
||||||
|
if ( (resPacket.len == 0) || (resPacket.data[0] != XCPLOADER_CMD_PID_RES) )
|
||||||
|
{
|
||||||
|
/* not a valid or positive response */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so all went well */
|
||||||
|
return true;
|
||||||
|
} /*** end of XcpLoaderSendCmdProgramClear ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \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.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static void XcpLoaderSetOrderedLong(uint32_t value, uint8_t *data)
|
||||||
|
{
|
||||||
|
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 ***/
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** end of xcploader.c ********************************/
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/************************************************************************************//**
|
||||||
|
* \file xcploader.h
|
||||||
|
* \brief XCP Loader module header file.
|
||||||
|
* \ingroup SerialBoot
|
||||||
|
* \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
|
||||||
|
****************************************************************************************/
|
||||||
|
#ifndef XCPLOADER_H
|
||||||
|
#define XCPLOADER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Include files
|
||||||
|
****************************************************************************************/
|
||||||
|
#include <stdint.h> /* for standard integer types */
|
||||||
|
#include <stdbool.h> /* for boolean type */
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Macro definitions
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief Total number of bytes in a master<->slave data packet. It should be at least
|
||||||
|
* equal or larger than that configured on the slave.
|
||||||
|
*/
|
||||||
|
#define XCPLOADER_PACKET_SIZE_MAX (255)
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Type definitions
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief XCP protocol specific settings. */
|
||||||
|
typedef struct t_xcp_settings
|
||||||
|
{
|
||||||
|
uint16_t timeoutT1; /**< Command response timeout in milliseconds. */
|
||||||
|
uint16_t timeoutT3; /**< Start programming timeout in milliseconds. */
|
||||||
|
uint16_t timeoutT4; /**< Erase memory timeout in milliseonds. */
|
||||||
|
uint16_t timeoutT5; /**< Program memory and reset timeout in milliseonds. */
|
||||||
|
uint16_t timeoutT7; /**< Busy wait timer timeout in milliseonds. */
|
||||||
|
} tXcpSettings;
|
||||||
|
|
||||||
|
|
||||||
|
/** \brief XCP packet type. */
|
||||||
|
typedef struct t_xcp_packet
|
||||||
|
{
|
||||||
|
uint8_t data[XCPLOADER_PACKET_SIZE_MAX]; /**< Packet data. */
|
||||||
|
uint8_t len; /**< Packet length. */
|
||||||
|
} tXcpPacket;
|
||||||
|
|
||||||
|
|
||||||
|
/** \brief XCP transport layer. */
|
||||||
|
typedef struct t_xcp_transport
|
||||||
|
{
|
||||||
|
/** \brief Initialization of the XCP transpor layer. */
|
||||||
|
void (*Init) (void *settings);
|
||||||
|
/** \brief Uninitializes the XCP transpor layer. */
|
||||||
|
void (*Deinit) (void);
|
||||||
|
/** \brief Connects the XCP transpor layer. */
|
||||||
|
bool (*Connect) (void);
|
||||||
|
/** \brief Disconnects the XCP transpor layer. */
|
||||||
|
void (*Disconnect) (void);
|
||||||
|
/** \brief Sends an XCP packet and waits for the response to come back. */
|
||||||
|
bool (*SendPacket) (tXcpPacket *txPacket, tXcpPacket *rxPacket, uint16_t timeout);
|
||||||
|
} tXcpTransport;
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Function prototypes
|
||||||
|
****************************************************************************************/
|
||||||
|
void XcpLoaderInit(tXcpSettings *settings, tXcpTransport const * const transport, void *tpsettings);
|
||||||
|
void XcpLoaderDeinit(void);
|
||||||
|
bool XcpLoaderConnect(void);
|
||||||
|
void XcpLoaderDisconnect(void);
|
||||||
|
bool XcpLoaderStartProgrammingSession(void);
|
||||||
|
bool XcpLoaderStopProgrammingSession(void);
|
||||||
|
bool XcpLoaderClearMemory(uint32_t addr, uint32_t len);
|
||||||
|
bool XcpLoaderReadData(uint32_t addr, uint32_t len, uint8_t *data);
|
||||||
|
bool XcpLoaderProgramData(uint32_t addr, uint32_t len, uint8_t *data);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* XCPLOADER_H */
|
||||||
|
/********************************* end of xcploader.h **********************************/
|
||||||
|
|
|
@ -1,689 +0,0 @@
|
||||||
/************************************************************************************//**
|
|
||||||
* \file xcpmaster.c
|
|
||||||
* \brief XCP Master source file.
|
|
||||||
* \ingroup SerialBoot
|
|
||||||
* \internal
|
|
||||||
*----------------------------------------------------------------------------------------
|
|
||||||
* C O P Y R I G H T
|
|
||||||
*----------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) 2014 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> /* assertion module */
|
|
||||||
#include <sb_types.h> /* C types */
|
|
||||||
#include "xcpmaster.h" /* XCP master protocol module */
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Macro definitions
|
|
||||||
****************************************************************************************/
|
|
||||||
/* XCP command codes as defined by the protocol currently supported by this module */
|
|
||||||
#define XCP_MASTER_CMD_CONNECT (0xFF)
|
|
||||||
#define XCP_MASTER_CMD_DISCONNECT (0xFE)
|
|
||||||
#define XCP_MASTER_CMD_SET_MTA (0xF6)
|
|
||||||
#define XCP_MASTER_CMD_UPLOAD (0xF5)
|
|
||||||
#define XCP_MASTER_CMD_PROGRAM_START (0xD2)
|
|
||||||
#define XCP_MASTER_CMD_PROGRAM_CLEAR (0xD1)
|
|
||||||
#define XCP_MASTER_CMD_PROGRAM (0xD0)
|
|
||||||
#define XCP_MASTER_CMD_PROGRAM_RESET (0xCF)
|
|
||||||
#define XCP_MASTER_CMD_PROGRAM_MAX (0xC9)
|
|
||||||
|
|
||||||
/* XCP response packet IDs as defined by the protocol */
|
|
||||||
#define XCP_MASTER_CMD_PID_RES (0xFF) /* positive response */
|
|
||||||
|
|
||||||
/* timeout values */
|
|
||||||
#define XCP_MASTER_CONNECT_TIMEOUT_MS (20)
|
|
||||||
#define XCP_MASTER_TIMEOUT_T1_MS (1000) /* standard command timeout */
|
|
||||||
#define XCP_MASTER_TIMEOUT_T2_MS (2000) /* build checksum timeout */
|
|
||||||
#define XCP_MASTER_TIMEOUT_T3_MS (2000) /* program start timeout */
|
|
||||||
#define XCP_MASTER_TIMEOUT_T4_MS (10000) /* erase timeout */
|
|
||||||
#define XCP_MASTER_TIMEOUT_T5_MS (1000) /* write and reset timeout */
|
|
||||||
#define XCP_MASTER_TIMEOUT_T6_MS (1000) /* user specific connect connect timeout */
|
|
||||||
#define XCP_MASTER_TIMEOUT_T7_MS (2000) /* wait timer timeout */
|
|
||||||
|
|
||||||
/** \brief Number of retries to connect to the XCP slave. */
|
|
||||||
#define XCP_MASTER_CONNECT_RETRIES (5)
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Function prototypes
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 XcpMasterSendCmdConnect(void);
|
|
||||||
static sb_uint8 XcpMasterSendCmdSetMta(sb_uint32 address);
|
|
||||||
static sb_uint8 XcpMasterSendCmdUpload(sb_uint8 data[], sb_uint8 length);
|
|
||||||
static sb_uint8 XcpMasterSendCmdProgramStart(void);
|
|
||||||
static sb_uint8 XcpMasterSendCmdProgramReset(void);
|
|
||||||
static sb_uint8 XcpMasterSendCmdProgram(sb_uint8 length, sb_uint8 data[]);
|
|
||||||
static sb_uint8 XcpMasterSendCmdProgramMax(sb_uint8 data[]);
|
|
||||||
static sb_uint8 XcpMasterSendCmdProgramClear(sb_uint32 length);
|
|
||||||
static void XcpMasterSetOrderedLong(sb_uint32 value, sb_uint8 data[]);
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Local data declarations
|
|
||||||
****************************************************************************************/
|
|
||||||
/** \brief Store the byte ordering of the XCP slave. */
|
|
||||||
static sb_uint8 xcpSlaveIsIntel = SB_FALSE;
|
|
||||||
|
|
||||||
/** \brief The max number of bytes in the command transmit object (master->slave). */
|
|
||||||
static sb_uint8 xcpMaxCto;
|
|
||||||
|
|
||||||
/** \brief The max number of bytes in the command transmit object (master->slave) during
|
|
||||||
* a programming session.
|
|
||||||
*/
|
|
||||||
static sb_uint8 xcpMaxProgCto = 0;
|
|
||||||
|
|
||||||
/** \brief The max number of bytes in the data transmit object (slave->master). */
|
|
||||||
static sb_uint8 xcpMaxDto;
|
|
||||||
|
|
||||||
/** \brief Internal data buffer for storing the data of the XCP response packet. */
|
|
||||||
static tXcpTransportResponsePacket responsePacket;
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Initializes the XCP master protocol layer.
|
|
||||||
** \param device Serial communication device name. For example "COM4".
|
|
||||||
** \param baudrate Communication speed in bits/sec.
|
|
||||||
** \return SB_TRUE is successful, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpMasterInit(sb_char *device, sb_uint32 baudrate)
|
|
||||||
{
|
|
||||||
/* initialize the underlying transport layer that is used for the communication */
|
|
||||||
return XcpTransportInit(device, baudrate);
|
|
||||||
} /*** end of XcpMasterInit ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Uninitializes the XCP master protocol layer.
|
|
||||||
** \return none.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
void XcpMasterDeinit(void)
|
|
||||||
{
|
|
||||||
XcpTransportClose();
|
|
||||||
} /*** end of XcpMasterDeinit ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Connect to the XCP slave.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpMasterConnect(void)
|
|
||||||
{
|
|
||||||
sb_uint8 cnt;
|
|
||||||
|
|
||||||
/* try to connect with a finite amount of retries */
|
|
||||||
for (cnt=0; cnt<XCP_MASTER_CONNECT_RETRIES; cnt++)
|
|
||||||
{
|
|
||||||
/* send the connect command */
|
|
||||||
if (XcpMasterSendCmdConnect() == SB_TRUE)
|
|
||||||
{
|
|
||||||
/* connected so no need to retry */
|
|
||||||
return SB_TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* still here so could not connect */
|
|
||||||
return SB_FALSE;
|
|
||||||
} /*** end of XcpMasterConnect ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Disconnect the slave.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpMasterDisconnect(void)
|
|
||||||
{
|
|
||||||
/* send reset command instead of the disconnect. this causes the user program on the
|
|
||||||
* slave to automatically start again if present.
|
|
||||||
*/
|
|
||||||
return XcpMasterSendCmdProgramReset();
|
|
||||||
} /*** end of XcpMasterDisconnect ***/
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Puts a connected slave in programming session.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpMasterStartProgrammingSession(void)
|
|
||||||
{
|
|
||||||
/* place the slave in programming mode */
|
|
||||||
return XcpMasterSendCmdProgramStart();
|
|
||||||
} /*** end of XcpMasterStartProgrammingSession ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Stops the programming session by sending a program command with size 0 and
|
|
||||||
** then resetting the slave.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpMasterStopProgrammingSession(void)
|
|
||||||
{
|
|
||||||
/* stop programming by sending the program command with size 0 */
|
|
||||||
if (XcpMasterSendCmdProgram(0, SB_NULL) == SB_FALSE)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* request a reset of the slave */
|
|
||||||
return XcpMasterSendCmdProgramReset();
|
|
||||||
} /*** end of XcpMasterStopProgrammingSession ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Erases non volatile memory on the slave.
|
|
||||||
** \param addr Base memory address for the erase operation.
|
|
||||||
** \param len Number of bytes to erase.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpMasterClearMemory(sb_uint32 addr, sb_uint32 len)
|
|
||||||
{
|
|
||||||
/* first set the MTA pointer */
|
|
||||||
if (XcpMasterSendCmdSetMta(addr) == SB_FALSE)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* now perform the erase operation */
|
|
||||||
return XcpMasterSendCmdProgramClear(len);
|
|
||||||
} /*** end of XcpMasterClearMemory ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Reads data from the slave's memory.
|
|
||||||
** \param addr Base memory address for the read operation
|
|
||||||
** \param len Number of bytes to read.
|
|
||||||
** \param data Destination buffer for storing the read data bytes.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpMasterReadData(sb_uint32 addr, sb_uint32 len, sb_uint8 data[])
|
|
||||||
{
|
|
||||||
sb_uint8 currentReadCnt;
|
|
||||||
sb_uint32 bufferOffset = 0;
|
|
||||||
|
|
||||||
/* first set the MTA pointer */
|
|
||||||
if (XcpMasterSendCmdSetMta(addr) == SB_FALSE)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* perform segmented upload of the data */
|
|
||||||
while (len > 0)
|
|
||||||
{
|
|
||||||
/* set the current read length to make optimal use of the available packet data. */
|
|
||||||
currentReadCnt = len % (xcpMaxDto - 1);
|
|
||||||
if (currentReadCnt == 0)
|
|
||||||
{
|
|
||||||
currentReadCnt = (xcpMaxDto - 1);
|
|
||||||
}
|
|
||||||
/* upload some data */
|
|
||||||
if (XcpMasterSendCmdUpload(&data[bufferOffset], currentReadCnt) == SB_FALSE)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* update loop variables */
|
|
||||||
len -= currentReadCnt;
|
|
||||||
bufferOffset += currentReadCnt;
|
|
||||||
}
|
|
||||||
/* still here so all data successfully read from the slave */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterReadData ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Programs data to the slave's non volatile memory. Note that it must be
|
|
||||||
** erased first.
|
|
||||||
** \param addr Base memory address for the program operation
|
|
||||||
** \param len Number of bytes to program.
|
|
||||||
** \param data Source buffer with the to be programmed bytes.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
sb_uint8 XcpMasterProgramData(sb_uint32 addr, sb_uint32 len, sb_uint8 data[])
|
|
||||||
{
|
|
||||||
sb_uint8 currentWriteCnt;
|
|
||||||
sb_uint32 bufferOffset = 0;
|
|
||||||
|
|
||||||
/* first set the MTA pointer */
|
|
||||||
if (XcpMasterSendCmdSetMta(addr) == SB_FALSE)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* perform segmented programming of the data */
|
|
||||||
while (len > 0)
|
|
||||||
{
|
|
||||||
/* set the current read length to make optimal use of the available packet data. */
|
|
||||||
currentWriteCnt = len % (xcpMaxProgCto - 1);
|
|
||||||
if (currentWriteCnt == 0)
|
|
||||||
{
|
|
||||||
currentWriteCnt = (xcpMaxProgCto - 1);
|
|
||||||
}
|
|
||||||
/* prepare the packed data for the program command */
|
|
||||||
if (currentWriteCnt < (xcpMaxProgCto - 1))
|
|
||||||
{
|
|
||||||
/* program data */
|
|
||||||
if (XcpMasterSendCmdProgram(currentWriteCnt, &data[bufferOffset]) == SB_FALSE)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* program max data */
|
|
||||||
if (XcpMasterSendCmdProgramMax(&data[bufferOffset]) == SB_FALSE)
|
|
||||||
{
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* update loop variables */
|
|
||||||
len -= currentWriteCnt;
|
|
||||||
bufferOffset += currentWriteCnt;
|
|
||||||
}
|
|
||||||
/* still here so all data successfully programmed */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterProgramData ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Sends the XCP Connect command.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 XcpMasterSendCmdConnect(void)
|
|
||||||
{
|
|
||||||
sb_uint8 packetData[2];
|
|
||||||
tXcpTransportResponsePacket *responsePacketPtr;
|
|
||||||
|
|
||||||
/* prepare the command packet */
|
|
||||||
packetData[0] = XCP_MASTER_CMD_CONNECT;
|
|
||||||
packetData[1] = 0; /* normal mode */
|
|
||||||
|
|
||||||
/* send the packet */
|
|
||||||
if (XcpTransportSendPacket(packetData, 2, XCP_MASTER_CONNECT_TIMEOUT_MS) == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* cound not set packet or receive response within the specified timeout */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* still here so a response was received */
|
|
||||||
responsePacketPtr = XcpTransportReadResponsePacket();
|
|
||||||
|
|
||||||
/* check if the reponse was valid */
|
|
||||||
if ( (responsePacketPtr->len == 0) || (responsePacketPtr->data[0] != XCP_MASTER_CMD_PID_RES) )
|
|
||||||
{
|
|
||||||
/* not a valid or positive response */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* process response data */
|
|
||||||
if ((responsePacketPtr->data[2] & 0x01) == 0)
|
|
||||||
{
|
|
||||||
/* store slave's byte ordering information */
|
|
||||||
xcpSlaveIsIntel = SB_TRUE;
|
|
||||||
}
|
|
||||||
/* store max number of bytes the slave allows for master->slave packets. */
|
|
||||||
xcpMaxCto = responsePacketPtr->data[3];
|
|
||||||
xcpMaxProgCto = xcpMaxCto;
|
|
||||||
/* store max number of bytes the slave allows for slave->master packets. */
|
|
||||||
if (xcpSlaveIsIntel == SB_TRUE)
|
|
||||||
{
|
|
||||||
xcpMaxDto = responsePacketPtr->data[4] + (responsePacketPtr->data[5] << 8);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xcpMaxDto = responsePacketPtr->data[5] + (responsePacketPtr->data[4] << 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* double check size configuration of the master */
|
|
||||||
assert(XCP_MASTER_TX_MAX_DATA >= xcpMaxCto);
|
|
||||||
assert(XCP_MASTER_RX_MAX_DATA >= xcpMaxDto);
|
|
||||||
|
|
||||||
/* still here so all went well */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterSendCmdConnect ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Sends the XCP Set MTA command.
|
|
||||||
** \param address New MTA address for the slave.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 XcpMasterSendCmdSetMta(sb_uint32 address)
|
|
||||||
{
|
|
||||||
sb_uint8 packetData[8];
|
|
||||||
tXcpTransportResponsePacket *responsePacketPtr;
|
|
||||||
|
|
||||||
/* prepare the command packet */
|
|
||||||
packetData[0] = XCP_MASTER_CMD_SET_MTA;
|
|
||||||
packetData[1] = 0; /* reserved */
|
|
||||||
packetData[2] = 0; /* reserved */
|
|
||||||
packetData[3] = 0; /* address extension not supported */
|
|
||||||
|
|
||||||
/* set the address taking into account byte ordering */
|
|
||||||
XcpMasterSetOrderedLong(address, &packetData[4]);
|
|
||||||
|
|
||||||
/* send the packet */
|
|
||||||
if (XcpTransportSendPacket(packetData, 8, XCP_MASTER_TIMEOUT_T1_MS) == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* cound not set packet or receive response within the specified timeout */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* still here so a response was received */
|
|
||||||
responsePacketPtr = XcpTransportReadResponsePacket();
|
|
||||||
|
|
||||||
/* check if the reponse was valid */
|
|
||||||
if ( (responsePacketPtr->len == 0) || (responsePacketPtr->data[0] != XCP_MASTER_CMD_PID_RES) )
|
|
||||||
{
|
|
||||||
/* not a valid or positive response */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* still here so all went well */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterSendCmdSetMta ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \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 sb_uint8 XcpMasterSendCmdUpload(sb_uint8 data[], sb_uint8 length)
|
|
||||||
{
|
|
||||||
sb_uint8 packetData[2];
|
|
||||||
tXcpTransportResponsePacket *responsePacketPtr;
|
|
||||||
sb_uint8 data_index;
|
|
||||||
|
|
||||||
/* cannot request more data then the max rx data - 1 */
|
|
||||||
assert(length < XCP_MASTER_RX_MAX_DATA);
|
|
||||||
|
|
||||||
/* prepare the command packet */
|
|
||||||
packetData[0] = XCP_MASTER_CMD_UPLOAD;
|
|
||||||
packetData[1] = length;
|
|
||||||
|
|
||||||
/* send the packet */
|
|
||||||
if (XcpTransportSendPacket(packetData, 2, XCP_MASTER_TIMEOUT_T1_MS) == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* cound not set packet or receive response within the specified timeout */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* still here so a response was received */
|
|
||||||
responsePacketPtr = XcpTransportReadResponsePacket();
|
|
||||||
|
|
||||||
/* check if the reponse was valid */
|
|
||||||
if ( (responsePacketPtr->len == 0) || (responsePacketPtr->data[0] != XCP_MASTER_CMD_PID_RES) )
|
|
||||||
{
|
|
||||||
/* not a valid or positive response */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now store the uploaded data */
|
|
||||||
for (data_index=0; data_index<length; data_index++)
|
|
||||||
{
|
|
||||||
data[data_index] = responsePacketPtr->data[data_index+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* still here so all went well */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterSendCmdUpload ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Sends the XCP PROGRAM START command.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 XcpMasterSendCmdProgramStart(void)
|
|
||||||
{
|
|
||||||
sb_uint8 packetData[1];
|
|
||||||
tXcpTransportResponsePacket *responsePacketPtr;
|
|
||||||
|
|
||||||
/* prepare the command packet */
|
|
||||||
packetData[0] = XCP_MASTER_CMD_PROGRAM_START;
|
|
||||||
|
|
||||||
/* send the packet */
|
|
||||||
if (XcpTransportSendPacket(packetData, 1, XCP_MASTER_TIMEOUT_T3_MS) == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* cound not set packet or receive response within the specified timeout */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* still here so a response was received */
|
|
||||||
responsePacketPtr = XcpTransportReadResponsePacket();
|
|
||||||
|
|
||||||
/* check if the reponse was valid */
|
|
||||||
if ( (responsePacketPtr->len == 0) || (responsePacketPtr->data[0] != XCP_MASTER_CMD_PID_RES) )
|
|
||||||
{
|
|
||||||
/* not a valid or positive response */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* store max number of bytes the slave allows for master->slave packets during the
|
|
||||||
* programming session
|
|
||||||
*/
|
|
||||||
xcpMaxProgCto = responsePacketPtr->data[3];
|
|
||||||
|
|
||||||
/* still here so all went well */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterSendCmdProgramStart ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Sends the XCP PROGRAM RESET command. Note that this command is a bit
|
|
||||||
** different as in it does not require a response.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 XcpMasterSendCmdProgramReset(void)
|
|
||||||
{
|
|
||||||
sb_uint8 packetData[1];
|
|
||||||
tXcpTransportResponsePacket *responsePacketPtr;
|
|
||||||
|
|
||||||
/* prepare the command packet */
|
|
||||||
packetData[0] = XCP_MASTER_CMD_PROGRAM_RESET;
|
|
||||||
|
|
||||||
/* send the packet, assume the sending itself is ok and check if a response was
|
|
||||||
* received.
|
|
||||||
*/
|
|
||||||
if (XcpTransportSendPacket(packetData, 1, XCP_MASTER_TIMEOUT_T5_MS) == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* probably no response received within the specified timeout, but that is allowed
|
|
||||||
* for the reset command.
|
|
||||||
*/
|
|
||||||
return SB_TRUE;
|
|
||||||
}
|
|
||||||
/* still here so a response was received */
|
|
||||||
responsePacketPtr = XcpTransportReadResponsePacket();
|
|
||||||
|
|
||||||
/* check if the reponse was valid */
|
|
||||||
if ( (responsePacketPtr->len == 0) || (responsePacketPtr->data[0] != XCP_MASTER_CMD_PID_RES) )
|
|
||||||
{
|
|
||||||
/* not a valid or positive response */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* still here so all went well */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterSendCmdProgramReset ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \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 SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 XcpMasterSendCmdProgram(sb_uint8 length, sb_uint8 data[])
|
|
||||||
{
|
|
||||||
sb_uint8 packetData[XCP_MASTER_TX_MAX_DATA];
|
|
||||||
tXcpTransportResponsePacket *responsePacketPtr;
|
|
||||||
sb_uint8 cnt;
|
|
||||||
|
|
||||||
/* verify that this number of bytes actually first in this command */
|
|
||||||
assert(length <= (xcpMaxProgCto-2) && (xcpMaxProgCto <= XCP_MASTER_TX_MAX_DATA));
|
|
||||||
|
|
||||||
/* prepare the command packet */
|
|
||||||
packetData[0] = XCP_MASTER_CMD_PROGRAM;
|
|
||||||
packetData[1] = length;
|
|
||||||
for (cnt=0; cnt<length; cnt++)
|
|
||||||
{
|
|
||||||
packetData[cnt+2] = data[cnt];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send the packet */
|
|
||||||
if (XcpTransportSendPacket(packetData, length+2, XCP_MASTER_TIMEOUT_T5_MS) == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* cound not set packet or receive response within the specified timeout */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* still here so a response was received */
|
|
||||||
responsePacketPtr = XcpTransportReadResponsePacket();
|
|
||||||
|
|
||||||
/* check if the reponse was valid */
|
|
||||||
if ( (responsePacketPtr->len == 0) || (responsePacketPtr->data[0] != XCP_MASTER_CMD_PID_RES) )
|
|
||||||
{
|
|
||||||
/* not a valid or positive response */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* still here so all went well */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterSendCmdProgram ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Sends the XCP PROGRAM MAX command.
|
|
||||||
** \param data Array with data bytes to program.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 XcpMasterSendCmdProgramMax(sb_uint8 data[])
|
|
||||||
{
|
|
||||||
sb_uint8 packetData[XCP_MASTER_TX_MAX_DATA];
|
|
||||||
tXcpTransportResponsePacket *responsePacketPtr;
|
|
||||||
sb_uint8 cnt;
|
|
||||||
|
|
||||||
/* verify that this number of bytes actually first in this command */
|
|
||||||
assert(xcpMaxProgCto <= XCP_MASTER_TX_MAX_DATA);
|
|
||||||
|
|
||||||
/* prepare the command packet */
|
|
||||||
packetData[0] = XCP_MASTER_CMD_PROGRAM_MAX;
|
|
||||||
for (cnt=0; cnt<(xcpMaxProgCto-1); cnt++)
|
|
||||||
{
|
|
||||||
packetData[cnt+1] = data[cnt];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send the packet */
|
|
||||||
if (XcpTransportSendPacket(packetData, xcpMaxProgCto, XCP_MASTER_TIMEOUT_T5_MS) == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* cound not set packet or receive response within the specified timeout */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* still here so a response was received */
|
|
||||||
responsePacketPtr = XcpTransportReadResponsePacket();
|
|
||||||
|
|
||||||
/* check if the reponse was valid */
|
|
||||||
if ( (responsePacketPtr->len == 0) || (responsePacketPtr->data[0] != XCP_MASTER_CMD_PID_RES) )
|
|
||||||
{
|
|
||||||
/* not a valid or positive response */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* still here so all went well */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterSendCmdProgramMax ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \brief Sends the XCP PROGRAM CLEAR command.
|
|
||||||
** \return SB_TRUE is successfull, SB_FALSE otherwise.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static sb_uint8 XcpMasterSendCmdProgramClear(sb_uint32 length)
|
|
||||||
{
|
|
||||||
sb_uint8 packetData[8];
|
|
||||||
tXcpTransportResponsePacket *responsePacketPtr;
|
|
||||||
|
|
||||||
/* prepare the command packet */
|
|
||||||
packetData[0] = XCP_MASTER_CMD_PROGRAM_CLEAR;
|
|
||||||
packetData[1] = 0; /* use absolute mode */
|
|
||||||
packetData[2] = 0; /* reserved */
|
|
||||||
packetData[3] = 0; /* reserved */
|
|
||||||
|
|
||||||
/* set the erase length taking into account byte ordering */
|
|
||||||
XcpMasterSetOrderedLong(length, &packetData[4]);
|
|
||||||
|
|
||||||
|
|
||||||
/* send the packet */
|
|
||||||
if (XcpTransportSendPacket(packetData, 8, XCP_MASTER_TIMEOUT_T4_MS) == SB_FALSE)
|
|
||||||
{
|
|
||||||
/* cound not set packet or receive response within the specified timeout */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
/* still here so a response was received */
|
|
||||||
responsePacketPtr = XcpTransportReadResponsePacket();
|
|
||||||
|
|
||||||
/* check if the reponse was valid */
|
|
||||||
if ( (responsePacketPtr->len == 0) || (responsePacketPtr->data[0] != XCP_MASTER_CMD_PID_RES) )
|
|
||||||
{
|
|
||||||
/* not a valid or positive response */
|
|
||||||
return SB_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* still here so all went well */
|
|
||||||
return SB_TRUE;
|
|
||||||
} /*** end of XcpMasterSendCmdProgramClear ***/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************************//**
|
|
||||||
** \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.
|
|
||||||
** \return none.
|
|
||||||
**
|
|
||||||
****************************************************************************************/
|
|
||||||
static void XcpMasterSetOrderedLong(sb_uint32 value, sb_uint8 data[])
|
|
||||||
{
|
|
||||||
if (xcpSlaveIsIntel == SB_TRUE)
|
|
||||||
{
|
|
||||||
data[3] = (sb_uint8)(value >> 24);
|
|
||||||
data[2] = (sb_uint8)(value >> 16);
|
|
||||||
data[1] = (sb_uint8)(value >> 8);
|
|
||||||
data[0] = (sb_uint8)value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data[0] = (sb_uint8)(value >> 24);
|
|
||||||
data[1] = (sb_uint8)(value >> 16);
|
|
||||||
data[2] = (sb_uint8)(value >> 8);
|
|
||||||
data[3] = (sb_uint8)value;
|
|
||||||
}
|
|
||||||
} /*** end of XcpMasterSetOrderedLong ***/
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************** end of xcpmaster.c ********************************/
|
|
|
@ -0,0 +1,255 @@
|
||||||
|
/************************************************************************************//**
|
||||||
|
* \file xcptpuart.c
|
||||||
|
* \brief XCP transport layer module for UART source file.
|
||||||
|
* \ingroup SerialBoot
|
||||||
|
* \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 <stddef.h> /* for NULL declaration */
|
||||||
|
#include <stdlib.h> /* for standard library */
|
||||||
|
#include <string.h> /* for string library */
|
||||||
|
#include <assert.h> /* for assertions */
|
||||||
|
#include "xcptpuart.h" /* XCP transport layer for UART */
|
||||||
|
#include "timeutil.h" /* for time utilities module */
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Macro definitions
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief Maximum amount of data bytes that this module supports for XCP packets. */
|
||||||
|
#define XCP_TP_UART_MAX_DATA_LEN (256)
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Function prototypes
|
||||||
|
****************************************************************************************/
|
||||||
|
static void XcpTpUartInit(void *settings);
|
||||||
|
static void XcpTpUartDeinit(void);
|
||||||
|
static bool XcpTpUartConnect(void);
|
||||||
|
static void XcpTpUartDisconnect(void);
|
||||||
|
static bool XcpTpUartSendPacket(tXcpPacket *txPacket, tXcpPacket *rxPacket, uint16_t timeout);
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Local constant declarations
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief XCP transport structure filled with UART specifics. */
|
||||||
|
static const tXcpTransport uartTransport =
|
||||||
|
{
|
||||||
|
XcpTpUartInit,
|
||||||
|
XcpTpUartDeinit,
|
||||||
|
XcpTpUartConnect,
|
||||||
|
XcpTpUartDisconnect,
|
||||||
|
XcpTpUartSendPacket
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Local data declarations
|
||||||
|
****************************************************************************************/
|
||||||
|
/** \brief The settings to use in this transport layer. */
|
||||||
|
static tXcpTpUartSettings tpUartSettings;
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************************************//**
|
||||||
|
** \brief Obtains a pointer to the XCP UART transport structure, so that it can
|
||||||
|
** be linked to the XCP loader module.
|
||||||
|
** \return Pointer to XCP UART transport structure.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
tXcpTransport const * const XcpTpUartGetTransport(void)
|
||||||
|
{
|
||||||
|
return &uartTransport;
|
||||||
|
} /*** end of XcpTpUartGetTransport ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Initializes the transport layer.
|
||||||
|
** \param settings Pointer to settings structure.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static void XcpTpUartInit(void *settings)
|
||||||
|
{
|
||||||
|
/* verify parameters */
|
||||||
|
assert(settings != NULL);
|
||||||
|
|
||||||
|
/* shallow copy the transport layer settings for layer usage */
|
||||||
|
tpUartSettings = *((tXcpTpUartSettings *)settings);
|
||||||
|
/* the portname is a pointer and it is not gauranteed that it stays valid so we need
|
||||||
|
* to deep copy this one. note the +1 for '\0' in malloc.
|
||||||
|
*/
|
||||||
|
tpUartSettings.portname = malloc(strlen(((tXcpTpUartSettings *)settings)->portname) + 1);
|
||||||
|
strcpy(tpUartSettings.portname, ((tXcpTpUartSettings *)settings)->portname);
|
||||||
|
} /*** end of XcpTpUartInit ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Uninitializes the transport layer.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static void XcpTpUartDeinit(void)
|
||||||
|
{
|
||||||
|
/* release memory that was allocated for storing the port name */
|
||||||
|
if (tpUartSettings.portname != NULL)
|
||||||
|
{
|
||||||
|
free(tpUartSettings.portname);
|
||||||
|
}
|
||||||
|
} /*** end of XcpTpUartDeinit ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Connects to the transport layer.
|
||||||
|
** \return True is connected, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static bool XcpTpUartConnect(void)
|
||||||
|
{
|
||||||
|
/* connect to the serial port */
|
||||||
|
return SerialPortOpen(tpUartSettings.portname, tpUartSettings.baudrate);
|
||||||
|
} /*** end of XcpTpUartConnect ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Disconnects from the transport layer.
|
||||||
|
** \return None.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static void XcpTpUartDisconnect(void)
|
||||||
|
{
|
||||||
|
/* disconnect from the serial port */
|
||||||
|
SerialPortClose();
|
||||||
|
} /*** end of XcpTpUartDisconnect ***/
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
** \brief Transmits an XCP packet on the transport layer and attempts to receive the
|
||||||
|
** response packet within the specified timeout.
|
||||||
|
** \return True is successful and a response packet was received, false otherwise.
|
||||||
|
**
|
||||||
|
****************************************************************************************/
|
||||||
|
static bool XcpTpUartSendPacket(tXcpPacket *txPacket, tXcpPacket *rxPacket, uint16_t timeout)
|
||||||
|
{
|
||||||
|
static uint8_t uartBuffer[XCP_TP_UART_MAX_DATA_LEN + 1]; /* static to lower stack load */
|
||||||
|
uint8_t byteIdx;
|
||||||
|
uint32_t responseTimeoutTime;
|
||||||
|
bool packetReceptionComplete;
|
||||||
|
|
||||||
|
/* verify parameters */
|
||||||
|
assert(txPacket != NULL);
|
||||||
|
assert(txPacket->len <= XCP_TP_UART_MAX_DATA_LEN);
|
||||||
|
assert(rxPacket != NULL);
|
||||||
|
assert(timeout > 0);
|
||||||
|
|
||||||
|
|
||||||
|
/* prepare the XCP packet for transmission on UART. this is basically the same as the
|
||||||
|
* xcp packet data but just the length of the packet is added to the first byte.
|
||||||
|
*/
|
||||||
|
uartBuffer[0] = txPacket->len;
|
||||||
|
for (byteIdx=0; byteIdx<txPacket->len; byteIdx++)
|
||||||
|
{
|
||||||
|
uartBuffer[byteIdx + 1] = txPacket->data[byteIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* transmit the packet */
|
||||||
|
if (!SerialPortWrite(uartBuffer, txPacket->len + 1))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* determine timeout time for the response packet */
|
||||||
|
responseTimeoutTime = TimeUtilGetSystemTimeMs() + timeout;
|
||||||
|
|
||||||
|
/* initialize packet reception length */
|
||||||
|
uartBuffer[0] = 0;
|
||||||
|
/* poll with timeout detection to receive the first byte. this one contains the
|
||||||
|
* packet length and cannot be zero.
|
||||||
|
*/
|
||||||
|
while (TimeUtilGetSystemTimeMs() < responseTimeoutTime)
|
||||||
|
{
|
||||||
|
if (SerialPortRead(&uartBuffer[0], 1))
|
||||||
|
{
|
||||||
|
/* length received. validate it before accepting it */
|
||||||
|
if (uartBuffer[0] > 0)
|
||||||
|
{
|
||||||
|
/* start of packet received. stop this loop to continue with the
|
||||||
|
* reception of the packet.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* check if a valid start of packet was received */
|
||||||
|
if (uartBuffer[0] == 0)
|
||||||
|
{
|
||||||
|
/* no valid start of packet received, so a timeout occurred. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* continue with reception of the packet */
|
||||||
|
packetReceptionComplete = false;
|
||||||
|
byteIdx = 1;
|
||||||
|
/* poll with timeout detection to receive the full packet */
|
||||||
|
while (TimeUtilGetSystemTimeMs() < responseTimeoutTime)
|
||||||
|
{
|
||||||
|
/* check if the next byte was received */
|
||||||
|
if (SerialPortRead(&uartBuffer[byteIdx], 1))
|
||||||
|
{
|
||||||
|
/* check if the packet reception is now complete */
|
||||||
|
if (byteIdx == uartBuffer[0])
|
||||||
|
{
|
||||||
|
/* set flag and stop the loop */
|
||||||
|
packetReceptionComplete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* increment indexer to the next byte */
|
||||||
|
byteIdx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if a timeout occurred */
|
||||||
|
if (!packetReceptionComplete)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* still here so a full packet was received. copy its contents except the length info
|
||||||
|
* which is stored in the first byte
|
||||||
|
*/
|
||||||
|
for (byteIdx=0; byteIdx<uartBuffer[0]; byteIdx++)
|
||||||
|
{
|
||||||
|
rxPacket->data[byteIdx] = uartBuffer[byteIdx + 1];
|
||||||
|
}
|
||||||
|
rxPacket->len = uartBuffer[0];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} /*** end of XcpTpUartSendPacket ***/
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** end of xcptpuart.c ********************************/
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
/************************************************************************************//**
|
/************************************************************************************//**
|
||||||
* \file sb_types.h
|
* \file xcptpuart.h
|
||||||
* \brief Serial Boot type definitions header file.
|
* \brief XCP transport layer module for UART header file.
|
||||||
* \ingroup SerialBoot
|
* \ingroup SerialBoot
|
||||||
* \internal
|
* \internal
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* C O P Y R I G H T
|
* C O P Y R I G H T
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* Copyright (c) 2014 by Feaser http://www.feaser.com All rights reserved
|
* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved
|
||||||
*
|
*
|
||||||
*----------------------------------------------------------------------------------------
|
*----------------------------------------------------------------------------------------
|
||||||
* L I C E N S E
|
* L I C E N S E
|
||||||
|
@ -25,41 +25,42 @@
|
||||||
*
|
*
|
||||||
* \endinternal
|
* \endinternal
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
#ifndef SB_TYPES_H
|
#ifndef XCPTPUART_H
|
||||||
#define SB_TYPES_H
|
#define XCPTPUART_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Include files
|
* Include files
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
#include <stdio.h> /* standard I/O library */
|
#include "xcploader.h" /* XCP loader module */
|
||||||
|
#include "serialport.h" /* serial port module */
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
/***************************************************************************************
|
||||||
* Macro definitions
|
|
||||||
****************************************************************************************/
|
|
||||||
/** \brief Generic boolean true value. */
|
|
||||||
#define SB_TRUE (1u)
|
|
||||||
|
|
||||||
/** \brief Ceneric boolean false value. */
|
|
||||||
#define SB_FALSE (0u)
|
|
||||||
|
|
||||||
/** \brief NULL pointer value. */
|
|
||||||
#define SB_NULL ((void *)0)
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Type definitions
|
* Type definitions
|
||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
typedef signed char sb_char;
|
/** \brief Layout of structure with settings specific to the XCP transport layer module
|
||||||
typedef signed char sb_int8;
|
* for UART.
|
||||||
typedef signed short sb_int16;
|
*/
|
||||||
typedef signed int sb_int32;
|
typedef struct t_xcp_tp_uart_settings
|
||||||
typedef unsigned char sb_uint8;
|
{
|
||||||
typedef unsigned short sb_uint16;
|
tSerialPortBaudrate baudrate; /**< UART communication speed. */
|
||||||
typedef unsigned int sb_uint32;
|
char *portname; /**< interface port name, i.e. /dev/ttyUSB0. */
|
||||||
typedef FILE * sb_file;
|
} tXcpTpUartSettings;
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************************
|
||||||
|
* Function prototypes
|
||||||
|
****************************************************************************************/
|
||||||
|
tXcpTransport const * const XcpTpUartGetTransport(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* XCPTPUART_H */
|
||||||
|
/********************************* end of xcptpuart.h **********************************/
|
||||||
|
|
||||||
#endif /* SB_TYPES_H */
|
|
||||||
/*********************************** end of sb_types.h *********************************/
|
|
Loading…
Reference in New Issue