752 lines
27 KiB
C
752 lines
27 KiB
C
/************************************************************************************//**
|
|
* \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 *******************************/
|
|
|