Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

/*
 * File:	StSRecordFile.cpp
 *
 * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
 * See included license file for license details.
 */

#include "stdafx.h"
#include "StSRecordFile.h"
#include "string.h"

StSRecordFile::StSRecordFile(std::istream & inStream)
:	m_stream(inStream)
{
}

//! Frees any data allocated as part of an S-record.
StSRecordFile::~StSRecordFile()
{
	const_iterator it;
	for (it = m_records.begin(); it != m_records.end(); it++)
	{
		SRecord & theRecord = (SRecord &)*it;
		if (theRecord.m_data)
		{
			delete [] theRecord.m_data;
			theRecord.m_data = NULL;
		}
	}
}

//! Just looks for "S[0-9]" as the first two characters of the file.
bool StSRecordFile::isSRecordFile()
{
	int savePosition = m_stream.tellg();
	m_stream.seekg(0, std::ios_base::beg);
	
	char buffer[2];
	m_stream.read(buffer, 2);
	bool isSRecord = (buffer[0] == 'S' && isdigit(buffer[1]));
	
	m_stream.seekg(savePosition, std::ios_base::beg);
	
	return isSRecord;
}

//! Extract records one line at a time and hand them to the parseLine()
//! method. Either CR, LF, or CRLF line endings are supported. The input
//! stream is read until EOF.
//! The parse() method must be called after the object has been constructed
//! before any of the records will become accessible.
//! \exception StSRecordParseException will be thrown if any error occurs while
//!		parsing the input.
void StSRecordFile::parse()
{
	// back to start of stream
	m_stream.seekg(0, std::ios_base::beg);
	
	std::string thisLine;
	
	do {
		char thisChar;
		m_stream.get(thisChar);
		
		if (thisChar == '\r' || thisChar == '\n')
		{
			// skip the LF in a CRLF
			if (thisChar == '\r' && m_stream.peek() == '\n')
				m_stream.ignore();
			
			// parse line if it's not empty
			if (!thisLine.empty())
			{
				parseLine(thisLine);
			
				// reset line
				thisLine.clear();
			}
		}
		else
		{
			thisLine += thisChar;
		}
	} while (!m_stream.eof());
}

bool StSRecordFile::isHexDigit(char c)
{
	return (isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
}

int StSRecordFile::hexDigitToInt(char digit)
{
	if (isdigit(digit))
		return digit - '0';
	else if (digit >= 'a' && digit <= 'f')
		return 10 + digit - 'a';
	else if (digit >= 'A' && digit <= 'F')
		return 10 + digit - 'A';
	
	// unknow char
	return 0;
}

//! \exception StSRecordParseException is thrown if either of the nibble characters
//!		is not a valid hex digit.
int StSRecordFile::readHexByte(std::string & inString, int inIndex)
{
	char nibbleCharHi= inString[inIndex];
	char nibbleCharLo = inString[inIndex + 1];
	
	// must be hex digits
	if (!(isHexDigit(nibbleCharHi) && isHexDigit(nibbleCharLo)))
    {
		throw StSRecordParseException("invalid hex digit");
    }
	
	return (hexDigitToInt(nibbleCharHi) << 4) | hexDigitToInt(nibbleCharLo);
}

//! \brief Parses individual S-records.
//!
//! Takes a single S-record line as input and appends a new SRecord struct
//! to the m_records vector.
//! \exception StSRecordParseException will be thrown if any error occurs while
//!		parsing \a inLine.
void StSRecordFile::parseLine(std::string & inLine)
{
	int checksum = 0;
	SRecord newRecord;
	memset(&newRecord, 0, sizeof(newRecord));
	
	// must start with "S" and be at least a certain length
	if (inLine[0] != SRECORD_START_CHAR && inLine.length() >= SRECORD_MIN_LENGTH)
    {
        throw StSRecordParseException("invalid record length");
    }

	// parse type field
	char typeChar = inLine[1];
	if (!isdigit(typeChar))
    {
		throw StSRecordParseException("invalid S-record type");
    }
	newRecord.m_type = typeChar - '0';
	
	// parse count field
	newRecord.m_count = readHexByte(inLine, 2);
	checksum += newRecord.m_count;
	
	// verify the record length now that we know the count
	if (inLine.length() != 4 + newRecord.m_count * 2)
    {
		throw StSRecordParseException("invalid record length");
    }
	
	// get address length
	int addressLength;	// len in bytes
	bool hasData = false;
	switch (newRecord.m_type)
	{
		case 0:     // contains header information
			addressLength = 2;
			hasData = true;
			break;
		case 1:     // data record with 2-byte address
			addressLength = 2;
			hasData = true;
			break;
		case 2:     // data record with 3-byte address
			addressLength = 3;
			hasData = true;
			break;
		case 3:     // data record with 4-byte address
			addressLength = 4;
			hasData = true;
			break;
		case 5:     // the 2-byte address field contains a count of all prior S1, S2, and S3 records
			addressLength = 2;
			break;
		case 7:     // entry point record with 4-byte address
			addressLength = 4;
			break;
		case 8:     // entry point record with 3-byte address
			addressLength = 3;
			break;
		case 9:     // entry point record with 2-byte address
			addressLength = 2;
			break;
		default:
			// unrecognized type
			//throw StSRecordParseException("unknown S-record type");
            break;
	}
	
	// read address
	int address = 0;
	int i;
	for (i=0; i < addressLength; ++i)
	{
		int addressByte = readHexByte(inLine, SRECORD_ADDRESS_START_CHAR_INDEX + i * 2);
		address = (address << 8) | addressByte;
		checksum += addressByte;
	}
	newRecord.m_address = address;
		
	// read data
	if (hasData)
	{
		int dataStartCharIndex = 4 + addressLength * 2;
		int dataLength = newRecord.m_count - addressLength - 1; // total rem - addr - cksum (in bytes)
		uint8_t * data = new uint8_t[dataLength];
		
		for (i=0; i < dataLength; ++i)
		{
			int dataByte = readHexByte(inLine, dataStartCharIndex + i * 2);
			data[i] = dataByte;
			checksum += dataByte;
		}
		
		newRecord.m_data = data;
		newRecord.m_dataCount = dataLength;
	}
	
	// read and compare checksum byte
	checksum = (~checksum) & 0xff;	// low byte of one's complement of sum of other bytes
	newRecord.m_checksum = readHexByte(inLine, (int)inLine.length() - 2);
	if (checksum != newRecord.m_checksum)
    {
		throw StSRecordParseException("invalid checksum");
    }
	
	// now save the new S-record
	m_records.push_back(newRecord);
}