2014-06-15 20:07:27 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* apps/system/hex2bin.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
|
|
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
|
|
*
|
2014-06-16 15:31:30 +02:00
|
|
|
* References:
|
|
|
|
* - http://en.wikipedia.org/wiki/Intel_HEX
|
|
|
|
* - Hexadecimal Object File Format Specification, Revision A January 6,
|
|
|
|
* 1988, Intel
|
|
|
|
*
|
2014-06-15 20:07:27 +02:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
|
|
* used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Included Files
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <nuttx/streams.h>
|
|
|
|
#include <apps/hex2bin.h>
|
|
|
|
|
|
|
|
#ifdef CONFIG_SYSTEM_HEX2BIN
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Pre-processor Definitions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/* ASCII record sizes */
|
|
|
|
|
|
|
|
#define BYTECOUNT_ASCSIZE 2
|
|
|
|
#define ADDRESS_ASCSIZE 4
|
|
|
|
#define RECTYPE_ASCSIZE 2
|
|
|
|
|
|
|
|
#define BYTECOUNT_LINENDX (0)
|
|
|
|
#define ADDRESS_LINENDX (BYTECOUNT_LINENDX + BYTECOUNT_ASCSIZE)
|
|
|
|
#define RECTYPE_LINENDX (ADDRESS_LINENDX + ADDRESS_ASCSIZE)
|
|
|
|
#define DATA_LINENDX (RECTYPE_LINENDX + RECTYPE_ASCSIZE)
|
|
|
|
#define HEADER_ASCSIZE DATA_LINENDX
|
|
|
|
|
|
|
|
#define CHECKSUM_ASCSIZE 2
|
|
|
|
#define TRAILER_SIZE (CHECKSUM_ASCSIZE)
|
|
|
|
|
|
|
|
#define MAXDATA_BINSIZE 255
|
|
|
|
#define RECORD_ASCSIZE(n) (HEADER_ASCSIZE + TRAILER_SIZE + 2*(n))
|
|
|
|
#define MAXRECORD_ASCSIZE RECORD_ASCSIZE(MAXDATA_BINSIZE)
|
|
|
|
#define MINRECORD_ASCSIZE RECORD_ASCSIZE(0)
|
|
|
|
#define LINE_ALLOC MAXRECORD_ASCSIZE
|
|
|
|
|
|
|
|
/* Binary record sizes */
|
|
|
|
|
|
|
|
#define BYTECOUNT_BINSIZE 1
|
|
|
|
#define ADDRESS_BINSIZE 2
|
|
|
|
#define RECTYPE_BINSIZE 1
|
|
|
|
|
|
|
|
#define BYTECOUNT_BINNDX (0)
|
|
|
|
#define ADDRESS_BINNDX (BYTECOUNT_BINNDX + BYTECOUNT_BINSIZE)
|
|
|
|
#define RECTYPE_BINNDX (ADDRESS_BINNDX + ADDRESS_BINSIZE)
|
|
|
|
#define DATA_BINNDX (RECTYPE_BINNDX + RECTYPE_BINSIZE)
|
|
|
|
#define HEADER_BINSIZE DATA_BINNDX
|
|
|
|
|
|
|
|
#define CHECKSUM_BINSIZE 1
|
|
|
|
#define TRAILER_BINSIZE CHECKSUM_BINSIZE
|
|
|
|
|
|
|
|
#define RECORD_BINSIZE(n) (HEADER_BINSIZE + TRAILER_BINSIZE + (n))
|
2014-06-16 15:31:30 +02:00
|
|
|
#define MAXRECORD_BINSIZE RECORD_BINSIZE(MAXDATA_BINSIZE)
|
|
|
|
#define MINRECORD_BKINSIZE RECORD_BINSIZE(0)
|
2014-06-15 20:07:27 +02:00
|
|
|
#define BIN_ALLOC MAXRECORD_BINSIZE
|
|
|
|
|
|
|
|
/* Record start code */
|
|
|
|
|
|
|
|
#define RECORD_STARTCODE ':'
|
|
|
|
|
|
|
|
/* Record Types */
|
|
|
|
|
|
|
|
#define RECORD_DATA 0 /* Data */
|
|
|
|
#define RECORD_EOF 1 /* End of file */
|
|
|
|
#define RECORD_EXT_SEGADDR 2 /* Extended segment address record */
|
|
|
|
#define RECORD_START_SEGADDR 3 /* Start segment address record */
|
|
|
|
#define RECORD_EXT_LINADDR 4 /* Extended linear address record */
|
|
|
|
#define RECORD_START_LINADDR 5 /* Start linear address record */
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Types
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: nibble2bin
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int nibble2bin(uint8_t ascii)
|
|
|
|
{
|
|
|
|
if (ascii >= '0' && ascii <= '9')
|
|
|
|
{
|
|
|
|
return (ascii - 0x30);
|
|
|
|
}
|
|
|
|
else if (ascii >= 'a' && ascii <= 'f')
|
|
|
|
{
|
|
|
|
return (ascii - 'a' + 10);
|
|
|
|
}
|
|
|
|
else if (ascii >= 'A' && ascii <= 'F')
|
|
|
|
{
|
|
|
|
return (ascii - 'A' + 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: byte2bin
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int byte2bin(FAR const uint8_t *ascii)
|
|
|
|
{
|
|
|
|
int nibble;
|
|
|
|
int byte;
|
|
|
|
|
|
|
|
/* Get the MS nibble (big endian order) */
|
|
|
|
|
|
|
|
nibble = nibble2bin(*ascii++);
|
|
|
|
if (nibble < 0)
|
|
|
|
{
|
|
|
|
return nibble;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte = (nibble << 4);
|
|
|
|
|
|
|
|
/* Get the MS nibble */
|
|
|
|
|
|
|
|
nibble = nibble2bin(*ascii);
|
|
|
|
if (nibble < 0)
|
|
|
|
{
|
|
|
|
return nibble;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte |= nibble;
|
|
|
|
return byte;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: word2bin
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#if 0 /* Not used */
|
|
|
|
static int word2bin(FAR const char *ascii)
|
|
|
|
{
|
|
|
|
int byte;
|
|
|
|
int word;
|
|
|
|
|
|
|
|
/* Get the MS byte (big endian order) */
|
|
|
|
|
|
|
|
byte = word2bin(ascii);
|
|
|
|
if (byte < 0)
|
|
|
|
{
|
|
|
|
return byte;
|
|
|
|
}
|
|
|
|
|
|
|
|
word = (byte << 8);
|
|
|
|
|
|
|
|
/* Get the MS byte */
|
|
|
|
|
|
|
|
byte = word2bin(ascii + 2);
|
|
|
|
if (byte < 0)
|
|
|
|
{
|
|
|
|
return byte;
|
|
|
|
}
|
|
|
|
|
|
|
|
word |= byte;
|
|
|
|
return word;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: data2bin
|
|
|
|
****************************************************************************/
|
|
|
|
|
2014-06-16 15:31:30 +02:00
|
|
|
int data2bin(FAR uint8_t* dest, FAR const uint8_t *src, int nsrcbytes)
|
2014-06-15 20:07:27 +02:00
|
|
|
{
|
|
|
|
int byte;
|
|
|
|
|
|
|
|
/* An even number of source bytes is expected */
|
|
|
|
|
2014-06-16 15:31:30 +02:00
|
|
|
if ((nsrcbytes & 1) != 0)
|
2014-06-15 20:07:27 +02:00
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert src bytes in groups of 2, writing one byte to the output on each
|
|
|
|
* pass through the loop. */
|
|
|
|
|
2014-06-16 15:31:30 +02:00
|
|
|
while (nsrcbytes > 0)
|
2014-06-15 20:07:27 +02:00
|
|
|
{
|
|
|
|
/* Get the MS nibble (big endian order) */
|
|
|
|
|
|
|
|
byte = byte2bin(src);
|
|
|
|
if (byte < 0)
|
|
|
|
{
|
|
|
|
return byte;
|
|
|
|
}
|
|
|
|
|
|
|
|
src += 2;
|
|
|
|
|
|
|
|
/* And write the byte to the destination */
|
|
|
|
|
|
|
|
*dest++ = byte;
|
2014-06-16 15:31:30 +02:00
|
|
|
nsrcbytes -= 2;
|
2014-06-15 20:07:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: readstream
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static int readstream(FAR struct lib_instream_s *instream,
|
|
|
|
FAR uint8_t *line, unsigned int lineno)
|
|
|
|
{
|
|
|
|
int nbytes = 0;
|
2014-06-15 22:06:46 +02:00
|
|
|
int ch;
|
2014-06-15 20:07:27 +02:00
|
|
|
|
|
|
|
/* Skip until the beginning of line start code is encountered */
|
|
|
|
|
2014-06-15 22:06:46 +02:00
|
|
|
ch = instream->get(instream);
|
2014-06-15 20:07:27 +02:00
|
|
|
while (ch != RECORD_STARTCODE && ch != EOF)
|
|
|
|
{
|
|
|
|
ch = instream->get(instream);
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:06:46 +02:00
|
|
|
/* Skip over the startcode */
|
2014-06-15 21:28:03 +02:00
|
|
|
|
|
|
|
if (ch != EOF)
|
|
|
|
{
|
|
|
|
ch = instream->get(instream);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Then read, verify, and buffer until the end of line is encountered */
|
2014-06-15 20:07:27 +02:00
|
|
|
|
|
|
|
while (ch != EOF && nbytes < (MAXRECORD_ASCSIZE-1))
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_EOL_IS_LF)
|
|
|
|
if (ch == '\n')
|
|
|
|
{
|
|
|
|
*line = '\0';
|
|
|
|
return nbytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
#elif defined(CONFIG_EOL_IS_BOTH_CRLF)
|
|
|
|
if (ch == '\r')
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (ch == '\n')
|
|
|
|
{
|
|
|
|
*line = '\0';
|
|
|
|
return nbytes;
|
|
|
|
}
|
|
|
|
#elif defined(CONFIG_EOL_IS_CR)
|
|
|
|
if (ch == '\r')
|
|
|
|
{
|
|
|
|
*line = '\0';
|
|
|
|
return nbytes;
|
|
|
|
}
|
2015-09-01 16:18:40 +02:00
|
|
|
#elif defined(CONFIG_EOL_IS_EITHER_CRLF)
|
2014-06-15 20:07:27 +02:00
|
|
|
if (ch == '\n' || ch == '\r')
|
|
|
|
{
|
|
|
|
*line = '\0';
|
|
|
|
return nbytes;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Only hex data goes into the line buffer */
|
|
|
|
|
|
|
|
else if (isxdigit(ch))
|
|
|
|
{
|
|
|
|
*line++ = ch;
|
|
|
|
nbytes++;
|
|
|
|
}
|
2014-06-16 15:31:30 +02:00
|
|
|
else if (!isspace(ch)) /* Not expected */
|
2014-06-15 20:07:27 +02:00
|
|
|
{
|
|
|
|
hex2bin_debug("Line %u ERROR: Unexpected character %c[%02x] in stream\n",
|
|
|
|
lineno, isprint(ch) ? ch : '.', ch);
|
|
|
|
break;
|
|
|
|
}
|
2014-06-15 22:06:46 +02:00
|
|
|
|
|
|
|
/* Read the next character from the input stream */
|
|
|
|
|
|
|
|
ch = instream->get(instream);
|
2014-06-15 20:07:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Some error occurred: Unexpected EOF, line too long, or bad character in
|
|
|
|
* stream
|
|
|
|
*/
|
|
|
|
|
|
|
|
hex2bin_debug("Line %u ERROR: Failed to read line. %d characters read\n",
|
|
|
|
lineno, nbytes);
|
|
|
|
return EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: hex2bin_swap16 and hex2bin_swap32
|
|
|
|
****************************************************************************/
|
|
|
|
|
2014-06-16 16:37:44 +02:00
|
|
|
static inline void hex2bin_swap16(FAR uint8_t *data, int bytecount)
|
2014-06-15 20:07:27 +02:00
|
|
|
{
|
|
|
|
for (; bytecount > 0; bytecount -= 2)
|
|
|
|
{
|
2014-06-16 16:37:44 +02:00
|
|
|
uint8_t b0 = data[0];
|
|
|
|
uint8_t b1 = data[1];
|
2014-06-15 20:07:27 +02:00
|
|
|
|
2014-06-16 16:37:44 +02:00
|
|
|
*data++ = b1;
|
|
|
|
*data++ = b0;
|
2014-06-15 20:07:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-16 16:37:44 +02:00
|
|
|
static inline void hex2bin_swap32(FAR uint8_t *data, int bytecount)
|
2014-06-15 20:07:27 +02:00
|
|
|
{
|
|
|
|
for (; bytecount > 0; bytecount -= 4)
|
|
|
|
{
|
2014-06-16 16:37:44 +02:00
|
|
|
uint8_t b0 = data[0];
|
|
|
|
uint8_t b1 = data[1];
|
|
|
|
uint8_t b2 = data[2];
|
|
|
|
uint8_t b3 = data[3];
|
|
|
|
|
|
|
|
*data++ = b3;
|
|
|
|
*data++ = b2;
|
|
|
|
*data++ = b1;
|
|
|
|
*data++ = b0;
|
2014-06-15 20:07:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: writedata
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static inline void writedata(FAR struct lib_sostream_s *outstream,
|
2014-06-16 16:37:44 +02:00
|
|
|
FAR uint8_t *data, int bytecount)
|
2014-06-15 20:07:27 +02:00
|
|
|
{
|
|
|
|
for (; bytecount > 0; bytecount--)
|
|
|
|
{
|
2014-06-16 16:37:44 +02:00
|
|
|
outstream->put(outstream, *data++);
|
2014-06-15 20:07:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Public Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: hex2bin
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Read the Intel HEX ASCII data provided on the serial IN stream and write
|
|
|
|
* the binary to the seek-able serial OUT stream.
|
|
|
|
*
|
|
|
|
* These streams may be files or, in another usage example, the IN stream
|
|
|
|
* could be a serial port and the OUT stream could be a memory stream. This
|
|
|
|
* would decode and write the serial input to memory.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* instream - The incoming stream from which Intel HEX data will be
|
|
|
|
* received.
|
|
|
|
* outstream - The outgoing stream in which binary data will be written.
|
|
|
|
* baseaddr - The base address of the outgoing stream. Seeking in the
|
|
|
|
* output stream will be relative to this address.
|
|
|
|
* endpaddr - The end address (plus 1) of the outgoing stream. This
|
|
|
|
* value is used only for range checking. endpaddr must
|
|
|
|
* be larger than baseaddr. A zero value for endpaddr
|
|
|
|
* disables range checking.
|
|
|
|
* swap - Controls byte ordering. See enum hex2bin_swap_e for
|
|
|
|
* description of the values.
|
|
|
|
*
|
|
|
|
* Returned Value
|
|
|
|
* Zero (OK) is returned on success; a negated errno value is returned on
|
|
|
|
* failure.
|
2014-06-16 23:43:23 +02:00
|
|
|
*
|
2014-06-15 20:07:27 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
int hex2bin(FAR struct lib_instream_s *instream,
|
|
|
|
FAR struct lib_sostream_s *outstream, uint32_t baseaddr,
|
|
|
|
uint32_t endpaddr, enum hex2bin_swap_e swap)
|
|
|
|
{
|
|
|
|
FAR uint8_t *alloc;
|
|
|
|
FAR uint8_t *line;
|
|
|
|
FAR uint8_t *bin;
|
|
|
|
int nbytes;
|
|
|
|
int bytecount;
|
|
|
|
uint32_t address;
|
2014-06-16 16:37:44 +02:00
|
|
|
uint32_t endaddr;
|
2014-06-15 20:07:27 +02:00
|
|
|
uint32_t expected;
|
|
|
|
uint16_t extension;
|
|
|
|
uint16_t address16;
|
|
|
|
uint8_t checksum;
|
|
|
|
unsigned int lineno;
|
|
|
|
int i;
|
|
|
|
int ret = OK;
|
|
|
|
|
|
|
|
/* Allocate buffer memory */
|
|
|
|
|
|
|
|
alloc = (FAR uint8_t *)malloc(LINE_ALLOC + BIN_ALLOC);
|
|
|
|
if (alloc == NULL)
|
|
|
|
{
|
|
|
|
hex2bin_debug("ERROR: Failed to allocate memory\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
line = alloc;
|
|
|
|
bin = &alloc[LINE_ALLOC];
|
|
|
|
|
|
|
|
extension = 0;
|
|
|
|
expected = 0;
|
|
|
|
lineno = 0;
|
|
|
|
|
|
|
|
while ((nbytes = readstream(instream, line, lineno)) != EOF)
|
|
|
|
{
|
|
|
|
/* Increment the line number */
|
|
|
|
|
|
|
|
lineno++;
|
|
|
|
|
|
|
|
/* Did we read enough data to do anything? */
|
|
|
|
|
|
|
|
if (nbytes < MINRECORD_ASCSIZE)
|
|
|
|
{
|
|
|
|
hex2bin_debug("Line %u ERROR: Record too short: %d\n",
|
|
|
|
lineno, nbytes);
|
|
|
|
goto errout_with_einval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We should always read an even number of bytes */
|
|
|
|
|
|
|
|
if ((nbytes & 1) != 0)
|
|
|
|
{
|
|
|
|
hex2bin_debug("Line %u ERROR: Record length is odd: %d\n",
|
|
|
|
lineno, nbytes);
|
|
|
|
goto errout_with_einval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the data byte count */
|
|
|
|
|
|
|
|
bytecount = byte2bin(&line[BYTECOUNT_LINENDX]);
|
|
|
|
if (bytecount < 0)
|
|
|
|
{
|
|
|
|
hex2bin_debug("Line %u ERROR: Failed to read bytecount: %d\n",
|
|
|
|
lineno, bytecount);
|
|
|
|
ret = bytecount;
|
|
|
|
goto errout_with_buffers;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Verify that the bytecount matches the length of the record */
|
|
|
|
|
|
|
|
if (RECORD_ASCSIZE(bytecount) != nbytes)
|
|
|
|
{
|
|
|
|
hex2bin_debug("Line %u ERROR: Expected %d bytes, read %d\n",
|
|
|
|
lineno, RECORD_ASCSIZE(bytecount), nbytes);
|
|
|
|
goto errout_with_einval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert the entire line to binary. We need to do this for
|
|
|
|
* checksum calculation which includes the entire line (minus
|
|
|
|
* the start code and the checksum at the end of the line itself)
|
|
|
|
*/
|
|
|
|
|
2014-06-16 15:31:30 +02:00
|
|
|
ret = data2bin(bin, line, nbytes);
|
2014-06-15 20:07:27 +02:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
hex2bin_debug("Line %u ERROR: Failed to convert line to binary: %d\n",
|
|
|
|
lineno, ret);
|
|
|
|
goto errout_with_buffers;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate and verify the checksum over all of the data */
|
|
|
|
|
2014-06-16 15:31:30 +02:00
|
|
|
nbytes >>= 1; /* Number of bytes in bin[] */
|
2014-06-15 20:07:27 +02:00
|
|
|
checksum = 0;
|
2014-06-16 15:31:30 +02:00
|
|
|
|
|
|
|
for (i = 0; i < nbytes; i++)
|
2014-06-15 20:07:27 +02:00
|
|
|
{
|
|
|
|
checksum += bin[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (checksum != 0)
|
|
|
|
{
|
|
|
|
hex2bin_debug("Line %u ERROR: Bad checksum %02x\n",
|
|
|
|
lineno, checksum);
|
|
|
|
goto errout_with_einval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the 16-bit (unextended) address from the record */
|
|
|
|
|
|
|
|
address16 = (uint16_t)bin[ADDRESS_BINNDX] << 8 |
|
|
|
|
(uint16_t)bin[ADDRESS_BINNDX+1];
|
|
|
|
|
|
|
|
/* Handle the record by its record type */
|
|
|
|
|
|
|
|
switch (bin[RECTYPE_BINNDX])
|
|
|
|
{
|
|
|
|
case RECORD_DATA: /* Data */
|
|
|
|
{
|
|
|
|
/* Swap data in place in the binary buffer as required */
|
|
|
|
|
|
|
|
switch (swap)
|
|
|
|
{
|
|
|
|
case HEX2BIN_NOSWAP: /* No swap, stream is the correct byte order */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HEX2BIN_SWAP16: /* Swap bytes in 16-bit values */
|
|
|
|
{
|
|
|
|
if ((bytecount & 1) != 0)
|
|
|
|
{
|
|
|
|
hex2bin_debug("Line %d ERROR: Byte count %d is not a multiple of 2\n",
|
|
|
|
lineno, bytecount);
|
|
|
|
goto errout_with_einval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do the byte swap */
|
|
|
|
|
2014-06-16 16:37:44 +02:00
|
|
|
hex2bin_swap16(&bin[DATA_BINNDX], bytecount);
|
2014-06-15 20:07:27 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HEX2BIN_SWAP32: /* Swap bytes in 32-bit values */
|
|
|
|
{
|
|
|
|
if ((bytecount & 3) != 0)
|
|
|
|
{
|
|
|
|
hex2bin_debug("Line %d ERROR: Byte count %d is not a multiple of 4\n",
|
|
|
|
lineno, bytecount);
|
|
|
|
goto errout_with_einval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do the byte swap */
|
|
|
|
|
2014-06-16 16:37:44 +02:00
|
|
|
hex2bin_swap32(&bin[DATA_BINNDX], bytecount);
|
2014-06-15 20:07:27 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
hex2bin_debug("ERROR: Invalid swap argument: %d\n", swap);
|
|
|
|
goto errout_with_einval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get and verify the full 32-bit address */
|
|
|
|
|
|
|
|
address = ((uint32_t)extension << 16) | (uint32_t)address16;
|
2014-06-16 16:37:44 +02:00
|
|
|
endaddr = address + bytecount;
|
|
|
|
|
|
|
|
if (address < baseaddr || (endpaddr != 0 && endaddr >= endpaddr))
|
2014-06-15 20:07:27 +02:00
|
|
|
{
|
2014-06-16 23:43:23 +02:00
|
|
|
hex2bin_debug("Line %d ERROR: Extended address %08lx is out of range\n",
|
2014-06-15 20:07:27 +02:00
|
|
|
lineno, (unsigned long)address);
|
|
|
|
goto errout_with_einval;
|
|
|
|
}
|
|
|
|
|
2014-06-16 18:58:33 +02:00
|
|
|
/* Seek to the correct position in the OUT stream if we have
|
|
|
|
* made an unexpected jump in the data address.
|
|
|
|
*/
|
2014-06-15 20:07:27 +02:00
|
|
|
|
|
|
|
if (address != expected)
|
|
|
|
{
|
|
|
|
off_t pos = outstream->seek(outstream, address - baseaddr, SEEK_SET);
|
|
|
|
if (pos == (off_t)-1)
|
|
|
|
{
|
|
|
|
hex2bin_debug("Line %u ERROR: Seek to address %08lu failed\n",
|
|
|
|
lineno, (unsigned long)address);
|
|
|
|
ret = -ESPIPE;
|
|
|
|
goto errout_with_buffers;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Transfer data to the OUT stream */
|
|
|
|
|
2014-06-16 16:37:44 +02:00
|
|
|
writedata(outstream, &bin[DATA_BINNDX], bytecount);
|
2014-06-16 18:58:33 +02:00
|
|
|
|
|
|
|
/* This is the next data address that we expect to see */
|
|
|
|
|
|
|
|
expected = address + bytecount;
|
2014-06-15 20:07:27 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RECORD_EOF: /* End of file */
|
|
|
|
/* End Of File record. Must occur exactly once per file in the
|
|
|
|
* last line of the file. The byte count is 00 and the data field
|
|
|
|
* is empty. Usually the address field is also 0000.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (bytecount == 0)
|
|
|
|
{
|
|
|
|
ret = OK;
|
|
|
|
goto exit_with_buffers;
|
|
|
|
}
|
|
|
|
|
|
|
|
hex2bin_debug("Line %u ERROR: Nonzero bytecount %d in EOF\n",
|
|
|
|
lineno, bytecount);
|
|
|
|
goto errout_with_einval;
|
|
|
|
|
|
|
|
case RECORD_EXT_SEGADDR: /* Extended segment address record */
|
|
|
|
/* The address specified by the data field is multiplied by 16
|
|
|
|
* (shifted 4 bits left) and added to the subsequent data record
|
|
|
|
* addresses. This allows addressing of up to a megabyte of
|
|
|
|
* address space. The address field of this record has to be
|
|
|
|
* 0000, the byte count is 02 (the segment is 16-bit). The
|
|
|
|
* least significant hex digit of the segment address is always
|
|
|
|
* 0.
|
|
|
|
*/
|
|
|
|
|
2014-06-16 16:37:44 +02:00
|
|
|
if (bytecount != 2 || address16 != 0 || bin[DATA_BINNDX+1] != 0)
|
2014-06-15 20:07:27 +02:00
|
|
|
{
|
|
|
|
hex2bin_debug("Line %u ERROR: Invalid segment address\n",
|
|
|
|
lineno);
|
|
|
|
hex2bin_debug(" bytecount=%d address=%04x segment=%02x%02x\n",
|
2014-06-16 16:37:44 +02:00
|
|
|
bytecount, address16, bin[DATA_BINNDX],
|
|
|
|
bin[DATA_BINNDX+1]);
|
2014-06-15 20:07:27 +02:00
|
|
|
goto errout_with_einval;
|
|
|
|
}
|
|
|
|
|
2014-06-16 16:37:44 +02:00
|
|
|
extension = (uint16_t)bin[DATA_BINNDX];
|
2014-06-15 20:07:27 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RECORD_START_SEGADDR: /* Start segment address record */
|
|
|
|
/* For 80x86 processors, it specifies the initial content of
|
|
|
|
* the CS:IP registers. The address field is 0000, the byte
|
|
|
|
* count is 04, the first two bytes are the CS value, the
|
|
|
|
* latter two are the IP value.
|
|
|
|
*/
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RECORD_EXT_LINADDR: /* Extended linear address record */
|
|
|
|
/* The address field is 0000, the byte count is 02. The two
|
|
|
|
* data bytes (two hex digit pairs in big endian order)
|
|
|
|
* represent the upper 16 bits of the 32 bit address for
|
|
|
|
* all subsequent 00 type records until the next 04 type
|
|
|
|
* record comes. If there is not a 04 type record, the
|
|
|
|
* upper 16 bits default to 0000. To get the absolute
|
|
|
|
* address for subsequent 00 type records, the address
|
|
|
|
* specified by the data field of the most recent 04 record
|
|
|
|
* is added to the 00 record addresses.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (bytecount != 2 || address16 != 0)
|
|
|
|
{
|
|
|
|
hex2bin_debug("Line %u ERROR: Invalid linear address\n",
|
|
|
|
lineno);
|
|
|
|
hex2bin_debug(" bytecount=%d address=%04x\n",
|
|
|
|
bytecount, address16);
|
|
|
|
goto errout_with_einval;
|
|
|
|
}
|
|
|
|
|
2014-06-16 16:37:44 +02:00
|
|
|
extension = (uint16_t)bin[DATA_BINNDX] << 8 |
|
|
|
|
(uint16_t)bin[DATA_BINNDX+1];
|
2014-06-15 20:07:27 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RECORD_START_LINADDR: /* Start linear address record */
|
|
|
|
/* The address field is 0000, the byte count is 04. The 4
|
|
|
|
* data bytes represent the 32-bit value loaded into the EIP
|
|
|
|
* register of the 80386 and higher CPU.
|
|
|
|
*/
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hex2bin_debug("ERROR: No EOF record found\n");
|
|
|
|
|
|
|
|
errout_with_einval:
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
|
|
|
errout_with_buffers:
|
|
|
|
exit_with_buffers:
|
|
|
|
free(alloc);
|
2014-06-16 15:31:30 +02:00
|
|
|
return ret;
|
2014-06-15 20:07:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_SYSTEM_HEX2BIN */
|