/************************************************************************************//**
* \file         srecparser.c
* \brief        Motorola S-record file parser source file.
* \ingroup      Firmware
* \internal
*----------------------------------------------------------------------------------------
*                          C O P Y R I G H T
*----------------------------------------------------------------------------------------
*   Copyright (c) 2017  by Feaser    http://www.feaser.com    All rights reserved
*
*----------------------------------------------------------------------------------------
*                            L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/

/****************************************************************************************
* Include files
****************************************************************************************/
#include <assert.h>                         /* for assertions                          */
#include <stdint.h>                         /* for standard integer types              */
#include <stddef.h>                         /* for NULL declaration                    */
#include <stdbool.h>                        /* for boolean type                        */
#include <stdio.h>                          /* for standard I/O library                */
#include <string.h>                         /* for string library                      */
#include <ctype.h>                          /* for toupper() etc.                      */
#include "util.h"                           /* Utility module                          */
#include "firmware.h"                       /* Firmware data module                    */
#include "srecparser.h"                     /* S-record parser                         */


/****************************************************************************************
* Type definitions
****************************************************************************************/
/** \brief Enumeration for the different supported S-record line types. */
typedef enum t_srec_parser_line_type
{
  SREC_PARSER_LINE_TYPE_S0,                      /**< Header record.                   */
  SREC_PARSER_LINE_TYPE_S1,                      /**< 16-bit address data record.      */
  SREC_PARSER_LINE_TYPE_S2,                      /**< 24-bit address data record.      */
  SREC_PARSER_LINE_TYPE_S3,                      /**< 32-bit address data record.      */
  SREC_PARSER_LINE_TYPE_S7,                      /**< 32-bit address termination.      */
  SREC_PARSER_LINE_TYPE_S8,                      /**< 24-bit address termination.      */
  SREC_PARSER_LINE_TYPE_S9,                      /**< 16-bit address termination.      */
  SREC_PARSER_LINE_TYPE_UNSUPPORTED              /**< Unsupported line.                */
} tSRecParserLineType;


/****************************************************************************************
* Function prototypes
****************************************************************************************/
static bool SRecParserLoadFromFile (char const * firmwareFile, uint32_t addressOffset);
static bool SRecParserSaveToFile (char const * firmwareFile);
static bool SRecParserExtractLineData(char const * line, uint32_t * address, 
                                      uint32_t * len, uint8_t * data);
static tSRecParserLineType SRecParserGetLineType(char const * line);
static bool SRecParserVerifyChecksum(char const * line);
static bool SRecParserConstructLine(char * line, tSRecParserLineType lineType, 
                                    uint32_t address,
                                    uint8_t const * data, uint8_t dataLen);
static uint8_t SRecParserHexStringToByte(char const * hexstring);


/****************************************************************************************
* Local constant declarations
****************************************************************************************/
/** \brief File parser structure filled with Motorola S-record parsing specifics. */
static const tFirmwareParser srecParser =
{
  .LoadFromFile = SRecParserLoadFromFile,
  .SaveToFile = SRecParserSaveToFile
};


/***********************************************************************************//**
** \brief     Obtains a pointer to the parser structure, so that it can be linked to the
**            firmware data module.
** \return    Pointer to firmware parser structure.
**
****************************************************************************************/
tFirmwareParser const * SRecParserGetParser(void)
{
  return &srecParser;
} /*** end of SRecParserGetParser ***/


/************************************************************************************//**
** \brief     Parses the specified firmware file to extract firmware data and adds this 
**            data to the firmware data that is currently managed by the firmware data
**            module.
** \param     firmwareFile Filename of the firmware file to load.
** \param     addressOffset Optional memory address offset to add when loading the 
**            firmware data from the file.
** \return    True if successful, false otherwise.
**
****************************************************************************************/
static bool SRecParserLoadFromFile (char const * firmwareFile, uint32_t addressOffset)
{
  bool result = false;
  FILE *fp;
  /* The bytes count entry on the S-record line is max 255 bytes. This include the 
   * address and the checksum. This would result in 255 * 2 = 510 characters. Another
   * 4 characters are needed for the bytes count and line type characters. Then another
   * two for possible line termination (new line + cariage return). This brings the total
   * characters to 516. Note that this array was made static to lower the stack load.
   */
  static char line[516];
  /* The bytes count entry on the S-record line is max 255 bytes. This includes the 
   * address and checksum. This means the worst case max data bytes per line is 255 - 3.
   * Note that this array was made static to lower the stack load.
   */
  static uint8_t data[252];
  uint32_t address;
  uint32_t len;
  
  /* Check parameters. */
  assert(firmwareFile != NULL);
  
  /* Only continue if the parameters are valid. */
  if (firmwareFile != NULL) /*lint !e774 */
  {
    /* Open the file for reading. */
    fp = fopen(firmwareFile, "r");
    /* Only continue if the filepointer is valid. */
    if (fp != NULL)
    {
      /* Start at the beginning of the file. */
      rewind(fp);
      /* Assume that everyting goes okay and then only set a negative result value upon
       * detection of a problem.
       */
      result = true;
      /* Read the entire file, one line at a time. */
      while (fgets(line, sizeof(line)/sizeof(line[0]), fp) != NULL )
      {
        /* Replace the line termination with a string termination. */
        line[strcspn(line, "\n\r")] = '\0'; 
        /* Extra data from the S-record line. */
        if (SRecParserExtractLineData(line, &address, &len, data))
        {
          /* Only add data if there is actually something to add. */
          if (len > 0)
          {
            /* Add the extracted data to the firmware data module and add the memory
             * address that was specified by the caller.
             */
            if (!FirmwareAddData(address + addressOffset, len, data))
            {
              /* Error detected. Flag it and abort. */
              result = false;
              break;
            }
          }
        }
      }      
      /* Close the file now that we are done with it. */
      fclose(fp);
    }
  }
  /* Give the result back to the caller. */
  return result;
} /*** end of SRecParserLoadFromFile ***/


/************************************************************************************//**
** \brief     Writes firmware data to the specified file in the correct file format.
** \param     firmwareFile Filename of the firmware file to write to.
** \return    True if successful, false otherwise.
**
****************************************************************************************/
static bool SRecParserSaveToFile (char const * firmwareFile)
{
  bool result = false;
  FILE *fp;
  /* The bytes count entry on the S-record line is max 255 bytes. This include the 
   * address and the checksum. This would result in 255 * 2 = 510 characters. Another
   * 4 characters are needed for the bytes count and line type characters. Then another
   * two for possible line termination (new line + cariage return). This brings the total
   * characters to 516. Note that this array was made static to lower the stack load.
   */
  static char line[516];
  /* The bytes count entry on the S-record line is max 255 bytes. This includes the 
   * address and checksum. This means the worst case max data bytes per line is 255 - 3.
   * Note that this array was made static to lower the stack load.
   */
  static uint8_t data[252];
  tFirmwareSegment * segment;
  uint32_t progDataLowestAddress = 0x00000000;
  uint32_t progDataHighestAddress = 0xffffffff;
  tSRecParserLineType dataLineType = SREC_PARSER_LINE_TYPE_S1;
  tSRecParserLineType terminationLineType = SREC_PARSER_LINE_TYPE_S9;
  uint32_t segmentIdx;
  uint32_t currentAddress;
  uint8_t currentByteCnt;
  uint8_t const * currentDataPtr;
  uint32_t segmentBytesLeft;
  const uint8_t maxDataBytesPerLine = 32;
 
  /* Check parameters. */
  assert(firmwareFile != NULL);
  
  /* Only continue if the parameters are valid and if there is something to save. */
  if ( (firmwareFile != NULL) && (FirmwareGetSegmentCount() > 0) ) /*lint !e774 */
  {
    /* Open the file for writing. */
    fp = fopen(firmwareFile, "w");
    /* Only continue if the filepointer is valid. */
    if (fp != NULL)
    {
      /* Init result value to okay at this point and only set it to error in case a 
       * problem was detected.
       */
      result = true;

      /* Determine the lowest memory address used in the program data. This address needs
       * to be specified in the termination record at the end of the S-record file.
       */
      segment = FirmwareGetSegment(0);
      /* Sanity check. */
      assert(segment != NULL);
      if (segment == NULL) /*lint !e774 */
      {
        /* Flag error. */
        result = false;
      }
      else
      {
        /* Store the lowest memory address. */
        progDataLowestAddress = segment->base;
      }

      /* Determine the highest memory address used in the program data. This address 
       * is needed to determine the number of bits needed for the address in the 
       * S-record data lines (16, 24 or 32), which determines the type of S-record for
       * the data lines (S1, S2, S3) and the termination line (S7, S8 or S9).
       */
      if (result) /*lint !e774 */
      {
        segment = FirmwareGetSegment(FirmwareGetSegmentCount() - 1u);
        /* Sanity check. */
        assert(segment != NULL);
        if (segment == NULL) /*lint !e774 */
        {
          /* Flag error. */
          result = false;
        }
        else
        {
          progDataHighestAddress = segment->base + segment->length - 1u;
          /* Does the address have more than 24 bits? */
          if (progDataHighestAddress > 0xffffff)
          {
            dataLineType = SREC_PARSER_LINE_TYPE_S3;
            terminationLineType = SREC_PARSER_LINE_TYPE_S7;
          }
          /* Does the address have more than 16 bits? */
          else if (progDataHighestAddress > 0xffff)
          {
            dataLineType = SREC_PARSER_LINE_TYPE_S2;
            terminationLineType = SREC_PARSER_LINE_TYPE_S8;
          }
        }
      }
      
      /* Extract just the filename and copy it to the data buffer. */
      if (result) /*lint !e774 */
      {
        if (!UtilFileExtractFilename(firmwareFile, (char *)data))
        {
          /* Could not extract filename. */
          result = false;
        }
      }
      
      /* Construct and add the S0-record with the filename. */
      if (result)
      {
        /* Construct the S0-record. */
        result = SRecParserConstructLine(line, SREC_PARSER_LINE_TYPE_S0, 0x0000, data, 
                                         (uint8_t)strlen((char *)data));
        if (result)
        {
          /* Add the S0-record. */
          if (fprintf(fp, "%s\r\n", line) < 0)
          {
            /* Could not write line to the file. */
            result = false;
          }
        }
      }
      
      /* Write all the program data records. Process one segment at a time. */
      if (result)
      {
        /* Loop through all segments. */
        for (segmentIdx = 0; segmentIdx < FirmwareGetSegmentCount(); segmentIdx++)
        {
          /* Obtain the segment. */
          segment = FirmwareGetSegment(segmentIdx);
          /* Sanity check. */
          assert(segment != NULL);
          if (segment == 0) /*lint !e774 */
          {
            /* Flag error and abort loop. */
            result = false;
            break;
          }
          /* Initialize base address, byte count and data pointer. */
          currentAddress = segment->base;
          currentDataPtr = segment->data;
          segmentBytesLeft = segment->length;
          /* Process al bytes in the segment. */
          while (segmentBytesLeft > 0)
          {
            /* Determine the number of bytes that can be written. */
            currentByteCnt = maxDataBytesPerLine;
            if (segmentBytesLeft < maxDataBytesPerLine)
            {
              currentByteCnt = (uint8_t)segmentBytesLeft;
            }
            /* Construct the data record. */
            result = SRecParserConstructLine(line, dataLineType, currentAddress, 
                                             currentDataPtr, currentByteCnt);
            if (!result)
            {
              /* Error detected. No need to continue the loop. */
              break;
            }
            /* Add the data record. */
            if (fprintf(fp, "%s\r\n", line) < 0)
            {
              /* Could not write line to the file. Abort loop. */
              result = false;
              break;
            }
            /* Update loop variables. */
            currentAddress += currentByteCnt;
            currentDataPtr += currentByteCnt;
            segmentBytesLeft -= currentByteCnt;
          }
        }
      }
      
      /* Write the termination record. */
      if (result)
      {
        /* Construct the termination record. */
        result = SRecParserConstructLine(line, terminationLineType, 
                                         progDataLowestAddress, data, 0u);
        if (result)
        {
          /* Add the termination record. */
          if (fprintf(fp, "%s\r\n", line) < 0)
          {
            /* Could not write line to the file. */
            result = false;
          }
        }
      }
    }
  }
  /* Give the result back to the caller. */
  return result;
} /*** end of SRecParserSaveToFile ***/


/************************************************************************************//**
** \brief     Checks if the specified S-record line is of the type that contains program
**            data. If it does, then the program data and base address are extracted and
**            stored at the function parameter pointers.
** \param     line Pointer to the line from an S-record file.
** \param     address Pointer where the start address of the program data is stored.
** \param     len Pointer for storing the number of extracted program data bytes.
** \param     data Pointer to byte array where the extracted program data bytes are 
**            stored.
** \return    True if successful, false otherwise.
**
****************************************************************************************/
static bool SRecParserExtractLineData(char const * line, uint32_t * address, 
                                      uint32_t * len, uint8_t * data)
{
  bool result = false;
  tSRecParserLineType lineType;
  uint8_t dataByteCount = 0;
  uint8_t bytesOnLine;
  uint8_t idx;
  
  /* Verify parameters. */
  assert(line != NULL);
  assert(address != NULL);
  assert(len != NULL);
  assert(data != NULL);
  
  /* Only continue with valid parameters. */
  if ( (line != NULL) && (address != NULL) && (len != NULL) && 
       (data != NULL) ) /*lint !e774 */
  {
    /* Determine the line type. */
    lineType = SRecParserGetLineType(line);
    /* Only continue if the S-record line contains program data. */
    if ( (lineType == SREC_PARSER_LINE_TYPE_S1) || 
         (lineType == SREC_PARSER_LINE_TYPE_S2) ||
         (lineType == SREC_PARSER_LINE_TYPE_S3) )
    {
      /* Verify the checksum of the line. */
      if (SRecParserVerifyChecksum(line))
      {
        /* Adjust pointer to point to byte count value. */
        line += 2u;
        /* Read out the number of byte values that follow on the line. */
        bytesOnLine = SRecParserHexStringToByte(line);
        /* Process S1 line type. */
        if (lineType == SREC_PARSER_LINE_TYPE_S1)
        {
          /* Read out the 16-bit address. */
          line += 2u;
          *address = (uint32_t)SRecParserHexStringToByte(line) << 8u;
          line += 2u;
          *address += (uint32_t)SRecParserHexStringToByte(line);
          /* Determine how many data bytes are on the line. -2 bytes for address and 
            * -1 byte for the checksum.
            */
          dataByteCount = bytesOnLine - 3u; 
        }
        /* Process S2 line type. */
        else if (lineType == SREC_PARSER_LINE_TYPE_S2)
        {
          /* Read out the 24-bit address. */
          line += 2u;
          *address = (uint32_t)SRecParserHexStringToByte(line) << 16u;
          line += 2u;
          *address += (uint32_t)SRecParserHexStringToByte(line) << 8u;
          line += 2u;
          *address += (uint32_t)SRecParserHexStringToByte(line);
          /* Determine how many data bytes are on the line. -3 bytes for address and 
            * -1 byte for the checksum.
            */
          dataByteCount = bytesOnLine - 4u;
        }
        /* Process S3 line type. */
        else
        {
          /* Read out the 32-bit address. */
          line += 2u;
          *address = (uint32_t)SRecParserHexStringToByte(line) << 24u;
          line += 2u;
          *address += (uint32_t)SRecParserHexStringToByte(line) << 16u;
          line += 2u;
          *address += (uint32_t)SRecParserHexStringToByte(line) << 8u;
          line += 2u;
          *address += (uint32_t)SRecParserHexStringToByte(line);
          /* Determine how many data bytes are on the line. -4 bytes for address and 
            * -1 byte for the checksum.
            */
          dataByteCount = bytesOnLine - 5u;
        }
        /* Adjust pointer to point to the first data byte after the address. */
        line += 2u;
        /* Read and store data bytes. */
        for (idx = 0; idx < dataByteCount; idx++)
        {
          data[idx] = SRecParserHexStringToByte(line);
          line += 2u;
        }
        /* Store the number of data bytes that were copied to the array. */
        *len = dataByteCount;
        /* Data extraction complete. */
        result = true;
      }
    }
  }
  /* Give the result back to the caller. */
  return result;
} /*** end of SRecParserExtractLineData ***/


/************************************************************************************//**
** \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 tSRecParserLineType SRecParserGetLineType(char const * line)
{
  tSRecParserLineType result = SREC_PARSER_LINE_TYPE_UNSUPPORTED;
  
  /* Verify parameters. */
  assert(line != NULL);
  
  /* Only continue with valid parameters. */
  if (line != NULL) /*lint !e774 */
  {
    /* Check if the line starts with the 'S' character, followed by a digit */
    if ( (toupper((int32_t)(line[0])) == 'S') && (isdigit((int32_t)(line[1]))) )
    {
      /* Check the digit that follows the 'S' character. Currently only line types that
      * contain program data are needed.
      */
      switch (line[1])
      {
        case '0':
          result = SREC_PARSER_LINE_TYPE_S0;
          break;
        case '1':
          result = SREC_PARSER_LINE_TYPE_S1;
          break;
        case '2':
          result = SREC_PARSER_LINE_TYPE_S2;
          break;
        case '3':
          result = SREC_PARSER_LINE_TYPE_S3;
          break;
        case '7':
          result = SREC_PARSER_LINE_TYPE_S7;
          break;
        case '8':
          result = SREC_PARSER_LINE_TYPE_S8;
          break;
        case '9':
          result = SREC_PARSER_LINE_TYPE_S9;
          break;
        default:
          result = SREC_PARSER_LINE_TYPE_UNSUPPORTED;
          break;
      }
    }
  }
  
  /* Give the result back to the caller. */
  return result;
} /*** end of SRecParserGetLineType ***/


/************************************************************************************//**
** \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    True if the checksum is correct, false otherwise.
**
****************************************************************************************/
static bool SRecParserVerifyChecksum(char const * line)
{
  bool result = false;
  uint8_t bytes_on_line;
  uint8_t checksum = 0;
  
  /* Verify parameters. */
  assert(line != NULL);
  /* Only continue with with valid parameters. */
  if (line != NULL) /*lint !e774 */
  {
    /* Adjust pointer to point to byte count value. */
    line += 2u;
    /* Read out the number of byte values that follow on the line. */
    bytes_on_line = SRecParserHexStringToByte(line);
    /* An S-record will always have at least a 16-bit address and a checksum value. */
    if (bytes_on_line >= 3u)
    {
      /* Byte count is part of checksum. */
      checksum += bytes_on_line;
      /* Adjust pointer to the first byte of the address. */
      line += 2u;
      /* Add byte values of address and data, but not the final checksum. */
      do
      {
        /* Add the next byte value to the checksum. */
        checksum += SRecParserHexStringToByte(line);
        /* Update counter. */
        bytes_on_line--;
        /* Point to next hex string in the line. */
        line += 2u;
      }
      while (bytes_on_line > 1u);
      /* 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 == SRecParserHexStringToByte(line))
      {
        /* Checksum correct. */
        result = true;
      }
    }
  }
  /* Give the result back to the caller. */
  return result;
} /*** end of SRecParserVerifyChecksum ***/


/************************************************************************************//**
** \brief     Creates a NUL terminated S-record line, given the specified line type,
**            address and data bytes. The checksum at the end of the line is also
**            calculated and added.
** \param     line Pointer to character array where the string will be stored.
** \param     lineType The type of S-record line to construct.
** \param     address The address to embed into the line after the byte count.
** \param     data Point to byte array with data bytes to add to the line.
** \param     dataLen The number of data bytes present in the data-array.
** \return    True if successful, false otherwise.
**
****************************************************************************************/
static bool SRecParserConstructLine(char * line, tSRecParserLineType lineType, 
                                    uint32_t address, uint8_t const * data, 
                                    uint8_t dataLen)
{
  bool result = false;
  uint8_t addressBits;
  uint8_t byteVal;
  char hexByteStr[3];
  uint8_t cnt;
  uint8_t checksumVal = 0;
  
  /* Verify parameters. */
  assert(line != NULL);
  if (dataLen > 0)
  {
    assert(data != NULL);
  }
  
  /* Only continue with valid parameters. */
  if (line != NULL) /*lint !e774 */
  {
    /* Set the result value to successful and only change this upon detection of an
     * error.
     */
    result = true;
    /* Start of with an empty string. */
    line[0] = '\0';
    /* Add the record type based on the lineType and store the number of bits in the 
     * address.
     */
    switch (lineType)
    {
      case SREC_PARSER_LINE_TYPE_S0:
        strcat(line, "S0");
        addressBits = 16;
        break;
      case SREC_PARSER_LINE_TYPE_S1:
        strcat(line, "S1");
        addressBits = 16;
        break;
      case SREC_PARSER_LINE_TYPE_S2:
        strcat(line, "S2");
        addressBits = 24;
        break;
      case SREC_PARSER_LINE_TYPE_S3:
        strcat(line, "S3");
        addressBits = 32;
        break;
      case SREC_PARSER_LINE_TYPE_S7:
        strcat(line, "S7");
        addressBits = 32;
        break;
      case SREC_PARSER_LINE_TYPE_S8:
        strcat(line, "S8");
        addressBits = 24;
        break;
      case SREC_PARSER_LINE_TYPE_S9:
        strcat(line, "S9");
        addressBits = 16;
        break;
      case SREC_PARSER_LINE_TYPE_UNSUPPORTED:
      default:
        /* Invalid line type specified. Should not happed. */
        addressBits = 16;
        assert(false); /*lint !e506 */
        break;
    }
    
    /* Next, add the number of bytes that will follow on the line. This is the size in
     * bytes of the address, the actual number of data bytes and the checksum.
     */
    byteVal = (addressBits / 8u) + dataLen + 1u;
    checksumVal += byteVal;
    if (sprintf(hexByteStr, "%02X", byteVal) != 2)
    {
      /* Error occurred. */
      result = false;
    }
    else
    {
      /* Append it to the line. */
      strcat(line, hexByteStr);
    }
      
    /* Add the address. */
    for (cnt = (addressBits / 8u); cnt > 0; cnt--)
    {
      byteVal = (uint8_t)(address >> ( (cnt - 1u) * 8u));
      checksumVal += byteVal;
      if (sprintf(hexByteStr, "%02X", byteVal) != 2)
      {
        /* Error occurred. */
        result = false;
      }
      else
      {
        /* Append it to the line. */
        strcat(line, hexByteStr);
      }
    }
    
    /* Add the data bytes. */
    for (cnt = 0; cnt < dataLen; cnt++)
    {
      byteVal = 0;
      if (data != NULL)
      {
        byteVal = data[cnt];
      }
      checksumVal += byteVal;
      if (sprintf(hexByteStr, "%02X", byteVal) != 2)
      {
        /* Error occurred. */
        result = false;
      }
      else
      {
        /* Append it to the line. */
        strcat(line, hexByteStr);
      }
    }
    
    /* Calculate and add the checksum. */
    checksumVal = ~checksumVal;
    byteVal = checksumVal;
    if (sprintf(hexByteStr, "%02X", byteVal) != 2)
    {
      /* Error occurred. */
      result = false;
    }
    else
    {
      /* Append it to the line. */
      strcat(line, hexByteStr);
    }
  }
  /* Give the result back to the caller. */
  return result;
} /*** end of SRecParserConstructLine ***/


/************************************************************************************//**
** \brief     Helper function to convert a sequence of 2 characters that represent
**            a hexadecimal value to the actual byte value.
**              Example: SRecParserHexStringToByte("2f")  --> returns 47.
** \param     hexstring String beginning with 2 characters that represent a hexa-
**            decimal value.
** \return    The resulting byte value.
**
****************************************************************************************/
static uint8_t SRecParserHexStringToByte(char const * hexstring)
{
  uint8_t result = 0;
  int32_t c;
  uint8_t counter;

  /* Verify parameters. */
  assert(hexstring != NULL);
  /* Only continue with valid parameters. */
  if (hexstring != NULL) /*lint !e774 */
  {
    /* A hexadecimal character is 2 characters long (i.e 0x4F minus the 0x part). */
    for (counter = 0; counter < 2u; counter++)
    {
      /* Read out the character */
      c = toupper((int32_t)(hexstring[counter]));
      /* Check that the character is 0..9 or A..F */
      if (isxdigit(c))
      {
        /* 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 = (uint8_t)((result << 4u) + c);
      }
    }
  }
  /* Give the result back to the caller. */
  return result;
} /*** end of SRecParserHexStringToByte ***/


/*********************************** end of srecparser.c *******************************/

 
 
