commit 6648373d8ec44ece24cb852e8d66c593de617182 Author: Keenan Tims Date: Sun Nov 5 23:32:03 2023 -0800 Initial commit from NXP 2.6.7 sources NXP is constantly breaking links, but it was found at: https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/mcu-bootloader-for-nxp-microcontrollers:MCUBOOT diff --git a/SW-Content-Register.txt b/SW-Content-Register.txt new file mode 100644 index 0000000..b597f49 --- /dev/null +++ b/SW-Content-Register.txt @@ -0,0 +1,55 @@ +Release Name: blhost +Release Version: 2.6.7 +Package License: LA_OPT_NXP_Software_License.htm - Production Use, Section 2.3 applies + +Host tools Source Description: Source code for blhost + Author: Freescale + License: Open Source - BSD-3-Clause + Format: source code + Location: src + +Host tools - Serial support Description: Windows Serial peripheral support + Author: Bus Pirate Project + License: Open Source - CC0-1.0 (Creative Commons Zero) + URL: http://code.google.com/p/the-bus-pirate/ + Format: source code + Location: + src/blfwk/serial.h, + src/blfwk/src/serial.c + +Host tools - USB HID Description: Windows USB HID support +support Author: HIDAPI + License: Open Source - BSD-3-Clause + URL: http://github.com/signal11/hidapi + Format: source code + Location: + src/blfwk/hidapi.h, + src/blfwk/src/hid-*.c + +Host tools - JSON support Description: Windows JSON support + Author: JSONCPP + License: Open Source - MIT + Format: source code + Location: + src/blfwk/json.h, + src/jsoncpp.cpp + +Host tools - options Description: Command line parsing utility +support Author: bradapp@enteract.com + License: Open Source - MIT + URL: http://www.bradapp.com + Format: source code + Location: + src/blfwk/options.h, + src/options.cpp + +Host tools - blfwk.lib Description: C++ interface to the Vincent Rijmen's + Rijndael block cipher + Author: Szymon Stefanek (stefanek@tin.it) + License: Public Domain + URL: + http://www.pragmaware.net/software/rijndael/index.php + Format: source code + Location: + src/blfwk/rijndael.h, + src/blfwk/src/rijndael.cpp diff --git a/bin/linux/amd64/blhost b/bin/linux/amd64/blhost new file mode 100755 index 0000000..051cf64 Binary files /dev/null and b/bin/linux/amd64/blhost differ diff --git a/bin/mac/blhost b/bin/mac/blhost new file mode 100644 index 0000000..becd22d Binary files /dev/null and b/bin/mac/blhost differ diff --git a/bin/win/blhost.exe b/bin/win/blhost.exe new file mode 100644 index 0000000..746c4d0 Binary files /dev/null and b/bin/win/blhost.exe differ diff --git a/blhost Release Notes.pdf b/blhost Release Notes.pdf new file mode 100644 index 0000000..0067066 Binary files /dev/null and b/blhost Release Notes.pdf differ diff --git a/docs/blhost User's Guide.pdf b/docs/blhost User's Guide.pdf new file mode 100644 index 0000000..0f8a473 Binary files /dev/null and b/docs/blhost User's Guide.pdf differ diff --git a/mk/common.mk b/mk/common.mk new file mode 100644 index 0000000..ef0864b --- /dev/null +++ b/mk/common.mk @@ -0,0 +1,126 @@ +#------------------------------------------------------------------------------- +# Copyright (c) 2012 Freescale Semiconductor, Inc. +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +#------------------------------------------------------------------------------- + +#------------------------------------------------------------------------------- +# Utility +#------------------------------------------------------------------------------- + +# Kludge to create a variable equal to a single space. +empty := +space := $(empty) $(empty) + +#------------------------------------------------------------------------------- +# OS +#------------------------------------------------------------------------------- + +# Get the OS name. Known values are "Linux", "CYGWIN_NT-5.1", and "Darwin". +os_name := $(shell uname -s) + +# Set to 1 if running on cygwin. +is_cygwin := $(and $(findstring CYGWIN,$(os_name)),1) + +# Set to 1 if running on cygwin. +is_mingw := $(and $(findstring MINGW,$(os_name)),1) + +# Set to 1 if running on redhat. +is_redhat := $(shell if [ -f /etc/redhat-release ]; then echo 1 ; fi) + +# Disable parallel builds for cygwin since they hang. +ifeq "$(is_cygwin)" "1" +.NOTPARALLEL: +endif + +ifeq "$(is_mingw)" "1" +.NOTPARALLEL: +endif + +#------------------------------------------------------------------------------- +# Logging options +#------------------------------------------------------------------------------- + +# Enable color output by default. +BUILD_SDK_COLOR ?= 1 + +# MAKE +MAKE := make +ifeq "$(is_cygwin)" "1" +MAKE := mingw32-make +endif + +ifeq "$(is_mingw)" "1" +MAKE := mingw32-make +endif + +# Normally, commands in recipes are prefixed with '@' so the command itself +# is not echoed by make. But if VERBOSE is defined (set to anything non-empty), +# then the '@' is removed from recipes. The 'at' variable is used to control +# this. Similarly, 'silent_make' is used to pass the -s option to child make +# invocations when not in VERBOSE mode. +ifeq "$(VERBOSE)" "1" +at := +silent_make := +else +at := @ +silent_make := -s +endif + +# These colors must be printed with the printf command. echo won't handle the +# escape sequences. +color_default = \033[00m +color_bold = \033[01m +color_red = \033[31m +color_green = \033[32m +color_yellow = \033[33m +color_blue = \033[34m +color_magenta = \033[35m +color_cyan = \033[36m +color_orange = \033[38;5;172m +color_light_blue = \033[38;5;039m +color_gray = \033[38;5;008m +color_purple = \033[38;5;097m + +ifeq "$(BUILD_SDK_COLOR)" "1" +color_build := $(color_light_blue) +color_c := $(color_green) +color_cxx := $(color_green) +color_cpp := $(color_orange) +color_asm := $(color_magenta) +color_ar := $(color_yellow) +color_link := $(color_purple) +endif + +# Used in printmessage if the color args are not present. +color_ := + +# Use in recipes to print color messages if printing to a terminal. If +# BUILD_SDK_COLOR is not set to 1, this reverts to a simple uncolorized printf. +# A newline is added to the end of the printed message. +# +# Arguments: +# 1 - name of the color variable (see above), minus the "color_" prefix +# 2 - first colorized part of the message +# 3 - first uncolorized part of the message +# 4 - color name for second colorized message +# 5 - second colorized message +# 6 - second uncolorized part of the message +# 7 - uncolorized prefix on the whole line; this is last because it is expected to be used rarely +# +# All arguments are optional. +# +# Use like: +# $(call printmessage,cyan,Building, remainder of the message...) +ifeq "$(BUILD_SDK_COLOR)" "1" +define printmessage +if [ -t 1 ]; then printf "$(7)$(color_$(1))$(2)$(color_default)$(3)$(color_$(4))$(5)$(color_default)$(6)\n" ; \ +else printf "$(7)$(2)$(3)$(5)$(6)\n" ; fi +endef +else +define printmessage +printf "$(7)$(2)$(3)$(5)$(6)\n" ; fi +endef +endif + diff --git a/src/blfwk/AESCounter.h b/src/blfwk/AESCounter.h new file mode 100644 index 0000000..1ef9087 --- /dev/null +++ b/src/blfwk/AESCounter.h @@ -0,0 +1,149 @@ +/* + * File: AESCounter.h + * + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_AESCounter_h_) +#define _AESCounter_h_ + +#include +#include +#include "Random.h" + +//! An AES-128 counter is 128 bits, or 16 bytes. +typedef uint8_t aes128_counter_t[16]; + +/*! + * \brief Base class for AESCounter. + * + * This class implements some bigger, non-template methods used in the + * AESCounter templated subclass. + */ +class AESCounterBase +{ +public: + //! \brief Reads hex encoded data from \a stream. + void _readFromStream(std::istream &stream, unsigned bytes, uint8_t *buffer); + + //! \brief Writes hex encoded data to \a stream. + void _writeToStream(std::ostream &stream, unsigned bytes, const uint8_t *buffer) const; +}; + +/*! + * \brief Generic AES Counter class. + * + * The template parameter \a S is the number of bits in the counter. + * + * The underlying counter type can be accessed like this: AESCounter<128>::counter_t + * + * When a counter instance is destroyed, it erases the counter data by setting it + * to all zeroes. + * + * \todo Add a way to allow only counter sizes of 128, 192, and 256 bits. + * \todo Find a cross platform way to prevent the counter data from being written + * to the VM swapfile. + * + * AESCounter<128> counter = AESCounter<128>::readFromStream(s); + */ +template +class AESCounter : public AESCounterBase +{ +public: + //! Type for this size of AES counter. + typedef uint8_t counter_t[S / 8]; + +public: + //! \brief Default constructor. + //! + //! Initializes the counter to 0. + AESCounter() { memset(m_counter, 0, sizeof(m_counter)); } + //! \brief Constructor taking a counter value reference. + AESCounter(const counter_t &counter) { memcpy(m_counter, &counter, sizeof(m_counter)); } + // \brief Constructor taking a counter value pointer. + AESCounter(const counter_t *counter) { memcpy(m_counter, counter, sizeof(m_counter)); } + //! \brief Constructor, reads counter from stream in hex format. + AESCounter(std::istream &stream) { readFromStream(stream); } + //! \brief Copy constructor. + AESCounter(const AESCounter &other) { memcpy(m_counter, other.m_counter, sizeof(m_counter)); } + //! \brief Destructor. + //! + //! Sets the counter value to zero. + ~AESCounter() { memset(m_counter, 0, sizeof(m_counter)); } + //! \brief Set to the counter to a randomly generated value. + void randomize() + { + RandomNumberGenerator rng; + rng.generateBlock(m_counter, sizeof(m_counter)); + } + + //! \brief Reads the counter from a hex encoded data stream. + void readFromStream(std::istream &stream) + { + _readFromStream(stream, S / 8, reinterpret_cast(&m_counter)); + } + + //! \brief Writes the counter to a data stream in hex encoded format. + void writeToStream(std::ostream &stream) const + { + _writeToStream(stream, S / 8, reinterpret_cast(&m_counter)); + } + + //! \brief Increments the counter by val + void incrementCounter(unsigned val) + { + for (unsigned j = 0; j < val; j++) + { + for (int i = sizeof(AESCounter::counter_t) - 1, carry = 1; (i >= 0) && carry; i--) + { + carry = !++m_counter[i]; + } + } + } + + //! \name Counter accessors + //@{ + inline const counter_t &getCounter() const { return m_counter; } + inline void getCounter(counter_t *counter) const { memcpy(counter, m_counter, sizeof(m_counter)); } + inline void setCounter(const counter_t &counter) { memcpy(m_counter, &counter, sizeof(m_counter)); } + inline void setCounter(const counter_t *counter) { memcpy(m_counter, counter, sizeof(m_counter)); } + inline void setCounter(const AESCounter &counter) { memcpy(m_counter, counter.m_counter, sizeof(m_counter)); } + //@} + + //! \name Operators + //@{ + const AESCounter &operator=(const AESCounter &counter) + { + setCounter(counter); + return *this; + } + const AESCounter &operator=(const counter_t &counter) + { + setCounter(counter); + return *this; + } + const AESCounter &operator=(const counter_t *counter) + { + setCounter(counter); + return *this; + } + + operator const counter_t &() const { return m_counter; } + operator const counter_t *() const { return m_counter; } + friend std::ostream &operator<<(std::ostream &os, const AESCounter &counter) + { + counter.writeToStream(os); + return os; + } + //@} + +protected: + counter_t m_counter; //!< The counter value. +}; + +//! Standard type definition for an AES-128 counter. +typedef AESCounter<128> AES128Counter; + +#endif // _AESCounter_h_ diff --git a/src/blfwk/AESKey.h b/src/blfwk/AESKey.h new file mode 100644 index 0000000..ff655dc --- /dev/null +++ b/src/blfwk/AESKey.h @@ -0,0 +1,134 @@ +/* + * File: AESKey.h + * + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_AESKey_h_) +#define _AESKey_h_ + +#include +#include +#include "Random.h" + +//! An AES-128 key is 128 bits, or 16 bytes. +typedef uint8_t aes128_key_t[16]; + +/*! + * \brief Base class for AESKey. + * + * This class implements some bigger, non-template methods used in the + * AESKey templated subclass. + */ +class AESKeyBase +{ +public: + //! \brief Reads hex encoded data from \a stream. + void _readFromStream(std::istream &stream, unsigned bytes, uint8_t *buffer) const; + + //! \brief Writes hex encoded data to \a stream. + void _writeToStream(std::ostream &stream, unsigned bytes, const uint8_t *buffer) const; +}; + +/*! + * \brief Generic AES key class. + * + * The template parameter \a S is the number of bits in the key. + * + * The underlying key type can be accessed like this: AESKey<128>::key_t + * + * When a key instance is destroyed, it erases the key data by setting it + * to all zeroes. + * + * \todo Add a way to allow only key sizes of 128, 192, and 256 bits. + * \todo Find a cross platform way to prevent the key data from being written + * to the VM swapfile. + * + * AESKey<128> key = AESKey<128>::readFromStream(s); + */ +template +class AESKey : public AESKeyBase +{ +public: + //! Type for this size of AES key. + typedef uint8_t key_t[S / 8]; + +public: + //! \brief Default constructor. + //! + //! Initializes the key to 0. + AESKey() { memset(m_key, 0, sizeof(m_key)); } + //! \brief Constructor taking a key value reference. + AESKey(const key_t &key) { memcpy(m_key, &key, sizeof(m_key)); } + // \brief Constructor taking a key value pointer. + AESKey(const key_t *key) { memcpy(m_key, key, sizeof(m_key)); } + //! \brief Constructor, reads key from stream in hex format. + AESKey(std::istream &stream) { readFromStream(stream); } + //! \brief Copy constructor. + AESKey(const AESKey &other) { memcpy(m_key, other.m_key, sizeof(m_key)); } + //! \brief Destructor. + //! + //! Sets the key value to zero. + ~AESKey() { memset(m_key, 0, sizeof(m_key)); } + //! \brief Set to the key to a randomly generated value. + void randomize() + { + RandomNumberGenerator rng; + rng.generateBlock(m_key, sizeof(m_key)); + } + + //! \brief Reads the key from a hex encoded data stream. + void readFromStream(std::istream &stream) { _readFromStream(stream, S / 8, reinterpret_cast(&m_key)); } + //! \brief Writes the key to a data stream in hex encoded format. + void writeToStream(std::ostream &stream) const + { + _writeToStream(stream, S / 8, reinterpret_cast(&m_key)); + } + + //! \name Key accessors + //@{ + inline const key_t &getKey() const { return m_key; } + inline void getKey(key_t *key) const { memcpy(key, m_key, sizeof(m_key)); } + inline void setKey(const key_t &key) { memcpy(m_key, &key, sizeof(m_key)); } + inline void setKey(const key_t *key) { memcpy(m_key, key, sizeof(m_key)); } + inline void setKey(const AESKey &key) { memcpy(m_key, key.m_key, sizeof(m_key)); } + //@} + + //! \name Operators + //@{ + const AESKey &operator=(const AESKey &key) + { + setKey(key); + return *this; + } + const AESKey &operator=(const key_t &key) + { + setKey(key); + return *this; + } + const AESKey &operator=(const key_t *key) + { + setKey(key); + return *this; + } + + operator const key_t &() const { return m_key; } + operator const key_t *() const { return m_key; } + friend std::ostream &operator<<(std::ostream &os, const AESKey &key) + { + key.writeToStream(os); + return os; + } + + //@} + +protected: + key_t m_key; //!< The key value. +}; + +//! Standard type definition for an AES-128 key. +typedef AESKey<128> AES128Key; + +#endif // _AESKey_h_ diff --git a/src/blfwk/BlfwkErrors.h b/src/blfwk/BlfwkErrors.h new file mode 100644 index 0000000..a2379da --- /dev/null +++ b/src/blfwk/BlfwkErrors.h @@ -0,0 +1,36 @@ +/* + * File: BlfwkErrors.h + * + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_BlfwkErrors_h_) +#define _BlfwkErrors_h_ + +#include +#include + +//! @addtogroup host_error +//! @{ + +namespace blfwk +{ +/*! + * \brief A semantic error discovered while processing the command file AST. + */ +class semantic_error : public std::runtime_error +{ +public: + explicit semantic_error(const std::string &msg) + : std::runtime_error(msg) + { + } +}; + +}; // namespace blfwk + +//! @} + +#endif // _BlfwkErrors_h_ diff --git a/src/blfwk/Blob.h b/src/blfwk/Blob.h new file mode 100644 index 0000000..2edcdfb --- /dev/null +++ b/src/blfwk/Blob.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_Blob_h_) +#define _Blob_h_ + +#include "stdafx.h" + +//! \class Blob +//! +//! \brief Manages a binary object of arbitrary length. +//! +//! The data block is allocated with malloc() instead of the new +//! operator so that we can use realloc() to resize it. +//! +class Blob +{ +public: + //! \brief Default constructor. + Blob(); + + //! \brief Constructor. + Blob(const uint8_t *data, unsigned length); + + //! \brief Copy constructor. + Blob(const Blob &other); + + //! \brief Destructor. + virtual ~Blob(); + + //! \name Operations + //@{ + //! \brief Replaces the blob's data. + void setData(const uint8_t *data, unsigned length); + + //! \brief Change the size of the blob's data. + void setLength(unsigned length); + + //! \brief Adds data to the end of the blob. + void append(const uint8_t *newData, unsigned newDataLength); + + //! \brief Disposes of the data. + void clear(); + + //! \brief Tell the blob that it no longer owns its data. + void relinquish(); + //@} + + //! \name Accessors + //@{ + uint8_t *getData() { return m_data; } + const uint8_t *getData() const { return m_data; } + unsigned getLength() const { return m_length; } + //@} + + //! \name Operators + //@{ + operator uint8_t *() { return m_data; } + operator const uint8_t *() const { return m_data; } + //@} + +protected: + uint8_t *m_data; //!< The binary data held by this object. + unsigned m_length; //!< Number of bytes pointed to by #m_data. +}; + +#endif // _Blob_h_ diff --git a/src/blfwk/Bootloader.h b/src/blfwk/Bootloader.h new file mode 100644 index 0000000..a359204 --- /dev/null +++ b/src/blfwk/Bootloader.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _Bootloader_h_ +#define _Bootloader_h_ + +#include "Command.h" +#include "Packetizer.h" +#include "Peripheral.h" +#include "Logging.h" + +#include +#include + +//! @addtogroup blfwk +//! @{ + +namespace blfwk +{ +/*! + * @brief Represents the host bootloader. + * + * This class provides a convenient way to access other bootloader + * framework objects. + */ +class Bootloader +{ +public: + //! @brief Default Constructor for Simulator. + Bootloader(); + + //! @brief Constructor. + Bootloader(const Peripheral::PeripheralConfigData &config); + + //! @brief Destructor. + virtual ~Bootloader(); + + //! @brief Inject a command into the bootloader. + //! + //! @param cmd The command to send + void inject(Command &cmd) + { + clock_t start = clock(); + cmd.sendTo(*m_hostPacketizer); + clock_t finish = clock(); + Log::debug(" - took %2.3f seconds\n", (double)(finish - start) / CLOCKS_PER_SEC); + } + + //! @brief Flush state. + void flush(); + + //! \brief Execute the execute command. + void execute(uint32_t entry_point, uint32_t param = 0, uint32_t stack_pointer = 0); + + //! \brief Execute the reset command. + void reset(); + + //! \brief get Device's property by using get-property command. + //! + //! \exception std::runtime_error Thrown if an error occurred while sending the + //! GetProperty(property) bootloader command. + //! + //! \param property tag The property tag + //! + //! \param memoryIdorIndex memoryId is required by GetProperty 25(External attribute) + //! Index is required by GetProperty 14/15(RAM start addr/RAM size) + //! + //! \return vector of the response values. + uint32_vector_t getProperty(property_t tag, uint32_t memoryIdorIndex = kMemoryInternal); + + //! \brief Checks if Bootloader device supports a given command. + //! + //! \exception std::runtime_error Thrown if an error occurred while sending the + //! GetProperty(kProperty_AvailableCommands) bootloader command. + //! + //! \param command The command to check. + //! + //! \return true if command is supported, false if not. + bool isCommandSupported(const cmd_t &command); + + //! \brief Execute the get-property(current-version) command. + standard_version_t getVersion(); + + //! \brief Execute the get-property(flash-security-state) command. + uint32_t getSecurityState(); + + //! \brief Execute the get-property(max-supported-packet-size) command. + uint32_t getDataPacketSize(); + + //! \brief Send a ping if applicable. + void ping(int retries, unsigned int delay, int comSpeed, int* actualComSpeed); + + //! @name Accessors. + //@{ + + //! @brief Get the host packetizer. + Packetizer *getPacketizer() const { return m_hostPacketizer; } + //@} + +protected: + Packetizer *m_hostPacketizer; //!< Packet interface to send commands on. + FileLogger *m_logger; //!< Singleton logger instance. +}; + +} // namespace blfwk + +//! @} + +#endif // _Bootloader_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/BusPal.h b/src/blfwk/BusPal.h new file mode 100644 index 0000000..6351d81 --- /dev/null +++ b/src/blfwk/BusPal.h @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _BusPal_h_ +#define _BusPal_h_ + +#include "host_types.h" + +//! @addtogroup bus_pal +//! @{ + +namespace blfwk +{ +/*! + * @brief Interface with the BusPal. + * + * For hardware documentation, see http://dangerousprototypes.com/docs/Bus_Pirate. + * This class is based on the pyBusPirateLite python package, see + * http://dangerousprototypes.com/docs/Bus_Pirate_Scripting_in_Python. + */ +class BusPal +{ +public: + //! @brief Constants. + enum + { + kResetCount = 20, //!< Max number of nulls to send to enter BBIO mode + kResponseOk = 0x01, //!< Successful command response + kBulkTransferMax = 4096, //!< Max number of bytes per bulk transfer + kMaxResponse = kBulkTransferMax //!< Max number of bytes in command response, including bulk transfer response, + //!plus some padding + }; + + //! @brief BusPal Transports. + enum bus_pal_function_t + { + kBusPalFunction_None, + kBusPalFunction_SPI, + kBusPalFunction_I2C, + kBusPalFunction_CAN, + kBusPalFunction_SAI, + kBusPalFunction_GPIO_CONFIG, + kBusPalFunction_GPIO_SET, + kBusPalFunction_FPGA_CLOCK_SET + }; + + //! @brief SPI clock polarity configuration. + enum spi_clock_polarity_t + { + kSpiClockPolarity_ActiveHigh = 0, //!< Active-high SPI clock (idles low). + kSpiClockPolarity_ActiveLow = 1 //!< Active-low SPI clock (idles high). + }; + + //! @brief SPI clock phase configuration. + enum spi_clock_phase_t + { + kSpiClockPhase_FirstEdge = + 0, //!< First edge on SPSCK occurs at the middle of the first cycle of a data transfer. + kSpiClockPhase_SecondEdge = + 1 //!< First edge on SPSCK occurs at the start of the first cycle of a data transfer. + }; + + //! @brief SPI data shifter direction options. + enum spi_shift_direction_t + { + kSpiMsbFirst = 0, //!< Data transfers start with most significant bit. + kSpiLsbFirst = 1 //!< Data transfers start with least significant bit. + }; + + //! @brief Define the SAI bus type + enum sai_protocol_t + { + kSaiProtocol_BusLeftJustified = 0, /*!< Uses left justified format.*/ + kSaiProtocol_BusRightJustified = 1, /*!< Uses right justified format. */ + kSaiProtocol_BusI2S = 2, /*!< Uses I2S format. */ + kSaiProtocol_BusPCMA = 3, /*!< Uses I2S PCM A format.*/ + kSaiProtocol_BusPCMB = 4 /*!< Uses I2S PCM B format. */ + }; + + //! @brief Mono or stereo audio format + enum sai_mono_stereo_t + { + kSaiMonoStereo_Stereo = 0, /*!< Stereo sound. */ + kSaiMonoStereo_MonoRight = 1, /*!< Only Right channel have sound. */ + kSaiMonoStereo_MonoLeft = 2 /*!< Only left channel have sound. */ + }; + + //! @brief I2C default address. + static const uint8_t kBusPalDefaultI2cSlaveAddress = 0x10; // I2C Slave 7-bit address + +public: + //! @brief BusPal configuration data. + struct BusPalConfigData + { + bus_pal_function_t function; + uint32_t spiSpeedKHz; + spi_clock_polarity_t spiPolarity; + spi_clock_phase_t spiPhase; + spi_shift_direction_t spiDirection; + uint8_t i2cAddress; + uint32_t i2cSpeedKHz; + uint32_t canSpeed; + uint32_t canTxid; + uint32_t canRxid; + uint32_t saiSpeedHz; + sai_protocol_t saiProtocol; + sai_mono_stereo_t saiStereo; + uint8_t reserved0[2]; + uint8_t gpioPort; + uint8_t gpioPinNum; + uint8_t gpioLevel; + uint8_t gpioMux; + uint32_t fpgaClockMhz; + + BusPalConfigData(bus_pal_function_t bus = BusPal::kBusPalFunction_None) + : function(bus) + { + spiSpeedKHz = 100; + spiPolarity = BusPal::kSpiClockPolarity_ActiveLow; + spiPhase = BusPal::kSpiClockPhase_SecondEdge; + spiDirection = BusPal::kSpiMsbFirst; + i2cAddress = BusPal::kBusPalDefaultI2cSlaveAddress; + i2cSpeedKHz = 100; + canSpeed = 4; // 4: 1M + canTxid = 0x321; + canRxid = 0x123; + saiSpeedHz = 8000; + saiProtocol = BusPal::kSaiProtocol_BusI2S; + saiStereo = BusPal::kSaiMonoStereo_Stereo; + gpioPort = 0; + gpioPinNum = 0; + gpioLevel = 0; + gpioMux = 0; + fpgaClockMhz = 0; + } + }; + + //! @brief Constructor. + BusPal(int fileDescriptor); + + //! @brief Destructor. + virtual ~BusPal(){}; + + //! @brief parse the passed in options into the config structure. + static bool parse(const string_vector_t ¶ms, BusPal::BusPalConfigData &config); + + //! @brief Reset to bit bang mode from another peripheral mode. + virtual bool reset(); + + //! @brief Reset the bus pal, comes up in terminal mode. + virtual bool resetHardware(); + + //! @brief Enter bit bang (binary scripting) mode. + //! + //! Call this first before entering other peripheral modes. + virtual bool enterBitBangMode(); + + //! @brief Enter Spi mode. + virtual bool enterSpiMode(); + + //! @brief Enter Can mode. + virtual bool enterCanMode(); + + //! @brief Enter I2c mode. + virtual bool enterI2cMode(); + + //! @brief Enter Sai mode. + virtual bool enterSaiMode(); + + //! @brief Enter Uart mode. Not currently supported for bus pal + virtual bool enterUartMode() { return false; } + //! @brief Enter 1wire mode. Not currently supported for bus pal + virtual bool enter1WireMode() { return false; } + //! @brief Enter raw wire mode. Not currently supported for bus pal + virtual bool enterRawWireMode() { return false; } + //! @brief Raw configure pins. + virtual bool rawConfigurePins(uint8_t port, uint8_t pin, uint8_t muxVal); + + //! @brief Set GPIO pin + virtual bool rawSetPins(uint8_t port, uint8_t pin, uint8_t level); + + //! @brief Configure pins. + virtual bool configurePins(uint8_t config = 0) { return true; } + //! @brief Read pins. + virtual uint8_t readPins() { return 0; } + //! @brief Sets the FPGA clock, clock is in hertz + virtual bool setFPGAClock(uint32_t clock); + + //! @brief Set SPI speed. + virtual bool setSpiSpeed(unsigned int speed); + + //! @brief Sets the SPI configuration + virtual bool setSpiConfig(spi_clock_polarity_t polarity, spi_clock_phase_t phase, spi_shift_direction_t direction); + + //! @brief Set I2c address + virtual bool setI2cAddress(uint8_t address); + + //! @brief Set I2c speed + virtual bool setI2cSpeed(uint32_t speed); + + //! @brief Set CAN speed. + virtual bool setCanSpeed(unsigned int speed); + + //! @brief Set CAN txid. + virtual bool setCanTxid(unsigned int txid); + + //! @brief Set CAN rxid. + virtual bool setCanRxid(unsigned int rxid); + + //! @brief Set SAI speed. + virtual bool setSaiSpeed(unsigned int speed); + + //! @brief Sets the SAI configuration + virtual bool setSaiConfig(sai_protocol_t protocol, sai_mono_stereo_t stereo); + + //! @brief Read response. + //! + //! @retval NULL No response from device + //! @retval Non-NULL Pointer to internal array of bytes at least size byteCount + virtual uint8_t *response(size_t byteCount = 1); + + //! @brief writes the data over the previously selected interface + virtual bool write(const uint8_t *data, size_t byteCount = 1); + + //! @brief Read data. + virtual int read(uint8_t *data, size_t byteCount); + +protected: + //! @brief Write command, check string response. + virtual bool writeCommand(uint8_t commandByte, const char *expectedResponse); + + //! @brief Write command, return 1 byte response. + virtual uint8_t writeCommand(uint8_t commandByte); + + //! @brief write multi-byte command, return 1 byte response. + virtual uint8_t writeCommand(uint8_t *command, unsigned int length); + + //! @brief write via Spi + virtual bool writeSpi(const uint8_t *data, size_t byteCount = 1); + + //! @brief write via I2c + virtual bool writeI2c(const uint8_t *data, size_t byteCount = 1); + + //! @brief write via Can + virtual bool writeCan(const uint8_t *data, size_t byteCount = 1); + + //! @brief write via Sai + virtual bool writeSai(const uint8_t *data, size_t byteCount = 1); + + //! @brief read via Spi + virtual int readSpi(uint8_t *data, size_t byteCount); + + //! @brief read via Can + virtual int readCan(uint8_t *data, size_t byteCount); + + //! @brief worker function to actually read SPI data + int readSpiActual(uint8_t *data, size_t byteCount); + + //! @brief read via I2c + virtual int readI2c(uint8_t *data, size_t byteCount); + + //! @brief read via Sai + virtual int readSai(uint8_t *data, size_t byteCount); + + //! @brief Overriden serial_read for logging + int buspal_serial_read(uint8_t *buf, int size, bool isCommandData = false); + + //! @brief Overriden serial_write for logging + int buspal_serial_write(uint8_t *buf, int size, bool isCommandData = false); + + //! @brief Flushes the serial port of any RX data + void flushRX(); + +protected: + enum bus_pal_mode_t + { + kBusPalModeBitBang, + kBusPalModeSpi, + kBusPalModeI2c, + kBusPalModeCan, + kBusPalModeSai + }; + int m_fileDescriptor; //!< PC COM port file descriptor. + uint8_t m_responseBuffer[kMaxResponse]; //!< Command response buffer. + bus_pal_mode_t m_mode; + unsigned int m_spiWriteByteCount; + unsigned int m_canWriteByteCount; + unsigned int m_canFirstReadDelay; + unsigned int m_saiWriteByteCount; +}; + +} // namespace blfwk + +//! @} + +#endif // _BusPal_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/BusPalPeripheral.h b/src/blfwk/BusPalPeripheral.h new file mode 100644 index 0000000..70ad55c --- /dev/null +++ b/src/blfwk/BusPalPeripheral.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _BusPalPeripheral_h_ +#define _BusPalPeripheral_h_ + +#include "UartPeripheral.h" +#include "BusPal.h" + +//! @addtogroup bus_pal_peripheral +//! @{ + +namespace blfwk +{ +/*! + * @brief Peripheral that talks to the target device over BusPal UART hardware. + */ +class BusPalUartPeripheral : public UartPeripheral +{ +public: + //! @brief Parameterized constructor that opens the serial port. + //! + //! Opens and configures the port. Throws exception if port configuration fails. + //! + //! Note: following COM port configuration is assumed: 8 bits, 1 stop bit, no parity. + //! + //! @param port OS file path for COM port. For example "COM1" on Windows. + //! @param speed Port speed, e.g. 9600. + BusPalUartPeripheral(const char *port, long speed, const BusPal::BusPalConfigData &config); + + //! @brief Destructor. + virtual ~BusPalUartPeripheral(); + + //! @brief configure the bus pal with the passed in options + void configure(const BusPal::BusPalConfigData &config); + + //! @brief Read bytes. + //! + //! @param buffer Pointer to buffer + //! @param requestedBytes Number of bytes to read + virtual status_t read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t timeoutMs); + + //! @brief Write bytes. + //! + //! @param buffer Pointer to buffer to write + //! @param byteCount Number of bytes to write + virtual status_t write(const uint8_t *buffer, uint32_t byteCount); + + virtual _host_peripheral_types get_type(void) { return kHostPeripheralType_BUSPAL_UART; } + +protected: + BusPal *m_busPal; //!< Represents Bus Pal hardware. +}; + +} // namespace blfwk + +//! @} + +#endif // _BusPalPeripheral_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/Command.h b/src/blfwk/Command.h new file mode 100644 index 0000000..4e3772b --- /dev/null +++ b/src/blfwk/Command.h @@ -0,0 +1,2135 @@ +/* + * Copyright (c) 2013-2015, Freescale Semiconductor, Inc. + * Copyright 2016-2020 NXP. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _Command_h_ +#define _Command_h_ + +#include + +#include + +#include "BusPal.h" +#include "DataSource.h" +#include "Packetizer.h" +#include "Progress.h" +#include "SourceFile.h" +#include "format_string.h" +#include "host_types.h" +#include "memory/memory.h" +#include "property/property.h" + +//! @addtogroup host_commands +//! @{ + +using namespace std; + +namespace blfwk +{ +//! @name Command tags, masks, names. +//@{ +struct cmd_t +{ + uint8_t tag; + uint32_t mask; + const char *const name; + + cmd_t(uint8_t tag, uint32_t mask, const char *name) + : tag(tag) + , mask(mask) + , name(name) + { + } +}; + +const cmd_t kCommand_FlashEraseAll(kCommandTag_FlashEraseAll, 0x00000001, "flash-erase-all"); +const cmd_t kCommand_FlashEraseRegion(kCommandTag_FlashEraseRegion, 0x00000002, "flash-erase-region"); +const cmd_t kCommand_ReadMemory(kCommandTag_ReadMemory, 0x00000004, "read-memory"); +const cmd_t kCommand_WriteMemory(kCommandTag_WriteMemory, 0x00000008, "write-memory"); +const cmd_t kCommand_FillMemory(kCommandTag_FillMemory, 0x00000010, "fill-memory"); +const cmd_t kCommand_FlashSecurityDisable(kCommandTag_FlashSecurityDisable, 0x00000020, "flash-security-disable"); +const cmd_t kCommand_GetProperty(kCommandTag_GetProperty, 0x00000040, "get-property"); +const cmd_t kCommand_ReceiveSbFile(kCommandTag_ReceiveSbFile, 0x00000080, "receive-sb-file"); +const cmd_t kCommand_Execute(kCommandTag_Execute, 0x00000100, "execute"); +const cmd_t kCommand_Call(kCommandTag_Call, 0x00000200, "call"); +const cmd_t kCommand_Reset(kCommandTag_Reset, 0x00000400, "reset"); +const cmd_t kCommand_SetProperty(kCommandTag_SetProperty, 0x00000800, "set-property"); +const cmd_t kCommand_FlashEraseAllUnsecure(kCommandTag_FlashEraseAllUnsecure, 0x00001000, "flash-erase-all-unsecure"); +const cmd_t kCommand_FlashProgramOnce(kCommandTag_FlashProgramOnce, 0x00002000, "flash-program-once"); +const cmd_t kCommand_FlashReadOnce(kCommandTag_FlashReadOnce, 0x00004000, "flash-read-once"); +const cmd_t kCommand_FlashReadResource(kCommandTag_FlashReadResource, 0x00008000, "flash-read-resource"); +const cmd_t kCommand_ConfigureMemory(kCommandTag_ConfigureMemory, 0x00010000, "configure-memory"); +const cmd_t kCommand_ReliableUpdate(kCommandTag_ReliableUpdate, 0x00020000, "reliable-update"); +const cmd_t kCommand_GenerateKeyBlob(kCommandTag_GenerateKeyBlob, 0x00040000, "generate-key-blob"); +const cmd_t kCommand_FuseProgram(kCommandTag_FuseProgram, 0x00080000, "fuse-program"); +const cmd_t kCommand_KeyProvisioning(kCommandTag_KeyProvisioning, 0x00100000, "key-provisioning"); +const cmd_t kCommand_TrustProvisioning(kCommandTag_TrustProvisioning, 0x00200000, "trust-provisioning"); +const cmd_t kCommand_FuseRead(kCommandTag_FuseRead, 0x00400000, "fuse-read"); +// Command for Bus Pal. +const cmd_t kCommand_ConfigureI2c(kCommandTag_ConfigureI2c, 0x00020000, "i2c"); +const cmd_t kCommand_ConfigureSpi(kCommandTag_ConfigureSpi, 0x00040000, "spi"); +const cmd_t kCommand_ConfigureCan(kCommandTag_ConfigureCan, 0x00080000, "can"); +// flash-image and list-memory is not a ROM basic command, but a command sequence combined by several basic commands. +const cmd_t kCommand_FlashImage(0x00, 0x00000000, "flash-image"); +const cmd_t kCommand_ListMemory(0x00, 0x00000000, "list-memory"); +// efuse-program-once and efuse-read-once are alias of flash-program-once and flash-read-once, +// to avoid the confusion that efuse is not a part of internal flash. +const cmd_t kCommand_EfuseProgramOnce(0x00, 0x00000000, "efuse-program-once"); +const cmd_t kCommand_EfuseReadOnce(0x00, 0x00000000, "efuse-read-once"); +// load-image doesn't sent any command, but data packet directly. +const cmd_t kCommand_LoadImage(0x00, 0x00000000, "load-image"); +const cmd_t kCommand_ProgramAESKey(0x00, 0x00000000, "program-aeskey"); + +const array kCommands = { kCommand_FlashEraseAll, + kCommand_FlashEraseRegion, + kCommand_ReadMemory, + kCommand_WriteMemory, + kCommand_FillMemory, + kCommand_FlashSecurityDisable, + kCommand_GetProperty, + kCommand_ReceiveSbFile, + kCommand_Execute, + kCommand_Call, + kCommand_Reset, + kCommand_SetProperty, + kCommand_FlashEraseAllUnsecure, + kCommand_FlashProgramOnce, + kCommand_FlashReadOnce, + kCommand_FlashReadResource, + kCommand_ConfigureMemory, + kCommand_ReliableUpdate, + kCommand_GenerateKeyBlob, + kCommand_FuseProgram, + kCommand_KeyProvisioning, + kCommand_TrustProvisioning, + kCommand_FuseRead }; +//@} + +//! @name option tags, names. +//@{ +struct opt_t +{ + uint32_t tag; + const char *const name; + + opt_t(uint32_t tag, const char *name) + : tag(tag) + , name(name) + { + } +}; + +/*!< Operations of trust provisioning command. */ +/*!< OEM trusted facility commands. */ +const opt_t kOperation_Tp_OemGenMasterShare(kTrustProvisioning_Operation_Oem_GenMasterShare, "oem_gen_master_share"); +const opt_t kOperation_Tp_OemSetMasterShare(kTrustProvisioning_Operation_Oem_SetMasterShare, "oem_set_master_share"); +const opt_t kOperation_Tp_OemGetCustCertDicePuk(kTrustProvisioning_Operation_Oem_GetCustCertDicePuk, + "oem_get_cust_cert_dice_puk"); +const opt_t kOperation_Tp_HsmGenKey(kTrustProvisioning_Operation_Hsm_GenKey, "hsm_gen_key"); +const opt_t kOperation_Tp_HsmStoreKey(kTrustProvisioning_Operation_Hsm_StoreKey, "hsm_store_key"); +const opt_t kOperation_Tp_HsmEncryptBlock(kTrustProvisioning_Operation_Hsm_EncryptBlock, "hsm_enc_blk"); +const opt_t kOperation_Tp_HsmEncryptSign(kTrustProvisioning_Operation_Hsm_EncryptSign, "hsm_enc_sign"); +/*!< NXP factory commands. */ +const opt_t kOperation_Tp_NxpRtsGetId(kTrustProvisioning_Operation_Nxp_RtsGetId, "nxp_rts_get_id"); +const opt_t kOperation_Tp_NxpRtsInsertCertificate(kTrustProvisioning_Operation_Nxp_RtsInsertCertificate, + "nxp_rts_ins_cert"); +const opt_t kOperation_Tp_NxpSsfInsertCertificate(kTrustProvisioning_Operation_Nxp_SsfInsertCertificate, + "nxp_ssf_ins_cert"); +/*!< OEM/CM factory commands. */ +const opt_t kOperation_Tp_DevAuthChallengeNxp(kTrustProvisioning_Operation_Dev_AuthChallengeNxp, + "dev_auth_challenge_nxp"); +const opt_t kOperation_Tp_DevAuthChallengeOem(kTrustProvisioning_Operation_Dev_AuthChallengeOem, + "dev_auth_challenge_oem"); +const opt_t kOperation_Tp_DevSetWrapData(kTrustProvisioning_Operation_Dev_SetWrapData, "dev_set_wrap_data"); + +/* HSM GEN KEY - key type definition. */ +const opt_t kKeyType_Tp_HsmGenKey_MfwIsK(kKeyType_HsmGenKey_MfwIsK, "MFWISK"); +const opt_t kKeyType_Tp_HsmGenKey_MfwEncK(kKeyType_HsmGenKey_MfwEncK, "MFWENCK"); +const opt_t kKeyType_Tp_HsmGenKey_GenSignK(kKeyType_HsmGenKey_GenSignK, "SIGNK"); +const opt_t kKeyType_Tp_HsmGenKey_GenCustMkSK(kKeyType_HsmGenKey_GenCustMkSK, "CUSTMKSK"); +/* HSM STORE KEY - key type definition. */ +const opt_t kKeyType_Tp_HsmStoreKey_CKDFK(kKeyType_HsmStoreKey_CKDFK, "CKDFK"); +const opt_t kKeyType_Tp_HsmStoreKey_HKDFK(kKeyType_HsmStoreKey_HKDFK, "HKDFK"); +const opt_t kKeyType_Tp_HsmStoreKey_HMACK(kKeyType_HsmStoreKey_HMACK, "HMACK"); +const opt_t kKeyType_Tp_HsmStoreKey_CMACK(kKeyType_HsmStoreKey_CMACK, "CMACK"); +const opt_t kKeyType_Tp_HsmStoreKey_AESK(kKeyType_HsmStoreKey_AESK, "AESK"); +const opt_t kKeyType_Tp_HsmStoreKey_KUOK(kKeyType_HsmStoreKey_KUOK, "KUOK"); +//@} + +//! @name Property tags. +//@{ +struct property_t +{ + uint32_t value; + const char *description; + + property_t(uint8_t value, const char *description) + : value(value) + , description(description) + { + } + + string str() const { return format_string("0x%02x", value); } +}; +const property_t kProperty_ListProperties(kPropertyTag_ListProperties, "list-properties"); +const property_t kProperty_CurrentVersion(kPropertyTag_BootloaderVersion, "current-version"); +const property_t kProperty_AvailablePeripherals(kPropertyTag_AvailablePeripherals, "available-peripherals"); +const property_t kProperty_FlashStartAddress(kPropertyTag_FlashStartAddress, "flash-start-address"); +const property_t kProperty_FlashSizeInBytes(kPropertyTag_FlashSizeInBytes, "flash-size-in-bytes"); +const property_t kProperty_FlashSectorSize(kPropertyTag_FlashSectorSize, "flash-sector-size"); +const property_t kProperty_FlashBlockCount(kPropertyTag_FlashBlockCount, "flash-block-count"); +const property_t kProperty_AvailableCommands(kPropertyTag_AvailableCommands, "available-commands"); +const property_t kProperty_CheckStatus(kPropertyTag_CheckStatus, "check-status"); +const property_t kProperty_Reserved9(kPropertyTag_Reserved9, "reserved"); + +const property_t kProperty_VerifyWrites(kPropertyTag_VerifyWrites, "verify-writes"); +const property_t kProperty_MaxPacketSize(kPropertyTag_MaxPacketSize, "max-packet-size"); +const property_t kProperty_ReservedRegions(kPropertyTag_ReservedRegions, "reserved-regions"); +const property_t kProperty_Reserved13(kPropertyTag_Reserved13, "reserved"); +const property_t kProperty_RAMStartAddress(kPropertyTag_RAMStartAddress, "ram-start-address"); +const property_t kProperty_RAMSizeInBytes(kPropertyTag_RAMSizeInBytes, "ram-size-in-bytes"); +const property_t kProperty_SystemDeviceId(kPropertyTag_SystemDeviceId, "system-device-id"); +const property_t kProperty_FlashSecurityState(kPropertyTag_SecurityState, "security-state"); +const property_t kProperty_UniqueDeviceId(kPropertyTag_UniqueDeviceId, "unique-device-id"); +const property_t kProperty_FacSupport(kPropertyTag_FacSupport, "flash-fac-support"); +const property_t kProperty_FlashAccessSegmentSize(kPropertyTag_FlashAccessSegmentSize, "flash-access-segment-size"); +const property_t kProperty_FlashAccessSegmentCount(kPropertyTag_FlashAccessSegmentCount, "flash-access-segment-count"); +const property_t kProperty_FlashReadMargin(kPropertyTag_FlashReadMargin, "flash-read-margin"); +const property_t kProperty_QspiInitStatus(kPropertyTag_QspiInitStatus, "qspi/otfad-init-status"); +const property_t kProperty_TargetVersion(kPropertyTag_TargetVersion, "target-version"); +const property_t kProperty_ExernalMemoryAttributes(kPropertyTag_ExternalMemoryAttributes, "external-memory-attributes"); +const property_t kProperty_ReliableUpdateStatus(kPropertyTag_ReliableUpdateStatus, "reliable-update-status"); +const property_t kProperty_FlashPageSize(kPropertyTag_FlashPageSize, "flash-page-size"); +const property_t kProperty_IrqNotifierPin(kPropertyTag_IrqNotifierPin, "irq-notify-pin"); +const property_t kProperty_FfrKeystoreUpdateOpt(kPropertyTag_FfrKeystoreUpdateOpt, "ffr-keystore_update-opt"); +const property_t kProperty_ByteWriteTimeoutMs(kPropertyTag_ByteWriteTimeoutMs, "byte-write-timeout-ms"); +const property_t kProperty_Invalid(kPropertyTag_InvalidProperty, "invalid-property"); + +typedef array PropertyArray; + +const PropertyArray kProperties = { kProperty_ListProperties, + kProperty_CurrentVersion, + kProperty_AvailablePeripherals, + kProperty_FlashStartAddress, + kProperty_FlashSizeInBytes, + kProperty_FlashSectorSize, + kProperty_FlashBlockCount, + kProperty_AvailableCommands, + kProperty_CheckStatus, + kProperty_Reserved9, + kProperty_VerifyWrites, + kProperty_MaxPacketSize, + kProperty_ReservedRegions, + kProperty_Reserved13, + kProperty_RAMStartAddress, + kProperty_RAMSizeInBytes, + kProperty_SystemDeviceId, + kProperty_FlashSecurityState, + kProperty_UniqueDeviceId, + kProperty_FacSupport, + kProperty_FlashAccessSegmentSize, + kProperty_FlashAccessSegmentCount, + kProperty_FlashReadMargin, + kProperty_QspiInitStatus, + kProperty_TargetVersion, + kProperty_ExernalMemoryAttributes, + kProperty_ReliableUpdateStatus, + kProperty_FlashPageSize, + kProperty_IrqNotifierPin, + kProperty_ByteWriteTimeoutMs, + kProperty_FfrKeystoreUpdateOpt }; +//@} + +//! @name Memory IDs. +//@{ +struct memory_t +{ + uint32_t memoryId; + const char *description; + + memory_t(uint32_t memoryId, const char *description) + : memoryId(memoryId) + , description(description) + { + } +}; + +const memory_t kMemory_Internal(kMemoryInternal, "Internal"); +const memory_t kMemory_QuadSpi(kMemoryQuadSpi0, "QuadSPI"); +const memory_t kMemory_IFR0(kMemoryIFR0, "IFR0"); +const memory_t kMemory_ExecuteOnly(kMemoryFlashExecuteOnly, "ExecuteOnly"); +const memory_t kMemory_SemcNor(kMemorySemcNor, "SEMC NOR"); +const memory_t kMemory_FlexSpiNor(kMemoryFlexSpiNor, "FlexSPI NOR"); +const memory_t kMemory_SemcNand(kMemorySemcNand, "SEMC NAND"); +const memory_t kMemory_SpiNand(kMemorySpiNand, "SPI NAND"); +const memory_t kMemory_SpiEeprom(kMemorySpiNorEeprom, "SPI NOR/EEPROM"); +const memory_t kMemory_I2cEeprom(kMemoryI2cNorEeprom, "I2C NOR/EEPROM"); +const memory_t kMemory_SdCard(kMemorySDCard, "SD/SDHC"); +const memory_t kMemory_MmcCard(kMemoryMMCCard, "MMC/eMMC"); + +typedef array MemoryArray; + +const MemoryArray kMemories = { kMemory_QuadSpi, kMemory_SemcNor, kMemory_FlexSpiNor, + kMemory_SemcNand, kMemory_SpiNand, kMemory_SpiEeprom, + kMemory_I2cEeprom, kMemory_SdCard, kMemory_MmcCard }; +//@} + +//! @name Peripheral bits. +//@{ +struct peripheral_t +{ + uint32_t mask; + const char *description; + + peripheral_t(uint32_t mask, const char *description) + : mask(mask) + , description(description) + { + } +}; + +const peripheral_t kPeripheral_Uart(0x00000001, "UART"); +const peripheral_t kPeripheral_I2C(0x00000002, "I2C Slave"); +const peripheral_t kPeripheral_Spi(0x00000004, "SPI Slave"); +const peripheral_t kPeripheral_Can(0x00000008, "CAN"); +const peripheral_t kPeripheral_UsbHid(0x00000010, "USB HID"); +const peripheral_t kPeripheral_UsbCdc(0x00000020, "USB CDC"); +const peripheral_t kPeripheral_UsbDfu(0x00000040, "USB DFU"); + +const array kPeripherals = { kPeripheral_Uart, kPeripheral_I2C, kPeripheral_Spi, + kPeripheral_Can, kPeripheral_UsbHid, kPeripheral_UsbCdc, + kPeripheral_UsbDfu }; +//@} + +//! @brief Entry in a lookup table of status messages. +//! +//! This struct maps a status value to a description of that status. +struct StatusMessageTableEntry +{ + int32_t status; //!< Status code value. + const std::string message; //!< Description of the status. +}; + +//! @brief Status return code descriptions. +//! +//! @warning These strings need to be kept in sync with the platform status codes +//! in the src/include/bootloader_common.h file. +extern StatusMessageTableEntry g_statusCodes[]; + +//! @name Commands +//@{ + +/*! + * @brief Represents a bootloader command. + * + * Do not instantiate this class. Instead, use the create() method to + * create the appropriate subclass based on command name in argv[0]. + */ +class Command +{ +public: + //! @brief Create an appropriate command subclass. + //! + //! Pass the command name in argv[0] and optional + //! arguments in the rest of the string vector. + //! Caller is responsible for deleting the returned object. + //! + //! This factory method is just a shorthand for creating + //! a subclass directly, for example: + //! @code + //! string_vector_t cmdv; + //! cmdv.push_back("my-reset"); + //! Reset * reset = new Reset(&cmdv); + //! reset->init(); + //! Command * cmd = reset; + //! @endcode + //! + //! @param argv Argument vector + //! @retval Command object + static Command *create(const string_vector_t *argv); + +public: + //! @brief Get a status code description. + static std::string getStatusMessage(status_t code); + +protected: + //! @brief Constructor that takes a command name and list of arguments. + //! + //! @param argv Argument vector + Command(const string_vector_t *argv) + : m_argv(*argv) + , m_responseValues() + , m_responseDetails() + , m_progress() + { + } + + //! @brief Constructor that takes a command name. + //! + //! @param name Name of the command + Command(const char *const name) + : m_argv(1, name) + , m_responseValues() + , m_responseDetails() + , m_progress() + { + } + +public: + //! @brief Destructor. + virtual ~Command() {} + //! @brief Initialize. + //! + //! Subclasses should implement init() to check for valid arguments. + virtual bool init() { return true; } + //! @name String arguments accessors. + //@{ + + //! @brief Get the specified argument. + virtual std::string getArg(int arg) const { return m_argv.at(arg); } + //! @brief Get the command name (i.e. argv[0]). + virtual std::string getName() const { return getArg(0); } + //! @brief Get the number of arguments, including the command name. + virtual size_t getArgCount() const { return m_argv.size(); } + //@} + + //! @brief Send command to packetizer and on to peripheral. + virtual void sendTo(Packetizer &packetizer) {} + //! @brief Get response values vector. + virtual const uint32_vector_t *getResponseValues() const + { + return const_cast(&m_responseValues); + } + + //! @brief Get response as JSON string. + virtual std::string getResponse() const; + + //! @brief Log the response description. + void logResponses() const; + + //! @brief initial the process callback. + void registerProgress(Progress *progress) { m_progress = progress; } + +protected: + //! @brief Check generic response packet. + //! + //! @param packet Packet received from device + //! @param commandTag Expected command tag in packet + virtual bool processResponse(const generic_response_packet_t *packet, uint8_t commandTag); + +public: + Progress *m_progress; //!< Variable for progress control. + +protected: + string_vector_t m_argv; //!< Vector of argument strings. + uint32_vector_t m_responseValues; //!< Vector of response values. + string m_responseDetails; //!< Descriptive response. +}; + +/*! + * @brief Command packet operations. + * + * Formats command packets and runs the command phase. + */ +class CommandPacket +{ +protected: + //! @brief Constants. + enum _command_packet_constants + { + kMaxCommandArguments = 16 //!< 16 args max + }; + + //! Format of command packet. + struct PacketWithArgs + { + //! @brief Initialize the command packet. + void init(uint8_t tag, uint8_t flags, uint8_t numArguments) + { + m_header.commandTag = tag; + m_header.flags = flags; + m_header.reserved = 0; + m_header.parameterCount = numArguments; + } + command_packet_t m_header; //!< Packet header. + uint32_t m_arguments[kMaxCommandArguments]; //!< Command arguments. + }; + +public: + //! @brief Constructor that takes no command arguments. + CommandPacket(uint8_t tag, uint8_t flags = 0) + { + m_numArguments = 0; + m_packet.init(tag, flags, m_numArguments); + } + + //! @brief Constructor that takes one command argument. + CommandPacket(uint8_t tag, uint8_t flags, uint32_t arg) + { + m_numArguments = 1; + m_packet.init(tag, flags, m_numArguments); + m_packet.m_arguments[0] = arg; + } + + //! @brief Constructor that takes two command arguments. + CommandPacket(uint8_t tag, uint8_t flags, uint32_t arg1, uint32_t arg2) + { + m_numArguments = 2; + m_packet.init(tag, flags, m_numArguments); + m_packet.m_arguments[0] = arg1; + m_packet.m_arguments[1] = arg2; + } + + //! @brief Constructor that takes three command arguments. + CommandPacket(uint8_t tag, uint8_t flags, uint32_t arg1, uint32_t arg2, uint32_t arg3) + { + m_numArguments = 3; + m_packet.init(tag, flags, m_numArguments); + m_packet.m_arguments[0] = arg1; + m_packet.m_arguments[1] = arg2; + m_packet.m_arguments[2] = arg3; + } + + //! @brief Constructor that takes four command arguments. + CommandPacket(uint8_t tag, uint8_t flags, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4) + { + m_numArguments = 4; + m_packet.init(tag, flags, m_numArguments); + m_packet.m_arguments[0] = arg1; + m_packet.m_arguments[1] = arg2; + m_packet.m_arguments[2] = arg3; + m_packet.m_arguments[3] = arg4; + } + + //! @brief Constructor that takes four command arguments. + CommandPacket(uint8_t tag, uint8_t flags, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4, uint32_t arg5) + { + m_numArguments = 5; + m_packet.init(tag, flags, m_numArguments); + m_packet.m_arguments[0] = arg1; + m_packet.m_arguments[1] = arg2; + m_packet.m_arguments[2] = arg3; + m_packet.m_arguments[3] = arg4; + m_packet.m_arguments[4] = arg5; + } + + //! @brief Constructor that takes four command arguments. + CommandPacket(uint8_t tag, + uint8_t flags, + uint32_t arg1, + uint32_t arg2, + uint32_t arg3, + uint32_t arg4, + uint32_t arg5, + uint32_t arg6) + { + m_numArguments = 6; + m_packet.init(tag, flags, m_numArguments); + m_packet.m_arguments[0] = arg1; + m_packet.m_arguments[1] = arg2; + m_packet.m_arguments[2] = arg3; + m_packet.m_arguments[3] = arg4; + m_packet.m_arguments[4] = arg5; + m_packet.m_arguments[5] = arg6; + } + + //! @brief Constructor that takes four command arguments. + CommandPacket(uint8_t tag, + uint8_t flags, + uint32_t arg1, + uint32_t arg2, + uint32_t arg3, + uint32_t arg4, + uint32_t arg5, + uint32_t arg6, + uint32_t arg7) + { + m_numArguments = 7; + m_packet.init(tag, flags, m_numArguments); + m_packet.m_arguments[0] = arg1; + m_packet.m_arguments[1] = arg2; + m_packet.m_arguments[2] = arg3; + m_packet.m_arguments[3] = arg4; + m_packet.m_arguments[4] = arg5; + m_packet.m_arguments[5] = arg6; + m_packet.m_arguments[6] = arg7; + } + + //! @brief Constructor that takes four command arguments. + CommandPacket(uint8_t tag, + uint8_t flags, + uint32_t arg1, + uint32_t arg2, + uint32_t arg3, + uint32_t arg4, + uint32_t arg5, + uint32_t arg6, + uint32_t arg7, + uint32_t arg8) + { + m_numArguments = 8; + m_packet.init(tag, flags, m_numArguments); + m_packet.m_arguments[0] = arg1; + m_packet.m_arguments[1] = arg2; + m_packet.m_arguments[2] = arg3; + m_packet.m_arguments[3] = arg4; + m_packet.m_arguments[4] = arg5; + m_packet.m_arguments[5] = arg6; + m_packet.m_arguments[6] = arg7; + m_packet.m_arguments[7] = arg8; + } + + //! @brief Constructor that takes four command arguments. + CommandPacket(uint8_t tag, + uint8_t flags, + uint32_t arg1, + uint32_t arg2, + uint32_t arg3, + uint32_t arg4, + uint32_t arg5, + uint32_t arg6, + uint32_t arg7, + uint32_t arg8, + uint32_t arg9) + { + m_numArguments = 9; + m_packet.init(tag, flags, m_numArguments); + m_packet.m_arguments[0] = arg1; + m_packet.m_arguments[1] = arg2; + m_packet.m_arguments[2] = arg3; + m_packet.m_arguments[3] = arg4; + m_packet.m_arguments[4] = arg5; + m_packet.m_arguments[5] = arg6; + m_packet.m_arguments[6] = arg7; + m_packet.m_arguments[7] = arg8; + m_packet.m_arguments[8] = arg9; + } + + //! @brief Get size of command packet, including arguments. + uint32_t getSize() const { return sizeof(command_packet_t) + (m_numArguments * sizeof(uint32_t)); } + //! @brief Get pointer to command packet data. + const uint8_t *getData() { return reinterpret_cast(&m_packet.m_header); } + //! @brief Send command packet and read response. + //! + //! @return Pointer to response packet. + const uint8_t *sendCommandGetResponse(Packetizer &device); + +protected: + int m_numArguments; //!< Number of command arguments. + PacketWithArgs m_packet; //!< Command packet data. +}; + +/*! + * @brief Data packet operations. + * + * Formats data packets and runs data phase. + */ +class DataPacket +{ +public: + /*! + * @brief Abstract class to provide data for data phase. + */ + class DataProducer + { + public: + //! @brief Query if more data is available. + virtual bool hasMoreData() const = 0; + + //! @brief Query the total size of the data. + virtual uint32_t getDataSize() const = 0; + + //! @brief Get the next data chunk. + //! + //! Before calling getData(), call hasMoreData() to determine if + //! data is available. + virtual uint32_t getData(uint8_t *data, uint32_t size) = 0; + }; + + /*! + * @brief Provide file data for data phase. + */ + class FileDataProducer : public DataProducer + { + public: + //! @brief Default constructor. + FileDataProducer() + : m_filePath() + , m_filePointer(NULL) + , m_fileSize(0) + { + } + + //! @brief Destructor. + virtual ~FileDataProducer() + { + if (m_filePointer) + fclose(m_filePointer); + } + + //! @brief Initialize with a file path. + bool init(std::string filePath, uint32_t count); + + //! \name DataProducer + //@{ + //! @brief Query if more data is available. + virtual bool hasMoreData() const + { + assert(m_filePointer); + return (m_fileSize && !feof(m_filePointer)); + } + + //! @brief Query the total size of the data. + virtual uint32_t getDataSize() const { return (uint32_t)m_fileSize; } + //! @brief Get the next data chunk. + //! + //! Before calling getData(), call moreData() to determine if + //! data is available. + virtual uint32_t getData(uint8_t *data, uint32_t size); + //@} + + protected: + std::string m_filePath; //!< Data file path. + FILE *m_filePointer; //!< Data file pointer. + long m_fileSize; //!< Size in bytes of data file. + }; + + /*! + * @brief Provide data from hex string for data phase. + */ + class HexDataProducer : public DataProducer + { + public: + //! @brief Default constructor. + HexDataProducer() + : m_byteIndex(0) + , m_data() + { + } + + //! @brief Constructor that takes a vector parameter. + HexDataProducer(const uchar_vector_t &data) + : m_byteIndex(0) + , m_data(data) + { + } + + //! @brief Initialize with a data string. + //! + //! @param hexData String with hex digits surrounded by double brackets, e.g. '{{11 22 33}}', white space + //! ignored + //! @return Number of hex bytes parsed from the string. + uint32_t initFromString(const std::string hexData); + + //! @brief Destructor. + virtual ~HexDataProducer() {} + //! \name DataProducer + //@{ + //! @brief Query if more data is available. + virtual bool hasMoreData() const { return (m_byteIndex < m_data.size()); } + //! @brief Query the total size of the data. + virtual uint32_t getDataSize() const { return m_data.size(); } + //! @brief Get the next data chunk. + //! + //! Before calling getData(), call moreData() to determine if + //! data is available. + virtual uint32_t getData(uint8_t *data, uint32_t size); + //@} + + protected: + uint32_t m_byteIndex; //!< Current byte index. + uchar_vector_t m_data; //!< Data byte vector. + }; + + /*! + * @brief Provide DataSource::Segment data for data phase. + */ + class SegmentDataProducer : public DataProducer + { + public: + //! @brief Default constructor. + SegmentDataProducer(blfwk::DataSource::Segment *segment) + : m_segment(segment) + , m_byteIndex(0) + { + } + + //! @brief Destructor. + virtual ~SegmentDataProducer() {} + //! @brief Initialize with a file path. + bool init(std::string filePath); + + //! \name DataProducer + //@{ + //! @brief Query if more data is available. + virtual bool hasMoreData() const { return (m_byteIndex < m_segment->getLength()); } + //! @brief Query the total size of the data. + virtual uint32_t getDataSize() const { return m_segment->getLength(); } + //! @brief Get the next data chunk. + //! + //! Before calling getData(), call moreData() to determine if + //! data is available. + virtual uint32_t getData(uint8_t *data, uint32_t size); + //@} + + protected: + blfwk::DataSource::Segment *m_segment; //!< DataSource::Segment object. + uint32_t m_byteIndex; //!< Current byte index. + }; + + /*! + * @brief Abstract class to consume data from data phase. + */ + class DataConsumer + { + public: + //! @brief Process the next data chunk. + virtual void processData(const uint8_t *data, uint32_t size) = 0; + + //! @brief Finalize processing. + virtual void finalize() = 0; + }; + + /*! + * @brief Write file data for data phase receive. + */ + class FileDataConsumer : public DataConsumer + { + public: + //! @brief Default constructor. + FileDataConsumer() + : m_filePath() + , m_filePointer(NULL) + { + } + + //! @brief Destructor. + virtual ~FileDataConsumer() + { + if (m_filePointer) + fclose(m_filePointer); + } + + //! @brief Initialize with a file path. + bool init(std::string filePath); + + //! @brief Process the next data chunk. + virtual void processData(const uint8_t *data, uint32_t size); + + //! @brief Finalize processing. + virtual void finalize() {} + + protected: + std::string m_filePath; //!< Data file path. + FILE *m_filePointer; //!< Data file pointer. + }; + + /*! + * @brief Print data for data phase receive. + */ + class StdOutDataConsumer : public DataConsumer + { + public: + enum _constants + { + kBytesPerLine = 16 //!< Number of hex bytes to display per line + }; + + public: + //! @brief Constructor. + StdOutDataConsumer() + : m_currentCount(1) + , m_dataCacheCount(0) + { + } + + //! @brief Finalize processing. + virtual void finalize() + { + if (m_dataCacheCount) + { + for (int i = 0; i < m_dataCacheCount; ++i) + { + printf("%02x", m_dataCache[i]); + if ((m_currentCount++ % kBytesPerLine) == 0) + { + printf("\n"); + } + else + { + printf(" "); + } + } + m_dataCacheCount = 0; + } + if (((m_currentCount - 1) % kBytesPerLine) != 0) + { + // Fill space to clean the progress text. + for (int i = ((m_currentCount - 1) % kBytesPerLine) * 3; i < 9; i++) + { + printf(" "); + } + printf("\n"); + } + } + + //! @brief Process the next data chunk. + virtual void processData(const uint8_t *data, uint32_t size); + + protected: + uint32_t m_currentCount; //!< Current byte being processed, starts at 1 + uint8_t m_dataCache[kBytesPerLine]; //!< Cache the non-kBytesPerLine-aligned data. + uint8_t m_dataCacheCount; //!< Count in bytes of the cached data. + }; + +public: + //! @brief Constructor that takes a DataProducer. + DataPacket(DataProducer *dataProducer, uint32_t packetSize = kDefaultMaxPacketSize) + { + assert(dataProducer); + m_dataProducer = dataProducer; + m_dataConsumer = NULL; + m_packetSize = packetSize; + } + + //! @brief Constructor that takes a DataConsumer. + DataPacket(DataConsumer *dataConsumer) + { + assert(dataConsumer); + m_dataConsumer = dataConsumer; + m_dataProducer = NULL; + m_packetSize = kDefaultMaxPacketSize; /* In fact m_packetSize is not used for Data consumer. */ + } + + //! @brief Send data packet to device. + //! + //! Calls the data provide to get the data to send. + uint8_t *sendTo(Packetizer &device, uint32_t *bytesWritten, Progress *progress); + //! Calls the data provide to get the data to send. + uint8_t *sendTo(Packetizer &device, uint32_t *bytesWritten, Progress *progress, bool hasResponse); + + //! @brief Receive data packet from device. + //! + //! Calls the data consumer to process the receied data. + uint8_t *receiveFrom(Packetizer &device, uint32_t *byteCount, Progress *progress); + +protected: + DataProducer *m_dataProducer; //!< Provides data for the packet. + DataConsumer *m_dataConsumer; //!< Process the data in the packet. + uint8_t m_packet[kMaxHostPacketSize]; //!< The data packet pointer. + uint32_t m_packetSize; //!< The data packet size of current device. +}; + +/*! + * @brief Represents the bootloader Reset command. + * + * The Reset command has no arguments. + */ +class Reset : public Command +{ +public: + //! @brief Constructor that takes argument vector. + Reset(const string_vector_t *argv) + : Command(argv) + { + } + + //! @brief Default constructor. + Reset() + : Command(kCommand_Reset.name) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), kCommandTag_Reset); + } +}; + +/*! + * @brief Represents the bootloader GetProperty command. + */ +class GetProperty : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + GetProperty(const string_vector_t *argv) + : Command(argv) + , m_property(kProperty_Invalid.value, kProperty_Invalid.description) + , m_memoryIdorIndex(kMemoryInternal) + { + } + + //! @brief Constructor that takes a property_t argument. + GetProperty(property_t property, uint32_t memoryIdorIndex = kMemoryInternal /*Default: internal memory or index 0*/) + : Command(kCommand_GetProperty.name) + , m_property(property) + , m_memoryIdorIndex(memoryIdorIndex) + { + m_argv.push_back(format_string("0x%08x", property.value)); + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const get_property_response_packet_t *packet); + + //! @brief Check generic response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_GetProperty); + } + +protected: + property_t m_property; //!< Property tag. + uint32_t m_memoryIdorIndex; //!< External memory identifier +}; + +/*! + * @brief Represents the bootloader GetProperty command. + */ +class SetProperty : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + SetProperty(const string_vector_t *argv) + : Command(argv) + , m_propertyTag(kProperty_Invalid.value) + , m_propertyValue(0) + { + } + + //! @brief Constructor that takes a property_t argument and the value to set. + SetProperty(property_t property, uint32_t value) + : Command(kCommand_SetProperty.name) + , m_propertyTag(property.value) + , m_propertyValue(value) + { + m_argv.push_back(format_string("0x%08x", property.value)); + m_argv.push_back(format_string("0x%08x", value)); + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check generic response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_SetProperty); + } + +protected: + uint32_t m_propertyTag; //!< Property tag. + uint32_t m_propertyValue; //!< Value to set. +}; + +/*! + * @brief Represents the bootloader Flash Erase command. + */ +class FlashEraseRegion : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FlashEraseRegion(const string_vector_t *argv) + : Command(argv) + , m_startAddress(0) + , m_byteCount(0) + , m_memoryId(kMemoryInternal) + { + } + + //! @brief Constructor that takes a start and length arguments. + FlashEraseRegion(uint32_t start, uint32_t length, uint32_t memoryId) + : Command(kCommand_FlashEraseRegion.name) + , m_startAddress(start) + , m_byteCount(length) + , m_memoryId(memoryId) + { + m_argv.push_back(format_string("0x%08x", start)); + m_argv.push_back(format_string("0x%08x", length)); + m_argv.push_back(format_string("%d", memoryId)); + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_FlashEraseRegion); + } + +protected: + uint32_t m_startAddress; //!< Starting address in flash. + uint32_t m_byteCount; //!< Number of bytes to erase. + uint32_t m_memoryId; //!< Memory Device ID. +}; + +/*! + * @brief Represents the bootloader Flash Erase All command. + */ +class FlashEraseAll : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FlashEraseAll(const string_vector_t *argv) + : Command(argv) + , m_memoryId(kMemoryInternal) + { + } + + //! @brief Constructor that takes memory ID argument. + FlashEraseAll(uint32_t memoryId) + : Command(kCommand_FlashEraseAll.name) + , m_memoryId(memoryId) + { + m_argv.push_back(format_string("%d", memoryId)); + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_FlashEraseAll); + } + +protected: + uint32_t m_memoryId; //!< Flash Memory Identifier +}; + +/*! + * @brief Represents the bootloader Flash Erase All Unsecure command. + */ +class FlashEraseAllUnsecure : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FlashEraseAllUnsecure(const string_vector_t *argv) + : Command(argv) + { + } + + //! @brief Constructor that takes none argument. + FlashEraseAllUnsecure() + : Command(kCommand_GetProperty.name) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_FlashEraseAllUnsecure); + } +}; + +/*! + * @brief Represents the bootloader Generate Key Blob command. + */ +class GenerateKeyBlob : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + GenerateKeyBlob(const string_vector_t *argv) + : Command(argv) + , m_fileDek() + , m_fileKeyBlob() + , m_keySel(0) + , m_count(0) + , m_dataPhase(0) + { + } + + //! @brief Constructor + GenerateKeyBlob() + : Command(kCommand_GenerateKeyBlob.name) + , m_fileDek() + , m_fileKeyBlob() + , m_keySel(0) + , m_count(0) + , m_dataPhase(0) + { + m_argv.push_back(m_fileDek); + m_argv.push_back(m_fileKeyBlob); + } + + //! @brief master key selection for generate-key-blob. + enum + { + kKeySource_SNVS_OTPMK = 0, /*!< OTPMK from FUSE is used as KEK */ + kKeySource_SNVS_OTPMK_Sub = 1, /*!< 2nd enum value for OTPMK, the same effect with kKeySource_SNVS_OTPMK */ + kKeySource_SNVS_ZMK = 2, /*!< ZMK from SVNS is used as KEK */ + kKeySource_SNVS_CMK = 3, /*!< CMK from SVNS is used as KEK */ + }; + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const generate_key_blob_response_packet_t *packet); + //! @brief Check response packet. + + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_GenerateKeyBlob); + } + +protected: + std::string m_fileDek; //!< DEK key Data file path. + std::string m_fileKeyBlob; //!< Key Blob Data file path. + uint32_t m_keySel; //!< KEK source selected to generate the blob. + uint32_t m_count; + uint32_t m_dataPhase; +}; + +/*! + * @brief Represents the bootloader Read Memory command. + */ +class ReadMemory : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + ReadMemory(const string_vector_t *argv) + : Command(argv) + , m_startAddress(0) + , m_byteCount(0) + , m_dataFile() + , m_memoryId(kMemoryInternal) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const read_memory_response_packet_t *packet); + + //! @brief Check generic response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_ReadMemory); + } + +protected: + std::string m_dataFile; //!< Data file path. + uint32_t m_startAddress; //!< Destination memory address. + uint32_t m_byteCount; //!< Number of bytes to read. + uint32_t m_memoryId; //!< Memory device ID. +}; + +/*! + * @brief Represents the bootloader Write Memory command. + */ +class WriteMemory : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + WriteMemory(const string_vector_t *argv) + : Command(argv) + , m_fileOrData() + , m_segment(NULL) + , m_startAddress(0) + , m_count(0) + , m_data() + , m_memoryId(kMemoryInternal) + { + } + + //! @brief Constructor that takes an DataSource::Segment argument + WriteMemory(blfwk::DataSource::Segment *segment, uint32_t memoryId) + : Command(kCommand_WriteMemory.name) + , m_fileOrData() + , m_segment(segment) + , m_count(0) + , m_data() + , m_memoryId(memoryId) + { + m_startAddress = segment->getBaseAddress(); + m_argv.push_back(format_string("0x%08x", m_startAddress)); + m_argv.push_back(m_fileOrData); + m_argv.push_back(format_string("%d", memoryId)); + } + + //! @brief Constructor that takes an uchar_vector_t argument + WriteMemory(uint32_t address, const uchar_vector_t &data, uint32_t memoryId) + : Command(kCommand_WriteMemory.name) + , m_fileOrData() + , m_segment(NULL) + , m_startAddress(address) + , m_count(0) + , m_data(data) + , m_memoryId(memoryId) + { + m_argv.push_back(format_string("0x%08x", m_startAddress)); + m_argv.push_back(m_fileOrData); + m_argv.push_back(format_string("%d", memoryId)); + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_WriteMemory); + } + +protected: + std::string m_fileOrData; //!< Data file path or hex data string. + blfwk::DataSource::Segment *m_segment; //!< DataSource segment (instead of file or hex string). + uint32_t m_startAddress; //!< Destination memory address. + uint32_t m_count; //!< Number of bytes to write. + uchar_vector_t m_data; //!< The data to write to the device. + uint32_t m_memoryId; //!< Memory device ID +}; + +/*! + * @brief Represents the bootloader Fill Memory command. + */ +class FillMemory : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FillMemory(const string_vector_t *argv) + : Command(argv) + , m_startAddress(0) + , m_byteCount(0) + , m_patternWord(0xFFFFFFFF) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_FillMemory); + } + +protected: + uint32_t m_startAddress; //!< Destination memory address. + uint32_t m_byteCount; //!< Number of bytes to fill. + uint32_t m_patternWord; //!< Fill pattern. +}; + +/*! + * @brief Represents the bootloader Receive SB File command. + */ +class ReceiveSbFile : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + ReceiveSbFile(const string_vector_t *argv) + : Command(argv) + , m_dataFile() + { + } + + //! @brief Constructor that takes a filename argument. + ReceiveSbFile(const char *const filename) + : Command(kCommand_ReceiveSbFile.name) + , m_dataFile(filename) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_ReceiveSbFile); + } + +protected: + std::string m_dataFile; //!< SB file path. +}; + +/*! + * @brief Represents the bootloader Fuse Program command. + */ +class FuseProgram : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FuseProgram(const string_vector_t *argv) + : Command(argv) + , m_fileOrData() + , m_segment(NULL) + , m_startAddress(0) + , m_count(0) + , m_data() + , m_memoryId(kMemoryInternal) + { + } + + //! @brief Constructor that takes an DataSource::Segment argument + FuseProgram(blfwk::DataSource::Segment *segment, uint32_t memoryId) + : Command(kCommand_WriteMemory.name) + , m_fileOrData() + , m_segment(segment) + , m_count(0) + , m_data() + , m_memoryId(memoryId) + { + m_startAddress = segment->getBaseAddress(); + m_argv.push_back(format_string("0x%08x", m_startAddress)); + m_argv.push_back(m_fileOrData); + m_argv.push_back(format_string("%d", memoryId)); + } + + //! @brief Constructor that takes an uchar_vector_t argument + FuseProgram(uint32_t address, const uchar_vector_t &data, uint32_t memoryId) + : Command(kCommand_WriteMemory.name) + , m_fileOrData() + , m_segment(NULL) + , m_startAddress(address) + , m_count(0) + , m_data(data) + , m_memoryId(memoryId) + { + m_argv.push_back(format_string("0x%08x", m_startAddress)); + m_argv.push_back(m_fileOrData); + m_argv.push_back(format_string("%d", memoryId)); + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_FuseProgram); + } + +protected: + std::string m_fileOrData; //!< Data file path or hex data string. + blfwk::DataSource::Segment *m_segment; //!< DataSource segment (instead of file or hex string). + uint32_t m_startAddress; //!< Destination memory address. + uint32_t m_count; //!< Number of bytes to write. + uchar_vector_t m_data; //!< The data to write to the device. + uint32_t m_memoryId; //!< Memory device ID +}; + +/*! + * @brief Represents the bootloader Fuse Read command. + */ +class FuseRead : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FuseRead(const string_vector_t *argv) + : Command(argv) + , m_startAddress(0) + , m_byteCount(0) + , m_dataFile() + , m_memoryId(kMemoryInternal) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const read_memory_response_packet_t *packet); + + //! @brief Check generic response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_FuseRead); + } + +protected: + std::string m_dataFile; //!< Data file path. + uint32_t m_startAddress; //!< Destination memory address. + uint32_t m_byteCount; //!< Number of bytes to read. + uint32_t m_memoryId; //!< Memory device ID. +}; + +/*! + * @brief Represents the bootloader Load Image command. + */ +class LoadImage : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + LoadImage(const string_vector_t *argv) + : Command(argv) + , m_dataFile() + { + } + + //! @brief Constructor that takes a filename argument. + LoadImage(const char *const filename) + : Command(kCommand_LoadImage.name) + , m_dataFile(filename) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet); + +protected: + std::string m_dataFile; //!< SB file path. +}; + +/*! + * @brief Represents the bootloader Execute command. + */ +class Execute : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + Execute(const string_vector_t *argv) + : Command(argv) + , m_jumpAddress(0) + , m_wordArgument(0) + , m_stackpointer(0) + { + } + + //! @brief Constructor that takes entry_point, param and stack_pointer arguments. + Execute(uint32_t entry_point, uint32_t param, uint32_t stack_pointer) + : Command(kCommand_Execute.name) + , m_jumpAddress(entry_point) + , m_wordArgument(param) + , m_stackpointer(stack_pointer) + { + m_argv.push_back(format_string("0x%08x", entry_point)); + m_argv.push_back(format_string("0x%08x", param)); + m_argv.push_back(format_string("0x%08x", stack_pointer)); + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_Execute); + } + +protected: + uint32_t m_jumpAddress; //!< Destination memory address to jump to. + uint32_t m_wordArgument; //!< Word argument passed to function. + uint32_t m_stackpointer; //!< Stack pointer. +}; + +/*! + * @brief Represents the bootloader Call command. + */ +class Call : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + Call(const string_vector_t *argv) + : Command(argv) + , m_callAddress(0) + , m_wordArgument(0) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), kCommandTag_Call); + } + +protected: + uint32_t m_callAddress; //!< Destination memory address to call. + uint32_t m_wordArgument; //!< Word argument passed to function. +}; + +/*! + * @brief Represents the bootloader Flash Security Disable command. + */ +class FlashSecurityDisable : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FlashSecurityDisable(const string_vector_t *argv) + : Command(argv) + , m_keyHigh(0) + , m_keyLow(0) + { + } + + //! @brief Constructor that takes the key. + FlashSecurityDisable(uint32_t keyHigh, uint32_t keyLow) + : Command(kCommand_FlashSecurityDisable.name) + , m_keyHigh(keyHigh) + , m_keyLow(keyLow) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_FlashSecurityDisable); + } + +protected: + uint32_t m_keyLow; //!< Bytes 0-3 of the flash backdoor key. + uint32_t m_keyHigh; //!< Bytes 4-7 of the flash backdoor key. +}; + +/*! + * @brief Represents the bootloader Flash Program Once command. + */ +class FlashProgramOnce : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FlashProgramOnce(const string_vector_t *argv) + : Command(argv) + , m_index(0) + , m_byteCount(0) + , m_dataLow(0) + , m_dataHigh(0) + , m_lsb(true) + { + } + + //! @brief Constructor that takes arguments. + FlashProgramOnce(uint32_t index, uint32_t byteCount, uint32_t dataLow, uint32_t dataHigh, bool isLsb = true) + : Command(kCommand_FlashProgramOnce.name) + , m_index(index) + , m_byteCount(byteCount) + , m_dataLow(dataLow) + , m_dataHigh(dataHigh) + , m_lsb(isLsb) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_FlashProgramOnce); + } + +protected: + uint32_t m_index; //!< Index of program once field to be programed. + uint32_t m_byteCount; //!< Number of bytes to be programmed. + uint32_t m_dataLow; //!< Bytes 0-3 of the data to be written. + uint32_t m_dataHigh; //!< Bytes 4-7 of the data to be written. + bool m_lsb; //!< Sending sequence of the data. +}; + +/*! + * @brief Represents the bootloader Flash Read Once command. + */ +class FlashReadOnce : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FlashReadOnce(const string_vector_t *argv) + : Command(argv) + , m_index(0) + , m_byteCount(0) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const flash_read_once_response_packet_t *packet); + + //! @brief Check generic response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_FlashReadOnce); + } + +protected: + uint32_t m_index; //!< Index of Program Once Field. + uint32_t m_byteCount; //!< Number of bytes to be read. +}; + +/*! + * @brief Represents the bootloader Read Resource command. + */ +class FlashReadResource : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FlashReadResource(const string_vector_t *argv) + : Command(argv) + , m_dataFile() + , m_startAddress(0) + , m_byteCount(0) + , m_option(0) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const flash_read_resource_response_packet_t *packet); + + //! @brief Check generic response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_FlashReadResource); + } + +protected: + std::string m_dataFile; //!< Data file path. + uint32_t m_startAddress; //!< Destination memory address. + uint32_t m_byteCount; //!< Number of bytes to read. + uint32_t m_option; //!< Resrouce to be read +}; + +/*! + * @brief Represents the bootloader Configure QuadSpi command. + */ +class ConfigureMemory : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + ConfigureMemory(const string_vector_t *argv) + : Command(argv) + , m_memoryId(kMemoryInternal) + , m_configBlockAddress(0) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check generic response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_ConfigureMemory); + } + +protected: + uint32_t m_memoryId; //!< Memory ID. + uint32_t m_configBlockAddress; //!< Config block address (in RAM or in internal Flash). +}; + +/*! + * @brief Represents the bootloader Reliable Update command. + */ +class ReliableUpdate : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + ReliableUpdate(const string_vector_t *argv) + : Command(argv) + , m_address(0) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_ReliableUpdate); + } + +protected: + uint32_t m_address; //!< Destination address. +}; + +/*! + * @brief Represents the bootloader Key Provisioning command. + */ +class KeyProvisioning : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + KeyProvisioning(const string_vector_t *argv) + : Command(argv) + , m_operation(0) + , m_type(0) + , m_size(0) + , m_memoryId(0) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + + void sendCmdAndData(Packetizer &device); + + void sendCmdAndGetData(Packetizer &device); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const key_provisioning_response_packet_t *packet); + + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_KeyProvisioning); + } + +protected: + uint32_t m_operation; //!< Key Provisioning Operation. + uint32_t m_type; + uint32_t m_memoryId; + uint32_t m_size; + string m_fileOrData; +}; + +/*! + * @brief Represents the bootloader TrustProvisioning command. + */ +class TrustProvisioning : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + TrustProvisioning(const string_vector_t *argv) + : Command(argv) + , m_operation(0) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check response packet. + virtual bool processResponse(const trust_provisioning_response_packet_t *packet); + + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_TrustProvisioning); + } + +protected: + uint32_t m_operation; //!< Trust Provisioning Operation. + trust_provisioning_parms_t m_parms; //!< Parameters of Trust Provsioing Operation. +}; + +/*! + * @brief Represents the Flash Image command used for downloading formatted file. + */ +class FlashImage : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + FlashImage(const string_vector_t *argv) + : Command(argv) + , m_fileName() + , m_sourceFile(NULL) + , m_doEraseOpt(false) + , m_memoryId(kMemoryInternal) + { + } + + //! @brief Constructor that takes a SourceFile argument + FlashImage(SourceFile *sourceFile, bool doEraseOpt, uint32_t memoryId) + : Command(kCommand_FlashImage.name) + , m_fileName() + , m_sourceFile(sourceFile) + , m_doEraseOpt(doEraseOpt) + , m_memoryId(memoryId) + { + m_argv.push_back(m_sourceFile->getPath()); + m_argv.push_back(doEraseOpt ? "erase" : "none"); + m_argv.push_back(format_string("%d", memoryId)); + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + std::string m_fileName; //!< Image file name with full path. + SourceFile *m_sourceFile; //!< Sourcefile object containing the data and addresses. + bool m_doEraseOpt; //!< Detemine if doing erase operation before writting image file to flash. + uint32_t m_memoryId; //!< Memory device ID. +}; + +/*! + * @brief Represents the bootloader Configure I2C command. + */ +class ConfigureI2c : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + ConfigureI2c(const string_vector_t *argv) + : Command(argv) + , i2cAddress(0x10) + , i2cSpeedKHz(100) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check generic response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_ConfigureI2c); + } + +protected: + uint8_t i2cAddress; + uint32_t i2cSpeedKHz; +}; + +/*! + * @brief Represents the bootloader Configure SPI command. + */ +class ConfigureSpi : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + ConfigureSpi(const string_vector_t *argv) + : Command(argv) + , spiSpeedKHz(100) + , spiPolarity(BusPal::kSpiClockPolarity_ActiveLow) + , spiPhase(BusPal::kSpiClockPhase_SecondEdge) + , spiDirection(BusPal::kSpiMsbFirst) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check generic response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_ConfigureSpi); + } + +protected: + uint32_t spiSpeedKHz; + BusPal::spi_clock_polarity_t spiPolarity; + BusPal::spi_clock_phase_t spiPhase; + BusPal::spi_shift_direction_t spiDirection; +}; + +/*! + * @brief Represents the bootloader Configure CAN command. + */ +class ConfigureCan : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + ConfigureCan(const string_vector_t *argv) + : Command(argv) + , canSpeed(4) + , canTxid(0x321) + , canRxid(0x123) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + //! @brief Check generic response packet. + virtual bool processResponse(const uint8_t *packet) + { + return Command::processResponse(reinterpret_cast(packet), + kCommandTag_ConfigureCan); + } + +protected: + uint32_t canSpeed; + uint32_t canTxid; + uint32_t canRxid; +}; + +/*! + * @brief Represents the ListMemory command used to list all memories available on current target device + */ +class ListMemory : public Command +{ +public: + //! @brief Constructor that takes an argument vector. + ListMemory(const string_vector_t *argv) + : Command(argv) + { + } + + //! @brief Constructor that takes a SourceFile argument + ListMemory(void) + : Command(kCommand_FlashImage.name) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + // No processResponse here. + // Command sequence doesn't need to process response. +}; + +//@} + +} // namespace blfwk + +//! @} + +#endif // _Command_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/Crc.h b/src/blfwk/Crc.h new file mode 100644 index 0000000..28ed480 --- /dev/null +++ b/src/blfwk/Crc.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#if !defined(_Crc_h_) +#define _Crc_h_ + +#include + +namespace blfwk +{ +/*! +* \brief Crc class contains the functionality about crc ccalculation. +*/ +class Crc +{ +public: + //! \brief Default constructor. + Crc(){}; + + //! \brief Destructor. + virtual ~Crc(){}; + + //! \brief calculate crc32 for a given memory region + //! + //! \param start The start address of a memory region that contains the data + //! \ +//! \param length Length in bytes to calculate + //! \ +//! \return calculated result + static uint32_t calculate_application_crc32(const uint8_t *start, uint32_t length); + //@} + // +}; + +} // namespace blfwk +#endif // _Crc_h_ diff --git a/src/blfwk/DataSource.h b/src/blfwk/DataSource.h new file mode 100644 index 0000000..7cf4bd7 --- /dev/null +++ b/src/blfwk/DataSource.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_DataSource_h_) +#define _DataSource_h_ + +#include +#include "Value.h" +#include "smart_ptr.h" +#include "StExecutableImage.h" + +namespace blfwk +{ +// Forward declaration +class DataTarget; + +/*! + * \brief Abstract base class for data sources. + * + * Data sources represent any sort of data that can be placed or loaded + * into a target region. Sources may be a single blob of data read from + * a file or may consist of many segments. + * + * The three most important features of data sources are: + * - Sources may be multi-segmented. + * - Sources and/or segments can have a "natural" or default target location. + * - The target for a source may be taken into consideration when the source + * describes itself. + */ +class DataSource +{ +public: + /*! + * \brief Discrete, contiguous part of the source's data. + * + * This class is purely abstract and subclasses of DataSource are expected + * to subclass it to implement a segment particular to their needs. + */ + class Segment + { + public: + //! \brief Default constructor. + Segment(DataSource &source) + : m_source(source) + { + } + + //! \brief Destructor. + virtual ~Segment() {} + //! \brief Gets all or a portion of the segment's data. + //! + //! The data is copied into \a buffer. Up to \a maxBytes bytes may be + //! copied, so \a buffer must be at least that large. + //! + //! \param offset Index of the first byte to start copying from. + //! \param maxBytes The maximum number of bytes that can be returned. \a buffer + //! must be at least this large. + //! \param buffer Pointer to buffer where the data is copied. + //! \return The number of bytes copied into \a buffer. + virtual unsigned getData(unsigned offset, unsigned maxBytes, uint8_t *buffer) = 0; + + //! \brief Gets the length of the segment's data. + virtual unsigned getLength() = 0; + + //! \brief Returns whether the segment has an associated address. + virtual bool hasNaturalLocation() = 0; + + //! \brief Returns the address associated with the segment. + virtual uint32_t getBaseAddress() { return 0; } + protected: + DataSource &m_source; //!< The data source to which this segment belongs. + }; + + /*! + * \brief This is a special type of segment containing a repeating pattern. + * + * By default the segment doesn't have a specific length or data. The length depends + * on the target's address range. And the data is just the pattern, repeated + * many times. In addition, pattern segments do not have a natural location. + * + * Calling code should look for instances of PatternSegment and handle them + * as special cases that can be optimized. + */ + class PatternSegment : public Segment + { + public: + //! \brief Default constructor. + PatternSegment(DataSource &source); + + //! \brief Constructor taking a fill pattern. + PatternSegment(DataSource &source, const SizedIntegerValue &pattern); + + //! \brief Constructor taking a byte fill pattern. + PatternSegment(DataSource &source, uint8_t pattern); + + //! \brief Constructor taking a half-word fill pattern. + PatternSegment(DataSource &source, uint16_t pattern); + + //! \brief Constructor taking a word fill pattern. + PatternSegment(DataSource &source, uint32_t pattern); + + //! \name Segment methods + //@{ + //! \brief Pattern segments have no natural address. + virtual bool hasNaturalLocation() { return false; } + //! \brief Performs a pattern fill into the \a buffer. + virtual unsigned getData(unsigned offset, unsigned maxBytes, uint8_t *buffer); + + //! \brief Returns a length based on the data target's address range. + virtual unsigned getLength(); + //@} + + //! \name Pattern accessors + //@{ + //! \brief Assigns a new fill pattern. + inline void setPattern(const SizedIntegerValue &newPattern) { m_pattern = newPattern; } + //! \brief Return the fill pattern for the segment. + inline SizedIntegerValue &getPattern() { return m_pattern; } + //! \brief Assignment operator, sets the pattern value and length. + PatternSegment &operator=(const SizedIntegerValue &value) + { + m_pattern = value; + return *this; + } + //@} + + protected: + SizedIntegerValue m_pattern; //!< The fill pattern. + }; + +public: + //! \brief Default constructor. + DataSource() + : m_target(0) + { + } + + //! \brief Destructor. + virtual ~DataSource() {} + //! \name Data target + //@{ + //! \brief Sets the associated data target. + inline void setTarget(DataTarget *target) { m_target = target; } + //! \brief Gets the associated data target. + inline DataTarget *getTarget() const { return m_target; } + //@} + + //! \name Segments + //@{ + //! \brief Returns the number of segments in this data source. + virtual unsigned getSegmentCount() = 0; + + //! \brief Returns segment number \a index of the data source. + virtual Segment *getSegmentAt(unsigned index) = 0; + //@} + +protected: + DataTarget *m_target; //!< Corresponding target for this source. +}; + +/*! + * \brief Data source for a repeating pattern. + * + * The pattern is represented by a SizedIntegerValue object. Thus the pattern + * can be either byte, half-word, or word sized. + * + * This data source has only one segment, and the PatternSource instance acts + * as its own single segment. + */ +class PatternSource : public DataSource, public DataSource::PatternSegment +{ +public: + //! \brief Default constructor. + PatternSource(); + + //! \brief Constructor taking the pattern value. + PatternSource(const SizedIntegerValue &value); + + //! \brief There is only one segment. + virtual unsigned getSegmentCount() { return 1; } + //! \brief Returns this object, as it is its own segment. + virtual DataSource::Segment *getSegmentAt(unsigned index) { return this; } + //! \brief Assignment operator, sets the pattern value and length. + PatternSource &operator=(const SizedIntegerValue &value) + { + setPattern(value); + return *this; + } +}; + +/*! + * \brief Data source for data that is not memory mapped (has no natural address). + * + * This data source can only manage a single block of data, which has no + * associated address. It acts as its own Segment. + */ +class UnmappedDataSource : public DataSource, public DataSource::Segment +{ +public: + //! \brief Default constructor. + UnmappedDataSource(); + + //! \brief Constructor taking the data, which is copied. + UnmappedDataSource(const uint8_t *data, unsigned length); + + //! \brief Sets the source's data. + void setData(const uint8_t *data, unsigned length); + + //! \brief There is only one segment. + virtual unsigned getSegmentCount() { return 1; } + //! \brief Returns this object, as it is its own segment. + virtual DataSource::Segment *getSegmentAt(unsigned index) { return this; } + //! \name Segment methods + //@{ + //! \brief Unmapped data sources have no natural address. + virtual bool hasNaturalLocation() { return false; } + //! \brief Copies a portion of the data into \a buffer. + virtual unsigned getData(unsigned offset, unsigned maxBytes, uint8_t *buffer); + + //! \brief Returns the number of bytes of data managed by the source. + virtual unsigned getLength() { return m_length; } + //! \brief Returns the address associated with the segment. + virtual uint32_t getBaseAddress(); + + //@} + +protected: + smart_array_ptr m_data; //!< The data. + unsigned m_length; //!< Byte count of the data. +}; + +/*! + * \brief Data source that takes its data from an executable image. + * + * \see StExecutableImage + */ +class MemoryImageDataSource : public DataSource +{ +public: + //! \brief Default constructor. + MemoryImageDataSource(StExecutableImage *image); + + //! \brief Destructor. + virtual ~MemoryImageDataSource(); + + //! \brief Returns the number of memory regions in the image. + virtual unsigned getSegmentCount(); + + //! \brief Returns the data source segment at position \a index. + virtual DataSource::Segment *getSegmentAt(unsigned index); + +protected: + /*! + * \brief Segment corresponding to a text region of the executable image. + */ + class TextSegment : public DataSource::Segment + { + public: + //! \brief Default constructor + TextSegment(MemoryImageDataSource &source, StExecutableImage *image, unsigned index); + + virtual unsigned getData(unsigned offset, unsigned maxBytes, uint8_t *buffer); + virtual unsigned getLength(); + + virtual bool hasNaturalLocation() { return true; } + virtual uint32_t getBaseAddress(); + + protected: + StExecutableImage *m_image; //!< Coalesced image of the file. + unsigned m_index; //!< Record index. + }; + + /*! + * \brief Segment corresponding to a fill region of the executable image. + */ + class FillSegment : public DataSource::PatternSegment + { + public: + FillSegment(MemoryImageDataSource &source, StExecutableImage *image, unsigned index); + + virtual unsigned getLength(); + + virtual bool hasNaturalLocation() { return true; } + virtual uint32_t getBaseAddress(); + + protected: + StExecutableImage *m_image; //!< Coalesced image of the file. + unsigned m_index; //!< Record index. + }; + +protected: + StExecutableImage *m_image; //!< The memory image that is the data source. + + typedef std::vector segment_array_t; //!< An array of segments. + segment_array_t m_segments; //!< The array of Segment instances. +}; + +}; // namespace blfwk + +#endif // _DataSource_h_ diff --git a/src/blfwk/DataSourceImager.h b/src/blfwk/DataSourceImager.h new file mode 100644 index 0000000..5fc5ff7 --- /dev/null +++ b/src/blfwk/DataSourceImager.h @@ -0,0 +1,55 @@ +/* + * File: DataSourceImager.h + * + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_DataSourceImager_h_) +#define _DataSourceImager_h_ + +#include "Blob.h" +#include "DataSource.h" + +namespace blfwk +{ +/*! + * \brief Converts a DataSource into a single binary buffer. + */ +class DataSourceImager : public Blob +{ +public: + //! \brief Constructor. + DataSourceImager(); + + //! \name Setup + //@{ + void setBaseAddress(uint32_t address); + void setFillPattern(uint8_t pattern); + //@} + + void reset(); + + //! \name Accessors + //@{ + uint32_t getBaseAddress() { return m_baseAddress; } + //@} + + //! \name Operations + //@{ + //! \brief Adds all of the segments of which \a dataSource is composed. + void addDataSource(DataSource *source); + + //! \brief Adds the data from one data segment. + void addDataSegment(DataSource::Segment *segment); + //@} + +protected: + uint8_t m_fill; + uint32_t m_baseAddress; + bool m_isBaseAddressSet; +}; +}; + +#endif // _DataSourceImager_h_ diff --git a/src/blfwk/DataTarget.h b/src/blfwk/DataTarget.h new file mode 100644 index 0000000..e5c8290 --- /dev/null +++ b/src/blfwk/DataTarget.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_DataTarget_h_) +#define _DataTarget_h_ + +#include "stdafx.h" +#include "DataSource.h" + +namespace blfwk +{ +// Forward declaration +class DataSource; + +/*! + * \brief Abstract base class for the target address or range of data. + * + * Targets at the most basic level have a single address, and potentially + * an address range. Unbounded targets have a beginning address but no + * specific end address, while bounded targets do have an end address. + * + * Users of a data target can access the begin and end addresses directly. + * However, the most powerful way to use a target is with the + * getRangeForSegment() method. This method returns the target address range + * for a segment of a data source. The value of the resulting range can be + * completely dependent upon the segment's properties, those of its data + * source, and the type of data target. + * + * \see blfwk::DataSource + */ +class DataTarget +{ +public: + //! \brief Simple structure that describes an addressed region of memory. + //! \todo Decide if the end address is inclusive or not. + struct AddressRange + { + uint32_t m_begin; + uint32_t m_end; + }; + +public: + //! \brief Default constructor. + DataTarget() + : m_source(0) + { + } + + //! \brief Destructor. + virtual ~DataTarget() {} + //! \brief Whether the target is just a single address or has an end to it. + virtual bool isBounded() { return false; } + virtual uint32_t getBeginAddress() { return 0; } + virtual uint32_t getEndAddress() { return 0; } + //! \brief Return the address range for a segment of a data source. + virtual DataTarget::AddressRange getRangeForSegment(DataSource &source, DataSource::Segment &segment) = 0; + + inline void setSource(DataSource *source) { m_source = source; } + inline DataSource *getSource() const { return m_source; } +protected: + DataSource *m_source; //!< Corresponding data source for this target. +}; + +/*! + * \brief Target with a constant values for the addresses. + * + * This target type supports can be both bounded and unbounded. It always has + * at least one address, the beginning address. The end address is optional, + * and if not provided makes the target unbounded. + */ +class ConstantDataTarget : public DataTarget +{ +public: + //! \brief Constructor taking only a begin address. + ConstantDataTarget(uint32_t start) + : DataTarget() + , m_begin(start) + , m_end(0) + , m_hasEnd(false) + { + } + + //! \brief Constructor taking both begin and end addresses. + ConstantDataTarget(uint32_t start, uint32_t end) + : DataTarget() + , m_begin(start) + , m_end(end) + , m_hasEnd(true) + { + } + + //! \brief The target is bounded if an end address was specified. + virtual bool isBounded() { return m_hasEnd; } + virtual uint32_t getBeginAddress() { return m_begin; } + virtual uint32_t getEndAddress() { return m_end; } + //! \brief Return the address range for a segment of a data source. + virtual DataTarget::AddressRange getRangeForSegment(DataSource &source, DataSource::Segment &segment); + +protected: + uint32_t m_begin; //!< Start address. + uint32_t m_end; //!< End address. + bool m_hasEnd; //!< Was an end address specified? +}; + +/*! + * \brief Target address that is the "natural" location of whatever the source data is. + * + * The data source used with the target must have a natural location. If + * getRangeForSegment() is called with a segment that does not have a natural + * location, a semantic_error will be thrown. + */ +class NaturalDataTarget : public DataTarget +{ +public: + //! \brief Default constructor. + NaturalDataTarget() + : DataTarget() + { + } + + //! \brief Natural data targets are bounded by their source's segment lengths. + virtual bool isBounded() { return true; } + //! \brief Return the address range for a segment of a data source. + virtual DataTarget::AddressRange getRangeForSegment(DataSource &source, DataSource::Segment &segment); +}; + +}; // namespace blfwk + +#endif // _DataTarget_h_ diff --git a/src/blfwk/ELF.h b/src/blfwk/ELF.h new file mode 100644 index 0000000..8d38c38 --- /dev/null +++ b/src/blfwk/ELF.h @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_ELF_h_) +#define _ELF_h_ + +//! \name ELF types +//! Types used in ELF file structures. +//@{ +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef /*off_t*/ uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; +//@} + +// All ELF structures are byte aligned. Any alignment padding is explicit. +#pragma pack(1) + +//! \name File header +//@{ + +/*! + * Constants for the various fields of Elf32_Ehdr.e_ident. + */ +enum +{ + EI_MAG0 = 0, + EI_MAG1, + EI_MAG2, + EI_MAG3, + EI_CLASS, + EI_DATA, + EI_VERSION, + EI_PAD, + EI_NIDENT = 16, + + // Magic number. + ELFMAG0 = 0x7f, + ELFMAG1 = 'E', + ELFMAG2 = 'L', + ELFMAG3 = 'F', + + // EI_CLASS + ELFCLASSNONE = 0, + ELFCLASS32 = 1, + ELFCLASS64 = 2, + + // EI_DATA + ELFDATANONE = 0, + ELFDATA2LSB = 1, + ELFDATA2MSB = 2 +}; + +/*! + * \brief ELF file header. + */ +struct Elf32_Ehdr +{ + unsigned char e_ident[EI_NIDENT]; //!< Magic number identifying the file format. + Elf32_Half e_type; //!< Identifies the object file format. + Elf32_Half e_machine; //!< Specified the architecture for the object file. + Elf32_Word e_version; //!< Object file version. + Elf32_Addr e_entry; //!< Virtual address of the entry point, or 0. + Elf32_Off e_phoff; //!< Program header table offset in bytes, or 0 if no program header table. + Elf32_Off e_shoff; //!< Section header table offset in bytes, or 0 if no section header table. + Elf32_Word e_flags; //!< Processor-specific flags associated with the file. + Elf32_Half e_ehsize; //!< The ELF header's size in bytes. + Elf32_Half e_phentsize; //!< Size in bytes of one entry in the program header table. + Elf32_Half e_phnum; //!< Number of entries in the program header table. + Elf32_Half e_shentsize; //!< Size in bytes of an entry in the section header table. + Elf32_Half e_shnum; //!< Number of entries in the section header table. + Elf32_Half e_shstrndx; //!< Section header table index of the section name string table. +}; + +/*! + * Constants for #Elf32_Ehdr.e_type. + */ +enum +{ + ET_NONE, //!< No file type. + ET_REL, //!< Relocatable file. + ET_EXEC, //!< Executable file. + ET_DYN, //!< Shared object file. + ET_CORE, //!< Core file. + ET_LOPROC, //!< Low bound of processor-specific file types. + ET_HIPROC //!< High bound of processor-specific file types. +}; + +/*! + * ARM-specific #Elf32_Ehdr.e_flags + */ +enum +{ + EF_ARM_HASENTRY = 0x02, //!< #Elf32_Ehdr.e_entry contains a program-loader entry point. + EF_ARM_SYMSARESORTED = 0x04, //!< Each subsection of the symbol table is sorted by symbol value. + EF_ARM_DYNSYMSUSESEGIDX = 0x08, //!< Symbols in dynamic symbol tables that are defined in sections included in + //!program segment n have #Elf32_Sym.st_shndx = n + 1. + EF_ARM_MAPSYMSFIRST = 0x10, //!< Mapping symbols precede other local symbols in the symbol table. + EF_ARM_EABIMASK = 0xff000000, //!< This masks an 8-bit version number, the version of the ARM EABI to which this ELF + //!file conforms. The current EABI version is #ARM_EABI_VERSION. + + ARM_EABI_VERSION = 0x02000000 //!< Current ARM EABI version. +}; + +//@} + +//! \name Sections +//@{ + +/*! + * \brief ELF section header. + * + * An object file's section header table lets one locate all the file's + * sections. The section header table is an array of #Elf32_Shdr structures. + * A section header table index is a subscript into this array. The ELF + * header's #Elf32_Ehdr::e_shoff member gives the byte offset from the beginning of + * the file to the section header table; #Elf32_Ehdr::e_shnum tells how many entries + * the section header table contains; #Elf32_Ehdr::e_shentsize gives the size in bytes + * of each entry. + * + * Some section header table indexes are reserved. An object file will not + * have sections for these special indexes: + * - #SHN_UNDEF + * - #SHN_LORESERVE + * - #SHN_LOPROC + * - #SHN_HIPROC + * - #SHN_ABS + * - #SHN_COMMON + * - #SHN_HIRESERVE + */ +struct Elf32_Shdr +{ + Elf32_Word sh_name; //!< The section's name. Index into the section header string table section. + Elf32_Word sh_type; //!< Section type, describing the contents and semantics. + Elf32_Word sh_flags; //!< Section flags describing various attributes. + Elf32_Addr sh_addr; //!< The address at which the section will appear in the memory image, or 0. + Elf32_Off sh_offset; //!< Offset from beginning of the file to the first byte in the section. + Elf32_Word sh_size; //!< The section's size in bytes. + Elf32_Word sh_link; //!< Section header table link index. Interpretation depends on section type. + Elf32_Word sh_info; //!< Extra information about the section. Depends on section type. + Elf32_Word sh_addralign; //!< Address alignment constraint. Values are 0 and positive powers of 2. + Elf32_Word sh_entsize; //!< Size in bytes of section entries, or 0 if the section does not have fixed-size entries. +}; + +/*! + * Special section indexes. + */ +enum +{ + SHN_UNDEF = 0, + SHN_LORESERVE = 0xff00, + SHN_LOPROC = 0xff00, + SHN_HIPROC = 0xff1f, + SHN_ABS = 0xfff1, //!< The symbol has an absolute value that will not change because of relocation. + SHN_COMMON = 0xfff2, //!< The symbol labels a common block that has not yet been allocated. + SHN_HIRESERVE = 0xffff +}; + +/*! + * Section type constants. + */ +enum +{ + SHT_NULL = 0, + SHT_PROGBITS = 1, + SHT_SYMTAB = 2, + SHT_STRTAB = 3, + SHT_RELA = 4, + SHT_HASH = 5, + SHT_DYNAMIC = 6, + SHT_NOTE = 7, + SHT_NOBITS = 8, + SHT_REL = 9, + SHT_SHLIB = 10, + SHT_DYNSYM = 11 +}; + +/*! + * Section flag constants. + */ +enum +{ + SHF_WRITE = 0x1, //!< Section is writable. + SHF_ALLOC = 0x2, //!< Allocate section. + SHF_EXECINSTR = 0x4 //!< Section contains executable instructions. +}; + +/*! + * ARM-specific section flag constants + */ +enum +{ + SHF_ENTRYSECT = 0x10000000, //!< The section contains an entry point. + SHF_COMDEF = 0x80000000 //!< The section may be multiply defined in the input to a link step. +}; + +#define BSS_SECTION_NAME ".bss" +#define DATA_SECTION_NAME ".data" +#define TEXT_SECTION_NAME ".text" +#define SHSTRTAB_SECTION_NAME ".shstrtab" +#define STRTAB_SECTION_NAME ".strtab" +#define SYMTAB_SECTION_NAME ".symtab" + +//@} + +//! \name Segments +//@{ + +/*! + * \brief ELF program header. + * + * An executable or shared object file's program header table is an array of + * structures, each describing a segment or other information the system needs + * to prepare the program for execution. An object file segment contains one + * or more sections. Program headers are meaningful only for executable and + * shared object files. A file specifies its own program header size with the + * ELF header's #Elf32_Ehdr::e_phentsize and #Elf32_Ehdr::e_phnum members. + */ +struct Elf32_Phdr +{ + Elf32_Word p_type; //!< What type of segment this header describes. + Elf32_Off p_offset; //!< Offset in bytes from start of file to the first byte of the segment. + Elf32_Addr p_vaddr; //!< Virtual address at which the segment will reside in memory. + Elf32_Addr p_paddr; //!< Physical address, for systems where this is relevant. + Elf32_Word p_filesz; //!< Number of bytes of file data the segment consumes. May be zero. + Elf32_Word p_memsz; //!< Size in bytes of the segment in memory. May be zero. + Elf32_Word p_flags; //!< Flags relevant to the segment. + Elf32_Word p_align; //!< Alignment constraint for segment addresses. Possible values are 0 and positive powers of 2. +}; + +/*! + * Segment type constants. + */ +enum +{ + PT_NULL = 0, + PT_LOAD = 1, + PT_DYNAMIC = 2, + PT_INTERP = 3, + PT_NOTE = 4, + PT_SHLIB = 5, + PT_PHDR = 6 +}; + +/*! + * Program header flag constants. + */ +enum +{ + PF_X = 0x1, //!< Segment is executable. + PF_W = 0x2, //!< Segment is writable. + PF_R = 0x4 //!< Segment is readable. +}; + +//@} + +//! \name Symbol table +//@{ + +enum +{ + STN_UNDEF = 0 //!< Undefined symbol index. +}; + +/*! + * \brief ELF symbol table entry. + * + * An object file's symbol table holds information needed to locate and + * relocate a program's symbolic definitions and references. A symbol + * table index is a subscript into this array. Index 0 both designates + * the first entry in the table and serves as the undefined symbol index. + */ +struct Elf32_Sym +{ + Elf32_Word st_name; //!< Index into file's string table. + Elf32_Addr st_value; //!< Value associated with the symbol. Depends on context. + Elf32_Word st_size; //!< Size associated with symbol. 0 if the symbol has no size or an unknown size. + unsigned char st_info; //!< Specified the symbol's type and binding attributes. + unsigned char st_other; //!< Currently 0 (reserved). + Elf32_Half st_shndx; //!< Section header table index for this symbol. +}; + +//! \name st_info macros +//! Macros for manipulating the st_info field of Elf32_Sym struct. +//@{ +#define ELF32_ST_BIND(i) ((i) >> 4) //!< Get binding attributes. +#define ELF32_ST_TYPE(i) ((i)&0x0f) //!< Get symbol type. +#define ELF32_ST_INFO(b, t) \ + (((b) << 4) + ((t)&0x0f)) //!< Construct st_info value from binding attributes and symbol type. + //@} + +/*! + * \brief Symbol binding attributes. + * + * These constants are mask values. + */ +enum +{ + STB_LOCAL = 0, //!< Local symbol not visible outside the object file. + STB_GLOBAL = 1, //!< Symbol is visible to all object files being linked together. + STB_WEAK = 2, //!< Like global symbols, but with lower precedence. + + // Processor-specific semantics. + STB_LOPROC = 13, + STB_HIPROC = 15 +}; + +/*! + * \brief Symbol types. + */ +enum +{ + STT_NOTYPE = 0, //!< The symbol's type is not specified. + STT_OBJECT = 1, //!< The symbol is associated with a data object, such as a variable or array. + STT_FUNC = 2, //!< The symbol is associated with a function or other executable code. + STT_SECTION = 3, //!< The synmbol is associated with a section. Primarily used for relocation. + STT_FILE = 4, //!< A file symbol has STB_LOCAL binding, its section index is SHN_ABS, and it precedes the other + //!STB_LOCAL symbols for the file, if it is present. + + STT_LOPROC = 13, //!< Low bound of processor-specific symbol types. + STT_HIPROC = 15 //!< High bound of processor-specific symbol types. +}; + +/*! + * GHS-specific constants + */ +enum +{ + STO_THUMB = 1 //!< This flag is set on #Elf32_Sym.st_other if the symbol is Thumb mode code. +}; + +#define ARM_SEQUENCE_MAPSYM "$a" +#define DATA_SEQUENCE_MAPSYM "$d" +#define THUMB_SEQUENCE_MAPSYM "$t" + +#define THUMB_BL_TAGSYM "$b" +#define FN_PTR_CONST_TAGSYM "$f" +#define INDIRECT_FN_CALL_TAGSYM "$p" +#define MAPPING_SYMBOL_COUNT_TAGSYM "$m" + +//@} + +#pragma pack() + +#endif // _ELF_h_ diff --git a/src/blfwk/ELFSourceFile.h b/src/blfwk/ELFSourceFile.h new file mode 100644 index 0000000..762ee30 --- /dev/null +++ b/src/blfwk/ELFSourceFile.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_ELFSourceFile_h_) +#define _ELFSourceFile_h_ + +#include "SourceFile.h" +#include "StELFFile.h" +#include "smart_ptr.h" +#include "DataSource.h" +#include "DataTarget.h" +#include "ELF.h" + +namespace blfwk +{ +//! Set of supported compiler toolsets. +enum elf_toolset_t +{ + kUnknownToolset, //!< Unknown. + kGHSToolset, //!< Green Hills Software MULTI + kGCCToolset, //!< GNU GCC + kADSToolset //!< ARM UK RealView +}; + +//! Options for handling the .secinfo section in GHS-produced ELF files. +enum secinfo_clear_t +{ + // Default value for the .secinfo action. + kSecinfoDefault, + + //! Ignore the .secinfo section if present. The standard ELF loading + //! rules are followed. + kSecinfoIgnore, + + //! The boot ROM clears only those SHT_NOBITS sections present in .secinfo. + kSecinfoROMClear, + + //! The C startup is responsible for clearing sections. No fill commands + //! are generated for any SHT_NOBITS sections. + kSecinfoCStartupClear +}; + +/*! + * \brief Executable and Loading Format (ELF) source file. + */ +class ELFSourceFile : public SourceFile +{ +public: + //! \brief Default constructor. + ELFSourceFile(const std::string &path); + + //! \brief Destructor. + virtual ~ELFSourceFile(); + + //! \brief Identifies whether the stream contains an ELF file. + static bool isELFFile(std::istream &stream); + + //! \name Opening and closing + //@{ + //! \brief Opens the file. + virtual void open(); + + //! \brief Closes the file. + virtual void close(); + //@} + + //! \name Format capabilities + //@{ + virtual bool supportsNamedSections() const { return true; } + virtual bool supportsNamedSymbols() const { return true; } + //@} + + //! \name Data source + //@{ + //! \brief Creates a data source from the entire file. + virtual DataSource *createDataSource(); + + //! \brief Creates a data source from one or more sections of the file. + virtual DataSource *createDataSource(StringMatcher &matcher); + //@} + + //! \brief Creates a data source from sections of the file that either match or do not match the + // baseAddress + virtual DataSource *createDataSource(const std::vector &baseAddress, bool match); + //@} + + //! \name Entry point + //@{ + //! \brief Returns true if an entry point was set in the file. + virtual bool hasEntryPoint(); + + //! \brief Returns the entry point address. + virtual uint32_t getEntryPointAddress(); + //@} + + //! \name Data target + //@{ + virtual DataTarget *createDataTargetForSection(const std::string §ion); + virtual DataTarget *createDataTargetForSymbol(const std::string &symbol); + //@} + + //! \name Symbols + //@{ + //! \brief Returns whether a symbol exists in the source file. + virtual bool hasSymbol(const std::string &name); + + //! \brief Returns the value of a symbol. + virtual uint32_t getSymbolValue(const std::string &name); + + //! \brief Returns the size of a symbol. + virtual unsigned getSymbolSize(const std::string &name); + //@} + + //! \name Direct ELF format access + //@{ + //! \brief Returns the underlying StELFFile object. + StELFFile *getELFFile() { return m_file; } + //! \brief Gets information about a symbol in the ELF file. + bool lookupSymbol(const std::string &name, Elf32_Sym &info); + //@} + +protected: + smart_ptr m_file; //!< Parser for the ELF file. + elf_toolset_t m_toolset; //!< Toolset that produced the ELF file. + secinfo_clear_t m_secinfoOption; //!< How to deal with the .secinfo section. Ignored if the toolset is not GHS. + +protected: + //! \brief Parses the toolset option value. + elf_toolset_t readToolsetOption(); + + //! \brief Reads the secinfoClear option. + secinfo_clear_t readSecinfoClearOption(); + +protected: + /*! + * \brief A data source with ELF file sections as the contents. + * + * Each segment of this data source corresponds directly with a named section + * of the ELF file it represents. When the data source is created, it contains + * no segments. Segments are created with the addSection() method, which takes + * the index of an ELF section and creates a corresponding segment. + * + * Two segment subclasses are used with this data source. The first, ProgBitsSegment, + * is used to represent sections whose type is #SHT_PROGBITS. These sections have + * binary data stored in the ELF file. The second segment type is NoBitsSegment. + * It is used to represent sections whose type is #SHT_NOBITS. These sections have + * no data, but simply allocate a region of memory to be filled with zeroes. + * As such, the NoBitsSegment class is a subclass of DataSource::PatternSegment. + */ + class ELFDataSource : public DataSource + { + public: + /*! + * \brief Represents one named #SHT_PROGBITS section within the ELF file. + */ + class ProgBitsSegment : public DataSource::Segment + { + public: + ProgBitsSegment(ELFDataSource &source, StELFFile *elf, unsigned index); + + virtual unsigned getData(unsigned offset, unsigned maxBytes, uint8_t *buffer); + virtual unsigned getLength(); + + virtual bool hasNaturalLocation() { return true; } + virtual uint32_t getBaseAddress(); + + protected: + StELFFile *m_elf; //!< The format parser instance for this ELF file. + unsigned m_sectionIndex; //!< The index of the section this segment represents. + }; + + /*! + * \brief Represents one named #SHT_NOBITS section within the ELF file. + * + * This segment class is a subclass of DataSource::PatternSegment since it + * represents a region of memory to be filled with zeroes. + */ + class NoBitsSegment : public DataSource::PatternSegment + { + public: + NoBitsSegment(ELFDataSource &source, StELFFile *elf, unsigned index); + + virtual unsigned getLength(); + + virtual bool hasNaturalLocation() { return true; } + virtual uint32_t getBaseAddress(); + + protected: + StELFFile *m_elf; //!< The format parser instance for this ELF file. + unsigned m_sectionIndex; //!< The index of the section this segment represents. + }; + + public: + //! \brief Default constructor. + ELFDataSource(StELFFile *elf) + : DataSource() + , m_elf(elf) + , m_secinfoOption(kSecinfoDefault) + { + } + + //! \brief Destructor. + virtual ~ELFDataSource(); + + //! Set the option to control .secinfo usage. + inline void setSecinfoOption(secinfo_clear_t option) { m_secinfoOption = option; } + //! \brief Adds the ELF section at position \a sectionIndex to the data source. + void addSection(unsigned sectionIndex); + + //! \brief Returns the number of segments in the source. + virtual unsigned getSegmentCount() { return (unsigned)m_segments.size(); } + //! \brief Returns the segment at position \a index. + virtual DataSource::Segment *getSegmentAt(unsigned index) { return m_segments[index]; } + protected: + StELFFile *m_elf; //!< The ELF file parser. + secinfo_clear_t m_secinfoOption; //!< How to deal with the .secinfo section. Ignored if the toolset is not GHS. + + typedef std::vector segment_vector_t; //!< A list of segment instances. + segment_vector_t m_segments; //!< The segments of this data source. + }; +}; + +}; // namespace blfwk + +#endif // _ELFSourceFile_h_ diff --git a/src/blfwk/EndianUtilities.h b/src/blfwk/EndianUtilities.h new file mode 100644 index 0000000..3b03b78 --- /dev/null +++ b/src/blfwk/EndianUtilities.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_EndianUtilities_h_) +#define _EndianUtilities_h_ + +//! \name Swap macros +//! These macros always swap the data. +//@{ + +//! Byte swap 16-bit value. +#define _BYTESWAP16(value) (((((uint16_t)value) << 8) & 0xFF00) | ((((uint16_t)value) >> 8) & 0x00FF)) + +//! Byte swap 32-bit value. +#define _BYTESWAP32(value) \ + (((((uint32_t)value) << 24) & 0xFF000000) | ((((uint32_t)value) << 8) & 0x00FF0000) | \ + ((((uint32_t)value) >> 8) & 0x0000FF00) | ((((uint32_t)value) >> 24) & 0x000000FF)) + +//! Byte swap 64-bit value. +#define _BYTESWAP64(value) \ + (((((uint64_t)value) << 56) & 0xFF00000000000000ULL) | ((((uint64_t)value) << 40) & 0x00FF000000000000ULL) | \ + ((((uint64_t)value) << 24) & 0x0000FF0000000000ULL) | ((((uint64_t)value) << 8) & 0x000000FF00000000ULL) | \ + ((((uint64_t)value) >> 8) & 0x00000000FF000000ULL) | ((((uint64_t)value) >> 24) & 0x0000000000FF0000ULL) | \ + ((((uint64_t)value) >> 40) & 0x000000000000FF00ULL) | ((((uint64_t)value) >> 56) & 0x00000000000000FFULL)) + +//@} + +//! \name Inline swap functions +//@{ + +inline uint16_t _swap_u16(uint16_t value) +{ + return _BYTESWAP16(value); +} +inline int16_t _swap_s16(int16_t value) +{ + return (int16_t)_BYTESWAP16((uint16_t)value); +} + +inline uint32_t _swap_u32(uint32_t value) +{ + return _BYTESWAP32(value); +} +inline int32_t _swap_s32(int32_t value) +{ + return (int32_t)_BYTESWAP32((uint32_t)value); +} + +inline uint64_t _swap_u64(uint64_t value) +{ + return _BYTESWAP64(value); +} +inline int64_t _swap_s64(int64_t value) +{ + return (uint64_t)_BYTESWAP64((uint64_t)value); +} + +//@} + +#if defined(__LITTLE_ENDIAN__) + +/* little endian host */ + +#define ENDIAN_BIG_TO_HOST_U16(value) (_swap_u16(value)) +#define ENDIAN_HOST_TO_BIG_U16(value) (_swap_u16(value)) + +#define ENDIAN_BIG_TO_HOST_S16(value) (_swap_s16(value)) +#define ENDIAN_HOST_TO_BIG_S16(value) (_swap_s16(value)) + +#define ENDIAN_BIG_TO_HOST_U32(value) (_swap_u32(value)) +#define ENDIAN_HOST_TO_BIG_U32(value) (_swap_u32(value)) + +#define ENDIAN_BIG_TO_HOST_S32(value) (_swap_s32(value)) +#define ENDIAN_HOST_TO_BIG_S32(value) (_swap_s32(value)) + +#define ENDIAN_BIG_TO_HOST_U64(value) (_swap_u64(value)) +#define ENDIAN_HOST_TO_BIG_U64(value) (_swap_u64(value)) + +#define ENDIAN_BIG_TO_HOST_S64(value) (_swap_s64(value)) +#define ENDIAN_HOST_TO_BIG_S64(value) (_swap_s64(value)) + +/* no-ops */ + +#define ENDIAN_LITTLE_TO_HOST_U16(value) (value) +#define ENDIAN_HOST_TO_LITTLE_U16(value) (value) + +#define ENDIAN_LITTLE_TO_HOST_S16(value) (value) +#define ENDIAN_HOST_TO_LITTLE_S16(value) (value) + +#define ENDIAN_LITTLE_TO_HOST_U32(value) (value) +#define ENDIAN_HOST_TO_LITTLE_U32(value) (value) + +#define ENDIAN_LITTLE_TO_HOST_S32(value) (value) +#define ENDIAN_HOST_TO_LITTLE_S32(value) (value) + +#define ENDIAN_LITTLE_TO_HOST_U64(value) (value) +#define ENDIAN_HOST_TO_LITTLE_U64(value) (value) + +#define ENDIAN_LITTLE_TO_HOST_S64(value) (value) +#define ENDIAN_HOST_TO_LITTLE_S64(value) (value) + +#elif defined(__BIG_ENDIAN__) + +/* big endian host */ + +#define ENDIAN_LITTLE_TO_HOST_U16(value) (_swap_u16(value)) +#define ENDIAN_HOST_TO_LITTLE_U16(value) (_swap_u16(value)) + +#define ENDIAN_LITTLE_TO_HOST_S16(value) (_swap_s16(value)) +#define ENDIAN_HOST_TO_LITTLE_S16(value) (_swap_s16(value)) + +#define ENDIAN_LITTLE_TO_HOST_U32(value) (_swap_u32(value)) +#define ENDIAN_HOST_TO_LITTLE_U32(value) (_swap_u32(value)) + +#define ENDIAN_LITTLE_TO_HOST_S32(value) (_swap_s32(value)) +#define ENDIAN_HOST_TO_LITTLE_S32(value) (_swap_s32(value)) + +#define ENDIAN_LITTLE_TO_HOST_U64(value) (_swap_u64(value)) +#define ENDIAN_HOST_TO_LITTLE_U64(value) (_swap_u64(value)) + +#define ENDIAN_LITTLE_TO_HOST_S64(value) (_swap_s64(value)) +#define ENDIAN_HOST_TO_LITTLE_S64(value) (_swap_s64(value)) + +/* no-ops */ + +#define ENDIAN_BIG_TO_HOST_U16(value) (value) +#define ENDIAN_HOST_TO_BIG_U16(value) (value) + +#define ENDIAN_BIG_TO_HOST_S16(value) (value) +#define ENDIAN_HOST_TO_BIG_S16(value) (value) + +#define ENDIAN_BIG_TO_HOST_U32(value) (value) +#define ENDIAN_HOST_TO_BIG_U32(value) (value) + +#define ENDIAN_BIG_TO_HOST_S32(value) (value) +#define ENDIAN_HOST_TO_BIG_S32(value) (value) + +#define ENDIAN_BIG_TO_HOST_U64(value) (value) +#define ENDIAN_HOST_TO_BIG_U64(value) (value) + +#define ENDIAN_BIG_TO_HOST_S64(value) (value) +#define ENDIAN_HOST_TO_BIG_S64(value) (value) + +#endif + +#endif // _EndianUtilities_h_ diff --git a/src/blfwk/ExcludesListMatcher.h b/src/blfwk/ExcludesListMatcher.h new file mode 100644 index 0000000..0cff177 --- /dev/null +++ b/src/blfwk/ExcludesListMatcher.h @@ -0,0 +1,68 @@ +/* + * File: ExcludesListMatcher.h + * + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_ExcludesListMatcher_h_) +#define _ExcludesListMatcher_h_ + +#include "GlobMatcher.h" +#include +#include + +namespace blfwk +{ +/*! + * \brief Matches strings using a series of include and exclude glob patterns. + * + * This string matcher class uses a sequential, ordered list of glob patterns to + * determine whether a string matches. Attached to each pattern is an include/exclude + * action. The patterns in the list effectively form a Boolean expression. Includes + * act as an OR operator while excludes act as an AND NOT operator. + * + * Examples (plus prefix is include, minus prefix is exclude): + * - +foo: foo + * - -foo: !foo + * - +foo, +bar: foo || bar + * - +foo, -bar: foo && !bar + * - +foo, -bar, +baz: foo && !bar || baz + * + * The only reason for inheriting from GlobMatcher is so we can access the protected + * globMatch() method. + */ +class ExcludesListMatcher : public GlobMatcher +{ +public: + //! \brief Default constructor. + ExcludesListMatcher(); + + //! \brief Destructor. + ~ExcludesListMatcher(); + + //! \name Pattern list + //@{ + //! \brief Add one include or exclude pattern to the end of the match list. + void addPattern(bool isInclude, const std::string &pattern); + //@} + + //! \brief Performs a single string match test against testValue. + virtual bool match(const std::string &testValue); + +protected: + //! \brief Information about one glob pattern entry in a match list. + struct glob_list_item_t + { + bool m_isInclude; //!< True if include, false if exclude. + std::string m_glob; //!< The glob pattern to match. + }; + + typedef std::vector glob_list_t; + glob_list_t m_patterns; //!< Ordered list of include and exclude patterns. +}; + +}; // namespace blfwk + +#endif // _ExcludesListMatcher_h_ diff --git a/src/blfwk/GHSSecInfo.h b/src/blfwk/GHSSecInfo.h new file mode 100644 index 0000000..587a349 --- /dev/null +++ b/src/blfwk/GHSSecInfo.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_GHSSecInfo_h_) +#define _GHSSecInfo_h_ + +#include "StELFFile.h" +#include "smart_ptr.h" + +namespace blfwk +{ +/*! + * \brief Wrapper around the GHS-specific .secinfo ELF section. + * + * ELF files produced by the Green Hills MULTI toolset will have a + * special .secinfo section. For the most part, this section contains + * a list of address + * ranges that should be filled by the C runtime startup code. The + * address ranges correspond to those of ELF sections whose type is + * #SHT_NOBITS. The GHS runtime uses this table instead of just filling + * all #SHT_NOBITS sections because the linker command file can + * be used to optionally not fill individual sections. + * + * The isSectionFilled() methods let calling code determine if an ELF + * section is found in the .secinfo table. If the section is found, + * then it should be filled. + */ +class GHSSecInfo +{ +public: + //! \brief Default constructor. + GHSSecInfo(StELFFile *elf); + + //! \brief Returns true if there is a .secinfo section present in the ELF file. + bool hasSecinfo() const { return m_hasInfo; } + //! \brief Determines if a section should be filled. + bool isSectionFilled(uint32_t addr, uint32_t length); + + //! \brief Determines if \a section should be filled. + bool isSectionFilled(const Elf32_Shdr §ion); + +protected: +#pragma pack(1) + + /*! + * \brief The structure of one .secinfo entry. + */ + struct ghs_secinfo_t + { + uint32_t m_clearAddr; //!< Address to start filling from. + uint32_t m_clearValue; //!< Value to fill with. + uint32_t m_numBytesToClear; //!< Number of bytes to fill. + }; + +#pragma pack() + +protected: + StELFFile *m_elf; //!< The parser object for our ELF file. + bool m_hasInfo; //!< Whether .secinfo is present in the ELF file. + smart_array_ptr + m_info; //!< Pointer to the .secinfo entries. Will be NULL if there is no .secinfo section in the file. + unsigned m_entryCount; //!< Number of entries in #m_info. +}; + +}; // namespace blfwk + +#endif // _GHSSecInfo_h_ diff --git a/src/blfwk/GlobMatcher.h b/src/blfwk/GlobMatcher.h new file mode 100644 index 0000000..3bbdc8a --- /dev/null +++ b/src/blfwk/GlobMatcher.h @@ -0,0 +1,61 @@ +/* + * File: GlobMatcher.h + * + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_GlobMatcher_h_) +#define _GlobMatcher_h_ + +#include "StringMatcher.h" + +namespace blfwk +{ +/*! + * \brief This class uses glob pattern matching to match strings. + * + * Glob patterns: + * - * matches zero or more characters + * - ? matches any single character + * - [set] matches any character in the set + * - [^set] matches any character NOT in the set + * where a set is a group of characters or ranges. a range + * is written as two characters seperated with a hyphen: a-z denotes + * all characters between a to z inclusive. + * - [-set] set matches a literal hypen and any character in the set + * - []set] matches a literal close bracket and any character in the set + * + * - char matches itself except where char is '*' or '?' or '[' + * - \\char matches char, including any pattern character + * + * Examples: + * - a*c ac abc abbc ... + * - a?c acc abc aXc ... + * - a[a-z]c aac abc acc ... + * - a[-a-z]c a-c aac abc ... + */ +class GlobMatcher : public StringMatcher +{ +public: + //! \brief Constructor. + GlobMatcher(const std::string &pattern) + : StringMatcher() + , m_pattern(pattern) + { + } + + //! \brief Returns whether \a testValue matches the glob pattern. + virtual bool match(const std::string &testValue); + +protected: + std::string m_pattern; //!< The glob pattern to match against. + + //! \brief Glob implementation. + bool globMatch(const char *str, const char *p); +}; + +}; // namespace blfwk + +#endif // _GlobMatcher_h_ diff --git a/src/blfwk/HexValues.h b/src/blfwk/HexValues.h new file mode 100644 index 0000000..c9ccf50 --- /dev/null +++ b/src/blfwk/HexValues.h @@ -0,0 +1,23 @@ +/* + * File: HexValues.h + * + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_HexValues_h_) +#define _HexValues_h_ + +#include + +//! \brief Determines whether \a c is a hex digit character. +bool isHexDigit(char c); + +//! \brief Converts a hexidecimal character to the integer equivalent. +uint8_t hexCharToInt(char c); + +//! \brief Converts a hex-encoded byte to the integer equivalent. +uint8_t hexByteToInt(const char *encodedByte); + +#endif // _HexValues_h_ diff --git a/src/blfwk/I2cPeripheral.h b/src/blfwk/I2cPeripheral.h new file mode 100644 index 0000000..b25b681 --- /dev/null +++ b/src/blfwk/I2cPeripheral.h @@ -0,0 +1,100 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef _i2c_peripheral_h_ +#define _i2c_peripheral_h_ + +#include "Peripheral.h" +#include "blfwk/i2c.h" +#include "packet/command_packet.h" + +//! @addtogroup i2c_peripheral +//! @{ + +namespace blfwk +{ +/*! + * @brief Peripheral that talks to the target device over I2C port hardware. + */ +class I2cPeripheral : public Peripheral +{ +public: + //! @breif Constants. + enum _i2c_peripheral_constants + { + // The read() implementation for the I2cPeripheral does not use this the timeout parameter. + kI2cPeripheral_UnusedTimeout = 0, + // Serial timeout is set to this default during init(). + kI2cPeripheral_DefaultReadTimeoutMs = 10, + kI2cPeripheral_DefaultSpeedKHz = 100, + kI2cPeripheral_DefaultAddress = 0x10 + }; + +public: + //! @brief Parameterized constructor that opens the serial port. + //! + //! Opens and configures the port. Throws exception if port configuration fails. + //! + //! @param port OS file path for I2C port. For example "/dev/i2c-0.0" on Linux. + //! @param speed Port speed, e.g. 9600. + //! @param address I2C slave's address + I2cPeripheral(const char *port, + long speed = kI2cPeripheral_DefaultSpeedKHz, + uint8_t address = kI2cPeripheral_DefaultAddress); + + //! @brief Destructor. + virtual ~I2cPeripheral(); + + //! @brief Flush. + //! + //! should be called on an open I2C port in order to flush any remaining data in the I2C RX buffer + void flushRX(); + + //! @brief Read bytes. + //! + //! @param buffer Pointer to buffer. + //! @param requestedBytes Number of bytes to read. + //! @param actualBytes Number of bytes actually read. + //! @param timeoutMs Time in milliseconds to wait for read to complete. + virtual status_t read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t unused_timeoutMs); + + //! @brief Write bytes. + //! + //! @param buffer Pointer to buffer to write + //! @param byteCount Number of bytes to write + virtual status_t write(const uint8_t *buffer, uint32_t byteCount); + + //! @brief Return peripheral Type + virtual _host_peripheral_types get_type(void) { return kHostPeripheralType_I2C; } + +protected: + //! @brief Initialize. + //! + //! Opens and configures the port. + //! + //! Note: following COM port configuration is assumed: 8 bits, 1 stop bit, no parity. + //! + //! @param port OS file path for I2C port. For example "/dev/i2c-0.0" on Linux. + //! @param speed Port speed, e.g. 9600. + //! @param address I2C slave's address + bool init(const char *port, long speed, uint8_t address); + + int m_fileDescriptor; //!< Port file descriptor. + uint8_t m_buffer[kDefaultMaxPacketSize]; //!< Buffer for bytes used to build read packet. + uint32_t m_current_ReadTimeout; //!< The last value sent to serial_set_read_timeout(). +}; + +} // namespace blfwk + +//! @} + +#endif // _i2c_peripheral_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/IntelHexSourceFile.h b/src/blfwk/IntelHexSourceFile.h new file mode 100644 index 0000000..c680d3f --- /dev/null +++ b/src/blfwk/IntelHexSourceFile.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_IntelHexSourceFile_h_) +#define _IntelHexSourceFile_h_ + +#include "SourceFile.h" +#include "StIntelHexFile.h" +#include "StExecutableImage.h" + +namespace blfwk +{ +/*! + * \brief Executable file in the Intel Hex format. + * + * Instead of presenting each Intel Hex in the file separately, this class + * builds up a memory image of all of the records. Records next to each other + * in memory are coalesced into a single memory region. The data source that + * is returned from createDataSource() exposes these regions as its segments. + */ +class IntelHexSourceFile : public SourceFile +{ +public: + //! \brief Default constructor. + IntelHexSourceFile(const std::string &path); + + //! \brief Destructor. + virtual ~IntelHexSourceFile() {} + //! \brief Test whether the \a stream contains a valid Intel Hex file. + static bool isIntelHexFile(std::istream &stream); + + //! \name Opening and closing + //@{ + //! \brief Opens the file. + virtual void open(); + + //! \brief Closes the file. + virtual void close(); + //@} + + //! \name Format capabilities + //@{ + virtual bool supportsNamedSections() const { return false; } + virtual bool supportsNamedSymbols() const { return false; } + //@} + + //! \name Data sources + //@{ + //! \brief Returns data source for the entire file. + virtual DataSource *createDataSource(); + //@} + + //! \name Entry point + //@{ + //! \brief Returns true if an entry point was set in the file. + virtual bool hasEntryPoint(); + + //! \brief Returns the entry point address. + virtual uint32_t getEntryPointAddress(); + //@} + +protected: + StIntelHexFile *m_file; //!< Intel Hex parser instance. + StExecutableImage *m_image; //!< Memory image of the Intel Hex file.. + bool m_hasEntryRecord; //!< Whether a type 03 or 05 record was found. + StIntelHexFile::IntelHex m_entryRecord; //!< Record for the entry point. + +protected: + //! \brief Build memory image of the Intel Hex file. + void buildMemoryImage(); +}; + +}; // namespace blfwk + +#endif // _IntelHexSourceFile_h_ diff --git a/src/blfwk/Logging.h b/src/blfwk/Logging.h new file mode 100644 index 0000000..1b5c706 --- /dev/null +++ b/src/blfwk/Logging.h @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_Logging_h_) +#define _Logging_h_ + +#include +#include +#include +#include + +//! @addtogroup logging +//! @{ + +/*! + * \brief Base logger class. + * + * There are two types of logging levels that are used by this class. First + * there is the filter level. Any log message that is assigned a level + * higher than the current filter level is discarded. Secondly there is the + * current output level. Log messages that do not have their own level + * use the current output level to determine if they should be shown or + * not. + * + * The two methods setFilterLevel() and setOutputLevel() set the filter + * and default output logging levels, respectively. There are corresponding + * getter methods as well. Both the filter and output levels are + * initialized to #kInfo during object construction. + * + * Most use of the logger classes is expected to be through the Log + * class. It provides static logging methods that call through to a global + * singleton logger instance. There is also a Log::SetOutputLevel utility + * class that makes it extremely easiy to temporarily change the default + * output logging level. + * + * Of all the overloaded log() methods in this class, none of them are + * really expected to be reimplemented by subclasses. Instead, there is + * the single protected _log() method that takes a simple string pointer. + * The other log methods all wind up calling _log(), so it provides a + * single point to override. In fact, _log() is pure virtual, so subclasses + * must implement it. + * + * \see Log + */ +class Logger +{ +public: + //! \brief Logging levels. + enum log_level_t + { + kUrgent = 0, //!< The lowest level, for messages that must always be logged. + kJson, //!< For machine language output only. + kError, //!< For fatal error messages. + kWarning, //!< For non-fatal warning messages. + kInfo, //!< The normal log level, for status messages. + kInfo2, //!< For verbose status messages. + kDebug, //!< For internal reporting. + kDebug2, //!< Highest log level; verbose debug logging. + kInfo1 = kInfo, //!< Alias for #kInfo + kDebug1 = kDebug //!< Alias for #kDebug + }; + +public: + //! \brief Default constructor. + Logger() + : m_filter(kInfo) + , m_level(kInfo) + { + } + + //! \brief Destructor. + virtual ~Logger() {} + //! \name Logging levels + //@{ + //! \brief Changes the logging level to \a level. + inline void setFilterLevel(log_level_t level) { m_filter = level; } + //! \brief Returns the current logging filter level. + inline log_level_t getFilterLevel() const { return m_filter; } + //! \brief Changes the logging output level to \a level. + inline void setOutputLevel(log_level_t level) { m_level = level; } + //! \brief Returns the current logging output level. + inline log_level_t getOutputLevel() const { return m_level; } + //@} + + //! \name Logging + //@{ + //! \brief Log with format. + virtual void log(const char *fmt, ...); + + //! \brief Log a string object. + virtual void log(const std::string &msg) { log(msg.c_str()); } + //! \brief Log with format at a specific output level. + virtual void log(log_level_t level, const char *fmt, ...); + + //! \brief Log a string output at a specific output level. + virtual void log(log_level_t level, const std::string &msg) { log(level, msg.c_str()); } + //! \brief Log with format using an argument list. + virtual void log(const char *fmt, va_list args); + + //! \brief Log with format using an argument with a specific output level. + virtual void log(log_level_t level, const char *fmt, va_list args); + //@} + +protected: + log_level_t m_filter; //!< The current logging filter level. + log_level_t m_level; //!< The current log output level. + +protected: + //! \brief The base pure virtual logging function implemented by subclasses. + virtual void _log(const char *msg) = 0; +}; + +/*! + * \brief Wraps a set of static functions for easy global logging access. + * + * This class has a set of static methods that make it easy to access a global + * logger instance without having to worry about extern symbols. It does this + * by keeping a static member variable pointing at the singleton logger instance, + * which is set with the setLogger() static method. + * + * There is also an inner utility class called SetOutputLevel that uses + * C++ scoping rules to temporarily change the output logging level. When the + * SetOutputLevel instance falls out of scope the output level is restored + * to the previous value. + */ +class Log +{ +public: + //! \name Singleton logger access + //@{ + //! \brief Returns the current global logger singleton. + static inline Logger *getLogger() { return s_logger; } + //! \brief Sets the global logger singleton instance. + static inline void setLogger(Logger *logger) { s_logger = logger; } + //@} + + //! \name Logging + //@{ + //! \brief Log with format. + static void log(const char *fmt, ...); + + //! \brief Log a string object. + static void log(const std::string &msg); + + //! \brief Log with format at a specific output level. + static void log(Logger::log_level_t level, const char *fmt, ...); + + //! \brief Log a string output at a specific output level. + static void log(Logger::log_level_t level, const std::string &msg); + //@} + + //! @name Logging level helpers + //! + //! These static methods log a message with an implicit log level. + //@{ + static void urgent(const char *fmt, ...); //!< Log a message with #Logger::kUrgent level. + static void json(const char *fmt, ...); //!< Log a message with #Logger::kJson level. + static void error(const char *fmt, ...); //!< Log a message with #Logger::kError level. + static void warning(const char *fmt, ...); //!< Log a message with #Logger::kWarning level. + static void info(const char *fmt, ...); //!< Log a message with #Logger::kInfo level. + static void info2(const char *fmt, ...); //!< Log a message with #Logger::kInfo2 level. + static void debug(const char *fmt, ...); //!< Log a message with #Logger::kDebug level. + static void debug2(const char *fmt, ...); //!< Log a message with #Logger::kDebug2 level. + //@} + +protected: + static Logger *s_logger; //!< The single global logger instance. + +public: + /*! + * \brief Utility class to temporarily change the logging output level. + * + * This class will change the current logging output level of a given + * logger instance. Then when it falls out of scope it will set the + * level back to what it was originally. + * + * Use like this: + * \code + * // output level is some value here + * { + * Log::SetOutputLevel leveler(Logger::DEBUG); + * // now output level is DEBUG + * Log::log("my debug message 1"); + * Log::log("my debug message 2"); + * } + * // output level is restored to previous value + * \endcode + */ + class SetOutputLevel + { + public: + //! \brief Default constructor. + //! + //! Saves the current logging output level of the global logger, + //! as managed by the Log class, and sets the new level to \a level. + SetOutputLevel(Logger::log_level_t level) + : m_logger(Log::getLogger()) + , m_saved(Logger::kInfo) + { + assert(m_logger); + m_saved = m_logger->getOutputLevel(); + m_logger->setOutputLevel(level); + } + + //! \brief Constructor. + //! + //! Saves the current logging output level of \a logger and sets + //! the new level to \a level. + SetOutputLevel(Logger *logger, Logger::log_level_t level) + : m_logger(logger) + , m_saved(logger->getOutputLevel()) + { + assert(m_logger); + m_logger->setOutputLevel(level); + } + + //! \brief Destructor. + //! + //! Restores the saved logging output level. + ~SetOutputLevel() { m_logger->setOutputLevel(m_saved); } + protected: + Logger *m_logger; //!< The logger instance we're controlling. + Logger::log_level_t m_saved; //!< Original logging output level. + }; +}; + +/*! + * \brief Simple logger that writes to stdout. + */ +class StdoutLogger : public Logger +{ +protected: + //! \brief Logs the message to stdout. + virtual void _log(const char *msg); +}; + +/*! + * \brief Simple logger that writes to a file. + */ +class FileLogger : public Logger +{ +public: + FileLogger(const char *file_path); + ~FileLogger(); + +protected: + //! \brief Logs the message to the file. + virtual void _log(const char *msg); + + //! \brief The name of the file, including the path, to log to. + const std::string m_file_path; + //! \brief The name of the file, including the path, to log to. + std::ofstream m_logFile; +}; + +//! @} + +#endif // _Logging_h_ diff --git a/src/blfwk/LpcUsbSio.h b/src/blfwk/LpcUsbSio.h new file mode 100644 index 0000000..de8a847 --- /dev/null +++ b/src/blfwk/LpcUsbSio.h @@ -0,0 +1,232 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef _LpcUsbSio_h_ +#define _LpcUsbSio_h_ + +#include +#include "host_types.h" +#include "middleware/libusbsio/inc/lpcusbsio.h" + +//! @addtogroup lpc_usb_sio +//! @{ + +namespace blfwk +{ +/*! + * @brief Interface with the LPC USB Serial I/O + */ +class LpcUsbSio +{ +public: + //! @brief Constants + + enum _lpc_usb_sio_port_contants + { + kDefault_Vid = LPCUSBSIO_VID, //!< NXP VID + kDefault_Pid = LPCUSBSIO_PID, //!< PID for LPC USB Serial I/O + }; + + enum _lpc_usb_sio_timeout_contants + { + kDefault_TimeoutMs = LPCUSBSIO_READ_TMO, //!< 500ms, timeout miliseconds for reading + }; + + enum lpc_usb_sio_function_t + { + kFunction_None, + kFunction_SPI, + kFunction_I2C, + kFunction_GPIO, + }; + //! @brief SPI clock polarity configuration. + enum spi_clock_polarity_t + { + kSpiPolarity_ActiveHigh = 0, //!< Active-high SPI clock (idles low). + kSpiPolarity_ActiveLow = 1 //!< Active-low SPI clock (idles high). + }; + + //! @brief SPI clock phase configuration. + enum spi_clock_phase_t + { + kSpiPhase_FirstEdge = 0, //!< First edge on SPSCK occurs at the middle of the first cycle of a data transfer. + kSpiPhase_SecondEdge = 1 //!< First edge on SPSCK occurs at the start of the first cycle of a data transfer. + }; + + //! @brief I2C slave address + enum i2c_slave_address_t + { + kI2cAddress_Default = 0x10, //!< Default I2C slave address. + }; + + //! @brief LPC USB Serial I/O USB port configuration. + struct LpcUsbSioPortConfigData + { + uint16_t usbVid; + uint16_t usbPid; + std::string usbString; + std::string usbPath; + int32_t portIndex; + + LpcUsbSioPortConfigData() + { + usbVid = LpcUsbSio::kDefault_Vid; + usbPid = LpcUsbSio::kDefault_Pid; + portIndex = 0; + } + }; + + //! @brief LPC USB Serial I/O SPI configuration. + struct LpcUsbSioSpiConfigData + { + uint32_t spiSpeedHz; + spi_clock_polarity_t spiPolarity; + spi_clock_phase_t spiPhase; + uint8_t spiSselPort; + uint8_t spiSselPin; + int32_t spiPortIndex; + + LpcUsbSioSpiConfigData() + { + spiSpeedHz = 100000; + spiPolarity = LpcUsbSio::kSpiPolarity_ActiveLow; + spiPhase = LpcUsbSio::kSpiPhase_SecondEdge; + spiSselPort = 0; + spiSselPin = 0; + spiPortIndex = 0; + } + }; + + //! @brief LPC USB Serial I/O I2C configuration. + struct LpcUsbSioI2cConfigData + { + uint8_t i2cAddress; + I2C_CLOCKRATE_T i2cSpeedHz; + int32_t i2cPortIndex; + + LpcUsbSioI2cConfigData() + { + i2cAddress = LpcUsbSio::kI2cAddress_Default; + i2cSpeedHz = I2C_CLOCK_STANDARD_MODE; + i2cPortIndex = 0; + } + }; + + //! @brief LPC USB Serial I/O GPIO configuration. + struct LpcUsbSioGpioConfigData + { + uint8_t gpioPort; + uint8_t gpioPin; + uint8_t gpioValue; + uint8_t gpioMode; + int32_t gpioPortIndex; + + LpcUsbSioGpioConfigData() + { + gpioPort = 0; + gpioPin = 0; + gpioValue = 0; + gpioMode = 0; + gpioPortIndex = 0; + } + }; + + //! @brief LPC USB Serial I/O configuration data. + struct LpcUsbSioConfigData + { + lpc_usb_sio_function_t function; + LpcUsbSioPortConfigData portConfig; + LpcUsbSioSpiConfigData spiConfig; + LpcUsbSioI2cConfigData i2cConfig; + + LpcUsbSioConfigData() { function = LpcUsbSio::kFunction_None; } + }; + + //! @brief Constructor. + LpcUsbSio(const LpcUsbSio::LpcUsbSioPortConfigData &config); + + //! @brief Destructor. + virtual ~LpcUsbSio(); + + //! @brief Parse the params in options into the config structure. + static bool parse(const string_vector_t ¶ms, LpcUsbSio::LpcUsbSioConfigData &config); + + //! @brief Convert LPC USB Serial I/O error code to detail string. + static const std::string getError(LPCUSBSIO_ERR_T errorCode); + + //! @brief Write data. + virtual uint32_t write(const uint8_t *data, uint32_t byteCount) = 0; + + //! @brief Read data. + virtual uint32_t read(uint8_t *data, uint32_t byteCount) = 0; + +protected: + uint16_t m_usbVid; //!< The VID of LPC USB Serial I/O. + uint16_t m_usbPid; //!< The PID of LPC USB Serial I/O. + std::string m_usbString; //!< The USB Serial String of LPC USB Serial I/O port. + std::string m_usbPath; //!< The USB instance path of LPC USB Serial I/O port. + int32_t m_portCount; //!< The number of LPC USB Serial I/O connected. + int32_t m_portIndex; //!< The port of LPC USB Serial I/O which is active. Index starts from 0. + LPC_HANDLE m_portHandler; //!< The LPC USB Serial I/O hardware operation handler. +}; + +class LpcUsbSioSpi : public LpcUsbSio +{ +public: + //! @brief Constructor. + LpcUsbSioSpi(const LpcUsbSio::LpcUsbSioPortConfigData &portConfig, + const LpcUsbSio::LpcUsbSioSpiConfigData &spiConfig); + + //! @brief Destructor. + virtual ~LpcUsbSioSpi(); + + //! @brief Write data. + virtual uint32_t write(const uint8_t *data, uint32_t byteCount); + + //! @brief Read data. + virtual uint32_t read(uint8_t *data, uint32_t byteCount); + +protected: + int32_t m_spiPortCount; //!< The number of SPI port on current LPC USB Serial I/O. + int32_t m_spiPortIndex; //!< The port of SPI which is active. Index starts from 0. + uint8_t m_spiSselPort; //!< The port numer of the SPI->SSELn pin on LPC USB Serial I/O. + uint8_t m_spiSselPin; //!< The pin number of the SPI->SSELn pin on LPC USB Serial I/O. + LPC_HANDLE m_spiPortHandler; //!< The SPI operation handler. +}; + +class LpcUsbSioI2c : public LpcUsbSio +{ +public: + //! @brief Constructor. + LpcUsbSioI2c(const LpcUsbSio::LpcUsbSioPortConfigData &portConfig, + const LpcUsbSio::LpcUsbSioI2cConfigData &i2cConfig); + + //! @brief Destructor. + virtual ~LpcUsbSioI2c(); + + //! @brief Write data. + virtual uint32_t write(const uint8_t *data, uint32_t byteCount); + + //! @brief Read data. + virtual uint32_t read(uint8_t *data, uint32_t byteCount); + +protected: + int32_t m_i2cPortCount; //!< The number of I2C port on current LPC USB Serial I/O. + int32_t m_i2cPortIndex; //!< The port of I2C which is active. Index starts from 0. + LPC_HANDLE m_i2cPortHandler; //!< The I2C operation handler. + uint8_t m_i2cAddress; //!< The I2C slave address to communicate. +}; + +} // namespace blfwk + +//! @} + +#endif // _LpcUsbSio_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/LpcUsbSioPeripheral.h b/src/blfwk/LpcUsbSioPeripheral.h new file mode 100644 index 0000000..fea01f7 --- /dev/null +++ b/src/blfwk/LpcUsbSioPeripheral.h @@ -0,0 +1,63 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef _LpcUsbSioPeripheral_h_ +#define _LpcUsbSioPeripheral_h_ + +#include "Peripheral.h" + +//! @addtogroup lpc_usb_sio_peripheral +//! @{ + +namespace blfwk +{ +/*! + * @brief Peripheral that talks to the target device over LPCUSB Serial I/O hardware. + */ +class LpcUsbSioPeripheral : public Peripheral +{ +public: + //! @brief Parameterized constructor that opens the LPC USB Serial I/O port. + //! + //! Opens and configures the port. Throws exception if operation fails. + //! + //! @param config The configuration data of LPCUSB Serial I/O device. + LpcUsbSioPeripheral(const LpcUsbSio::LpcUsbSioConfigData &config); + + //! @brief Destructor. + virtual ~LpcUsbSioPeripheral(); + + //! @brief Read bytes. + //! + //! @param buffer Pointer to buffer + //! @param requestedBytes Number of bytes to read + //! @param actualBytes Number of bytes got + //! @param timeoutMs The timeout microseconds for the read operation + virtual status_t read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t timeoutMs); + + //! @brief Write bytes. + //! + //! @param buffer Pointer to buffer to write + //! @param byteCount Number of bytes to write + virtual status_t write(const uint8_t *buffer, uint32_t byteCount); + + //! @brief Get the peripheral type. + virtual _host_peripheral_types get_type(void) { return kHostPeripheralType_LPCUSBSIO; } + +protected: + LpcUsbSio *m_lpcUsbSio; //!< Represents LPC USB Serial I/O hardware. +}; + +} // namespace blfwk + +//! @} + +#endif // _LpcUsbSioPeripheral_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/OptionContext.h b/src/blfwk/OptionContext.h new file mode 100644 index 0000000..723fdb6 --- /dev/null +++ b/src/blfwk/OptionContext.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_OptionContext_h_) +#define _OptionContext_h_ + +#include +#include "Value.h" + +namespace blfwk +{ +/*! + * \brief Pure abstract interface class to a table of options. + */ +class OptionContext +{ +public: + //! @brief Force a virtual destructor. + virtual ~OptionContext() {} + //! \brief Detemine whether the named option is present in the table. + //! \param name The name of the option to query. + //! \retval true The option is present and has a value. + //! \retval false No option with that name is in the table. + virtual bool hasOption(const std::string &name) const = 0; + + //! \brief Returns the option's value. + //! \param name The name of the option. + //! \return The value for the option named \a name. + //! \retval NULL No option is in the table with that name. + virtual const Value *getOption(const std::string &name) const = 0; + + //! \brief Adds or changes an option's value. + //! + //! If the option was not already present in the table, it is added. + //! Otherwise the old value is replaced. + //! + //! \param name The option's name. + //! \param value New value for the option. + virtual void setOption(const std::string &name, Value *value) = 0; + + //! \brief Removes an option from the table. + //! \param name The name of the option to remove. + virtual void deleteOption(const std::string &name) = 0; +}; + +}; // namespace blfwk + +#endif // _OptionContext_h_ diff --git a/src/blfwk/Packetizer.h b/src/blfwk/Packetizer.h new file mode 100644 index 0000000..79332c2 --- /dev/null +++ b/src/blfwk/Packetizer.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _Packetizer_h_ +#define _Packetizer_h_ + +#include "bootloader_common.h" +#include "bootloader/bl_peripheral.h" + +#include + +//! @addtogroup host_packetizers +//! @{ + +namespace blfwk +{ +// Forward declarations. +class Peripheral; + +//! @brief Packetizer status codes. +enum _packetizer_status +{ + kStatus_NoPingResponse = MAKE_STATUS(kStatusGroup_Packetizer, 0), + kStatus_InvalidPacketType = MAKE_STATUS(kStatusGroup_Packetizer, 1), + kStatus_InvalidCRC = MAKE_STATUS(kStatusGroup_Packetizer, 2), + kStatus_NoCommandResponse = MAKE_STATUS(kStatusGroup_Packetizer, 3) +}; + +/*! + * @brief Interface class for packetization of commands and data. + */ +class Packetizer +{ +public: + Packetizer(Peripheral *peripheral, uint32_t packetTimeoutMs) + : m_peripheral(peripheral) + , m_packetTimeoutMs(packetTimeoutMs) + , m_version() + , m_options(0) + , m_isAbortEnabled(false) + , m_readCount(0) + { + } + + virtual ~Packetizer(){}; + + //! @brief Read a packet. + virtual status_t readPacket(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType) = 0; + + //! @brief Write a packet. + virtual status_t writePacket(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType) = 0; + + //! @brief Abort data phase. + virtual void abortPacket() = 0; + + //! @brief Send framing packet ack. + virtual void sync() = 0; + + //! @brief Finalize. + virtual void finalize() = 0; + + //! @brief Enable simulator command processor pump. + virtual void enableSimulatorPump() = 0; + + //! @brief Pump simulator command processor. + virtual void pumpSimulator() = 0; + + //! @brief Set aborted flag. + //! + //! Used for out-of-band flow control for simulator. + virtual void setAborted(bool aborted) = 0; + + //! @brief Return the max packet size. + virtual uint32_t getMaxPacketSize() = 0; + + //! @brieif Optional control of number of bytes requested from peripheral by readPacket(). + virtual void setReadCount(uint32_t byteCount) { m_readCount = byteCount; } + //! @brief Peripheral accessor. + virtual Peripheral *getPeripheral() { return m_peripheral; } + //! @brief Get Framing Protocol Version + standard_version_t getVersion() { return m_version; } + //! @brief Get Framing Protocol Options + uint16_t getOptions() { return m_options; } + //! @brief Set abort packet check enable. + void setAbortEnabled(bool isEnabled) { m_isAbortEnabled = isEnabled; } + //! @biref Check if abort data phase is enabled. + bool isAbortEnabled() { return m_isAbortEnabled; } +protected: + Peripheral *m_peripheral; //!< Peripheral to send/receive bytes on. + standard_version_t m_version; //!< Framing protocol version. + uint16_t m_options; //!< Framing protocol options bitfield. + uint32_t m_packetTimeoutMs; + bool m_isAbortEnabled; //!< True if allowing abort packet. Not used by all packetizers. + uint32_t m_readCount; //!< Optional control of number of bytes requested by readPacket(). +}; + +} // namespace blfwk + +//! @} + +#endif // _Packetizer_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/Peripheral.h b/src/blfwk/Peripheral.h new file mode 100644 index 0000000..9837091 --- /dev/null +++ b/src/blfwk/Peripheral.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _Peripheral_h_ +#define _Peripheral_h_ + +#include +#include "BusPal.h" +#include "LpcUsbSio.h" +#include "bootloader_common.h" + +//! @addtogroup host_peripherals +//! @{ + +namespace blfwk +{ +/*! + * @brief Represents a peripheral. + * + * Interface class for objects that provide the source for commands or sink for responses. + */ +class Peripheral +{ +public: + enum _host_peripheral_types + { + kHostPeripheralType_None, + kHostPeripheralType_UART, + kHostPeripheralType_BUSPAL_UART, + kHostPeripheralType_LPCUSBSIO, + kHostPeripheralType_USB_HID, + kHostPeripheralType_SIM, + kHostPeripheralType_I2C, + kHostPeripheralType_SPI, + }; + + struct PeripheralConfigData + { + _host_peripheral_types peripheralType; + bool ping; + std::string comPortName; + long comPortSpeed; + uint32_t packetTimeoutMs; + unsigned short usbHidVid; + unsigned short usbHidPid; + std::string usbHidSerialNumber; + std::string usbPath; +#if defined(LINUX) && defined(__ARM__) + unsigned char i2cAddress; + unsigned char spiPolarity; + unsigned char spiPhase; + unsigned char spiSequence; +#endif // #if defined(LINUX) && defined(__ARM__) + BusPal::BusPalConfigData busPalConfig; + LpcUsbSio::LpcUsbSioConfigData lpcUsbSioConfig; + }; + + virtual ~Peripheral(){}; + + //! @brief Read bytes. + //! + //! @param buffer Pointer to buffer + //! @param requestedBytes Number of bytes to read + //! @param timeoutMs Time in milliseconds to wait for read to complete. + //! @param actualBytes Number of bytes actually read. + virtual status_t read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t timeout) = 0; + + //! @brief Write bytes. + virtual status_t write(const uint8_t *buffer, uint32_t byteCount) = 0; + + //! @brief Return peripheral Type + virtual _host_peripheral_types get_type(void) = 0; +}; + +} // namespace blfwk + +//! @} + +#endif // _Peripheral_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/Progress.h b/src/blfwk/Progress.h new file mode 100644 index 0000000..cd2147c --- /dev/null +++ b/src/blfwk/Progress.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _Progress_h_ +#define _Progress_h_ + +#pragma once + +#include "blfwk/host_types.h" + +/*! +* @brief Contains the callback function for progress and abort phase. +* +*/ +class Progress +{ +public: + //! @brief Default constructor. + Progress() + : m_segmentIndex(1) + , m_segmentCount(1) + , m_progressCallback(NULL) + , m_abortPhase(NULL) + { + } + + //! @brief Constructor with initial callback. + Progress(void(*callback)(int, int, int), bool *abortPhase) + : m_segmentIndex(1) + , m_segmentCount(1) + , m_progressCallback(callback) + , m_abortPhase(abortPhase) + { + } + + //! @brief Default destructor. + ~Progress() {} + //! @brief execute the progress callback function. + //! + //! @param percentage the percentage of current executed progress. + void progressCallback(int percentage) + { + if (m_progressCallback != NULL) + { + m_progressCallback(percentage, m_segmentIndex, m_segmentCount); + } + } + + //! @brief Check whether the data phase is canceled. + bool abortPhase(void) + { + if ((m_abortPhase) && (*m_abortPhase == true)) + { + return true; + } + else + { + return false; + } + } + + //! @biref initialized the progress callback function and the variable of controlling data phase. + //! + //! @param callback The progress callback function. + //! + //! @param abortPhase The pointer pointing to a variable controlling whether abort current data phase. + void registerCallback(void(*callback)(int, int, int), bool *abortPhase) + { + m_progressCallback = callback; + m_abortPhase = abortPhase; + } + +public: + int m_segmentIndex; //!< Index of data segment in progressing. + int m_segmentCount; //!< The number of data segments. + +private: + void(*m_progressCallback)(int percentage, int segmentIndex, int segmentCount); //!< The progress callback function. + bool *m_abortPhase; //!< The pointer pointing to a variable controlling whether abort current data phase. +}; + +#endif // _Progress_h_ diff --git a/src/blfwk/Random.h b/src/blfwk/Random.h new file mode 100644 index 0000000..b726f47 --- /dev/null +++ b/src/blfwk/Random.h @@ -0,0 +1,58 @@ +/* + * File: Random.h + * + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_Random_h_) +#define _Random_h_ + +#include "stdafx.h" + +#ifdef WIN32 +/*! + * This class is from the crypto++ library. + */ +class MicrosoftCryptoProvider +{ +public: + MicrosoftCryptoProvider(); + ~MicrosoftCryptoProvider(); +#if defined(_WIN64) + typedef unsigned __int64 ProviderHandle; // type HCRYPTPROV, avoid #include +#else + typedef unsigned long ProviderHandle; +#endif + ProviderHandle GetProviderHandle() const { return m_hProvider; } +private: + ProviderHandle m_hProvider; +}; + +#pragma comment(lib, "advapi32.lib") +#endif // WIN32 + +/*! + * Encapsulates the Windows CryptoAPI's CryptGenRandom or /dev/urandom on Unix systems. + */ +class RandomNumberGenerator +{ +public: + RandomNumberGenerator(); + ~RandomNumberGenerator(); + + uint8_t generateByte(); + void generateBlock(uint8_t *output, unsigned count); + +protected: +#ifdef WIN32 +#ifndef WORKAROUND_MS_BUG_Q258000 + MicrosoftCryptoProvider m_provider; +#endif +#else // WIN32 + int m_fd; +#endif // WIN32 +}; + +#endif // _Random_h_ diff --git a/src/blfwk/RijndaelCTR.h b/src/blfwk/RijndaelCTR.h new file mode 100644 index 0000000..ae1aedd --- /dev/null +++ b/src/blfwk/RijndaelCTR.h @@ -0,0 +1,62 @@ +/* + * File: RijndaelCTR.h + * + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_RijndaelCTR_h_) +#define _RijndaelCTR_h_ + +#include "AESKey.h" +#include "AESCounter.h" +#include + +/*! + * \brief Class to compute AES 128 CTR encryption/decryption. + * + * Currently only supports 128-bit keys and block sizes. + */ +class RijndaelCTR +{ +public: + enum + { + BLOCK_SIZE = 16 //!< Number of bytes in one cipher block. + }; + + //! The cipher block data type. + typedef uint8_t block_t[BLOCK_SIZE]; + +public: + //! \brief Constructor. + RijndaelCTR(const AESKey<128> &key, const AESCounter<128> &counter); + + //! \brief Encrypt data. + void encrypt(const uint8_t *data, unsigned length, uint8_t *dest); + + //! \brief Encrypt data. + void decrypt(const uint8_t *data, unsigned length, uint8_t *dest); + + //! \brief Assignment operator. + RijndaelCTR &operator=(const RijndaelCTR &other) + { + m_key = other.m_key; + m_counter = other.m_counter; + return *this; + } + +protected: + AESKey<128> m_key; //!< 128-bit key to use for encrypt/decrypt + AESCounter<128> m_counter; //!< Counter value for encrypt/decrypt + + void incrementCounter(AESCounter<128>::counter_t *counter); + void baseProcess(const uint8_t *data, unsigned length, uint8_t *dest); + +private: + //! \brief inaccessible default constructor. + RijndaelCTR() {} +}; + +#endif // _RijndaelCTR_h_ diff --git a/src/blfwk/SBSourceFile.h b/src/blfwk/SBSourceFile.h new file mode 100644 index 0000000..1905798 --- /dev/null +++ b/src/blfwk/SBSourceFile.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_SBSourceFile_h_) +#define _SBSourceFile_h_ + +#include "blfwk/SourceFile.h" +#include "blfwk/StELFFile.h" +#include "blfwk/smart_ptr.h" +#include "blfwk/DataSource.h" +#include "blfwk/DataTarget.h" + +namespace blfwk +{ +/*! + * \brief SB Format (SB) source file. + */ +class SBSourceFile : public BinarySourceFile +{ +public: + //! \brief Default constructor. + SBSourceFile(const std::string &path); + + //! \brief Destructor. + virtual ~SBSourceFile(); + + //! \brief Identifies whether the stream contains an SB file. + static bool isSBFile(std::istream &stream); + + //! \name Data source creation + //@{ + //! \brief Creates an unmapped data source from the entire file. + virtual DataSource *createDataSource(); + //@} + +protected: + //! An AES-128 cipher block is 16 bytes. + typedef uint8_t cipher_block_t[16]; + + //! A SHA-1 digest is 160 bits, or 20 bytes. + typedef uint8_t sha1_digest_t[20]; + + //! Unique identifier type for a section. + typedef uint32_t section_id_t; + +// All of these structures are packed to byte alignment in order to match +// the structure on disk. +#pragma pack(1) + + //! Same version struct used for 3600 boot image. + struct version_t + { + uint16_t m_major; + uint16_t m_pad0; + uint16_t m_minor; + uint16_t m_pad1; + uint16_t m_revision; + uint16_t m_pad2; + }; + + //! \brief Header for the entire boot image. + //! + //! Fields of this header are arranged so that those used by the bootloader ROM all come + //! first. They are also set up so that all fields are not split across cipher block + //! boundaries. The fields not used by the bootloader are not subject to this + //! restraint. + //! + //! Image header size is always a round number of cipher blocks. The same also applies to + //! the boot image itself. The padding, held in #blfwk::EncoreBootImage::boot_image_header_t::m_padding0 + //! and #blfwk::EncoreBootImage::boot_image_header_t::m_padding1 is filled with random bytes. + //! + //! The DEK dictionary, section table, and each section data region must all start on + //! cipher block boundaries. + //! + //! This header is not encrypted in the image file. + //! + //! The m_digest field contains a SHA-1 digest of the fields of the header that follow it. + //! It is the first field in the header so it doesn't change position or split the header + //! in two if fields are added to the header. + struct boot_image_header_t + { + union + { + sha1_digest_t m_digest; //!< SHA-1 digest of image header. Also used as the crypto IV. + struct + { + cipher_block_t m_iv; //!< The first 16 bytes of the digest form the initialization vector. + uint8_t m_extra[4]; //!< The leftover top four bytes of the SHA-1 digest. + }; + }; + uint8_t m_signature[4]; //!< 'STMP', see #ROM_IMAGE_HEADER_SIGNATURE. + uint8_t m_majorVersion; //!< Major version for the image format, see #ROM_BOOT_IMAGE_MAJOR_VERSION. + uint8_t m_minorVersion; //!< Minor version of the boot image format, see #ROM_BOOT_IMAGE_MINOR_VERSION. + uint16_t m_flags; //!< Flags or options associated with the entire image. + uint32_t m_imageBlocks; //!< Size of entire image in blocks. + uint32_t m_firstBootTagBlock; //!< Offset from start of file to the first boot tag, in blocks. + section_id_t m_firstBootableSectionID; //!< ID of section to start booting from. + uint16_t m_keyCount; //!< Number of entries in DEK dictionary. + uint16_t m_keyDictionaryBlock; //!< Starting block number for the key dictionary. + uint16_t m_headerBlocks; //!< Size of this header, including this size word, in blocks. + uint16_t m_sectionCount; //!< Number of section headers in this table. + uint16_t m_sectionHeaderSize; //!< Size in blocks of a section header. + uint8_t m_padding0[2]; //!< Padding to align #m_timestamp to long word. + uint8_t m_signature2[4]; //!< Second signature to distinguish this .sb format from the 36xx format, see + //!#ROM_IMAGE_HEADER_SIGNATURE2. + uint64_t m_timestamp; //!< Timestamp when image was generated in microseconds since 1-1-2000. + version_t m_productVersion; //!< Product version. + version_t m_componentVersion; //!< Component version. + uint16_t m_driveTag; //!< Drive tag for the system drive which this boot image belongs to. + uint8_t m_padding1[6]; //!< Padding to round up to next cipher block. + }; + +#pragma pack() + +public: + /*! + * \brief Simple exception thrown to indicate an error in the input SB file format. + */ + class SBFileException : public std::runtime_error + { + public: + //! \brief Default constructor. + SBFileException(const std::string &inMessage) + : std::runtime_error(inMessage) + { + } + }; +}; + +}; // namespace blfwk + +#endif // _SBSourceFile_h_ diff --git a/src/blfwk/SDPCommand.h b/src/blfwk/SDPCommand.h new file mode 100644 index 0000000..843edef --- /dev/null +++ b/src/blfwk/SDPCommand.h @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * Copyright 2016-2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _SDPCommand_h_ +#define _SDPCommand_h_ + +#pragma once + +#include "host_types.h" +#include "Progress.h" +#include "Packetizer.h" + +//! @addtogroup sdp_commands +//! @{ + +using namespace std; + +namespace blfwk +{ +//! @brief SDP Command structure. +struct sdp_cmd_t +{ + uint16_t cmdType; + const char *const name; + + sdp_cmd_t(uint16_t cmdType, const char *name) + : cmdType(cmdType) + , name(name) + { + } +}; + +//! @name SDP command types and names. +//@{ +const sdp_cmd_t kSDPCommand_ReadRegister(0x0101, "read-register"); +const sdp_cmd_t kSDPCommand_WriteRegister(0x0202, "write-register"); +const sdp_cmd_t kSDPCommand_WriteFile(0x0404, "write-file"); +const sdp_cmd_t kSDPCommand_ErrorStatus(0x0505, "error-status"); +const sdp_cmd_t kSDPCommand_DcdWrite(0x0a0a, "dcd-write"); +const sdp_cmd_t kSDPCommand_SkipDcdHeader(0x0c0c, "skip-dcd-header"); +const sdp_cmd_t kSDPCommand_JumpAddress(0x0b0b, "jump-address"); +const sdp_cmd_t kSDPCommand_SetBaudrate(0x0d0d, "set-baudrate"); +const sdp_cmd_t kSDPCommand_Ping(0x5aa6, "ping"); +//@} + +/*! +* @brief Represents an SDP command. +* +* Do not instantiate this class. Instead, use the create() method to +* create the appropriate subclass based on command name in argv[0]. +*/ +class SDPCommand +{ +public: + //! @brief Constants + enum _constants + { + kCmdSizeBytes = 16, //!< Number of bytes in SDP command. + kHabModeSizeBytes = 4, //!< Number of bytes in HAB mode response. + kResponseSizeBytes = 64, //!< Number of bytes in status response. + kSendDataSizeBytes = 1024, //!< Number of bytes in data packet. + kMaxDcdSizeBytes = 1024, //!< Max number bytes in DCD table. + kHabEnabled = 0x12343412, //!< HAB mode enabled. + kHabDisabled = 0x56787856, //!< HAB mode disabled. + kWriteComplete = 0x128a8a12, //!< Write Complete status response. + kWriteFileComplete = 0x88888888, //!< Write File Complete status response. + kHabStatusFailure = 0x33333333, //!< HAB failed response. + kHabStatusWarning = 0x69696969, //!< HAB wrarning response. + kHabStatusSuccess = 0xf0f0f0f0, //!< HAB success resopnse. + kReEnum_Ack = 0x89232389, //!< Return Error Number Ack + kStatus_NoResponse = 10004, //!< Matches Bootloader kStatusNoResponse. + kOk_Ack = 0x900DD009, //!< Response to skip-dcd-header command. + }; + +public: + //! @brief Create an appropriate command subclass. + //! + //! Pass the command name in argv[0] and optional + //! arguments in the rest of the string vector. + //! Caller is responsible for deleting the returned object. + //! + //! @param argv Argument vector + //! @retval Command object + static SDPCommand *create(const string_vector_t *argv); + +protected: + //! @brief Constructor that takes a command name and list of arguments. + //! + //! @param argv Argument vector + SDPCommand(const string_vector_t *argv) + : m_argv(*argv) + , m_progress() + , m_responseValues() + , m_cmdBuf() + { + } + +public: + //! @brief Destructor. + virtual ~SDPCommand() {} + //! @brief Initialize. + //! + //! Subclasses should implement init() to check for valid arguments. + virtual bool init() { return true; } + //! @name String arguments accessors. + //@{ + + //! @brief Get the specified argument. + virtual std::string getArg(int arg) const { return m_argv.at(arg); } + //! @brief Get the command name (i.e. argv[0]). + virtual std::string getName() const { return getArg(0); } + //! @brief Get the number of arguments, including the command name. + virtual size_t getArgCount() const { return m_argv.size(); } + //@} + + //! @brief Send command to packetizer and on to peripheral. + virtual void sendTo(Packetizer &packetizer) {} + //! @brief Get response values vector. + virtual const uint32_vector_t *getResponseValues() const + { + return const_cast(&m_responseValues); + } + + //! @brief Get response as JSON string. + virtual std::string getResponse() const; + + //! @brief Log the response description. + virtual void logResponses() const; + + //! @brief Register an object for displaying transfer progress. + virtual void registerProgress(Progress *progress) { m_progress = progress; } +protected: + //! @brief Create packed buffer with SDP command. + void packCommand(uint16_t cmdType, uint32_t address, uint32_t format, uint32_t dataCount, uint32_t data); + + //! @brief Send command to packetizer. + status_t sendCommand(Packetizer &packetizer); + + //! @brief Send command and read HAB mode response. + status_t sendCommandGetHabMode(Packetizer &packetizer); + + //! @brief Read response value from device. + //! + //! Use kPacketTypeData for 64-byte read-register response, kPacketTypeCommand for 4-byte status response and HAB + //! mode response. + uint8_t *getStatusResponse(Packetizer &packetizer, packet_type_t type = kPacketType_Command); + + //! @brief Read HAB mode response from device. + bool isHabModeValid(Packetizer &packetizer); + +protected: + string_vector_t m_argv; //!< Vector of argument strings. + Progress *m_progress; //!< Variable for progress control. + uint32_vector_t m_responseValues; //!< Vector of response values. + uint8_t m_cmdBuf[kCmdSizeBytes]; //!< Buffer for command bytes. +}; + +/*! +* @brief Represents the SDP ReadRegister command. +* +*/ +class SDPReadRegister : public SDPCommand +{ +public: + //! @brief Constructor that takes argument vector. + SDPReadRegister(const string_vector_t *argv) + : SDPCommand(argv) + , m_address(0) + , m_format(32) + , m_dataCount(4) + , m_file() + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + uint32_t m_address; //!< Target memory address. + uint32_t m_format; //!< Register format. + uint32_t m_dataCount; //!< Number of bytes to read. + std::string m_file; //!< Data file path. +}; + +/*! +* @brief Represents the SDP WriteRegister command. +* +*/ +class SDPWriteRegister : public SDPCommand +{ +public: + //! @brief Constructor that takes argument vector. + SDPWriteRegister(const string_vector_t *argv) + : SDPCommand(argv) + , m_address(0) + , m_format(32) + , m_data(0) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + uint32_t m_address; //!< Target memory address. + uint32_t m_format; //!< Register format. + uint32_t m_data; //!< Data value to write. +}; + +/*! +* @brief Represents the SDP WriteFile command. +* +*/ +class SDPWriteFile : public SDPCommand +{ +public: + //! @brief Constructor that takes argument vector. + SDPWriteFile(const string_vector_t *argv) + : SDPCommand(argv) + , m_address(0) + , m_file() + , m_dataCount(0) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + uint32_t m_address; //!< Target memory address. + std::string m_file; //!< Data file path. + uint32_t m_dataCount; //!< Number of bytes to write. +}; + +/*! +* @brief Represents the SDP ErrorStatus command. +* +*/ +class SDPErrorStatus : public SDPCommand +{ +public: + //! @brief Constructor that takes argument vector. + SDPErrorStatus(const string_vector_t *argv) + : SDPCommand(argv) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); +}; + +/*! +* @brief Represents the SDP DcdWrite command. +* +*/ +class SDPDcdWrite : public SDPCommand +{ +public: + //! @brief Constructor that takes argument vector. + SDPDcdWrite(const string_vector_t *argv) + : SDPCommand(argv) + , m_address(0) + , m_file() + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + uint32_t m_address; //!< Temporary storage address. + std::string m_file; //!< Data file path. +}; + +/*! +* @brief Represents the SDP SkipDcdHeader command. +* +*/ +class SDPSkipDcdHeader : public SDPCommand +{ +public: + //! @brief Constructor that takes argument vector. + SDPSkipDcdHeader(const string_vector_t *argv) + : SDPCommand(argv) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); +}; + +/*! +* @brief Represents the SDP JumpAddress command. +* +*/ +class SDPJumpAddress : public SDPCommand +{ +public: + //! @brief Constructor that takes argument vector. + SDPJumpAddress(const string_vector_t *argv) + : SDPCommand(argv) + , m_address(0) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + uint32_t m_address; //!< Target memory address. +}; + +/*! +* @brief REpresents the SDP SetBaudrate command. +* +*/ +class SDPSetBaudrate : public SDPCommand +{ +public: + //! @brief Constructor that takes argument vector. + SDPSetBaudrate(const string_vector_t *argv) + : SDPCommand(argv) + , m_baudrate(115200) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); + +protected: + uint32_t m_baudrate; //!< Baudrate +}; + +/*! +* @brief Represents the SDP SkipDcdHeader command. +* +*/ +class SDPPing : public SDPCommand +{ +public: + //! @brief Constructor that takes argument vector. + SDPPing(const string_vector_t *argv) + : SDPCommand(argv) + { + } + + //! @brief Initialize. + virtual bool init(); + + //! @brief Send command to packetizer. + virtual void sendTo(Packetizer &packetizer); +}; + +} // namespace blfwk + +//! @} + +#endif // _SDPCommand_h_ diff --git a/src/blfwk/SDPUartPacketizer.h b/src/blfwk/SDPUartPacketizer.h new file mode 100644 index 0000000..b288b76 --- /dev/null +++ b/src/blfwk/SDPUartPacketizer.h @@ -0,0 +1,108 @@ +/* +* Copyright (c) 2015 Freescale Semiconductor, Inc. +* All rights reserved. +* +* SPDX-License-Identifier: BSD-3-Clause +*/ + +#ifndef _sdp_uart_packetizer_h_ +#define _sdp_uart_packetizer_h_ + +#include "Packetizer.h" +#include "UartPeripheral.h" +#include "serial.h" + +#pragma once + +//! @addtogroup sdp_uart_packetizer +//! @{ + +namespace blfwk +{ + +/*! +* @brief Provides source and sink for SDP packets that go over UART. +*/ +class SDPUartPacketizer : public Packetizer +{ +public: + //! @brief Constants. + enum _constants + { + kMaxReadSizeBytes = 64, //!< Max bytes returned by single read. + kNumBytesCommandResponse = 4, //!< Number of bytes in a kPacketTypeCommand read. + }; + +public: + //! @brief Default Constructor. + SDPUartPacketizer(UartPeripheral *peripheral, uint32_t readPacketTimeoutMs) + : Packetizer(peripheral, readPacketTimeoutMs) + , m_readBuf() + { + } + + //! @brief Destructor. + virtual ~SDPUartPacketizer() {} + + //! @brief Read a packet. + //! + //! Provides the address of a buffer containing the packet. + //! + //! @param packet Pointer location to write packet pointer + //! @param packetLength Number of bytes in returned packet + virtual status_t readPacket(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType) + { + uint32_t count = ((packetType == kPacketType_Command) || (m_readCount == 0)) ? kNumBytesCommandResponse : m_readCount; + if (count > sizeof(m_readBuf)) + { + count = sizeof(m_readBuf); + } + status_t status = getPeripheral()->read(m_readBuf, count, packetLength, m_packetTimeoutMs); + *packet = m_readBuf; + return status; + } + + //! @brief Write a packet. + //! + //! @param packet Pointer to packet to write + //! @param byteCount Number of bytes in packet + virtual status_t writePacket(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType) + { + return getPeripheral()->write(packet, byteCount); + } + + //! @brief Finalize. + virtual void finalize() {}; + + //! @brief Peripheral accessor. + virtual UartPeripheral *getPeripheral() { return (UartPeripheral *)m_peripheral; } + + //! @brief Abort data phase. + virtual void abortPacket() {}; + + //! @brief Send framing packet ack. + virtual void sync() {}; + + //! @brief Enable simulator command processor pump. + virtual void enableSimulatorPump() {}; + + //! @brief Pump simulator command processor. + virtual void pumpSimulator() {}; + + //! @brief Set aborted flag. + //! + //! Used for out-of-band flow control for simulator. + virtual void setAborted(bool aborted) {}; + + //! @brief Return the max packet size. + virtual uint32_t getMaxPacketSize() { return 0; } + +protected: + uint8_t m_readBuf[kMaxReadSizeBytes]; //!< Buffer for read data. +}; + +} // namespace blfwk + +//! @} + +#endif // _sdp_uart_packetizer_h_ diff --git a/src/blfwk/SDPUsbHidPacketizer.h b/src/blfwk/SDPUsbHidPacketizer.h new file mode 100644 index 0000000..52769e0 --- /dev/null +++ b/src/blfwk/SDPUsbHidPacketizer.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _sdp_usb_hid_packetizer_h_ +#define _sdp_usb_hid_packetizer_h_ + +#include "Packetizer.h" +#include "UsbHidPeripheral.h" +#include "hidapi.h" + +#pragma once + + //! @addtogroup sdp_usb_hid_packetizer + //! @{ + +namespace blfwk +{ + +/*! +* @brief Provides source and sink for SDP packets that go over USB HID class. +*/ +class SDPUsbHidPacketizer : public Packetizer +{ +public: + //! @brief Constants + enum _usbhid_constants + { + kMaxReportSizeBytes = 1024 + 1, //!< Number of bytes in largest report, including report type byte. + kIdReport1 = 1, //!< Code for report ID 1. + kReport1SizeBytes = 16 + 1, //!< Number of bytes in report 1, including report type byte. + kIdReport2 = 2, //!< Code for report ID 2. + kReport2SizeBytes = 1024 + 1, //!< Number of bytes in report 2, including report type byte. + kIdReport3 = 3, //!< Code for report ID 3. + #if defined(WIN32) + kReport3SizeBytes = 64 + 1, //!< Number of bytes in report 3, including report type byte. + #elif defined(LINUX) || defined(MACOSX) + kReport3SizeBytes = 4 + 1, //!< Number of bytes in report 3, including report type byte. + #endif + kIdReport4 = 4, //!< Code for report ID 4. + kReport4SizeBytes = 64 + 1, //!< Number of bytes in report 4, including report type byte. + kPollPacketMaxRetryCnt = 50, //!< Number of read retries. + }; + +public: + //! @brief Default Constructor. + SDPUsbHidPacketizer(UsbHidPeripheral *peripheral, uint32_t readPacketTimeoutMs) + : Packetizer(peripheral, readPacketTimeoutMs) + { + } + + //! @brief Destructor. + virtual ~SDPUsbHidPacketizer() {} + + //! @brief Read a packet. + //! + //! Provides the address of a buffer containing the packet. + //! + //! @param packet Pointer location to write packet pointer + //! @param packetLength Number of bytes in returned packet + virtual status_t readPacket(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType); + + //! @brief Write a packet. + //! + //! @param packet Pointer to packet to write + //! @param byteCount Number of bytes in packet + virtual status_t writePacket(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType); + + //! @brief Finalize. + virtual void finalize() {}; + + //! @brief Peripheral accessor. + virtual UsbHidPeripheral *getPeripheral() { return (UsbHidPeripheral *)m_peripheral; } + + //! @brief Abort data phase. + virtual void abortPacket() {}; + + //! @brief Send framing packet ack. + virtual void sync() {}; + + //! @brief Enable simulator command processor pump. + virtual void enableSimulatorPump() {}; + + //! @brief Pump simulator command processor. + virtual void pumpSimulator() {}; + + //! @brief Set aborted flag. + //! + //! Used for out-of-band flow control for simulator. + virtual void setAborted(bool aborted) {}; + + //! @brief Return the max packet size. + virtual uint32_t getMaxPacketSize() { return 0; } + +protected: + uint8_t m_report[kMaxReportSizeBytes]; //!< Buffer for report bytes. +}; + +} // namespace blfwk + +//! @} + +#endif // _sdp_usb_hid_packetizer_h_ diff --git a/src/blfwk/SRecordSourceFile.h b/src/blfwk/SRecordSourceFile.h new file mode 100644 index 0000000..a849a04 --- /dev/null +++ b/src/blfwk/SRecordSourceFile.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_SRecordSourceFile_h_) +#define _SRecordSourceFile_h_ + +#include "SourceFile.h" +#include "StSRecordFile.h" +#include "StExecutableImage.h" + +namespace blfwk +{ +/*! + * \brief Executable file in the Motorola S-record format. + * + * Instead of presenting each S-record in the file separately, this class + * builds up a memory image of all of the records. Records next to each other + * in memory are coalesced into a single memory region. The data source that + * is returned from createDataSource() exposes these regions as its segments. + * + * Because the S-record format does not support the concepts, no support is + * provided for named sections or symbols. + */ +class SRecordSourceFile : public SourceFile +{ +public: + //! \brief Default constructor. + SRecordSourceFile(const std::string &path); + + //! \brief Destructor. + virtual ~SRecordSourceFile() {} + //! \brief Test whether the \a stream contains a valid S-record file. + static bool isSRecordFile(std::istream &stream); + + //! \name Opening and closing + //@{ + //! \brief Opens the file. + virtual void open(); + + //! \brief Closes the file. + virtual void close(); + //@} + + //! \name Format capabilities + //@{ + virtual bool supportsNamedSections() const { return false; } + virtual bool supportsNamedSymbols() const { return false; } + //@} + + //! \name Data sources + //@{ + //! \brief Returns data source for the entire file. + virtual DataSource *createDataSource(); + //@} + + //! \name Entry point + //@{ + //! \brief Returns true if an entry point was set in the file. + virtual bool hasEntryPoint(); + + //! \brief Returns the entry point address. + virtual uint32_t getEntryPointAddress(); + //@} + +protected: + StSRecordFile *m_file; //!< S-record parser instance. + StExecutableImage *m_image; //!< Memory image of the S-record file. + bool m_hasEntryRecord; //!< Whether an S7,8,9 record was found. + StSRecordFile::SRecord m_entryRecord; //!< Record for the entry point. + +protected: + //! \brief Build memory image of the S-record file. + void buildMemoryImage(); +}; + +}; // namespace blfwk + +#endif // _SRecordSourceFile_h_ diff --git a/src/blfwk/SearchPath.h b/src/blfwk/SearchPath.h new file mode 100644 index 0000000..0df3c90 --- /dev/null +++ b/src/blfwk/SearchPath.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_searchpath_h_) +#define _searchpath_h_ + +#include +#include + +/*! + * \brief Handles searching a list of paths for a file. + */ +class PathSearcher +{ +public: + //! + enum _target_type + { + kFindFile, + kFindDirectory + }; + + //! + typedef enum _target_type target_type_t; + +protected: + //! Global search object singleton. + static PathSearcher *s_searcher; + +public: + //! \brief Access global path searching object. + static PathSearcher &getGlobalSearcher(); + +public: + //! \brief Constructor. + PathSearcher() {} + //! \brief Add a new search path to the end of the list. + void addSearchPath(std::string &path); + + //! \brief Attempts to locate a file by using the search paths. + bool search(const std::string &base, target_type_t targetType, bool searchCwd, std::string &result); + +protected: + typedef std::list string_list_t; //!< Linked list of strings. + string_list_t m_paths; //!< Ordered list of paths to search. + + //! \brief Returns whether \a path is absolute. + bool isAbsolute(const std::string &path); + + //! \brief Combines two paths into a single one. + std::string joinPaths(const std::string &first, const std::string &second); +}; + +#endif // _searchpath_h_ diff --git a/src/blfwk/SerialPacketizer.h b/src/blfwk/SerialPacketizer.h new file mode 100644 index 0000000..8b8c715 --- /dev/null +++ b/src/blfwk/SerialPacketizer.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _serial_packetizer_h_ +#define _serial_packetizer_h_ + +#include "Packetizer.h" +#include "UartPeripheral.h" +#include "bootloader_common.h" +#include "packet/serial_packet.h" + +//! @addtogroup serial_packetizer +//! @{ + +namespace blfwk +{ +// Forward declarations. +class Peripheral; + +/*! + * @brief Provides source and sink for packets that go over the serial peripherals. + */ +class SerialPacketizer : public Packetizer +{ +public: + //! @brief Constructor. + SerialPacketizer(Peripheral *peripheral, uint32_t packetTimeoutMs); + + //! @brief Destructor. + virtual ~SerialPacketizer(); + + //! @brief Peripheral accessor. + virtual UartPeripheral *getPeripheral() { return dynamic_cast(m_peripheral); } + //! @brief Read a packet. + //! + //! Provides the address of a buffer containing the packet. + //! + //! @param packet Pointer location to write packet pointer + //! @param packetLength Number of bytes in returned packet. + virtual status_t readPacket(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType); + + //! @brief Write a packet. + //! + //! @param packet Pointer to packet to write. + //! @param byteCount Number of bytes in packet. + virtual status_t writePacket(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType); + + //! @brief Abort data phase. + virtual void abortPacket(); + + //! @brief Send framing packet ack. + virtual void sync(); + + //! @brief Finalize. + virtual void finalize(); + + //! @brief Enable simulator command processor pump. + virtual void enableSimulatorPump() {} + //! @brief Pump simulator command processor. + virtual void pumpSimulator() {} + //! @brief Set aborted flag. + virtual void setAborted(bool aborted) {} + //! @brief Return the max packet size. + virtual uint32_t getMaxPacketSize(); + + //! @brief Delay milliseconds. + void host_delay(uint32_t milliseconds); + + //! @brief Send a ping packet and receive an ack. + //! + //! This is a method for host only side pinging of the target. The reponse from the + //! target to a ping packet is a ping response packet. Since the target may or may + //! not be online there is optionally a series of retries to make the best attempt + //! at communication possible + //! + //! @param retries The number of attempts that should be made. + //! @param delay The time in milliseconds between each attempt. + //! @param comSpeed The peripheral baud rate. Used in order to calculate the + //! receive delay in the case of low com speeds such as 100 and 300 which need + //! nearly a second to complete + virtual status_t ping( + int retries, unsigned int delay, ping_response_t *response, int comSpeed, int *actualComSpeed); + +protected: + //! @brief Send ACK if needed. + status_t send_deferred_ack(); + + //! @brief Read packet using serial framing. + //! + //! On return, caller must call flow control method to send AckContinue or AckWait followed by Continue. + status_t serial_packet_read(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType); + + //! @brief Write packet using serial framing. + status_t serial_packet_write(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType); + + //! @brief Abort data phase. + //! + //! Respond to next host data packet with AckAbort instead of Ack + //! (i.e. receiver data phase abort). + void serial_packet_abort(); + + //! @brief Get max packet size. + uint32_t serial_packet_get_max_packet_size(); + + //! @brief Send a sync packet of the specified type. + status_t serial_packet_send_sync(uint8_t framingPacketType); + + //! @brief Send a ping message back in response to a ping. + status_t serial_send_ping_response(); + + //! @brief Wait for an ACK, handling NAKs as needed. + status_t wait_for_ack_packet(); + + //! @brief Read from peripheral until entire data framing packet read. + status_t read_data_packet(framing_data_packet_t *packet, uint8_t *data, packet_type_t packetType); + + //! @brief Read from peripheral until start byte found. + status_t read_start_byte(framing_header_t *header); + + //! @brief Read from peripheral until packet header found. + status_t read_header(framing_header_t *header); + + //! @brief Read from peripheral until packet length found. + status_t read_length(framing_data_packet_t *packet); + + //! @brief Read from peripheral until crc16 is found. + status_t read_crc16(framing_data_packet_t *packet); + + //! @brief Calculate crc over framing data packet. + uint16_t calculate_framing_crc16(framing_data_packet_t *packet, const uint8_t *data); + + serial_data_t m_serialContext; +}; + +} // namespace blfwk + +//! @} + +#endif // _serial_packetizer_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/SimPacketizer.h b/src/blfwk/SimPacketizer.h new file mode 100644 index 0000000..6fec971 --- /dev/null +++ b/src/blfwk/SimPacketizer.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _sim_packetizer_h_ +#define _sim_packetizer_h_ + +#include "Packetizer.h" +#include "SimPeripheral.h" +#include "packet/command_packet.h" + +namespace blfwk +{ +// Forward declarations. +class Peripheral; + +/*! + * @brief Provides source and sink for packets in the simulator space. + */ +class SimPacketizer : public Packetizer +{ +public: + //! @brief Constants + enum _simPacketizer_contants + { + kSimReadTimeoutMs = 5000 + }; + +public: + //! @brief Default Constructor. + SimPacketizer(SimPeripheral *peripheral); + + //! @brief Destructor. + virtual ~SimPacketizer(); + + //! @brief Peripheral accessor. + virtual SimPeripheral *getPeripheral() { return dynamic_cast(m_peripheral); } + //! @brief Read a packet. + //! + //! Provides the address of a buffer containing the packet. + //! + //! @param packet Pointer location to write packet pointer + //! @param packetLength Number of bytes in returned packet. + virtual status_t readPacket(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType); + + //! @brief Write a packet. + //! + //! @param packet Pointer to packet to write. + //! @param byteCount Number of bytes in packet. + virtual status_t writePacket(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType); + + //! @brief Abort data phase. + virtual void abortPacket() { setAborted(true); } + //! @brief Send framing packet ack. + virtual void sync(){}; + + //! @brief Finalize. + virtual void finalize(); + + //! @brief Enable simulator command processor pump. + virtual void enableSimulatorPump() { m_isPumpEnabled = true; } + //! @brief Pump simulator command processor. + virtual void pumpSimulator(); + + //! @brief Set aborted flag. + //! + //! Used for out-of-band flow control for simulator. + virtual void setAborted(bool aborted) { m_isAborted = aborted; } + //! @brief Return the max packet size. + virtual uint32_t getMaxPacketSize() { return kDefaultMaxPacketSize; } +protected: + bool m_isPumpEnabled; //!< True if simulator pump enabled. + bool m_isAborted; //!< Data phase abort requested by receiver. + uint8_t m_buffer[kDefaultMaxPacketSize]; //!< Buffer for bytes used to build read packet. +}; + +} // namespace blfwk + +#endif // _sim_packetizer_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/SimPeripheral.h b/src/blfwk/SimPeripheral.h new file mode 100644 index 0000000..2d4d6ce --- /dev/null +++ b/src/blfwk/SimPeripheral.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _sim_peripheral_h_ +#define _sim_peripheral_h_ + +#include "blfwk/Peripheral.h" + +namespace blfwk +{ +/*! + * @brief Peripheral that operates in the simulation space. + * + * Simulation peripherals are connected together using the streams provided to the init() method. + * For example, to connect a "host" peripheral to a "device" peripheral, specify the host's output + * stream as the device's input stream. + */ +class SimPeripheral : public Peripheral +{ +public: + //! @brief Default Constructor. + SimPeripheral(uchar_deque_t *inStream, uchar_deque_t *outStream) + : m_inStream(inStream) + , m_outStream(outStream) + { + } + + //! @brief Read bytes. + //! + //! @param buffer Pointer to buffer. + //! @param requestedBytes Number of bytes to read. + //! @param actualBytes Number of bytes actually read. + //! @param timeoutMs Time in milliseconds to wait for read to complete. + virtual status_t read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t timeoutMs); + + //! @brief Write bytes. + //! + //! @param buffer Pointer to buffer to write + //! @param byteCount Number of bytes to write + virtual status_t write(const uint8_t *buffer, uint32_t byteCount); + +protected: + uchar_deque_t *m_inStream; //!< Writes go to this stream. + uchar_deque_t *m_outStream; //!< Reads come from this stream. +}; + +} // namespace blfwk + +#endif // _sim_peripheral_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/Simulator.h b/src/blfwk/Simulator.h new file mode 100644 index 0000000..a9c5c40 --- /dev/null +++ b/src/blfwk/Simulator.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _Simulator_h_ +#define _Simulator_h_ + +#include "Bootloader.h" +#include "Command.h" +#include "SimulatorMemory.h" +#include "SimPacketizer.h" +#include "Peripheral.h" +#include "Logging.h" + +#include "bootloader/bl_command.h" +#include "bootloader/bl_context.h" + +namespace blfwk +{ +/*! + * @brief Represents the host bootloader. + * + * This class provides a convenient way to access other bootloader + * framework objects. + */ +class Simulator : public Bootloader +{ +public: + //! @brief A vector of memory stores. + typedef std::vector memory_vector_t; + + //! @brief Get the singleton simulator object. + static Simulator &getSimulator() + { + //! @brief Singleton object. + static Simulator theSimulator; + return theSimulator; + } + + //! @brief Destructor. + virtual ~Simulator(); + + //! @brief Initialize. + void init(); + + //! @brief Configure and open state files. + //! + //! Must be called to open or create state files. + //! + //! @param pathToDir Directory for state files. + //! @param forceCreate True to re-create state files even if they exist. + bool openStateFiles(const std::string &pathToDir, bool forceCreate); + + //! @name Accessors. + //@{ + + //! @brief Get the host packetizer. + SimPacketizer *getHost() const { return dynamic_cast(m_hostPacketizer); } + //! @brief Get the device packetizer. + SimPacketizer *getDevice() const { return m_devicePacketizer; } + //! @brief Get a device state memory store. + //! + //! index Index into memory map for the simulated device. + MemoryStore *getMemoryStore(int index) const { return m_memoryStore[index]; } + //@} + +protected: + //! @brief Constructor. + Simulator(); + SimPacketizer *m_devicePacketizer; //!< Packet interface to recieve commands on. + uchar_deque_t m_commandStream; + uchar_deque_t m_responseStream; + command_processor_data_t m_commandProcessor; + bool m_areStateFilesOpen; //!< True if state files are in use + memory_vector_t m_memoryStore; //!< Vector of memory stores, one per map entry. + OptionsStore m_optionsStore; //!< Persistent options store. + property_store_t m_propertyStore; + peripheral_descriptor_t m_activePeripheral; //!< Descriptor for the active peripheral. + FileLogger *m_logger; //!< Singleton logger instance. +}; + +} // namespace blfwk + +#endif // _Simulator_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/SimulatorMemory.h b/src/blfwk/SimulatorMemory.h new file mode 100644 index 0000000..2cd2821 --- /dev/null +++ b/src/blfwk/SimulatorMemory.h @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _Memory_h_ +#define _Memory_h_ + +#include "blfwk/host_types.h" + +namespace blfwk +{ +/*! + * @brief Represents target device memory as a disk file. + */ +class MemoryStore +{ +public: + //! @brief Map indicies. + enum + { + kMapIndexFlash = 0, //!< Flash memory + kMapIndexSRAM = 1 //!< SRAM + }; + +public: + //! @brief Constructor that takes a map index. + //! + //! @param mapIndex Index into memory map array + //! @param fillByte Optional byte erase value, default 0 + MemoryStore(int mapIndex, uint32_t fillByte = 0) + : m_mapIndex(mapIndex) + , m_fillByte(fillByte) + , m_size(0) + , m_startAddress(0) + , m_memoryFile(NULL) + { + } + + //! @brief Open the store. + //! + //! @param pathToDir Directory for memory backing file. + //! @param forceCreate True to re-create backing file even if it exists. + bool open(const std::string &pathToDir, bool forceCreate); + + //! @brief Close the store. + void close() + { + if (m_memoryFile) + { + fclose(m_memoryFile); + m_memoryFile = NULL; + } + } + + //! @brief Read from memory. + //! + //! The requested offset is adjusted by the store's start address to create a zero-based + //! file offset. + //! + //! @param offset Offset into memory region + //! @param size Number of bytes to read + //! @param buffer Destination buffer + //! @return Number of bytes read + size_t read(long int offset, size_t size, unsigned char *buffer); + + //! @brief Write to memory. + //! + //! The requested offset is adjusted by the store's start address to create a zero-based + //! file offset. + //! + //! @param offset Offset into memory region + //! @param size Number of bytes to write + //! @param buffer Source buffer + //! @return Number of bytes written + size_t write(long int offset, size_t size, const unsigned char *buffer); + + //! @brief Erase memory. + //! + //! The requested offset is adjusted by the store's start address to create a zero-based + //! file offset. + //! + //! @param offset Offset into memory region + //! @param size Number of bytes to erase + void erase(long int offset, size_t size); + + //! @brief Erase all memory. + void erase() { erase(0, m_size); } +protected: + int m_mapIndex; //!< Index of memory map entry. + uint8_t m_fillByte; //!< Erase value. + size_t m_size; //!< Size of memory file. + uint32_t m_startAddress; //!< Address address from map. + FILE *m_memoryFile; //!< Handle to memory file. +}; + +/*! + * @brief Represents target Flash memory. + */ +class FlashMemoryStore : public MemoryStore +{ +public: + //! @brief Default Constructor. + FlashMemoryStore() + : MemoryStore(MemoryStore::kMapIndexFlash, ~0) + { + } +}; + +/*! + * @brief Represents target SRAM memory. + */ +class SramMemoryStore : public MemoryStore +{ +public: + //! @brief Default Constructor. + SramMemoryStore() + : MemoryStore(MemoryStore::kMapIndexSRAM) + { + } +}; + +/*! + * @brief Holds persistent options. + */ +class OptionsStore +{ +public: + //! @brief Constructor. + OptionsStore() + : m_optionsFile() + { + } + + //! @brief Initialize the store. + //! + //! @param pathToDir Directory for options file. + //! @param forceCreate True to re-create options file even if it exists. + bool init(const std::string &pathToDir, bool forceCreate); + + //! @brief Persist the store. + void persist(); + + //! @name Accessors. + //@{ + + //! @todo get/set for property store values + + //@} + +protected: + std::string m_optionsFile; //!< Options file name. +}; + +} // namespace blfwk + +//////////////////////////////////////////////////////////////////////////////// +// Prototypes +//////////////////////////////////////////////////////////////////////////////// + +#if defined(__cplusplus) +extern "C" { +#endif // __cplusplus + +//! @brief Read from simulated flash memory. +//! +//! @param address Simulated address. +//! @param length Number of bytes to read. +//! @param buffer Destination buffer. +//! +//! @retval kStatusMemoryReadFailed +//! @retval kStatus_Success +status_t sim_flash_read(uint32_t address, uint32_t length, uint8_t *buffer); + +//! @brief Write to simulated flash memory. +//! +//! @param address Simulated address. +//! @param length Number of bytes to write. +//! @param buffer Source buffer. +//! +//! @retval kStatusMemoryWriteFailed +//! @retval kStatus_Success +status_t sim_flash_write(uint32_t address, uint32_t length, const uint8_t *buffer); + +//! @brief Fill simulated flash memory. +//! +//! @param address Simulated address. +//! @param length Number of bytes to write. +//! @param pattern 4-byte pattern. +//! +//! @retval kStatusMemoryWriteFailed +//! @retval kStatus_Success +status_t sim_flash_fill(uint32_t address, uint32_t length, uint32_t pattern); + +//! @brief Erase simulated flash memory. +//! +//! @param address Simulated address. +//! @param length Number of bytes to write. +//! +//! @retval kStatusMemoryWriteFailed +//! @retval kStatus_Success +status_t sim_flash_erase(uint32_t address, uint32_t length); + +//! @brief Erase all simulated flash memory. +//! +//! @retval kStatusMemoryWriteFailed +//! @retval kStatus_Success +status_t sim_flash_erase_all(void); + +//! @brief Read from simulated SRAM memory. +//! +//! @param address Simulated address. +//! @param length Number of bytes to read. +//! @param buffer Destination buffer. +//! +//! @retval kStatusMemoryReadFailed +//! @retval kStatus_Success +status_t sim_sram_mem_read(uint32_t address, uint32_t length, uint8_t *buffer); + +//! @brief Write to simulated SRAM memory. +//! +//! @param address Simulated address. +//! @param length Number of bytes to write. +//! @param buffer Source buffer. +//! +//! @retval kStatusMemoryWriteFailed +//! @retval kStatus_Success +status_t sim_sram_mem_write(uint32_t address, uint32_t length, const uint8_t *buffer); + +//! @brief Fill simulated SRAM memory. +//! +//! @param address Simulated address. +//! @param length Number of bytes to write. +//! @param pattern 4-byte pattern. +//! +//! @retval kStatusMemoryWriteFailed +//! @retval kStatus_Success +status_t sim_sram_mem_fill(uint32_t address, uint32_t length, uint32_t pattern); + +//! Read from simulated device memory. +//! +//! Not supported. +//! +//! @retval kStatus_Success; +status_t sim_device_mem_read(uint32_t address, uint32_t length, uint8_t *buffer); + +//! Write to simulated device memory. +//! +//! Not supported. +//! +//! @retval kStatus_Success; +status_t sim_device_mem_write(uint32_t address, uint32_t length, const uint8_t *buffer); + +//! Fill simulated device memory. +//! +//! Not supported. +//! +//! @retval kStatus_Success; +status_t sim_device_mem_fill(uint32_t address, uint32_t length, uint32_t pattern); + +#if defined(__cplusplus) +} +#endif // __cplusplus + +#endif // _Memory_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/SourceFile.h b/src/blfwk/SourceFile.h new file mode 100644 index 0000000..765529f --- /dev/null +++ b/src/blfwk/SourceFile.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_SourceFile_h_) +#define _SourceFile_h_ + +#include +#include +#include +#include "smart_ptr.h" +#include "DataSource.h" +#include "DataTarget.h" +#include "StringMatcher.h" +#include "OptionContext.h" + +namespace blfwk +{ +/*! + * \brief Abstract base class for a source file containing executable code. + * + * The purpose of this class cluster is to provide a common interface for + * accessing the contents of different file formats. This is accomplished + * through several small sets of methods along with the DataSource and + * DataTarget classes. + * + * The primary interface for creating instances of SourceFile is the static + * SourceFile::openFile() function. It will create the correct subclass of + * SourceFile by inspecting the file to determine its type. + */ +class SourceFile +{ +public: + // \brief Factory function that creates the correct subclass of SourceFile. + static SourceFile *openFile(const std::string &path); + + //! Set of supported executable image file formats. + enum source_file_t + { + kBinarySourceFile, //!< \see blfwk::BinarySourceFile + kELFSourceFile, //!< \see blfwk::ELFSourceFile + kIntelHexSourceFile, //!< \see blfwk::IntelHexSourceFile + kSBSourceFile, //!< \see blfwk::SBSourceFile + kSRecordSourceFile //!< \see blfwk::SRecordSourceFile + }; + +public: + //! \brief Default constructor. + SourceFile(const std::string &path, source_file_t filetype); + + //! \brief Destructor. + virtual ~SourceFile(); + + //! \brief Set the option context. + //! + //! The source file will take ownership of the @a context and delete it + //! when the source file is itself deleted. + inline void setOptions(OptionContext *context) { m_options = context; } + //! \brief Return the option context. + inline const OptionContext *getOptions() const { return m_options; } + //! \brief Return the file type. + inline source_file_t getFileType() const { return m_filetype; } + //! \brief Returns the path to the file. + inline const std::string &getPath() const { return m_path; } + //! \brief Get the size in bytes of the file. + unsigned getSize() const { return m_size; } + //! \name Opening and closing + //@{ + //! \brief Opens the file. + virtual void open(); + + //! \brief Closes the file. + virtual void close(); + + //! \brief Returns whether the file is already open. + virtual bool isOpen() const { return (bool)m_stream && const_cast(m_stream.get())->is_open(); } + //@} + + //! \name Format capabilities + //@{ + virtual bool supportsNamedSections() const = 0; + virtual bool supportsNamedSymbols() const = 0; + //@} + + //! \name Data source creation + //@{ + //! \brief Creates a data source from the entire file. + virtual DataSource *createDataSource() = 0; + + //! \brief Creates a data source out of one or more sections of the file. + //! + //! The \a selector object is used to perform the section name comparison. + //! If the file does not support named sections, or if there is not a + //! section with the given name, this method may return NULL. + virtual DataSource *createDataSource(StringMatcher &matcher) { return NULL; } + //! \brief Creates a data source out of one section of the file. + virtual DataSource *createDataSource(const std::string §ion); + //@} + + //! \name Entry point + //@{ + //! \brief Returns true if an entry point was set in the file. + virtual bool hasEntryPoint() = 0; + + //! \brief Returns the entry point address. + virtual uint32_t getEntryPointAddress() { return 0; } + //@} + + //! \name Data target creation + //@{ + virtual DataTarget *createDataTargetForSection(const std::string §ion) { return NULL; } + virtual DataTarget *createDataTargetForSymbol(const std::string &symbol) { return NULL; } + virtual DataTarget *createDataTargetForEntryPoint(); + //@} + + //! \name Symbols + //@{ + //! \brief Returns whether a symbol exists in the source file. + virtual bool hasSymbol(const std::string &name) { return false; } + //! \brief Returns the value of a symbol. + virtual uint32_t getSymbolValue(const std::string &name) { return 0; } + //! \brief Returns the size of a symbol. + virtual unsigned getSymbolSize(const std::string &name) { return 0; } + //@} + +protected: + std::string m_path; //!< Path to the file. + smart_ptr m_stream; //!< File stream, or NULL if file is closed. + smart_ptr m_options; //!< Table of option values. + source_file_t m_filetype; //!< Image file type. + unsigned m_size; //!< The size in bytes of the file. + + //! \brief Internal access to the input stream object. + inline std::ifstream *getStream() { return m_stream; } +}; + +/*! + * \brief Binary data file. + */ +class BinarySourceFile : public SourceFile +{ +public: + //! \brief Default constructor. + BinarySourceFile(const std::string &path, source_file_t sourceFileType = kBinarySourceFile); + + //! \name Format capabilities + //@{ + virtual bool supportsNamedSections() const { return false; } + virtual bool supportsNamedSymbols() const { return false; } + //@} + + //! \name Data source creation + //@{ + //! \brief Creates an unmapped data source from the entire file. + virtual DataSource *createDataSource(); + //@} + + //! \name Entry point + //@{ + //! \brief Initialize entry point and stack pointer from assumed vetcor table + //! at the beginning of the file. + void guessEntryPointAndStackPointer(); + + //! \brief Returns true if an entry point was set in the file. + virtual bool hasEntryPoint() { return m_entry_point != 0xffffffff; } + //! \brief Returns the entry point address. + virtual uint32_t getEntryPointAddress() { return m_entry_point; } + //! \brief Returns the stack pointer. + uint32_t getStackPointer() { return m_stack_pointer; } + //@} + +protected: + uint32_t m_entry_point; + uint32_t m_stack_pointer; +}; + +}; // namespace blfwk + +#endif // _SourceFile_h_ diff --git a/src/blfwk/SpiPeripheral.h b/src/blfwk/SpiPeripheral.h new file mode 100644 index 0000000..6d31571 --- /dev/null +++ b/src/blfwk/SpiPeripheral.h @@ -0,0 +1,109 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef _spi_peripheral_h_ +#define _spi_peripheral_h_ + +#include "Peripheral.h" +#include "blfwk/spi.h" +#include "packet/command_packet.h" + +//! @addtogroup spi_peripheral +//! @{ + +namespace blfwk +{ +/*! + * @brief Peripheral that talks to the target device over SPI port hardware. + */ +class SpiPeripheral : public Peripheral +{ +public: + //! @breif Constants. + enum _spi_peripheral_constants + { + // The read() implementation for the SpiPeripheral does not use this the timeout parameter. + kSpiPeripheral_UnusedTimeout = 0, + // Serial timeout is set to this default during init(). + kSpiPeripheral_DefaultReadTimeoutMs = 1, + kSpiPeripheral_DefaultSpeedKHz = 100, + kSpiPeripheral_DefaultClockPolarity = 1, + kSpiPeripheral_DefaultClockPhase = 1, + kSpiPeripheral_DefaultBitSequence = 1, + kSpiPeripheral_DefaultBitsPerWord = 8, + }; + +public: + //! @brief Parameterized constructor that opens the serial port. + //! + //! Opens and configures the port. Throws exception if port configuration fails. + //! + //! @param port OS file path for SPI port. For example "/dev/spidev-0.0" on Linux. + //! @param speed Port speed in KHz, e.g. 100(means 100KHz). + //! @param polarity SPI clock polarity, 1 for active-high, 0 for active-low. + //! @param phase SPI clock phase, 1 for second clock edge, 0 for first clock edge. + //! @param sequence SPI data transfer bits sequence. 1 for LSB, 0 for MSB. + SpiPeripheral(const char *port, + long speed = kSpiPeripheral_DefaultSpeedKHz, + uint8_t polarity = kSpiPeripheral_DefaultClockPolarity, + uint8_t phase = kSpiPeripheral_DefaultClockPhase, + uint8_t sequence = kSpiPeripheral_DefaultBitSequence); + + //! @brief Destructor. + virtual ~SpiPeripheral(); + + //! @brief Flush. + //! + //! should be called on an open SPI port in order to flush any remaining data in the SPI RX buffer + void flushRX(); + + //! @brief Read bytes. + //! + //! @param buffer Pointer to buffer. + //! @param requestedBytes Number of bytes to read. + //! @param actualBytes Number of bytes actually read. + //! @param timeoutMs Time in milliseconds to wait for read to complete. + virtual status_t read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t unused_timeoutMs); + + //! @brief Write bytes. + //! + //! @param buffer Pointer to buffer to write + //! @param byteCount Number of bytes to write + virtual status_t write(const uint8_t *buffer, uint32_t byteCount); + + //! @brief Return peripheral Type + virtual _host_peripheral_types get_type(void) { return kHostPeripheralType_SPI; } + +protected: + //! @brief Initialize. + //! + //! Opens and configures the port. + //! + //! Note: following COM port configuration is assumed: 8 bits, 1 stop bit, no parity. + //! + //! @param port OS file path for SPI port. For example "/dev/spidev-0.0" on Linux. + //! @param speed Port speed in KHz, e.g. 100(means 100KHz). + //! @param polarity SPI clock polarity, 1 for active-high, 0 for active-low. + //! @param phase SPI clock phase, 1 for second clock edge, 0 for first clock edge. + //! @param direction SPI data transfer bits direction. 1 for LSB, 0 for MSB. + bool init(const char *port, long speed, uint8_t polarity, uint8_t phase, uint8_t direction); + + int m_fileDescriptor; //!< Port file descriptor. + uint8_t m_buffer[kDefaultMaxPacketSize]; //!< Buffer for bytes used to build read packet. + uint32_t m_current_ReadTimeout; //!< The last value sent to serial_set_read_timeout(). +}; + +} // namespace blfwk + +//! @} + +#endif // _spi_peripheral_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/StELFFile.h b/src/blfwk/StELFFile.h new file mode 100644 index 0000000..9c50533 --- /dev/null +++ b/src/blfwk/StELFFile.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_StELFFile_h_) +#define _StELFFile_h_ + +#include +#include +#include +#include +#include + +#include "blfwk/ELF.h" +#include "blfwk/stdafx.h" + +//! Variations of the ARM ELF format. +typedef enum +{ + eIllegalVariant = 0, //!< Illegal variant. + eARMVariant = 1, //!< Standard ARM ELF specification. + eGHSVariant, //!< Green Hills Software variant. + eGCCVariant //!< GNU Compiler Collection variant. +} ELFVariant_t; + +//! Possible ARM ELF symbol types. +typedef enum +{ + eUnknownSymbol, + eARMSymbol, + eThumbSymbol, + eDataSymbol +} ARMSymbolType_t; + +/*! + * \brief Parser for Executable and Linking Format (ELF) files. + * + * The stream passed into the constructor needs to stay open for the life + * of the object. This is because calls to getSectionDataAtIndex() and + * getSegmentDataAtIndex() read the data directly from the input stream. + */ +class StELFFile +{ +public: + typedef std::vector::const_iterator const_section_iterator; + typedef std::vector::const_iterator const_segment_iterator; + +public: + //! \brief Constructor. + StELFFile(std::istream &inStream); + + //! \brief Destructor. + virtual ~StELFFile(); + + //! \name File format variant + //@{ + //! \brief Return the ELF format variant to which this file is set. + virtual ELFVariant_t ELFVariant() { return m_elfVariant; } + //! \brief Set the ELF format variation to either #eARMVariant or #eGHSVariant. + virtual void setELFVariant(ELFVariant_t variant) { m_elfVariant = variant; } + //@} + + //! \name File name + //@{ + virtual void setName(const std::string &inName) { m_name = inName; } + virtual std::string getName() const { return m_name; } + //@} + + //! \name ELF header + //@{ + //! \brief Returns the ELF file header. + inline const Elf32_Ehdr &getFileHeader() const { return m_header; } + //@} + + //! \name Sections + //! Methods pertaining to the object file's sections. + //@{ + //! \brief Returns the number of sections in the file. + inline unsigned getSectionCount() const { return static_cast(m_sectionHeaders.size()); } + //! \brief Returns a reference to section number \a inIndex. + const Elf32_Shdr &getSectionAtIndex(unsigned inIndex) const; + + inline const_section_iterator getSectionBegin() const { return m_sectionHeaders.begin(); } + inline const_section_iterator getSectionEnd() const { return m_sectionHeaders.end(); } + //! \brief Returns the index of the section with the name \a inName. + unsigned getIndexOfSectionWithName(const std::string &inName); + + //! \brief Returns the data for the section. + uint8_t *getSectionDataAtIndex(unsigned inIndex); + + //! \brief Returns the data for the section. + uint8_t *getSectionData(const_section_iterator inSection); + //@} + + //! \name Segments + //! Methods for accessing the file's program headers for segments. + //@{ + //! \brief Returns the number of segments, or program headers, in the file. + inline unsigned getSegmentCount() const { return static_cast(m_programHeaders.size()); } + //! \brief Returns a reference to the given segment. + const Elf32_Phdr &getSegmentAtIndex(unsigned inIndex) const; + + inline const_segment_iterator getSegmentBegin() const { return m_programHeaders.begin(); } + inline const_segment_iterator getSegmentEnd() const { return m_programHeaders.end(); } + //! \brief Returns the data of the specified segment. + uint8_t *getSegmentDataAtIndex(unsigned inIndex); + + //! \brief Returns the data of the specified segment. + uint8_t *getSegmentData(const_segment_iterator inSegment); + //@} + + //! \name String table + //! Methods for accessing the string tables. + //@{ + //! \brief Returns a string from the file's section name string table. + std::string getSectionNameAtIndex(unsigned inIndex); + + //! \brief Returns a string from any string table in the object file. + std::string getStringAtIndex(unsigned inStringTableSectionIndex, unsigned inStringIndex); + //@} + + //! \name Symbol table + //! Methods for accessing the object file's symbol table. Currently only + //! a single symbol table with the section name ".symtab" is supported. + //@{ + //! \brief Returns the number of symbols in the default ".symtab" symbol table. + unsigned getSymbolCount(); + + //! \brief Returns the symbol with index \a inIndex. + const Elf32_Sym &getSymbolAtIndex(unsigned inIndex); + + //! \brief Returns the section index of the string table containing symbol names. + unsigned getSymbolNameStringTableIndex() const; + + //! \brief Returns the name of the symbol described by \a inSymbol. + std::string getSymbolName(const Elf32_Sym &inSymbol); + + unsigned getIndexOfSymbolAtAddress(uint32_t symbolAddress, bool strict = true); + + ARMSymbolType_t getTypeOfSymbolAtIndex(unsigned symbolIndex); + //@} + + //! \name Debugging + //@{ + void dumpSections(); + void dumpSymbolTable(); + //@} + +protected: + std::istream &m_stream; //!< The source stream for the ELF file. + ELFVariant_t m_elfVariant; //!< Variant of the ARM ELF format specification. + std::string m_name; //!< File name. (optional) + Elf32_Ehdr m_header; //!< The ELF file header. + std::vector m_sectionHeaders; //!< All of the section headers. + std::vector m_programHeaders; //!< All of the program headers. + unsigned m_symbolTableIndex; //!< Index of ".symtab" section, or #SHN_UNDEF if not present. + + /*! + * Little structure containing information about cached section data. + */ + struct SectionDataInfo + { + uint8_t *m_data; //!< Pointer to section data. + unsigned m_size; //!< Section data size in bytes. + bool m_swapped; //!< Has this section been byte swapped yet? Used for symbol table. + }; + typedef std::map SectionDataMap; + SectionDataMap m_sectionDataCache; //!< Cached data of sections. + + //! \brief Reads a section's data either from cache or from disk. + SectionDataInfo &getCachedSectionData(unsigned inSectionIndex); + + //! \brief Reads the file, section, and program headers into memory. + void readFileHeaders(); + + uint8_t *readSectionData(const Elf32_Shdr &inHeader); + uint8_t *readSegmentData(const Elf32_Phdr &inHeader); + + //! \brief Byte swaps the symbol table data into host endianness. + void byteSwapSymbolTable(const Elf32_Shdr &header, SectionDataInfo &info); +}; + +/*! + * \brief Simple exception thrown to indicate an error in the input ELF file format. + */ +class StELFFileException : public std::runtime_error +{ +public: + //! \brief Default constructor. + StELFFileException(const std::string &inMessage) + : std::runtime_error(inMessage) + { + } +}; + +#endif // _StELFFile_h_ diff --git a/src/blfwk/StExecutableImage.h b/src/blfwk/StExecutableImage.h new file mode 100644 index 0000000..a92dd03 --- /dev/null +++ b/src/blfwk/StExecutableImage.h @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_StExecutableImage_h_) +#define _StExecutableImage_h_ + +#include "blfwk/stdafx.h" +#include + +/*! + * \brief Used to build a representation of memory regions. + * + * An intermediate representation of the memory regions and segments loaded + * from an executable file. Also used to find contiguous segments that are + * specified separately in the source file. + * + * When regions are added, an attempt is made to coalesce contiguous regions. + * In order for this to succeed, the touching regions must be of the same + * type and have the same permissions. Regions are also kept sorted by their + * address range as they are added. + * + * \todo Implement alignment support. + */ +class StExecutableImage +{ +public: + //! Possible types of memory regions. + typedef enum + { + TEXT_REGION, //!< A region containing data or instructions. + FILL_REGION //!< Region to be initialized with zero bytes. + } MemoryRegionType; + + //! Memory region flag constants. + enum + { + REGION_READ_FLAG = 1, //!< Region is readable. + REGION_WRITE_FLAG = 2, //!< Region is writable. + REGION_EXEC_FLAG = 4, //!< Region may contain executable code. + + REGION_RW_FLAG = REGION_READ_FLAG | REGION_WRITE_FLAG, //!< Region is read-write. + + //! Mask to access only permissions flags for a region. + REGION_PERM_FLAG_MASK = 0x7 + }; + + /*! + * Representation of a contiguous region of memory. + * + * \todo Add comparison operators so we can use the STL sort algorithm. + */ + struct MemoryRegion + { + MemoryRegionType m_type; //!< Memory region type. + uint32_t m_address; //!< The 32-bit start address of this region. + uint32_t m_length; //!< Number of bytes in this region. + uint8_t *m_data; //!< Pointer to data. Will be NULL for FILL_REGION type. + unsigned m_flags; //!< Flags for the region. + + //! \brief Calculates the address of the last byte occupied by this region. + inline uint32_t endAddress() const { return m_address + m_length - 1; } + //! \brief Equality operator. + bool operator==(const MemoryRegion &other) const; + }; + + //! A list of #StExecutableImage::MemoryRegion objects. + typedef std::list MemoryRegionList; + + //! The iterator type used to access #StExecutableImage::MemoryRegion objects. This type + //! is used by the methods #getRegionBegin() and #getRegionEnd(). + typedef MemoryRegionList::const_iterator const_iterator; + + //! The possible actions for regions matching an address filter range. + typedef enum + { + ADDR_FILTER_NONE, //!< Do nothing. + ADDR_FILTER_ERROR, //!< Raise an error exception. + ADDR_FILTER_WARNING, //!< Raise a warning exception. + ADDR_FILTER_CROP //!< Don't include the matching address range in the executable image. + } AddressFilterAction; + + /*! + * An address filter consists of a single address range and an action. If a + * memory region overlaps the filter's range then the action will be performed. + * The possible filter actions are defined by the #AddressFilterAction enumeration. + */ + struct AddressFilter + { + AddressFilterAction m_action; //!< Action to be performed when the filter is matched. + uint32_t m_fromAddress; //!< Start address of the filter. Should be lower than or equal to #m_toAddress. + uint32_t m_toAddress; //!< End address of the filter. Should be higher than or equal to #m_fromAddress. + unsigned m_priority; //!< Priority for this filter. Zero is the lowest priority. + + //! \brief Constructor. + AddressFilter(AddressFilterAction action, uint32_t from, uint32_t to, unsigned priority = 0) + : m_action(action) + , m_fromAddress(from) + , m_toAddress(to) + , m_priority(priority) + { + } + + //! \brief Test routine. + bool matchesMemoryRegion(const MemoryRegion ®ion) const; + + //! \brief Compares two address filter objects. + int compare(const AddressFilter &other) const; + + //! \name Comparison operators + //@{ + inline bool operator<(const AddressFilter &other) const { return compare(other) == -1; } + inline bool operator>(const AddressFilter &other) const { return compare(other) == 1; } + inline bool operator==(const AddressFilter &other) const { return compare(other) == 0; } + inline bool operator<=(const AddressFilter &other) const { return compare(other) != 1; } + inline bool operator>=(const AddressFilter &other) const { return compare(other) != -1; } + //@} + }; + + //! List of #StExecutableImage::AddressFilter objects. + typedef std::list AddressFilterList; + + //! The exception class raised for the #ADDR_FILTER_ERROR and #ADDR_FILTER_WARNING + //! filter actions. + class address_filter_exception + { + public: + //! \brief Constructor. + //! + //! A local copy of \a matchingFilter is made, in case the image and/or filter + //! are on the stack and would be disposed of when the exception is raised. + address_filter_exception(bool isError, std::string &imageName, const AddressFilter &matchingFilter) + : m_isError(isError) + , m_imageName(imageName) + , m_filter(matchingFilter) + { + } + + //! \brief Returns true if the exception is an error. Otherwise the exception + //! is for a warning. + inline bool isError() const { return m_isError; } + //! \brief + inline std::string getImageName() const { return m_imageName; } + //! \brief + inline const AddressFilter &getMatchingFilter() const { return m_filter; } + protected: + bool m_isError; + std::string m_imageName; + AddressFilter m_filter; + }; + +public: + //! \brief Constructor. + StExecutableImage(int inAlignment = 256); + + //! \brief Copy constructor. + StExecutableImage(const StExecutableImage &inOther); + + //! \brief Destructor. + virtual ~StExecutableImage(); + + //! \name Image name + //! Methods for getting and setting the image name. + //@{ + //! \brief Sets the image's name to \a inName. + virtual void setName(const std::string &inName); + + //! \brief Returns a copy of the image's name. + virtual std::string getName() const; + //@} + + //! \name Regions + //! Methods to add and access memory regions. + //@{ + //! \brief Add a region to be filled with zeroes. + virtual void addFillRegion(uint32_t inAddress, unsigned inLength); + + //! \brief Add a region containing data to be loaded. + virtual void addTextRegion(uint32_t inAddress, const uint8_t *inData, unsigned inLength); + + //! \brief Returns the total number of regions. + //! + //! Note that this count may not be the same as the number of calls to + //! addFillRegion() and addTextRegion() due to region coalescing. + inline unsigned getRegionCount() const { return static_cast(m_image.size()); } + //! \brief Returns a reference to the region specified by \a inIndex. + const MemoryRegion &getRegionAtIndex(unsigned inIndex) const; + + //! \brief Return an iterator to the first region. + inline const_iterator getRegionBegin() const { return m_image.begin(); } + //! \brief Return an iterator to the next-after-last region. + inline const_iterator getRegionEnd() const { return m_image.end(); } + //@} + + //! \name Entry point + //@{ + //! \brief Sets the entry point address. + inline void setEntryPoint(uint32_t inEntryAddress) + { + m_entry = inEntryAddress; + m_hasEntry = true; + } + + //! \brief Returns true if an entry point has been set. + inline bool hasEntryPoint() const { return m_hasEntry; } + //! \brief Returns the entry point address. + inline uint32_t getEntryPoint() const { return hasEntryPoint() ? m_entry : 0; } + //@} + + //! \name Address filter + //@{ + //! \brief Add a new address filter. + virtual void addAddressFilter(const AddressFilter &filter); + + //! \brief Add multiple address filters at once. + //! + //! The template argument \a _T must be an iterator or const iterator that + //! dereferences to an StExecutableImage::AddressFilter reference. All filters + //! from \a from to \a to will be added to the address filter list. + template + void addAddressFilters(_T from, _T to) + { + _T it = from; + for (; it != to; ++it) + { + addAddressFilter(*it); + } + } + + //! \brief Remove all active filters. + virtual void clearAddressFilters(); + + //! \brief Process all active filters and perform associated actions. + virtual void applyAddressFilters(); + //@} + +protected: + std::string m_name; //!< The name of the image (can be a file name, for instance). + int m_alignment; //!< The required address alignment for each memory region. + bool m_hasEntry; //!< True if an entry point has been set. + uint32_t m_entry; //!< Entry point address. + MemoryRegionList m_image; //!< The memory regions. + AddressFilterList m_filters; //!< List of active address filters. + + //! \brief Deletes the portion \a region that overlaps \a filter. + void cropRegionToFilter(MemoryRegion ®ion, const AddressFilter &filter); + + //! \brief Inserts the region in sorted order or merges with one already in the image. + void insertOrMergeRegion(MemoryRegion &inRegion); + + //! \brief Merges two memory regions into one. + void mergeRegions(MemoryRegion &inOldRegion, MemoryRegion &inNewRegion); +}; + +#endif // _StExecutableImage_h_ diff --git a/src/blfwk/StIntelHexFile.h b/src/blfwk/StIntelHexFile.h new file mode 100644 index 0000000..4d2c0b9 --- /dev/null +++ b/src/blfwk/StIntelHexFile.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_StIntelHexFile_h_) +#define _StIntelHexFile_h_ + +#include "stdafx.h" +#include +#include +#include +#include + +enum +{ + //! The required first character of a Intel Hex + INTELHEX_START_CHAR = ':', + + //! The minimum length of a Hex. This is the start char (1) + datacount (2) + + //! addr (4) + type (2) + check sum (2). + INTELHEX_MIN_LENGTH = 11, + + //! Index of the first character of the address field. + INTELHEX_ADDRESS_START_CHAR_INDEX = 3, + + //! Index of the first character of the record type field. + INTELHEX_TYPE_START_CHAR_INDEX = 7, + + //! Index of the first character of the record type field. + INTELHEX_DATA_START_CHAR_INDEX = 9 +}; + +//! Intel Hex Record Type +enum +{ + //! Data Record, which contains data and a 16-bit start address for the data. + INTELHEX_RECORD_DATA = 0x00, + + //! End of File Record, which specifies the end of the hex file, and + //! must occur exactly once per file in the last line of the file. + INTELHEX_RECORD_END_OF_FILE = 0x01, + + //! Extended Segment Address Record, which is used to specify bits 4- 19 of the Segment Base Address. + INTELHEX_RECORD_EXTENDED_SEGMENT_ADDRESS = 0x02, + + //! Start Segment Address Record, which is used to specify the execution start address for the object file. + INTELHEX_RECORD_START_SEGMENT_ADDRESS = 0x03, + + //! Extended Linear Address Record, which is used to specify bits 16- 31 of the Linear Base Address. + INTELHEX_RECORD_EXTENDED_LINEAR_ADDRESS = 0x04, + + //! Start Linear Address Record, which is used to specify the execution start address for the object file. + INTELHEX_RECORD_START_LINEAR_ADDRESS = 0x05 +}; + +/*! + * \brief Intel Hex parser. + * + * This class takes an input stream and parses it as a Intel Hex file. While + * the individual records that comprise the file are available for access, the + * class also provides a higher-level view of the contents. It processes the + * individual records and builds an image of what the memory touched by the + * file looks like. Then you can access the contiguous sections of memory. + */ +class StIntelHexFile +{ +public: + /*! + * Structure representing each individual line of the Intel Hex input data. + */ + struct IntelHex + { + unsigned m_dataCount; //!< The number of bytes in the data field. + uint32_t m_address; //!< The address offset of the data. + unsigned m_type; //!< Type of the data field. 00: Data + //!< 01: End of File + //!< 02: Extended Segment Address + //!< 03: Start Segment Address + //!< 04: Extended Linear Address + //!< 05: Start Linear Address + uint8_t *m_data; //!< Pointer to data, or NULL if no data for this record. + uint8_t m_checksum; //!< The checksum byte used to verify the record. + }; + + //! Iterator type. + typedef std::vector::const_iterator const_iterator; + +public: + //! \brief Constructor. + StIntelHexFile(std::istream &inStream); + + //! \brief Destructor. + virtual ~StIntelHexFile(); + + //! \name File name + //@{ + virtual void setName(const std::string &inName) { m_name = inName; } + virtual std::string getName() const { return m_name; } + //@} + + //! \name Parsing + //@{ + //! \brief Determine if the file is a Intel Hex file. + virtual bool isIntelHexFile(); + + //! \brief Parse the entire IntelHex input stream. + virtual void parse(); + //@} + + //! \name Record access + //@{ + //! \return the number of Intel Hex that have been parsed from the input stream. + inline unsigned getRecordCount() const { return static_cast(m_records.size()); } + //! \return iterator for + inline const_iterator getBegin() const { return m_records.begin(); } + inline const_iterator getEnd() const { return m_records.end(); } + //@} + + //! \name Operators + //@{ + inline const IntelHex &operator[](unsigned inIndex) { return m_records[inIndex]; } + //@} + +protected: + std::istream &m_stream; //!< The input stream for the Intel Hex data. + std::vector m_records; //!< Vector of Intel Hex in the input data. + + std::string m_name; //!< File name. (optional) + + //! \name Parsing utilities + //@{ + virtual void parseLine(std::string &inLine); + + bool isHexDigit(char c); + int hexDigitToInt(char digit); + int readHexByte(std::string &inString, int inIndex); + //@} +}; + +/*! + * \brief Simple exception thrown to indicate an error in the input Intel Hex data format. + */ +class StIntelHexParseException : public std::runtime_error +{ +public: + //! \brief Default constructor. + StIntelHexParseException(const std::string &inMessage) + : std::runtime_error(inMessage) + { + } +}; + +#endif // _StIntelHexFile_h_ diff --git a/src/blfwk/StSRecordFile.h b/src/blfwk/StSRecordFile.h new file mode 100644 index 0000000..b540494 --- /dev/null +++ b/src/blfwk/StSRecordFile.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_StSRecordFile_h_) +#define _StSRecordFile_h_ + +//#include +#include "stdafx.h" +#include +#include +#include +#include + +enum +{ + //! The required first character of an S-record. + SRECORD_START_CHAR = 'S', + + //! The minimum length of a S-record. This is the type (2) + count (2) + addr (4) + cksum (2). + SRECORD_MIN_LENGTH = 10, + + //! Index of the first character of the address field. + SRECORD_ADDRESS_START_CHAR_INDEX = 4 +}; + +/*! + * \brief S-record parser. + * + * This class takes an input stream and parses it as an S-record file. While + * the individual records that comprise the file are available for access, the + * class also provides a higher-level view of the contents. It processes the + * individual records and builds an image of what the memory touched by the + * file looks like. Then you can access the contiguous sections of memory. + */ +class StSRecordFile +{ +public: + /*! + * Structure representing each individual line of the S-record input data. + */ + struct SRecord + { + unsigned m_type; //!< Record number type, such as 9 for "S9", 3 for "S3" and so on. + unsigned m_count; //!< Number of character pairs (bytes) from address through checksum. + uint32_t m_address; //!< The address specified as part of the S-record. + unsigned m_dataCount; //!< Number of bytes of data. + uint8_t *m_data; //!< Pointer to data, or NULL if no data for this record type. + uint8_t m_checksum; //!< The checksum byte present in the S-record. + }; + + //! Iterator type. + typedef std::vector::const_iterator const_iterator; + +public: + //! \brief Constructor. + StSRecordFile(std::istream &inStream); + + //! \brief Destructor. + virtual ~StSRecordFile(); + + //! \name File name + //@{ + virtual void setName(const std::string &inName) { m_name = inName; } + virtual std::string getName() const { return m_name; } + //@} + + //! \name Parsing + //@{ + //! \brief Determine if the file is an S-record file. + virtual bool isSRecordFile(); + + //! \brief Parses the entire S-record input stream. + virtual void parse(); + //@} + + //! \name Record access + //@{ + //! \return the number of S-records that have been parsed from the input stream. + inline unsigned getRecordCount() const { return static_cast(m_records.size()); } + //! \return iterator for + inline const_iterator getBegin() const { return m_records.begin(); } + inline const_iterator getEnd() const { return m_records.end(); } + //@} + + //! \name Operators + //@{ + inline const SRecord &operator[](unsigned inIndex) { return m_records[inIndex]; } + //@} + +protected: + std::istream &m_stream; //!< The input stream for the S-record data. + std::vector m_records; //!< Vector of S-records in the input data. + + std::string m_name; //!< File name. (optional) + + //! \name Parsing utilities + //@{ + virtual void parseLine(std::string &inLine); + + bool isHexDigit(char c); + int hexDigitToInt(char digit); + int readHexByte(std::string &inString, int inIndex); + //@} +}; + +/*! + * \brief Simple exception thrown to indicate an error in the input SRecord data format. + */ +class StSRecordParseException : public std::runtime_error +{ +public: + //! \brief Default constructor. + StSRecordParseException(const std::string &inMessage) + : std::runtime_error(inMessage) + { + } +}; + +#endif // _StSRecordFile_h_ diff --git a/src/blfwk/StringMatcher.h b/src/blfwk/StringMatcher.h new file mode 100644 index 0000000..62cbae3 --- /dev/null +++ b/src/blfwk/StringMatcher.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_StringMatcher_h_) +#define _StringMatcher_h_ + +#include + +namespace blfwk +{ +/*! + * \brief Abstract interface class used to select strings by name. + */ +class StringMatcher +{ +public: + //! \brief Performs a single string match test against testValue. + //! + //! \retval true The \a testValue argument matches. + //! \retval false No match was made against the argument. + virtual bool match(const std::string &testValue) = 0; +}; + +/*! + * \brief String matcher subclass that matches all test strings. + */ +class WildcardMatcher : public StringMatcher +{ +public: + //! \brief Always returns true, indicating a positive match. + virtual bool match(const std::string &testValue) { return true; } +}; + +/*! + * \brief Simple string matcher that compares against a fixed value. + */ +class FixedMatcher : public StringMatcher +{ +public: + //! \brief Constructor. Sets the string to compare against to be \a fixedValue. + FixedMatcher(const std::string &fixedValue) + : m_value(fixedValue) + { + } + + //! \brief Returns whether \a testValue is the same as the value passed to the constructor. + //! + //! \retval true The \a testValue argument matches the fixed compare value. + //! \retval false The argument is not the same as the compare value. + virtual bool match(const std::string &testValue) { return testValue == m_value; } +protected: + const std::string &m_value; //!< The section name to look for. +}; + +}; // namespace blfwk + +#endif // _StringMatcher_h_ diff --git a/src/blfwk/UartPeripheral.h b/src/blfwk/UartPeripheral.h new file mode 100644 index 0000000..17991b8 --- /dev/null +++ b/src/blfwk/UartPeripheral.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _uart_peripheral_h_ +#define _uart_peripheral_h_ + +#include "Peripheral.h" +#include "packet/command_packet.h" + +//! @addtogroup uart_peripheral +//! @{ + +namespace blfwk +{ +/*! + * @brief Peripheral that talks to the target device over COM port hardware. + */ +class UartPeripheral : public Peripheral +{ +public: + //! @breif Constants. + enum _uart_peripheral_constants + { + // The read() implementation for the UartPeripheral does not use this the timeout parameter. + kUartPeripheral_UnusedTimeout = 0, + // Serial timeout is set to this default during init(). + kUartPeripheral_DefaultReadTimeoutMs = 1000, + kUartPeripheral_DefaultBaudRate = 9600 + }; + +public: + //! @brief Parameterized constructor that opens the serial port. + //! + //! Opens and configures the port. Throws exception if port configuration fails. + //! + //! Note: following COM port configuration is assumed: 8 bits, 1 stop bit, no parity. + //! + //! @param port OS file path for COM port. For example "COM1" on Windows. + //! @param speed Port speed, e.g. 9600. + UartPeripheral(const char *port, long speed = kUartPeripheral_DefaultBaudRate); + + //! @brief Destructor. + virtual ~UartPeripheral(); + + //! @brief Flush. + //! + //! should be called on an open COM port in order to flush any remaining data in the UART RX buffer + void flushRX(); + + //! @brief Read bytes. + //! + //! @param buffer Pointer to buffer. + //! @param requestedBytes Number of bytes to read. + //! @param actualBytes Number of bytes actually read. + //! @param timeoutMs Time in milliseconds to wait for read to complete. + virtual status_t read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t unused_timeoutMs); + + //! @brief Write bytes. + //! + //! @param buffer Pointer to buffer to write + //! @param byteCount Number of bytes to write + virtual status_t write(const uint8_t *buffer, uint32_t byteCount); + + //! @brief Return peripheral Type + virtual _host_peripheral_types get_type(void) { return kHostPeripheralType_UART; } + +protected: + //! @brief Initialize. + //! + //! Opens and configures the port. + //! + //! Note: following COM port configuration is assumed: 8 bits, 1 stop bit, no parity. + //! + //! @param port OS file path for COM port. For example "COM1" on Windows. + //! @param speed Port speed, e.g. 9600. + bool init(const char *port, long speed); + char *port_name; //!< Port name + int m_fileDescriptor; //!< Port file descriptor. + uint8_t m_buffer[kDefaultMaxPacketSize]; //!< Buffer for bytes used to build read packet. +}; + +} // namespace blfwk + +//! @} + +#endif // _uart_peripheral_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/Updater.h b/src/blfwk/Updater.h new file mode 100644 index 0000000..c5f5457 --- /dev/null +++ b/src/blfwk/Updater.h @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_Updater_h_) +#define _Updater_h_ + +#include "Bootloader.h" +#include "SourceFile.h" + +//! @addtogroup host_updater +//! @{ + +using namespace blfwk; + +namespace blfwk +{ +//! @name Updater states with descriptions. +//@{ +struct updater_enum_t +{ + uint32_t value; + std::string description; + + updater_enum_t() + : value(0) + , description("") + { + } + + updater_enum_t(uint32_t value, const char *description) + : value(value) + , description(description) + { + } + + updater_enum_t(const updater_enum_t &old_enum) + { + value = old_enum.value; + description = old_enum.description.c_str(); + } +}; +const updater_enum_t kUpdaterState_NotReady(0, "Not ready"); +const updater_enum_t kUpdaterState_Ready(1, "Ready"); +const updater_enum_t kUpdaterState_Working(2, "Working"); +const updater_enum_t kUpdaterState_Idle(3, "Idle"); +const updater_enum_t kUpdaterState_Complete(4, "Complete"); +//@} + +//! @name Updater tasks with descriptions. +//@{ +const updater_enum_t kUpdaterTask_Erasing(0, "Erasing"); +const updater_enum_t kUpdaterTask_Flashing(1, "Writing"); +const updater_enum_t kUpdaterTask_Reseting(2, "Reseting"); +const updater_enum_t kUpdaterTask_Executing(3, "Jumping"); + +struct updater_task_t +{ + updater_enum_t task_desc; + uint32_t current; + uint32_t total; + + updater_task_t(const updater_enum_t &task_desc, uint32_t total) + : task_desc(task_desc) + , current(0) + , total(total) + { + } + + updater_task_t() + : task_desc() + , current(0) + , total(0) + { + } +}; + +typedef std::vector updater_task_vector_t; + +//! @name Updater operations with descriptions. +//@{ +const updater_enum_t kUpdaterOperation_Update(0, "Update"); + +//! @name Updater operation. +//@{ +struct updater_operation_t +{ + updater_enum_t operation_desc; + updater_task_vector_t tasks; + uint32_t current_task; + bool user_stopped; + + uint32_t current() + { + uint32_t current = 0; + + for (uint32_t i = 0; i < current_task; ++i) + { + current += tasks[i].total; + } + + current += tasks[current_task].current; + + return current; + } + + uint32_t total() + { + uint32_t total = 0; + for (uint32_t i = 0; i < tasks.size(); ++i) + { + total += tasks[i].total; + } + return total; + } + + updater_operation_t(updater_enum_t operation_desc) + : operation_desc(operation_desc) + , tasks() + , current_task(0) + , user_stopped(false) + { + } + + updater_operation_t(uint32_t value, const char *description) + : operation_desc(value, description) + , tasks() + , current_task(0) + , user_stopped(false) + { + } +}; +//@} + +/*! + * \brief Update class contains the functionality necessary to update the + * firmware on a device running Bootloader. + * + * The purpose of this class is to provide a common interface for + * updating any device running the Bootloader from several different file formats. + */ +class Updater : public Bootloader +{ +public: + //! \brief Default constructor. + Updater(const Peripheral::PeripheralConfigData &config); + + //! \brief Destructor. + virtual ~Updater(); + + //! \name Update API. + //@{ + + //! @brief Type for the progress callback routine. + typedef void (*progress_callback_t)(updater_operation_t *op); + + //! @brief Struct used to monitor the Operation progress. + updater_operation_t m_operation; + + //! \brief Set the user-defined function to call on progress events. + //! + //! \param callback The function to callback with the progress data. + void setCallback(progress_callback_t callback) { m_progressCallback = callback; } + //! \brief Set callback for progress and abort control. + //! + //! \param callback The function to callback with the progress data. + //! \param abort The variable used for abort phase control. + void registerCallback(void (*callback)(int, int, int), bool *abort) + { + m_progress.registerCallback(callback, abort); + } + + //! \brief Program flash on the device. + //! + //! \exception std::runtime_error Raised if the file could not be opened successfully. + //! + //! \param filename The file to program into the device. + //! \param base_address The address on the device where the file wiill be written. + status_t flashFirmware(const char *filename, uint32_t base_address, uint32_t memoryId); + + //! \brief Erase all flash blocks and release MCU security + //! + //! \exception std::runtime_error Raised if the FlashEraseAllUnsecure command does not + //! return success. + //! Raised if the FlashEraseAllUnsecure command is not + //! supported. + void eraseAllUnsecure(); + + //! \brief Erase the specified FLASH region. + //! + //! \exception std::runtime_error Thrown if an error occurred. + //! + //! \param start The beginning address of the memory region to be erased. + //! + //! \param length The length in bytes of the memory region to be erased. + //! + //! \param memoryId The ID of the memory to erase. + void eraseRegion(uint32_t start, uint32_t length, uint32_t memoryId); + + //! \brief Execute the FlashEraseAll bootloader command. + //! + //! \exception std::runtime_error Thrown if an error occurred + //! + //! \param memoryId The ID of the memory to erase. + void eraseAll(uint32_t memoryId); + + //! \brief Release security using BackdoorKey + //! + //! \exception std::runtime_error Raised if the FlashSecurityDisable command does not + //! return success. + //! Raised if the FlashSecurityDisable command is not + //! supported. + //! Raised if the parameter is illegal + //! \param backdoor_key The 16 hex digitals used to release the security + void unlock(string backdoor_key); + + //! \brief get total internal flash size of current device. + //! + //! \exception std::runtime_error Raised if the operation is failed. + //! + //! \return the size in bytes. Return 0 means no internal Flash available. + uint32_t getInternalFlashSize(void); + + //! \brief get total internal RAM size of current device. + //! + //! \exception std::runtime_error Raised if the operation is failed. + //! + //! \return the size in bytes. Return 0 means no internal RAM available. + uint32_t getInternalRAMSize(void); + + //! \brief Execute the FlashProgramOnce bootloader command. + //! + //! \exception std::runtime_error Thrown if an error occurred while sending the + //! FlashEraseAll bootloader command. + //! + //! \param index The index of a specific program once field. + //! + //! \param byteCount The length in bytes of a specific program once field. + //! + //! \param data The 8/16 hex digitals to write. + void programOnce(uint32_t index, uint32_t byteCount, string data, bool isLsb); + //@} + // + +protected: + //! \name Bootloader commands + //@{ + + //! \brief Execute the FlashEraseRegion bootloader command. + //! + //! \exception std::runtime_error Thrown if an error occurred while sending the + //! FlashEraseRegion(start, length) bootloader command. + //! + //! \param start The beginning address of the memory region to be erased. + //! + //! \param length The length in bytes of the memory region to be erased. + void eraseFlashRegion(uint32_t start, uint32_t length); + + //! \brief Execute the FlashEraseAll bootloader command. + //! + //! \exception std::runtime_error Thrown if an error occurred while sending the + //! FlashEraseAll bootloader command. + void eraseFlashAll(); + + //! \brief Execute the write-memory bootloader command. + //! + //! \exception std::runtime_error Thrown if an error occurred while sending the + //! WriteMemory(segment) bootloader command. + //! + //! \param [in,out] segment The DatSource::Segment that represents the data to be written to the device. + void writeMemory(DataSource::Segment *segment); + + //! \brief Execute the write-memory bootloader command. + //! + //! \exception std::runtime_error Thrown if an error occurred while sending the + //! WriteMemory(vector refernce that contains the data to be written to the device. + //! \param [in] address The address on the device where the data will be written. + void writeMemory(uint32_t address, const uchar_vector_t &data); + + //! \brief Execute the write-memory bootloader command. + //! + //! \exception std::runtime_error Thrown if an error occurred while sending the + //! WriteMemory(segment) bootloader command. + //! + //! \param [in,out] segment The DatSource::Segment that represents the data to be written to the device. + void fuseProgram(DataSource::Segment *segment); + + //! \brief Execute the write-memory bootloader command. + //! + //! \exception std::runtime_error Thrown if an error occurred while sending the + //! WriteMemory(vector refernce that contains the data to be written to the device. + //! \param [in] address The address on the device where the data will be written. + void fuseProgram(uint32_t address, const uchar_vector_t &data); + + //! \brief Program flash procedure for SourceFile types. + status_t flashFromSourceFile(); + + //! \brief Program flash procedure for SB files. + status_t flashFromSBFile(const char *filename); + + //! \brief Check if the memory is supported by current device. + bool isMemorySupported(uint32_t memoryId); + +protected: + uint32_t m_base_address; //!< Base address of the image. + SourceFile *m_sourceFile; //!< SourceFile object. + uint32_t m_memoryId; //!< ID of the memory to flush the image to. + progress_callback_t m_progressCallback; //!< Callback used to report update progress. + Progress m_progress; //!< Progress control. + standard_version_t m_version; //!< Version of the bootloader running on current device. +}; + +}; // namespace blfwk + +//! @} + +#endif // _Updater_h_ diff --git a/src/blfwk/UsbHidPacketizer.h b/src/blfwk/UsbHidPacketizer.h new file mode 100644 index 0000000..59be7b2 --- /dev/null +++ b/src/blfwk/UsbHidPacketizer.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _usb_hid_packetizer_h_ +#define _usb_hid_packetizer_h_ + +#include "Packetizer.h" +#include "UsbHidPeripheral.h" +#include "hidapi.h" + +#include "bootloader_common.h" + +#include "bootloader_hid_report_ids.h" + +//! @addtogroup usb_hid_packetizer +//! @{ + +namespace blfwk +{ +/*! + * @brief Provides source and sink for packets that go over USB HID class. + */ +class UsbHidPacketizer : public Packetizer +{ +public: + //! @brief Constants + enum _usbhid_contants + { + kReadFlushTimeoutMs = 100000, + kPollAbortTimeoutMs = 10, + kPollPacketMaxRetryCnt = 50, + kContinuousReadMargin = 2, + }; +public: + //! @brief Default Constructor. + UsbHidPacketizer(UsbHidPeripheral *peripheral, uint32_t readPacketTimeoutMs); + + //! @brief Destructor. + virtual ~UsbHidPacketizer(); + + //! @brief Read a packet. + //! + //! Provides the address of a buffer containing the packet. + //! + //! @param packet Pointer location to write packet pointer + //! @param packetLength Number of bytes in returned packet + virtual status_t readPacket(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType); + + //! @brief Write a packet. + //! + //! @param packet Pointer to packet to write + //! @param byteCount Number of bytes in packet + virtual status_t writePacket(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType); + + //! @brief Abort data phase. + virtual void abortPacket(); + + //! @brief Send framing packet ack/nak. + virtual void sync(){}; + + //! @brief Finalize. + virtual void finalize(){}; + + //! @brief Enable simulator command processor pump. + virtual void enableSimulatorPump() {} + //! @brief Pump simulator command processor. + virtual void pumpSimulator() {} + //! @brief Set aborted flag. + virtual void setAborted(bool aborted) {} + //! @brief Returns the max packet size supported + virtual uint32_t getMaxPacketSize(); + + //! @brief Peripheral accessor. + virtual UsbHidPeripheral *getPeripheral() { return (UsbHidPeripheral *)m_peripheral; } +protected: + //! @brief Flush input from device. + virtual void flushInput(); + + //! @brief Poll overlapped read for receiver data phase abort. + bool pollForAbortPacket(); + +protected: + bl_hid_report_t m_report; //!< Used for building and receiving the report. + bl_hid_report_t m_abortReport; //!< Used for received abort report. + +private: + uint32_t + m_continuousReadCount; //!< Used for distinguish abort report for write-memory/receive-sb-file or read-memory +}; + +} // namespace blfwk + +//! @} + +#endif // _usb_hid_packetizer_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/UsbHidPeripheral.h b/src/blfwk/UsbHidPeripheral.h new file mode 100644 index 0000000..1f9cf28 --- /dev/null +++ b/src/blfwk/UsbHidPeripheral.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _UsbHidPeripheral_h_ +#define _UsbHidPeripheral_h_ + +#include "Peripheral.h" +#include "hidapi.h" + +//! @addtogroup host_usb_hid_peripheral +//! @{ + +namespace blfwk +{ +/*! + * @brief Represents a USB HID peripheral. + * + * Interface class for objects that provide the source for commands or sink for responses. + */ +class UsbHidPeripheral : public Peripheral +{ +public: + //! @brief Constants + enum _usbhid_contants + { + kDefault_Vid = 0x15a2, //!< Freescale VID + kDefault_Pid = 0x0073, //!< PID for KL25Z48M + //kK32H_Pid = 0x0083 //!< PID for K32H (Ultra) + kK32H_Pid = 0x0052 //!< test pid (mx50) + }; + +public: + //! @brief Default constructor. + //! + //! Uses vendor_id = kDefault_Vid and product_id = kDefault_Pid. + UsbHidPeripheral(); + + //! @brief Parameterized constructor. + //! + //! @param vendor_id The Vendor ID of the USB HID device. + //! @param product_id The Product ID of the USB HID device. + //! @param serial_number The Serial Number of the USB HID device. + UsbHidPeripheral(unsigned short vendor_id, unsigned short product_id, const char *serial_number, const char *path); + + //! @brief Destructor. + virtual ~UsbHidPeripheral(); + + //! @brief Read bytes. + //! + //! @param buffer Pointer to buffer + //! @param requestedBytes Number of bytes to read + //! @param timeoutMs Time in milliseconds to wait for read to complete. + //! @param actualBytes Number of bytes actually read. + virtual status_t read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t timeoutMS); + + //! @brief Write bytes. This is a do nothing function implemented here to satisfy abstract base class + //! requirements. This function is not used. The write(buffer, count, timeout) function is used + //! in this child class instead of the write(buffer, cout) function declared in the base class. + virtual status_t write(const uint8_t *buffer, uint32_t byteCount) { return kStatus_Success; } + + //! @brief Return peripheral Type + virtual _host_peripheral_types get_type(void) { return kHostPeripheralType_USB_HID; } + + //! @brief Write bytes. + //! + //! @param buffer Pointer to buffer to write + //! @param byteCount Number of bytes to write + //! @param timeoutMs Time in milliseconds to wait for write to complete. + status_t write(const uint8_t *buffer, uint32_t byteCount, uint32_t timeoutMS); + + //! @brief Return USB Vendor ID + unsigned short getVendorId() { return m_vendor_id; } + //! @brief Return USB Product ID + unsigned short getProductId() { return m_product_id; } + //! @brief Return USB Serial Number + const wchar_t *getSerialNumber() { return m_serial_number.c_str(); } +private: + //! @brief Initialize. + //! + //! Opens the HID device. + bool init(); + + unsigned short m_vendor_id; + unsigned short m_product_id; + std::wstring m_serial_number; + std::string m_path; + hid_device *m_device; //!< Device handle. +}; + +} // namespace blfwk + +//! @} + +#endif // _UsbHidPeripheral_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/Value.h b/src/blfwk/Value.h new file mode 100644 index 0000000..86196ca --- /dev/null +++ b/src/blfwk/Value.h @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_Value_h_) +#define _Value_h_ + +#include +#include "blfwk/stdafx.h" +#include "blfwk/int_size.h" +#include "blfwk/Blob.h" + +namespace blfwk +{ +/*! + * \brief Abstract base class for values of arbitrary types. + */ +class Value +{ +public: + Value() {} + virtual ~Value() {} + virtual std::string getTypeName() const = 0; + virtual size_t getSize() const = 0; +}; + +/*! + * \brief 32-bit signed integer value. + */ +class IntegerValue : public Value +{ +public: + IntegerValue() + : m_value(0) + { + } + IntegerValue(uint32_t value) + : m_value(value) + { + } + IntegerValue(const IntegerValue &other) + : m_value(other.m_value) + { + } + + virtual std::string getTypeName() const { return "integer"; } + virtual size_t getSize() const { return sizeof(m_value); } + inline uint32_t getValue() const { return m_value; } + inline operator uint32_t() const { return m_value; } + inline IntegerValue &operator=(uint32_t value) + { + m_value = value; + return *this; + } + +protected: + uint32_t m_value; //!< The integer value. +}; + +/*! + * \brief Adds a word size attribute to IntegerValue. + * + * The word size really only acts as an attribute that is carried along + * with the integer value. It doesn't affect the actual value at all. + * However, you can use the getWordSizeMask() method to mask off bits + * that should not be there. + * + * The word size defaults to a 32-bit word. + */ +class SizedIntegerValue : public IntegerValue +{ +public: + SizedIntegerValue() + : IntegerValue() + , m_size(kWordSize) + { + } + SizedIntegerValue(uint32_t value, int_size_t size = kWordSize) + : IntegerValue(value) + , m_size(size) + { + } + SizedIntegerValue(uint16_t value) + : IntegerValue(value) + , m_size(kHalfWordSize) + { + } + SizedIntegerValue(uint8_t value) + : IntegerValue(value) + , m_size(kByteSize) + { + } + SizedIntegerValue(const SizedIntegerValue &other) + : IntegerValue(other) + , m_size(other.m_size) + { + } + + virtual std::string getTypeName() const { return "sized integer"; } + virtual size_t getSize() const; + + inline int_size_t getWordSize() const { return m_size; } + inline void setWordSize(int_size_t size) { m_size = size; } + //! \brief Returns a 32-bit mask value dependant on the word size attribute. + uint32_t getWordSizeMask() const; + + //! \name Assignment operators + //! These operators set the word size as well as the integer value. + //@{ + SizedIntegerValue &operator=(uint8_t value) + { + m_value = value; + m_size = kByteSize; + return *this; + } + SizedIntegerValue &operator=(uint16_t value) + { + m_value = value; + m_size = kHalfWordSize; + return *this; + } + SizedIntegerValue &operator=(uint32_t value) + { + m_value = value; + m_size = kWordSize; + return *this; + } + //@} + +protected: + int_size_t m_size; //!< Size of the integer. +}; + +/*! + * \brief String value. + * + * Simply wraps the STL std::string class. + */ +class StringValue : public Value +{ +public: + StringValue() + : m_value() + { + } + StringValue(const std::string &value) + : m_value(value) + { + } + StringValue(const std::string *value) + : m_value(*value) + { + } + StringValue(const StringValue &other) + : m_value(other.m_value) + { + } + + virtual std::string getTypeName() const { return "string"; } + virtual size_t getSize() const { return m_value.size(); } + operator const char *() const { return m_value.c_str(); } + operator const std::string &() const { return m_value; } + operator std::string &() { return m_value; } + operator const std::string *() { return &m_value; } + operator std::string *() { return &m_value; } + StringValue &operator=(const StringValue &other) + { + m_value = other.m_value; + return *this; + } + StringValue &operator=(const std::string &value) + { + m_value = value; + return *this; + } + StringValue &operator=(const char *value) + { + m_value = value; + return *this; + } + +protected: + std::string m_value; +}; + +/*! + * \brief Binary object value of arbitrary size. + */ +class BinaryValue : public Value, public Blob +{ +public: + BinaryValue() + : Value() + , Blob() + { + } + + virtual std::string getTypeName() const { return "binary"; } + virtual size_t getSize() const { return getLength(); } +}; + +}; // namespace blfwk + +#endif // _Value_h_ diff --git a/src/blfwk/bootloader_config.h b/src/blfwk/bootloader_config.h new file mode 100644 index 0000000..2c2d646 --- /dev/null +++ b/src/blfwk/bootloader_config.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014, Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __BOOTLOADER_CONFIG_H__ +#define __BOOTLOADER_CONFIG_H__ + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// + +// +// Bootloader configuration options +// + +#define BL_HAS_QSPI_MODULE (0) + +#define BL_FEATURE_ENCRYPTION (0) + +#endif // __BOOTLOADER_CONFIG_H__ +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/doc/blfwk.dox b/src/blfwk/doc/blfwk.dox new file mode 100644 index 0000000..75e8879 --- /dev/null +++ b/src/blfwk/doc/blfwk.dox @@ -0,0 +1,58 @@ +/*! + +@defgroup blfwk Host Bootloader Framework +@brief Host Bootloader components. + +@defgroup bus_pal Bus Pal Transport +@ingroup blfwk +@brief Bus Pal transport support. + +@defgroup host_peripherals Peripherals +@ingroup blfwk +@brief Peripheral support. + +@defgroup bus_pal_peripheral Bus Pal Peripheral +@ingroup host_peripherals +@brief Bus Pal Peripheral. + +@defgroup uart_peripheral UART Peripheral +@ingroup host_peripherals +@brief UART Peripheral. + +@defgroup host_usb_hid_peripheral USB-HID Peripheral +@ingroup host_peripherals +@brief USB-HID Peripheral. + +@defgroup host_commands Commands +@ingroup blfwk +@brief Command support. + +@defgroup host_packetizers Packetizers +@ingroup blfwk +@brief Packet creation. + +@defgroup serial_packetizer Serial Packetizer +@ingroup host_packetizers +@brief Serial Packet creation. + +@defgroup usb_hid_packetizer USB-HID Packetizer +@ingroup host_packetizers +@brief USB-HID Packet creation. + +@defgroup logging Logging +@ingroup blfwk +@brief Logging support. + +@defgroup smart_pointer Smart Pointer +@ingroup blfwk +@brief Smart pointer. + +@defgroup host_error Error +@ingroup blfwk +@brief Errors. + +@defgroup host_updater Updater +@ingroup blfwk +@brief Updater application support. + +*/ diff --git a/src/blfwk/doc/blfwk.md b/src/blfwk/doc/blfwk.md new file mode 100644 index 0000000..bf20dee --- /dev/null +++ b/src/blfwk/doc/blfwk.md @@ -0,0 +1,18 @@ +Host Bootloader Framework {#blfwk} +===== + +Introduction +----- +The Bootloader Framework is a library of C++ classes implementing communication from a host PC to bootloader firmware running on a target device. Due to the nature of the underlying bootloader command and data protocol, the Bootloader Frameworks supports all target devices without requiring any device-specific knowledge. + +Applications +----- +The Bootloader Framework library is used by the Blhost command line tool and by the KinetisFlashTool GUI firmware download tool. It can be built as a library or included as source in a PC or embedded application. The current release includes tool chains to build the library for Windows OS and Mac OS X. + +Peripherals +----- +Support for the following PC peripherals is included: + +- UART (COM port) +- USB-HID (using custom reports) +- Bus Pal example (UART to I2C/SPI using special hardware) diff --git a/src/blfwk/format_string.h b/src/blfwk/format_string.h new file mode 100644 index 0000000..948e6e5 --- /dev/null +++ b/src/blfwk/format_string.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_format_string_h_) +#define _format_string_h_ + +#include +#include + +/*! + * \brief Returns a formatted STL string using printf format strings. + */ +std::string format_string(const char *fmt, ...); + +#endif // _format_string_h_ diff --git a/src/blfwk/hidapi.h b/src/blfwk/hidapi.h new file mode 100644 index 0000000..3ebea7e --- /dev/null +++ b/src/blfwk/hidapi.h @@ -0,0 +1,419 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 +#define HID_API_EXPORT __declspec(dllexport) +#define HID_API_CALL +#else +#define HID_API_EXPORT /**< API export macro */ +#define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif +struct hid_device_; +typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + +/** hidapi info structure */ +struct hid_device_info +{ + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. Valid on both Linux implementations + in all cases, and valid on the Windows implementation + only if the device contains more than one interface. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; +}; + +/** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. +*/ +int HID_API_EXPORT HID_API_CALL hid_init(void); + +/** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. +*/ +int HID_API_EXPORT HID_API_CALL hid_exit(void); + +/** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). +*/ +struct hid_device_info HID_API_EXPORT *HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + +/** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). +*/ +void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + +/** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. +*/ +HID_API_EXPORT hid_device *HID_API_CALL hid_open(unsigned short vendor_id, + unsigned short product_id, + const wchar_t *serial_number); + +/** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. +*/ +HID_API_EXPORT hid_device *HID_API_CALL hid_open_path(const char *path); + +/** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. +*/ +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); + +/** @brief Write an Output report to a HID device with timeout. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. +*/ +int HID_API_EXPORT HID_API_CALL hid_write_timeout(hid_device *device, + const unsigned char *data, + size_t length, + int milliseconds); + +/** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. +*/ +int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + +/** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. +*/ +int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); + +/** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param device A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. +*/ +int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); + +/** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. +*/ +int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); + +/** @brief Get a feature report from a HID device. + + Make sure to set the first byte of @p data[] to the Report + ID of the report to be read. Make sure to allow space for + this extra byte in @p data[]. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read and + -1 on error. +*/ +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); + +/** @brief Close a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). +*/ +void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); + +/** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. +*/ +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); + +/** @brief Get The Product String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. +*/ +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); + +/** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. +*/ +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); + +/** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. +*/ +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); + +/** @brief Get a string describing the last error which occurred. + + @ingroup API + @param device A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. +*/ +HID_API_EXPORT const wchar_t *HID_API_CALL hid_error(hid_device *device); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/blfwk/host_types.h b/src/blfwk/host_types.h new file mode 100644 index 0000000..24f818a --- /dev/null +++ b/src/blfwk/host_types.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _host_types_h_ +#define _host_types_h_ + +#include +#include +#include +#include +#include +#include + +//! @brief An array of strings. +typedef std::vector string_vector_t; + +//! @brief An array of bytes. +typedef std::vector uchar_vector_t; + +//! @brief An array of uint32_t's. +typedef std::vector uint32_vector_t; + +//! @brief A stream of bytes. +typedef std::deque uchar_deque_t; + +#endif // _host_types_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/i2c.h b/src/blfwk/i2c.h new file mode 100644 index 0000000..facfd45 --- /dev/null +++ b/src/blfwk/i2c.h @@ -0,0 +1,86 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef __I2C_H__ +#define __I2C_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//! @addtogroup i2c +//! @{ + +/******************************************************************************* + * API + ******************************************************************************/ + +#if __cplusplus +extern "C" +{ +#endif + + //! @name I2C + //@{ + + //! @brief Configure the opened device port. + //! + //! @param fd Device port handler. + //! @param speed Device clock frequency. + //! @param address Slave device address. + int i2c_setup(int fd, uint32_t speed, uint8_t address); + + //! @brief Set the transfer timeout. + //! + //! @param fd Device port handler. + //! @param miliseconds Transfer timeout. + int i2c_set_timeout(int fd, uint32_t miliseconds); + + //! @brief Write bytes. + //! + //! @param fd Device port handler. + //! @param buf Pointer to buffer. + //! @param size Number of bytes to write. + int i2c_write(int fd, char *buf, int size); + + //! @brief Read bytes. + //! + //! @param fd Device port handler. + //! @param buf Pointer to buffer. + //! @param size Number of bytes to read. + int i2c_read(int fd, char *buf, int size); + + //! @brief Open the device port. + //! + //! @param port Device port string. + int i2c_open(char *port); + + //! @brief Close the device port. + //! + //! @param fd Device port handler. + int i2c_close(int fd); + + //@} + +#if __cplusplus +} +#endif + +//! @} + +#endif //__I2C_H__ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/int_size.h b/src/blfwk/int_size.h new file mode 100644 index 0000000..10e9f25 --- /dev/null +++ b/src/blfwk/int_size.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_int_size_h_) +#define _int_size_h_ + +namespace blfwk +{ +//! Supported sizes of integers. +typedef enum +{ + kWordSize, //!< 32-bit word. + kHalfWordSize, //!< 16-bit half word. + kByteSize //!< 8-bit byte. +} int_size_t; + +enum +{ + MinEraseAlignment = 1024, +}; + +}; // namespace blfwk + +#endif // _int_size_h_ diff --git a/src/blfwk/json.h b/src/blfwk/json.h new file mode 100644 index 0000000..080cd07 --- /dev/null +++ b/src/blfwk/json.h @@ -0,0 +1,1739 @@ +/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). +/// It is intented to be used with #include + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +#ifndef JSON_AMALGATED_H_INCLUDED +#define JSON_AMALGATED_H_INCLUDED +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +#define JSON_IS_AMALGATED + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 +/// If defined, indicates that Json specific container should be used +/// (hash table & simple deque container with customizable allocator). +/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 +//# define JSON_VALUE_USE_INTERNAL_MAP 1 +/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. +/// The memory pools allocator used optimization (initializing Value and ValueInternalLink +/// as if it was a POD) that may cause some validation tool to report errors. +/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. +//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 + +/// If defined, indicates that Json use exception to report invalid type manipulation +/// instead of C assert macro. +#define JSON_USE_EXCEPTION 1 + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGATED + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#define JSON_API __declspec(dllexport) +#elif defined(JSON_DLL) +#define JSON_API __declspec(dllimport) +#else +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json +{ +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGATED) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGATED) + +namespace Json +{ +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; +#ifdef JSON_VALUE_USE_INTERNAL_MAP +class ValueMapAllocator; +class ValueInternalLink; +class ValueInternalArray; +class ValueInternalMap; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGATED) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGATED) + +namespace Json +{ +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features +{ +public: + /** \brief A configuration that allows all features and assumes all strings are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c false. + bool strictRoot_; +}; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGATED) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGATED) +#include +#include + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include +#else +#include +#endif +#ifdef JSON_USE_CPPTL +#include +#endif + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json +{ +/** \brief Type of the value held by a Value object. + */ +enum ValueType +{ + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement +{ + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for root value) + numberOfCommentPlacement +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString +{ +public: + explicit StaticString(const char *czstring) + : str_(czstring) + { + } + + operator const char *() const { return str_; } + const char *c_str() const { return str_; } +private: + const char *str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. + * Non const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resize and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtanis default value in the case the required element + * does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + */ +class JSON_API Value +{ + friend class ValueIteratorBase; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + friend class ValueInternalLink; + friend class ValueInternalMap; +#endif +public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value null; + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; + +private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +#ifndef JSON_VALUE_USE_INTERNAL_MAP + class CZString + { + public: + enum DuplicationPolicy + { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(const char *cstr, DuplicationPolicy allocate); + CZString(const CZString &other); + ~CZString(); + CZString &operator=(const CZString &other); + bool operator<(const CZString &other) const; + bool operator==(const CZString &other) const; + ArrayIndex index() const; + const char *c_str() const; + bool isStaticString() const; + + private: + void swap(CZString &other); + const char *cstr_; + ArrayIndex index_; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. + This is useful since clear() and resize() will not alter types. + + Examples: + \code + Json::Value null_value; // null + Json::Value arr_value(Json::arrayValue); // [] + Json::Value obj_value(Json::objectValue); // {} + \endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char *value); + Value(const char *beginValue, const char *endValue); + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * \endcode + */ + Value(const StaticString &value); + Value(const std::string &value); +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString &value); +#endif + Value(bool value); + Value(const Value &other); + ~Value(); + + Value &operator=(const Value &other); + /// Swap values. + /// \note Currently, comments are intentionally not swapped, for + /// both logic and efficiency. + void swap(Value &other); + + ValueType type() const; + + bool operator<(const Value &other) const; + bool operator<=(const Value &other) const; + bool operator>=(const Value &other) const; + bool operator>(const Value &other) const; + + bool operator==(const Value &other) const; + bool operator!=(const Value &other) const; + + int compare(const Value &other) const; + + const char *asCString() const; + std::string asString() const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; + Int64 asInt64() const; + UInt64 asUInt64() const; + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isUInt() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element value, + /// otherwise returns defaultValue. + Value get(ArrayIndex index, const Value &defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value &append(const Value &value); + + /// Access an object value by name, create a null member if it does not exist. + Value &operator[](const char *key); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[](const char *key) const; + /// Access an object value by name, create a null member if it does not exist. + Value &operator[](const std::string &key); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[](const std::string &key) const; + /** \brief Access an object value by name, create a null member if it does not exist. + + * If the object as no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value &operator[](const StaticString &key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value &operator[](const CppTL::ConstString &key); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[](const CppTL::ConstString &key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const char *key, const Value &defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const std::string &key, const Value &defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const CppTL::ConstString &key, const Value &defaultValue) const; +#endif + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + Value removeMember(const char *key); + /// Same as removeMember(const char*) + Value removeMember(const std::string &key); + + /// Return true if the object has a member named key. + bool isMember(const char *key) const; + /// Return true if the object has a member named key. + bool isMember(const std::string &key) const; +#ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember(const CppTL::ConstString &key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// Comments must be //... or /* ... */ + void setComment(const char *comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const std::string &comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + std::string getComment(CommentPlacement placement) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + +private: + Value &resolveReference(const char *key, bool isStatic); + +#ifdef JSON_VALUE_USE_INTERNAL_MAP + inline bool isItemAvailable() const { return itemIsUsed_ == 0; } + inline void setItemUsed(bool isUsed = true) { itemIsUsed_ = isUsed ? 1 : 0; } + inline bool isMemberNameStatic() const { return memberNameIsStatic_ == 0; } + inline void setMemberNameIsStatic(bool isStatic) { memberNameIsStatic_ = isStatic ? 1 : 0; } +#endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP + +private: + struct CommentInfo + { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char *text); + + char *comment_; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder + { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char *string_; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + ValueInternalArray *array_; + ValueInternalMap *map_; +#else + ObjectValues *map_; +#endif + } value_; + ValueType type_ : 8; + int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. +#ifdef JSON_VALUE_USE_INTERNAL_MAP + unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. + int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. +#endif + CommentInfo *comments_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to access a node. + */ +class PathArgument +{ +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char *key); + PathArgument(const std::string &key); + +private: + enum Kind + { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class Path +{ +public: + Path(const std::string &path, + const PathArgument &a1 = PathArgument(), + const PathArgument &a2 = PathArgument(), + const PathArgument &a3 = PathArgument(), + const PathArgument &a4 = PathArgument(), + const PathArgument &a5 = PathArgument()); + + const Value &resolve(const Value &root) const; + Value resolve(const Value &root, const Value &defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on the node. + Value &make(Value &root) const; + +private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath(const std::string &path, const InArgs &in); + void addPathInArg(const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind); + void invalidPath(const std::string &path, int location); + + Args args_; +}; + +#ifdef JSON_VALUE_USE_INTERNAL_MAP +/** \brief Allocator to customize Value internal map. + * Below is an example of a simple implementation (default implementation actually + * use memory pool for speed). + * \code + class DefaultValueMapAllocator : public ValueMapAllocator + { + public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } + }; + * \endcode + */ +class JSON_API ValueMapAllocator +{ +public: + virtual ~ValueMapAllocator(); + virtual ValueInternalMap *newMap() = 0; + virtual ValueInternalMap *newMapCopy(const ValueInternalMap &other) = 0; + virtual void destructMap(ValueInternalMap *map) = 0; + virtual ValueInternalLink *allocateMapBuckets(unsigned int size) = 0; + virtual void releaseMapBuckets(ValueInternalLink *links) = 0; + virtual ValueInternalLink *allocateMapLink() = 0; + virtual void releaseMapLink(ValueInternalLink *link) = 0; +}; + +/** \brief ValueInternalMap hash-map bucket chain link (for internal use only). + * \internal previous_ & next_ allows for bidirectional traversal. + */ +class JSON_API ValueInternalLink +{ +public: + enum + { + itemPerLink = 6 + }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. + enum InternalFlags + { + flagAvailable = 0, + flagUsed = 1 + }; + + ValueInternalLink(); + + ~ValueInternalLink(); + + Value items_[itemPerLink]; + char *keys_[itemPerLink]; + ValueInternalLink *previous_; + ValueInternalLink *next_; +}; + +/** \brief A linked page based hash-table implementation used internally by Value. + * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked + * list in each bucket to handle collision. There is an addional twist in that + * each node of the collision linked list is a page containing a fixed amount of + * value. This provides a better compromise between memory usage and speed. + * + * Each bucket is made up of a chained list of ValueInternalLink. The last + * link of a given bucket can be found in the 'previous_' field of the following bucket. + * The last link of the last bucket is stored in tailLink_ as it has no following bucket. + * Only the last link of a bucket may contains 'available' item. The last link always + * contains at least one element unless is it the bucket one very first link. + */ +class JSON_API ValueInternalMap +{ + friend class ValueIteratorBase; + friend class Value; + +public: + typedef unsigned int HashKey; + typedef unsigned int BucketIndex; + +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState + { + IteratorState() + : map_(0) + , link_(0) + , itemIndex_(0) + , bucketIndex_(0) + { + } + ValueInternalMap *map_; + ValueInternalLink *link_; + BucketIndex itemIndex_; + BucketIndex bucketIndex_; + }; +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalMap(); + ValueInternalMap(const ValueInternalMap &other); + ValueInternalMap &operator=(const ValueInternalMap &other); + ~ValueInternalMap(); + + void swap(ValueInternalMap &other); + + BucketIndex size() const; + + void clear(); + + bool reserveDelta(BucketIndex growth); + + bool reserve(BucketIndex newItemCount); + + const Value *find(const char *key) const; + + Value *find(const char *key); + + Value &resolveReference(const char *key, bool isStatic); + + void remove(const char *key); + + void doActualRemove(ValueInternalLink *link, BucketIndex index, BucketIndex bucketIndex); + + ValueInternalLink *&getLastLinkInBucket(BucketIndex bucketIndex); + + Value &setNewItem(const char *key, bool isStatic, ValueInternalLink *link, BucketIndex index); + + Value &unsafeAdd(const char *key, bool isStatic, HashKey hashedKey); + + HashKey hash(const char *key) const; + + int compare(const ValueInternalMap &other) const; + +private: + void makeBeginIterator(IteratorState &it) const; + void makeEndIterator(IteratorState &it) const; + static bool equals(const IteratorState &x, const IteratorState &other); + static void increment(IteratorState &iterator); + static void incrementBucket(IteratorState &iterator); + static void decrement(IteratorState &iterator); + static const char *key(const IteratorState &iterator); + static const char *key(const IteratorState &iterator, bool &isStatic); + static Value &value(const IteratorState &iterator); + static int distance(const IteratorState &x, const IteratorState &y); + +private: + ValueInternalLink *buckets_; + ValueInternalLink *tailLink_; + BucketIndex bucketsSize_; + BucketIndex itemCount_; +}; + +/** \brief A simplified deque implementation used internally by Value. +* \internal +* It is based on a list of fixed "page", each page contains a fixed number of items. +* Instead of using a linked-list, a array of pointer is used for fast item look-up. +* Look-up for an element is as follow: +* - compute page index: pageIndex = itemIndex / itemsPerPage +* - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] +* +* Insertion is amortized constant time (only the array containing the index of pointers +* need to be reallocated when items are appended). +*/ +class JSON_API ValueInternalArray +{ + friend class Value; + friend class ValueIteratorBase; + +public: + enum + { + itemsPerPage = 8 + }; // should be a power of 2 for fast divide and modulo. + typedef Value::ArrayIndex ArrayIndex; + typedef unsigned int PageIndex; + +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState // Must be a POD + { + IteratorState() + : array_(0) + , currentPageIndex_(0) + , currentItemIndex_(0) + { + } + ValueInternalArray *array_; + Value **currentPageIndex_; + unsigned int currentItemIndex_; + }; +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalArray(); + ValueInternalArray(const ValueInternalArray &other); + ValueInternalArray &operator=(const ValueInternalArray &other); + ~ValueInternalArray(); + void swap(ValueInternalArray &other); + + void clear(); + void resize(ArrayIndex newSize); + + Value &resolveReference(ArrayIndex index); + + Value *find(ArrayIndex index) const; + + ArrayIndex size() const; + + int compare(const ValueInternalArray &other) const; + +private: + static bool equals(const IteratorState &x, const IteratorState &other); + static void increment(IteratorState &iterator); + static void decrement(IteratorState &iterator); + static Value &dereference(const IteratorState &iterator); + static Value &unsafeDereference(const IteratorState &iterator); + static int distance(const IteratorState &x, const IteratorState &y); + static ArrayIndex indexOf(const IteratorState &iterator); + void makeBeginIterator(IteratorState &it) const; + void makeEndIterator(IteratorState &it) const; + void makeIterator(IteratorState &it, ArrayIndex index) const; + + void makeIndexValid(ArrayIndex index); + + Value **pages_; + ArrayIndex size_; + PageIndex pageCount_; +}; + +/** \brief Experimental: do not use. Allocator to customize Value internal array. + * Below is an example of a simple implementation (actual implementation use + * memory pool). + \code +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator +virtual ~DefaultValueArrayAllocator() +{ +} + +virtual ValueInternalArray *newArray() +{ + return new ValueInternalArray(); +} + +virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) +{ + return new ValueInternalArray( other ); +} + +virtual void destruct( ValueInternalArray *array ) +{ + delete array; +} + +virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) +{ + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); +} +virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) +{ + if ( indexes ) + free( indexes ); +} + +virtual Value *allocateArrayPage() +{ + return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); +} + +virtual void releaseArrayPage( Value *value ) +{ + if ( value ) + free( value ); +} +}; + \endcode + */ +class JSON_API ValueArrayAllocator +{ +public: + virtual ~ValueArrayAllocator(); + virtual ValueInternalArray *newArray() = 0; + virtual ValueInternalArray *newArrayCopy(const ValueInternalArray &other) = 0; + virtual void destructArray(ValueInternalArray *array) = 0; + /** \brief Reallocate array page index. + * Reallocates an array of pointer on each page. + * \param indexes [input] pointer on the current index. May be \c NULL. + * [output] pointer on the new index of at least + * \a minNewIndexCount pages. + * \param indexCount [input] current number of pages in the index. + * [output] number of page the reallocated index can handle. + * \b MUST be >= \a minNewIndexCount. + * \param minNewIndexCount Minimum number of page the new index must be able to + * handle. + */ + virtual void reallocateArrayPageIndex(Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount) = 0; + virtual void releaseArrayPageIndex(Value **indexes, ValueInternalArray::PageIndex indexCount) = 0; + virtual Value *allocateArrayPage() = 0; + virtual void releaseArrayPage(Value *value) = 0; +}; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +/** \brief base class for Value iterators. + * + */ +class ValueIteratorBase +{ +public: + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + ValueIteratorBase(); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIteratorBase(const Value::ObjectValues::iterator ¤t); +#else + ValueIteratorBase(const ValueInternalArray::IteratorState &state); + ValueIteratorBase(const ValueInternalMap::IteratorState &state); +#endif + + bool operator==(const SelfType &other) const { return isEqual(other); } + bool operator!=(const SelfType &other) const { return !isEqual(other); } + difference_type operator-(const SelfType &other) const { return computeDistance(other); } + /// Return either the index or the member name of the referenced value as a Value. + Value key() const; + + /// Return the index of the referenced Value. -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value. "" if it is not an objectValue. + const char *memberName() const; + +protected: + Value &deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType &other) const; + + bool isEqual(const SelfType &other) const; + + void copy(const SelfType &other); + +private: +#ifndef JSON_VALUE_USE_INTERNAL_MAP + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; +#else + union + { + ValueInternalArray::IteratorState array_; + ValueInternalMap::IteratorState map_; + } iterator_; + bool isArray_; +#endif +}; + +/** \brief const iterator for object and array value. + * + */ +class ValueConstIterator : public ValueIteratorBase +{ + friend class Value; + +public: + typedef unsigned int size_t; + typedef int difference_type; + typedef const Value &reference; + typedef const Value *pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + +private: +/*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueConstIterator(const Value::ObjectValues::iterator ¤t); +#else + ValueConstIterator(const ValueInternalArray::IteratorState &state); + ValueConstIterator(const ValueInternalMap::IteratorState &state); +#endif +public: + SelfType &operator=(const ValueIteratorBase &other); + + SelfType operator++(int) + { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) + { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator*() const { return deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class ValueIterator : public ValueIteratorBase +{ + friend class Value; + +public: + typedef unsigned int size_t; + typedef int difference_type; + typedef Value &reference; + typedef Value *pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator(const ValueConstIterator &other); + ValueIterator(const ValueIterator &other); + +private: +/*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIterator(const Value::ObjectValues::iterator ¤t); +#else + ValueIterator(const ValueInternalArray::IteratorState &state); + ValueIterator(const ValueInternalMap::IteratorState &state); +#endif +public: + SelfType &operator=(const SelfType &other); + + SelfType operator++(int) + { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) + { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator*() const { return deref(); } +}; + +} // namespace Json + +#endif // CPPTL_JSON_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGATED) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGATED) +#include +#include +#include +#include + +namespace Json +{ +/** \brief Unserialize a JSON document into a Value. + * + */ +class JSON_API Reader +{ +public: + typedef char Char; + typedef const Char *Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features &features); + + /** \brief Read a Value from a JSON document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse(const std::string &document, Value &root, bool collectComments = true); + + /** \brief Read a Value from a JSON document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read. + \ Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse(const char *beginDoc, const char *endDoc, Value &root, bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(std::istream &is, Value &root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead") + std::string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + */ + std::string getFormattedErrorMessages() const; + +private: + enum TokenType + { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token + { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo + { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool expectToken(TokenType type, Token &token, const char *message); + bool readToken(Token &token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token &token); + bool readArray(Token &token); + bool decodeNumber(Token &token); + bool decodeString(Token &token); + bool decodeString(Token &token, std::string &decoded); + bool decodeDouble(Token &token); + bool decodeUnicodeCodePoint(Token &token, Location ¤t, Location end, unsigned int &unicode); + bool decodeUnicodeEscapeSequence(Token &token, Location ¤t, Location end, unsigned int &unicode); + bool addError(const std::string &message, Token &token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string &message, Token &token, TokenType skipUntilToken); + void skipUntilSpace(); + Value ¤tValue(); + Char getNextChar(); + void getLocationLineAndColumn(Location location, int &line, int &column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token &token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value *lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; +}; + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +std::istream &operator>>(std::istream &, Value &); + +} // namespace Json + +#endif // CPPTL_JSON_READER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGATED) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGATED) +#include +#include +#include + +namespace Json +{ +class Value; + +/** \brief Abstract class for writers. + */ +class JSON_API Writer +{ +public: + virtual ~Writer(); + + virtual std::string write(const Value &root) = 0; +}; + +/** \brief Outputs a Value in JSON format without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + */ +class JSON_API FastWriter : public Writer +{ +public: + FastWriter(); + virtual ~FastWriter() {} + void enableYAMLCompatibility(); + +public: // overridden from Writer + virtual std::string write(const Value &root); + +private: + void writeValue(const Value &value); + + std::string document_; + bool yamlCompatiblityEnabled_; +}; + +/** \brief Writes a Value in JSON format in a human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + */ +class JSON_API StyledWriter : public Writer +{ +public: + StyledWriter(); + virtual ~StyledWriter() {} +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write(const Value &root); + +private: + void writeValue(const Value &value); + void writeArrayValue(const Value &value); + bool isMultineArray(const Value &value); + void pushValue(const std::string &value); + void writeIndent(); + void writeWithIndent(const std::string &value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value &root); + void writeCommentAfterValueOnSameLine(const Value &root); + bool hasCommentForValue(const Value &value); + static std::string normalizeEOL(const std::string &text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; +}; + +/** \brief Writes a Value in JSON format in a human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + */ +class JSON_API StyledStreamWriter +{ +public: + StyledStreamWriter(std::string indentation = "\t"); + ~StyledStreamWriter() {} +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not return a value. + */ + void write(std::ostream &out, const Value &root); + +private: + void writeValue(const Value &value); + void writeArrayValue(const Value &value); + bool isMultineArray(const Value &value); + void pushValue(const std::string &value); + void writeIndent(); + void writeWithIndent(const std::string &value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value &root); + void writeCommentAfterValueOnSameLine(const Value &root); + bool hasCommentForValue(const Value &value); + static std::string normalizeEOL(const std::string &text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream *document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_; +}; + +#if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(Int value); +std::string JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(LargestInt value); +std::string JSON_API valueToString(LargestUInt value); +std::string JSON_API valueToString(double value); +std::string JSON_API valueToString(bool value); +std::string JSON_API valueToQuotedString(const char *value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +std::ostream &operator<<(std::ostream &, const Value &root); + +} // namespace Json + +#endif // JSON_WRITER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + +#endif // ifndef JSON_AMALGATED_H_INCLUDED diff --git a/src/blfwk/options.h b/src/blfwk/options.h new file mode 100644 index 0000000..58066fa --- /dev/null +++ b/src/blfwk/options.h @@ -0,0 +1,473 @@ +// COPY/REUSE POLICY +// ================= +// Permission is hereby granted to freely copy and redistribute this +// software, provided that the author is clearly credited in all copies +// and derivations. Neither the names of the authors nor that of their +// employers may be used to endorse or promote products derived from this +// software without specific written permission. +// +// +// DISCLAIMER +// ========== +// This software is provided ``As Is'' and without any express or implied +// warranties. Neither the authors nor any of their employers (including +// any of their subsidiaries and subdivisions) are responsible for maintaining +// or supporting this software or for any consequences resulting from the +// use of this software, no matter how awful, even if they arise from flaws +// in the software. +// **************************************************************************** +// ^FILE: options.h - option parsing classes +// +// ^DESCRIPTION: +// This file defines classes used to parse command-line options. +// Options may be parsed from an array of strings, or from any structure +// for which a corresponding option-iterator exists. +// +// ^HISTORY: +// 03/06/92 Brad Appleton Created +// +// 03/23/93 Brad Appleton +// - Added OptIstreamIter class +// +// 03/08/94 Brad Appleton +// - Added Options::reset() member function +// +// 07/31/97 Brad Appleton +// - Added PARSE_POS control flag and POSITIONAL return value +// +// 04/30/06 Chris Reed +// - Updated to modern C++ and STL +// - Converted comments to doxygen style +// ^^************************************************************************** + +#ifndef _options_h +#define _options_h + +#ifdef USE_STDIO +#include +#else +#include +#endif + +//! Abstract class to iterate through options/arguments +//! +class OptIter +{ +public: + OptIter(void) {} + virtual ~OptIter(void); + + //! curr() returns the current item in the iterator without + //! advancing on to the next item. If we are at the end of items + //! then NULL is returned. + virtual const char *curr(void) = 0; + + //! next() advances to the next item. + virtual void next(void) = 0; + + //! operator() returns the current item in the iterator and then + //! advances on to the next item. If we are at the end of items + //! then NULL is returned. + virtual const char *operator()(void); +}; + +//! Abstract class for a rewindable OptIter +//! +class OptIterRwd : public OptIter +{ +public: + OptIterRwd(void); + + virtual ~OptIterRwd(void); + + virtual const char *curr(void) = 0; + + virtual void next(void) = 0; + + virtual const char *operator()(void) = 0; + + //! rewind() resets the "current-element" to the first one in the "list" + virtual void rewind(void) = 0; +}; + +//! Class to iterate through an array of tokens. The array may be terminated +//! by NULL or a count containing the number of tokens may be given. +//! +class OptArgvIter : public OptIterRwd +{ +private: + int ndx; // index of current arg + int ac; // arg count + const char *const *av; // arg vector + +public: + OptArgvIter(const char *const argv[]) + : av(argv) + , ac(-1) + , ndx(0) + { + } + + OptArgvIter(int argc, const char *const argv[]) + : av(argv) + , ac(argc) + , ndx(0) + { + } + + virtual ~OptArgvIter(void); + + virtual const char *curr(void); + + virtual void next(void); + + virtual const char *operator()(void); + + virtual void rewind(void); + + //! index returns the current index to use for argv[] + int index(void) { return ndx; } +}; + +//! Class to iterate through a string containing delimiter-separated tokens +//! +class OptStrTokIter : public OptIterRwd +{ +private: + unsigned len; // length of token-string + const char *str; // the token-string + const char *seps; // delimiter-set (separator-characters) + const char *cur; // current token + char *tokstr; // our copy of the token-string + + static const char *default_delims; // default delimiters = whitespace + +public: + OptStrTokIter(const char *tokens, const char *delimiters = 0); + + virtual ~OptStrTokIter(void); + + virtual const char *curr(void); + + virtual void next(void); + + virtual const char *operator()(void); + + virtual void rewind(void); + + //! delimiters() with NO arguments returns the current set of delimiters, + //! If an argument is given then it is used as the new set of delimiters. + const char *delimiters(void) { return seps; } + void delimiters(const char *delims) { seps = (delims) ? delims : default_delims; } +}; + +//! OptIstreamIter is a class for iterating over arguments that come +//! from an input stream. Each line of the input stream is considered +//! to be a set of white-space separated tokens. If the the first +//! non-white character on a line is '#' ('!' for VMS systems) then +//! the line is considered a comment and is ignored. +//! +//! \note If a line is more than 1022 characters in length then we +//! treat it as if it were several lines of length 1022 or less. +//! +//! \note The string tokens returned by this iterator are pointers +//! to temporary buffers which may not necessarily stick around +//! for too long after the call to curr() or operator(), hence +//! if you need the string value to persist - you will need to +//! make a copy. +//! +class OptIstreamIter : public OptIter +{ +private: + std::istream &is; + OptStrTokIter *tok_iter; + + void fill(void); + +public: + static const unsigned MAX_LINE_LEN; + + OptIstreamIter(std::istream &input); + + virtual ~OptIstreamIter(void); + + virtual const char *curr(void); + + virtual void next(void); + + virtual const char *operator()(void); +}; + +//! \brief parse command-line options +//! +//! \section Synopsis +//! \code +//! #include +//! +//! Options opts(cmdname, optv); +//! char cmdname[], *optv[]; +//! \endcode +//! \section Description +//! The Options constructor expects a command-name (usually argv[0]) and +//! a pointer to an array of strings. The last element in this array MUST +//! be NULL. Each non-NULL string in the array must have the following format: +//! +//! The 1st character must be the option-name ('c' for a -c option). +//! +//! The 2nd character must be one of '|', '?', ':', '*', or '+'. +//! '|' -- indicates that the option takes NO argument; +//! '?' -- indicates that the option takes an OPTIONAL argument; +//! ':' -- indicates that the option takes a REQUIRED argument; +//! '*' -- indicates that the option takes 0 or more arguments; +//! '+' -- indicates that the option takes 1 or more arguments; +//! +//! The remainder of the string must be the long-option name. +//! +//! If desired, the long-option name may be followed by one or more +//! spaces and then by the name of the option value. This name will +//! be used when printing usage messages. If the option-value-name +//! is not given then the string "" will be used in usage +//! messages. +//! +//! One may use a space to indicate that a particular option does not +//! have a corresponding long-option. For example, "c: " (or "c:") +//! means the -c option takes a value & has NO corresponding long-option. +//! +//! To specify a long-option that has no corresponding single-character +//! option is a bit trickier: Options::operator() still needs an "option- +//! character" to return when that option is matched. One may use a whitespace +//! character or a non-printable character as the single-character option +//! in such a case. (hence " |hello" would only match "--hello"). +//! +//! \section Exceptions Exceptions to the above +//! If the 1st character of the string is '-', then the rest of the +//! string must correspond to the above format, and the option is +//! considered to be a hidden-option. This means it will be parsed +//! when actually matching options from the command-line, but will +//! NOT show-up if a usage message is printed using the usage() member +//! function. Such an example might be "-h|hidden". If you want to +//! use any "dummy" options (options that are not parsed, but that +//! to show up in the usage message), you can specify them along with +//! any positional parameters to the usage() member function. +//! +//! If the 2nd character of the string is '\0' then it is assumed +//! that there is no corresponding long-option and that the option +//! takes no argument (hence "f", and "f| " are equivalent). +//! +//! \code +//! const char * optv[] = { +//! "c:count ", +//! "s?str ", +//! "x", +//! " |hello", +//! "g+groups ", +//! NULL +//! } ; +//! \endcode +//! optv[] now corresponds to the following: +//! +//! usage: cmdname [-c|--count ] [-s|--str []] +//! [-x] [--hello] [-g|--groups ...] +//! +//! Long-option names are matched case-insensitive and only a unique prefix +//! of the name needs to be specified. +//! +//! Option-name characters are case-sensitive! +//! +//! \section Caveat +//! Because of the way in which multi-valued options and options with optional +//! values are handled, it is NOT possible to supply a value to an option in +//! a separate argument (different argv[] element) if the value is OPTIONAL +//! and begins with a '-'. What this means is that if an option "-s" takes an +//! optional value value and you wish to supply a value of "-foo" then you must +//! specify this on the command-line as "-s-foo" instead of "-s -foo" because +//! "-s -foo" will be considered to be two separate sets of options. +//! +//! A multi-valued option is terminated by another option or by the end-of +//! options. The following are all equivalent (if "-l" is a multi-valued +//! option and "-x" is an option that takes no value): +//! +//! cmdname -x -l item1 item2 item3 -- arg1 arg2 arg3 +//! cmdname -x -litem1 -litem2 -litem3 -- arg1 arg2 arg3 +//! cmdname -l item1 item2 item3 -x arg1 arg2 arg3 +//! +//! +//! \code +//! #include +//! +//! static const char * optv[] = { +//! "H|help", +//! "c:count ", +//! "s?str ", +//! "x", +//! " |hello", +//! "g+groups ", +//! NULL +//! } ; +//! +//! main(int argc, char * argv[]) { +//! int optchar; +//! const char * optarg; +//! const char * str = "default_string"; +//! int count = 0, xflag = 0, hello = 0; +//! int errors = 0, ngroups = 0; +//! +//! Options opts(*argv, optv); +//! OptArgvIter iter(--argc, ++argv); +//! +//! while( optchar = opts(iter, optarg) ) { +//! switch (optchar) { +//! case 'H' : +//! opts.usage(cout, "files ..."); +//! exit(0); +//! break; +//! case 'g' : +//! ++ngroups; break; //! the groupname is in "optarg" +//! case 's' : +//! str = optarg; break; +//! case 'x' : +//! ++xflag; break; +//! case ' ' : +//! ++hello; break; +//! case 'c' : +//! if (optarg == NULL) ++errors; +//! else count = (int) atol(optarg); +//! break; +//! default : ++errors; break; +//! } //!switch +//! } +//! +//! if (errors || (iter.index() == argc)) { +//! if (! errors) { +//! cerr << opts.name() << ": no filenames given." << endl ; +//! } +//! opts.usage(cerr, "files ..."); +//! exit(1); +//! } +//! +//! cout << "xflag=" << ((xflag) ? "ON" : "OFF") << endl +//! << "hello=" << ((hello) ? "YES" : "NO") << endl +//! << "count=" << count << endl +//! << "str=\"" << ((str) ? str : "No value given!") << "\"" << endl +//! << "ngroups=" << ngroups << endl ; +//! +//! if (iter.index() < argc) { +//! cout << "files=" ; +//! for (int i = iter.index() ; i < argc ; i++) { +//! cout << "\"" << argv[i] << "\" " ; +//! } +//! cout << endl ; +//! } +//! } +//! \endcode +class Options +{ +private: + unsigned explicit_end : 1; //!< were we terminated because of "--"? + unsigned optctrls : 7; //!< control settings (a set of OptCtrl masks) + const char *const *optvec; //!< vector of option-specifications (last=NULL) + const char *nextchar; //!< next option-character to process + const char *listopt; //!< last list-option we matched + const char *cmdname; //!< name of the command + + void check_syntax(void) const; + + const char *match_opt(char opt, int ignore_case = 0) const; + + const char *match_longopt(const char *opt, int len, int &ambiguous) const; + + int parse_opt(OptIter &iter, const char *&optarg); + + int parse_longopt(OptIter &iter, const char *&optarg); + +public: + enum OptCtrl + { + DEFAULT = 0x00, //!< Default setting + ANYCASE = 0x01, //!< Ignore case when matching short-options + QUIET = 0x02, //!< Dont print error messages + PLUS = 0x04, //!< Allow "+" as a long-option prefix + SHORT_ONLY = 0x08, //!< Dont accept long-options + LONG_ONLY = 0x10, //!< Dont accept short-options + //!< (also allows "-" as a long-option prefix). + NOGUESSING = 0x20, //!< Normally, when we see a short (long) option + //!< on the command line that doesnt match any + //!< known short (long) options, then we try to + //!< "guess" by seeing if it will match any known + //!< long (short) option. Setting this mask prevents + //!< this "guessing" from occurring. + PARSE_POS = 0x40 //!< By default, Options will not present positional + //!< command-line arguments to the user and will + //!< instead stop parsing when the first positonal + //!< argument has been encountered. If this flag + //!< is given, Options will present positional + //!< arguments to the user with a return code of + //!< POSITIONAL; ENDOPTS will be returned only + //!< when the end of the argument list is reached. + }; + + //! Error return values for operator() + //! + enum OptRC + { + ENDOPTS = 0, + BADCHAR = -1, + BADKWD = -2, + AMBIGUOUS = -3, + POSITIONAL = -4 + }; + + Options(const char *name, const char *const optv[]); + + virtual ~Options(void); + + //! name() returns the command name + const char *name(void) const { return cmdname; } + //! ctrls() (with no arguments) returns the existing control settings + unsigned ctrls(void) const { return optctrls; } + //! ctrls() (with 1 argument) sets new control settings + void ctrls(unsigned newctrls) { optctrls = newctrls; } + //! reset for another pass to parse for options + void reset(void) { nextchar = listopt = NULL; } + //! usage() prints options usage (followed by any positional arguments + //! listed in the parameter "positionals") on the given outstream + void usage(std::ostream &os, const char *positionals) const; + + //! operator() iterates through the arguments as necessary (using the + //! given iterator) and returns the character value of the option + //! (or long-option) that it matched. If the option has a value + //! then the value given may be found in optarg (otherwise optarg + //! will be NULL). + //! + //! 0 is returned upon end-of-options. At this point, "iter" may + //! be used to process any remaining positional parameters. If the + //! PARSE_POS control-flag is set then 0 is returned only when all + //! arguments in "iter" have been exhausted. + //! + //! If an invalid option is found then BADCHAR is returned and *optarg + //! is the unrecognized option character. + //! + //! If an invalid long-option is found then BADKWD is returned and optarg + //! points to the bad long-option. + //! + //! If an ambiguous long-option is found then AMBIGUOUS is returned and + //! optarg points to the ambiguous long-option. + //! + //! If the PARSE_POS control-flag is set then POSITIONAL is returned + //! when a positional argument is encountered and optarg points to + //! the positonal argument (and "iter" is advanced to the next argument + //! in the iterator). + //! + //! Unless Options::QUIET is used, missing option-arguments and + //! invalid options (and the like) will automatically cause error + //! messages to be issued to cerr. + int operator()(OptIter &iter, const char *&optarg); + + //! Call this member function after operator() has returned 0 + //! if you want to know whether or not options were explicitly + //! terminated because "--" appeared on the command-line. + //! + int explicit_endopts() const { return explicit_end; } +}; + +#endif /* _options_h */ diff --git a/src/blfwk/rijndael.h b/src/blfwk/rijndael.h new file mode 100644 index 0000000..8249b82 --- /dev/null +++ b/src/blfwk/rijndael.h @@ -0,0 +1,180 @@ +#ifndef _RIJNDAEL_H_ +#define _RIJNDAEL_H_ + +// +// File : rijndael.h +// Creation date : Sun Nov 5 2000 03:21:05 CEST +// Author : Szymon Stefanek (stefanek@tin.it) +// +// Another implementation of the Rijndael cipher. +// This is intended to be an easily usable library file. +// This code is public domain. +// Based on the Vincent Rijmen and K.U.Leuven implementation 2.4. +// + +// +// Original Copyright notice: +// +// rijndael-alg-fst.c v2.4 April '2000 +// rijndael-alg-fst.h +// rijndael-api-fst.c +// rijndael-api-fst.h +// +// Optimised ANSI C code +// +// authors: v1.0: Antoon Bosselaers +// v2.0: Vincent Rijmen, K.U.Leuven +// v2.3: Paulo Barreto +// v2.4: Vincent Rijmen, K.U.Leuven +// +// This code is placed in the public domain. +// + +// +// This implementation works on 128 , 192 , 256 bit keys +// and on 128 bit blocks +// + +// +// Example of usage: +// +// // Input data +// unsigned char key[32]; // The key +// initializeYour256BitKey(); // Obviously initialized with sth +// const unsigned char * plainText = getYourPlainText(); // Your plain text +// int plainTextLen = strlen(plainText); // Plain text length +// +// // Encrypting +// Rijndael rin; +// unsigned char output[plainTextLen + 16]; +// +// rin.init(Rijndael::CBC,Rijndael::Encrypt,key,Rijndael::Key32Bytes); +// // It is a good idea to check the error code +// int len = rin.padEncrypt(plainText,len,output); +// if(len >= 0)useYourEncryptedText(); +// else encryptError(len); +// +// // Decrypting: we can reuse the same object +// unsigned char output2[len]; +// rin.init(Rijndael::CBC,Rijndael::Decrypt,key,Rijndael::Key32Bytes)); +// len = rin.padDecrypt(output,len,output2); +// if(len >= 0)useYourDecryptedText(); +// else decryptError(len); +// + +#include + +#define _MAX_KEY_COLUMNS (256 / 32) +#define _MAX_ROUNDS 14 +#define MAX_IV_SIZE 16 + +// We assume that unsigned int is 32 bits long.... +// typedef unsigned char uint8_t; +// typedef unsigned int uint32_t; +// typedef unsigned short uint16_t; + +// Error codes +#define RIJNDAEL_SUCCESS 0 +#define RIJNDAEL_UNSUPPORTED_MODE -1 +#define RIJNDAEL_UNSUPPORTED_DIRECTION -2 +#define RIJNDAEL_UNSUPPORTED_KEY_LENGTH -3 +#define RIJNDAEL_BAD_KEY -4 +#define RIJNDAEL_NOT_INITIALIZED -5 +#define RIJNDAEL_BAD_DIRECTION -6 +#define RIJNDAEL_CORRUPTED_DATA -7 + +class Rijndael +{ +public: + enum Direction + { + Encrypt, + Decrypt + }; + enum Mode + { + ECB, + CBC, + CFB1 + }; + enum KeyLength + { + Key16Bytes, + Key24Bytes, + Key32Bytes + }; + // + // Creates a Rijndael cipher object + // You have to call init() before you can encrypt or decrypt stuff + // + Rijndael(); + ~Rijndael(); + +protected: + // Internal stuff + enum State + { + Valid, + Invalid + }; + + State m_state; + Mode m_mode; + Direction m_direction; + uint8_t m_initVector[MAX_IV_SIZE]; + uint32_t m_uRounds; + uint8_t m_expandedKey[_MAX_ROUNDS + 1][4][4]; + +public: + ////////////////////////////////////////////////////////////////////////////////////////// + // API + ////////////////////////////////////////////////////////////////////////////////////////// + + // init(): Initializes the crypt session + // Returns RIJNDAEL_SUCCESS or an error code + // mode : Rijndael::ECB, Rijndael::CBC or Rijndael::CFB1 + // You have to use the same mode for encrypting and decrypting + // dir : Rijndael::Encrypt or Rijndael::Decrypt + // A cipher instance works only in one direction + // (Well , it could be easily modified to work in both + // directions with a single init() call, but it looks + // useless to me...anyway , it is a matter of generating + // two expanded keys) + // key : array of unsigned octets , it can be 16 , 24 or 32 bytes long + // this CAN be binary data (it is not expected to be null terminated) + // keyLen : Rijndael::Key16Bytes , Rijndael::Key24Bytes or Rijndael::Key32Bytes + // initVector: initialization vector, you will usually use 0 here + int init(Mode mode, Direction dir, const uint8_t *key, KeyLength keyLen, uint8_t *initVector = 0); + // Encrypts the input array (can be binary data) + // The input array length must be a multiple of 16 bytes, the remaining part + // is DISCARDED. + // so it actually encrypts inputLen / 128 blocks of input and puts it in outBuffer + // Input len is in BITS! + // outBuffer must be at least inputLen / 8 bytes long. + // Returns the encrypted buffer length in BITS or an error code < 0 in case of error + int blockEncrypt(const uint8_t *input, int inputLen, uint8_t *outBuffer); + // Encrypts the input array (can be binary data) + // The input array can be any length , it is automatically padded on a 16 byte boundary. + // Input len is in BYTES! + // outBuffer must be at least (inputLen + 16) bytes long + // Returns the encrypted buffer length in BYTES or an error code < 0 in case of error + int padEncrypt(const uint8_t *input, int inputOctets, uint8_t *outBuffer); + // Decrypts the input vector + // Input len is in BITS! + // outBuffer must be at least inputLen / 8 bytes long + // Returns the decrypted buffer length in BITS and an error code < 0 in case of error + int blockDecrypt(const uint8_t *input, int inputLen, uint8_t *outBuffer); + // Decrypts the input vector + // Input len is in BYTES! + // outBuffer must be at least inputLen bytes long + // Returns the decrypted buffer length in BYTES and an error code < 0 in case of error + int padDecrypt(const uint8_t *input, int inputOctets, uint8_t *outBuffer); + +protected: + void keySched(uint8_t key[_MAX_KEY_COLUMNS][4]); + void keyEncToDec(); + void encrypt(const uint8_t a[16], uint8_t b[16]); + void decrypt(const uint8_t a[16], uint8_t b[16]); +}; + +#endif // _RIJNDAEL_H_ diff --git a/src/blfwk/serial.h b/src/blfwk/serial.h new file mode 100644 index 0000000..3164b21 --- /dev/null +++ b/src/blfwk/serial.h @@ -0,0 +1,70 @@ +/* +* This file is part of the Bus Pirate project (http://code.google.com/p/the-bus-pirate/). +* +* Written and maintained by the Bus Pirate project and http://dangerousprototypes.com +* +* To the extent possible under law, the project has +* waived all copyright and related or neighboring rights to Bus Pirate. This +* work is published from United States. +* +* For details see: http://creativecommons.org/publicdomain/zero/1.0/. +* +* This program 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. +*/ +/* +* OS independent serial interface +* +* Heavily based on Pirate-Loader: +* http://the-bus-pirate.googlecode.com/svn/trunk/bootloader-v4/pirate-loader/source/pirate-loader.c +* +*/ +#ifndef MYSERIAL_H_ +#define MYSERIAL_H_ + +#ifdef MACOSX +#include +#include + +#define B1500000 1500000 +#define B1000000 1000000 +#define B921600 921600 +#endif + +#include + +#ifdef WIN32 +#include +#include + +#define B115200 115200 +#define B921600 921600 + +typedef long speed_t; +#else + +#include +#include +#include +#include +#include + +#endif + +#if __cplusplus +extern "C" { +#endif + +int serial_setup(int fd, speed_t speed); +int serial_set_read_timeout(int fd, uint32_t timeoutMs); +int serial_write(int fd, char *buf, int size); +int serial_read(int fd, char *buf, int size); +int serial_open(char *port); +int serial_close(int fd); + +#if __cplusplus +} +#endif + +#endif diff --git a/src/blfwk/smart_ptr.h b/src/blfwk/smart_ptr.h new file mode 100644 index 0000000..d1e7112 --- /dev/null +++ b/src/blfwk/smart_ptr.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(_smart_ptr_h_) +#define _smart_ptr_h_ + +//! @addtogroup smart_pointer +//! @{ + +/*! + * \brief Simple, standard smart pointer class. + * + * This class only supports the single-owner paradigm. + */ +template +class smart_ptr +{ +public: + typedef T data_type; + typedef T *ptr_type; + typedef const T *const_ptr_type; + typedef T &ref_type; + typedef const T &const_ref_type; + + //! Default constuctor. Initialises with no pointer set. + smart_ptr() + : _p(0) + { + } + + //! This constructor takes a pointer to the object to be deleted. + smart_ptr(ptr_type p) + : _p(p) + { + } + + //! Destructor. If an object (pointer) has been set, it will be deleted. + //! Deletes the object using safe_delete(). + virtual ~smart_ptr() { safe_delete(); } + //! Return the current pointer value. + ptr_type get() { return _p; } + //! Return the const form of the current pointer value. + const_ptr_type get() const { return _p; } + //! Change the pointer value, or set if if the default constructor was used. + //! If a pointer had previously been associated with the object, and \a p is + //! different than that previous pointer, it will be deleted before taking + //! ownership of \a p. If this is not desired, call reset() beforehand. + void set(ptr_type p) + { + if (_p && p != _p) + { + safe_delete(); + } + _p = p; + } + + //! Dissociates any previously set pointer value without deleting it. + void reset() { _p = 0; } + //! Dissociates a previously set pointer value, deleting it at the same time. + void clear() { safe_delete(); } + //! Forces immediate deletion of the object. If you are planning on using + //! this method, think about just using a normal pointer. It probably makes + //! more sense. + virtual void safe_delete() + { + if (_p) + { + delete _p; + _p = 0; + } + } + + //! \name Operators + //@{ + + //! Makes the object transparent as the template type. + operator ptr_type() { return _p; } + //! Const version of the pointer operator. + operator const_ptr_type() const { return _p; } + //! Makes the object transparent as a reference of the template type. + operator ref_type() { return *_p; } + //! Const version of the reference operator. + operator const_ref_type() const { return *_p; } + //! Returns a boolean indicating whether the object has a pointer set or not. + operator bool() const { return _p != 0; } + //! To allow setting the pointer directly. Equivalent to a call to set(). + smart_ptr &operator=(const_ptr_type p) + { + set(const_cast(p)); + return *this; + } + + //! Another operator to allow you to treat the object just like a pointer. + ptr_type operator->() { return _p; } + //! Another operator to allow you to treat the object just like a pointer. + const_ptr_type operator->() const { return _p; } + // //! Pointer dereferencing operator. + // ref_type operator * () const { return *_p; } + // + // //! Const version of the pointer dereference operator. + // const_ref_type operator * () const { return *_p; } + + //@} + +protected: + ptr_type _p; //!< The wrapped pointer. +}; + +/*! + * \brief Simple, standard smart pointer class that uses the array delete operator. + * + * This class only supports the single-owner paradigm. + * + * This is almost entirely a copy of smart_ptr since the final C++ specification + * does not allow template subclass members to access members of the parent that + * do not depend on the template parameter. + */ +template +class smart_array_ptr +{ +public: + typedef T data_type; + typedef T *ptr_type; + typedef const T *const_ptr_type; + typedef T &ref_type; + typedef const T &const_ref_type; + + //! Default constuctor. Initialises with no pointer set. + smart_array_ptr() + : _p(0) + { + } + + //! This constructor takes a pointer to the object to be deleted. + smart_array_ptr(ptr_type p) + : _p(p) + { + } + + //! Destructor. If an array has been set, it will be deleted. + //! Deletes the array using safe_delete(). + virtual ~smart_array_ptr() { safe_delete(); } + //! Return the current pointer value. + ptr_type get() { return _p; } + //! Return the const form of the current pointer value. + const_ptr_type get() const { return _p; } + //! Change the pointer value, or set if if the default constructor was used. + //! If a pointer had previously been associated with the object, and \a p is + //! different than that previous pointer, it will be deleted before taking + //! ownership of \a p. If this is not desired, call reset() beforehand. + void set(ptr_type p) + { + if (_p && p != _p) + { + safe_delete(); + } + _p = p; + } + + //! Dissociates any previously set pointer value without deleting it. + void reset() { _p = 0; } + //! Dissociates a previously set pointer value, deleting it at the same time. + void clear() { safe_delete(); } + //! Forces immediate deletion of the object. If you are planning on using + //! this method, think about just using a normal pointer. It probably makes + //! more sense. + virtual void safe_delete() + { + if (_p) + { + delete[] _p; + _p = 0; + } + } + + //! \name Operators + //@{ + + //! Makes the object transparent as the template type. + operator ptr_type() { return _p; } + //! Const version of the pointer operator. + operator const_ptr_type() const { return _p; } + //! Makes the object transparent as a reference of the template type. + operator ref_type() { return *_p; } + //! Const version of the reference operator. + operator const_ref_type() const { return *_p; } + //! Returns a boolean indicating whether the object has a pointer set or not. + operator bool() const { return _p != 0; } + //! To allow setting the pointer directly. Equivalent to a call to set(). + smart_array_ptr &operator=(const_ptr_type p) + { + set(const_cast(p)); + return *this; + } + + //! Another operator to allow you to treat the object just like a pointer. + ptr_type operator->() { return _p; } + //! Another operator to allow you to treat the object just like a pointer. + const_ptr_type operator->() const { return _p; } + //! Indexing operator. + ref_type operator[](unsigned index) { return _p[index]; } + //! Indexing operator. + const_ref_type operator[](unsigned index) const { return _p[index]; } + //@} + +protected: + ptr_type _p; //!< The wrapped pointer. +}; + +//! @} + +#endif // _smart_ptr_h_ diff --git a/src/blfwk/spi.h b/src/blfwk/spi.h new file mode 100644 index 0000000..cb9a5af --- /dev/null +++ b/src/blfwk/spi.h @@ -0,0 +1,86 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef __SPI_H__ +#define __SPI_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//! @addtogroup spi +//! @{ + +/******************************************************************************* + * API + ******************************************************************************/ + +#if __cplusplus +extern "C" +{ +#endif + + //! @name SPI + //@{ + + //! @brief Configure the opened device port. + //! + //! @param fd Device port handler. + //! @param speed Device clock frequency. + //! @param mode Polarity and phase mode. + //! @param bits_per_word Transfer size. + int spi_setup(int fd, uint32_t speed, uint32_t mode, uint32_t bits_per_word); + + //! @brief Set the transfer timeout. + //! + //! @param fd Device port handler. + //! @param miliseconds Transfer timeout. + int spi_set_timeout(int fd, uint32_t miliseconds); + + //! @brief Write bytes. + //! + //! @param fd Device port handler. + //! @param buf Pointer to buffer. + //! @param size Number of bytes to write. + int spi_write(int fd, char *buf, int size); + + //! @brief Read bytes. + //! + //! @param fd Device port handler. + //! @param buf Pointer to buffer. + //! @param size Number of bytes to read. + int spi_read(int fd, char *buf, int size); + + //! @brief Open the device port. + //! + //! @param port Device port string. + int spi_open(char *port); + + //! @brief Close the device port. + //! + //! @param fd Device port handler. + int spi_close(int fd); + + //@} + +#if __cplusplus +} +#endif + +//! @} + +#endif //__SPI_H__ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/AESCounter.cpp b/src/blfwk/src/AESCounter.cpp new file mode 100644 index 0000000..400d454 --- /dev/null +++ b/src/blfwk/src/AESCounter.cpp @@ -0,0 +1,76 @@ +/* + * File: AESCounter.cpp + * + * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. + * See included license file for license details. + */ + +#include "blfwk/AESCounter.h" +#include +#include "blfwk/smart_ptr.h" +#include "blfwk/HexValues.h" +#include + +//! The data from the stream is expected to be hex encoded. Each two characters +//! from the stream encode a single result byte. All non-hexadecimal characters +//! are ignored, including newlines. Every two hexadecimal characters form +//! an encoded byte. This is true even if the two characters representing the +//! upper and lower nibbles are separated by whitespace or other characters. +//! +//! \post The stream read head is left pointing just after the last encoded byte. +//! +//! \param stream Input stream to read from. +//! \param bytes Number of encoded bytes to read. This is the number of \e +//! result bytes, not the count of bytes to read from the stream. +//! \param[out] buffer Pointer to the buffer where decoded data is written. +//! +//! \exception std::runtime_error This exception will be thrown if less +//! data than required is available from \a stream, or if some other +//! error occurs while reading from \a stream. +void AESCounterBase::_readFromStream(std::istream &stream, unsigned bytes, uint8_t *buffer) +{ + char temp[2]; + char c; + char n = 0; + + while (bytes) + { + if (stream.get(c).fail()) + { + throw std::runtime_error("not enough data in stream"); + } + + if (isHexDigit(c)) + { + temp[n++] = c; + if (n == 2) + { + *buffer++ = hexByteToInt(temp); + bytes--; + n = 0; + } + } + } +} + +//! Counter data is written to \a stream as a sequence of hex encoded octets, each two +//! characters long. No spaces or newlines are inserted between the encoded octets +//! or at the end of the sequence. +//! +//! \exception std::runtime_error Thrown if the \a stream reports an error while +//! writing the key data. +void AESCounterBase::_writeToStream(std::ostream &stream, unsigned bytes, const uint8_t *buffer) const +{ + const char hexChars[] = "0123456789ABCDEF"; + while (bytes--) + { + uint8_t thisByte = *buffer++; + char byteString[2]; + byteString[0] = hexChars[(thisByte & 0xf0) >> 4]; + byteString[1] = hexChars[thisByte & 0x0f]; + if (stream.write(byteString, 2).bad()) + { + throw std::runtime_error("error while writing to stream"); + } + } +} diff --git a/src/blfwk/src/AESKey.cpp b/src/blfwk/src/AESKey.cpp new file mode 100644 index 0000000..bd0f9a2 --- /dev/null +++ b/src/blfwk/src/AESKey.cpp @@ -0,0 +1,76 @@ +/* + * File: AESKey.cpp + * + * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. + * See included license file for license details. + */ + +#include "blfwk/AESKey.h" +#include +#include "blfwk/smart_ptr.h" +#include "blfwk/HexValues.h" +#include + +//! The data from the stream is expected to be hex encoded. Each two characters +//! from the stream encode a single result byte. All non-hexadecimal characters +//! are ignored, including newlines. Every two hexadecimal characters form +//! an encoded byte. This is true even if the two characters representing the +//! upper and lower nibbles are separated by whitespace or other characters. +//! +//! \post The stream read head is left pointing just after the last encoded byte. +//! +//! \param stream Input stream to read from. +//! \param bytes Number of encoded bytes to read. This is the number of \e +//! result bytes, not the count of bytes to read from the stream. +//! \param[out] buffer Pointer to the buffer where decoded data is written. +//! +//! \exception std::runtime_error This exception will be thrown if less +//! data than required is available from \a stream, or if some other +//! error occurs while reading from \a stream. +void AESKeyBase::_readFromStream(std::istream &stream, unsigned bytes, uint8_t *buffer) const +{ + char temp[2]; + char c; + char n = 0; + + while (bytes) + { + if (stream.get(c).fail()) + { + throw std::runtime_error("not enough data in stream"); + } + + if (isHexDigit(c)) + { + temp[n++] = c; + if (n == 2) + { + *buffer++ = hexByteToInt(temp); + bytes--; + n = 0; + } + } + } +} + +//! Key data is written to \a stream as a sequence of hex encoded octets, each two +//! characters long. No spaces or newlines are inserted between the encoded octets +//! or at the end of the sequence. +//! +//! \exception std::runtime_error Thrown if the \a stream reports an error while +//! writing the key data. +void AESKeyBase::_writeToStream(std::ostream &stream, unsigned bytes, const uint8_t *buffer) const +{ + const char hexChars[] = "0123456789ABCDEF"; + while (bytes--) + { + uint8_t thisByte = *buffer++; + char byteString[2]; + byteString[0] = hexChars[(thisByte & 0xf0) >> 4]; + byteString[1] = hexChars[thisByte & 0x0f]; + if (stream.write(byteString, 2).bad()) + { + throw std::runtime_error("error while writing to stream"); + } + } +} diff --git a/src/blfwk/src/Blob.cpp b/src/blfwk/src/Blob.cpp new file mode 100644 index 0000000..d93e056 --- /dev/null +++ b/src/blfwk/src/Blob.cpp @@ -0,0 +1,122 @@ +/* + * File: Blob.cpp + * + * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. + * See included license file for license details. + */ + +#include "blfwk/Blob.h" +#include +#include +#include + +Blob::Blob() + : m_data(0) + , m_length(0) +{ +} + +//! Makes a local copy of the \a data argument. +//! +Blob::Blob(const uint8_t *data, unsigned length) + : m_data(0) + , m_length(length) +{ + m_data = reinterpret_cast(malloc(length)); + memcpy(m_data, data, length); +} + +//! Makes a local copy of the data owned by \a other. +//! +Blob::Blob(const Blob &other) + : m_data(0) + , m_length(other.m_length) +{ + m_data = reinterpret_cast(malloc(m_length)); + memcpy(m_data, other.m_data, m_length); +} + +//! Disposes of the binary data associated with this object. +Blob::~Blob() +{ + if (m_data) + { + free(m_data); + } +} + +//! Copies \a data onto the blob's data. The blob does not assume ownership +//! of \a data. +//! +//! \param data Pointer to a buffer containing the data which will be copied +//! into the blob. +//! \param length Number of bytes pointed to by \a data. +void Blob::setData(const uint8_t *data, unsigned length) +{ + setLength(length); + memcpy(m_data, data, length); +} + +//! Sets the #m_length member variable to \a length and resizes #m_data to +//! the new length. The contents of #m_data past any previous contents are undefined. +//! If the new \a length is 0 then the data will be freed and a subsequent call +//! to getData() will return NULL. +//! +//! \param length New length of the blob's data in bytes. +void Blob::setLength(unsigned length) +{ + if (length == 0) + { + clear(); + return; + } + + // Allocate new block. + if (!m_data) + { + m_data = reinterpret_cast(malloc(length)); + if (!m_data) + { + throw std::runtime_error("failed to allocate memory"); + } + } + // Reallocate previous block. + else + { + void *newBlob = realloc(m_data, length); + if (!newBlob) + { + throw std::runtime_error("failed to reallocate memory"); + } + m_data = reinterpret_cast(newBlob); + } + + // Set length. + m_length = length; +} + +void Blob::append(const uint8_t *newData, unsigned newDataLength) +{ + unsigned oldLength = m_length; + + setLength(m_length + newDataLength); + + memcpy(m_data + oldLength, newData, newDataLength); +} + +void Blob::clear() +{ + if (m_data) + { + free(m_data); + m_data = NULL; + } + + m_length = 0; +} + +void Blob::relinquish() +{ + m_data = NULL; + m_length = 0; +} diff --git a/src/blfwk/src/Bootloader.cpp b/src/blfwk/src/Bootloader.cpp new file mode 100644 index 0000000..aca536a --- /dev/null +++ b/src/blfwk/src/Bootloader.cpp @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/Bootloader.h" +#include "blfwk/BusPalPeripheral.h" +#include "blfwk/LpcUsbSioPeripheral.h" +#include "blfwk/SerialPacketizer.h" +#include "blfwk/UartPeripheral.h" +#if defined(LINUX) && defined(__ARM__) +#include "blfwk/I2cPeripheral.h" +#include "blfwk/SpiPeripheral.h" +#endif +#include "blfwk/UsbHidPacketizer.h" +#include "blfwk/UsbHidPeripheral.h" +#include "blfwk/format_string.h" +#include "blfwk/json.h" +#include "blfwk/utils.h" + +using namespace blfwk; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// +enum +{ + kBuspalReadRetries = 3 +}; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// +// See host_bootloader.h for documentation of this method. +Bootloader::Bootloader() + : m_hostPacketizer(NULL) + , m_logger(NULL) +{ + // create logger instance + if (Log::getLogger() == NULL) + { + this->m_logger = new FileLogger("bootloader.log"); + this->m_logger->setFilterLevel(Logger::kDebug2 /*Logger::kInfo*/); + Log::setLogger(this->m_logger); + } +} + +// See host_bootloader.h for documentation of this method. +Bootloader::Bootloader(const Peripheral::PeripheralConfigData &config) + : m_hostPacketizer(NULL) + , m_logger(NULL) +{ + // create logger instance + if (Log::getLogger() == NULL) + { + this->m_logger = new FileLogger("bootloader.log"); + this->m_logger->setFilterLevel(Logger::kDebug2 /*Logger::kInfo*/); + Log::setLogger(this->m_logger); + } + + switch (config.peripheralType) + { + case Peripheral::kHostPeripheralType_UART: + { + UartPeripheral *peripheral = new UartPeripheral(config.comPortName.c_str(), config.comPortSpeed); + m_hostPacketizer = new SerialPacketizer(peripheral, config.packetTimeoutMs); + + if (config.ping) + { + // Send initial ping. + try + { + ping(0, 0, config.comPortSpeed, NULL); + } + catch (const std::exception &e) + { + delete m_hostPacketizer; + m_hostPacketizer = NULL; + throw std::runtime_error(format_string("Error: Initial ping failure: %s", e.what())); + } + } + + break; + } + case Peripheral::kHostPeripheralType_USB_HID: + { + UsbHidPeripheral *peripheral = NULL; + try + { + peripheral = new UsbHidPeripheral(config.usbHidVid, config.usbHidPid, config.usbHidSerialNumber.c_str(), + config.usbPath.c_str()); + m_hostPacketizer = new UsbHidPacketizer(peripheral, config.packetTimeoutMs); + } + catch (...) + { + delete peripheral; + throw; + } + break; + } +#if defined(LINUX) && defined(__ARM__) + case Peripheral::kHostPeripheralType_I2C: + { + I2cPeripheral *peripheral = + new I2cPeripheral(config.comPortName.c_str(), config.i2cAddress, config.comPortSpeed); + m_hostPacketizer = new SerialPacketizer(peripheral, config.packetTimeoutMs); + + if (config.ping) + { + // Send initial ping. + try + { + ping(0, 0, config.comPortSpeed, NULL); + } + catch (const std::exception &e) + { + throw std::runtime_error(format_string("Error: Initial ping failure: %s", e.what())); + } + } + + break; + } + case Peripheral::kHostPeripheralType_SPI: + { + SpiPeripheral *peripheral = new SpiPeripheral(config.comPortName.c_str(), config.comPortSpeed, + config.spiPolarity, config.spiPhase, config.spiSequence); + m_hostPacketizer = new SerialPacketizer(peripheral, config.packetTimeoutMs); + + if (config.ping) + { + // Send initial ping. + try + { + ping(0, 0, config.comPortSpeed, NULL); + } + catch (const std::exception &e) + { + throw std::runtime_error(format_string("Error: Initial ping failure: %s", e.what())); + } + } + + break; + } +#endif // #if defined(LINUX) && defined(__ARM__) + case Peripheral::kHostPeripheralType_BUSPAL_UART: + { + BusPalUartPeripheral *peripheral = + new BusPalUartPeripheral(config.comPortName.c_str(), config.comPortSpeed, config.busPalConfig); + m_hostPacketizer = new SerialPacketizer(peripheral, config.packetTimeoutMs); + + // Send initial ping. + // Bus pal peripheral interface will take care of the delays for us. + if (config.ping) + { + uint32_t buspalReadRetries; +#if defined(DEBUG) + buspalReadRetries = 0; +#else + buspalReadRetries = kBuspalReadRetries; +#endif + try + { + ping(buspalReadRetries, 0, 0, NULL); + } + catch (const std::exception &e) + { + throw std::runtime_error(format_string("Error: Initial ping failure: %s", e.what())); + } + } + + break; + } +#if defined(LPCUSBSIO) + case Peripheral::kHostPeripheralType_LPCUSBSIO: + { + LpcUsbSioPeripheral *peripheral = new LpcUsbSioPeripheral(config.lpcUsbSioConfig); + m_hostPacketizer = new SerialPacketizer(peripheral, config.packetTimeoutMs); + + if (config.ping) + { + // Send initial ping. + try + { + ping(0, 0, 0, NULL); + } + catch (const std::exception &e) + { + delete m_hostPacketizer; + m_hostPacketizer = NULL; + throw std::runtime_error(format_string("Error: Initial ping failure: %s", e.what())); + } + } + + break; + } +#endif // #if defined(LPCUSBSIO) + default: + throw std::runtime_error(format_string("Error: Unsupported peripheral type(%d).", config.peripheralType)); + } +} + +// See host_bootloader.h for documentation of this method. +Bootloader::~Bootloader() +{ + flush(); + + // Delete packetizer should close handles and free memory on Peripheral. + if (m_hostPacketizer) + { + delete m_hostPacketizer; + m_hostPacketizer = NULL; + } +} + +// See host_bootloader.h for documentation of this method. +void Bootloader::flush() +{ + // Finalize the packet interface. + if (m_hostPacketizer) + { + m_hostPacketizer->finalize(); + } +} + +// See host_bootloader.h for documentation of this method. +void Bootloader::execute(uint32_t entry_point, uint32_t param, uint32_t stack_pointer) +{ + // Inject the execute(start_address, param, stack_pointer) command. + Execute cmd(entry_point, param, stack_pointer); + Log::info("Inject command '%s'\n", cmd.getName().c_str()); + this->inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +// See host_bootloader.h for documentation of this method. +void Bootloader::reset() +{ + // Inject the reset command. + Reset cmd; + Log::info("Inject command '%s'\n", cmd.getName().c_str()); + this->inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +uint32_vector_t Bootloader::getProperty(property_t tag, uint32_t memoryIdorIndex) +{ + // Inject the get-property(tag) command. + GetProperty cmd(tag, memoryIdorIndex); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } + + return *cmd.getResponseValues(); +} + +// See Updater.h for documentation of this method. +bool Bootloader::isCommandSupported(const cmd_t &command) +{ + uint32_t fw_response = getProperty(kProperty_AvailableCommands, 0).at(1); + + // See if the command is supported. + return ((fw_response & command.mask) == command.mask); +} + +// See host_bootloader.h for documentation of this method. +standard_version_t Bootloader::getVersion() +{ + return standard_version_t(getProperty(kProperty_CurrentVersion, 0).at(1)); +} + +// See host_bootloader.h for documentation of this method. +uint32_t Bootloader::getSecurityState() +{ + // Inject the get-property command. + GetProperty cmd(kProperty_FlashSecurityState); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + this->inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } + + return getProperty(kProperty_FlashSecurityState, 0).at(1); +} + +// See host_bootloader.h for documentation of this method. +uint32_t Bootloader::getDataPacketSize() +{ + return getProperty(kProperty_MaxPacketSize, 0).at(1); +} + +// See host_bootloader.h for documentation of this method. +void Bootloader::ping(int retries, unsigned int delay, int comSpeed, int *actualComSpeed) +{ + this->flush(); + + SerialPacketizer *pPacketizer = dynamic_cast(m_hostPacketizer); + if (pPacketizer) + { + status_t status = pPacketizer->ping(retries, delay, NULL, comSpeed, actualComSpeed); + if (status != kStatus_Success) + { + this->flush(); + + Reset cmd; // dummy command to get access to status text. + // report ping failure in JSON output mode. + Json::Value root; + root["command"] = "ping"; + root["status"] = Json::Value(Json::objectValue); + root["status"]["value"] = static_cast(status); + root["status"]["description"] = + format_string("%d (0x%X) %s", status, status, cmd.getStatusMessage(status).c_str()); + root["response"] = Json::Value(Json::arrayValue); + + Json::StyledWriter writer; + Log::json(writer.write(root).c_str()); + + throw std::runtime_error(cmd.getStatusMessage(status)); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/BusPal.cpp b/src/blfwk/src/BusPal.cpp new file mode 100644 index 0000000..b7e9549 --- /dev/null +++ b/src/blfwk/src/BusPal.cpp @@ -0,0 +1,1353 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/BusPal.h" +#include "blfwk/Logging.h" +#include "blfwk/serial.h" +#include + +#ifdef LINUX +#include +#include +#endif + +using namespace blfwk; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Bit Bang Mode commands. +enum +{ + kBitBang_Reset = 0x00, //!< Reset, responds "BBIO1" + kBitBang_SpiMode = 0x01, //!< Enter binary SPI mode, responds "SPI1" + kBitBang_I2cMode = 0x02, //!< Enter binary I2C mode, responds "I2C1" + kBitBang_UartMode = 0x03, //!< Enter binary UART mode, responds "ART1" + kBitBang_1Wire = 0x04, //!< Enter binary 1-Wire mode, responds "1W01" + kBitBang_RawWire = 0x05, //!< Enter binary raw-wire mode, responds "RAW1" + kBitBang_Jtag = 0x06, //!< Enter OpenOCD JTAG mode + kBitBang_CanMode = 0x07, //!< Enter binary CAN mode, responds "CAN1" + kBitBang_SaiMode = 0x08, //!< Enter binary SAI mode, responds "SAI1" + kBitBang_HardReset = 0x0f, //!< Reset Bus Pal + kBitBang_SelfTest = 0x10, //!< Bus Pal self-tests + kBitBang_SetupPwm = 0x12, //!< Setup pulse-width modulation (requires 5 byte setup) + kBitBang_ClearPwm = 0x13, //!< Clear/disable PWM + kBitBang_Probe = 0x14, //!< Take voltage probe measurement (returns 2 bytes) + kBitBang_ContProbe = 0x15, //!< Continuous voltage probe measurement + kBitBang_FreqAux = 0x16, //!< Frequency measurement on AUX pin + kBitBang_SetFpgaClock = 0x20, //!< Set the clock frequency on the FPGA board + kBitBang_CfgPins = 0x40, //!< Configure pins as input(1) or output(0): AUX|MOSI|CLK|MISO|CS + kBitBang_SetPins = 0x80 //!< Set on (1) or off (0): POWER|PULLUP|AUX|MOSI|CLK|MISO|CS +}; + +//! @brief Uart Mode commands. +enum +{ + kUart_Version = 0x01, //!< Display mode version string, responds "ARTx" + kUart_EchoRx = 0x02, //!< Start (0)/stop(1) echo UART RX + kUart_SetBaud = 0x07, //!< Manual baud rate configuration, send 2 bytes + kUart_Bridge = 0x0f, //!< UART bridge mode (reset to exit) + kUart_BulkWrite = 0x10, //!< Bulk UART write, send 1-16 bytes (0=1byte!) + kUart_ConfigPeriph = 0x40, //!< Configure peripherals w=power, x=pullups, y=AUX, z=CS + kUart_SetSpeed = 0x60, //!< Set UART speed + kUart_ConfigUart = 0x80 //!< Configure UART settings +}; + +//! @brief Spi mode commands. +enum +{ + kSpi_Exit = 0x00, //!< 00000000 - Exit to bit bang mode + kSpi_Version = 0x01, //!< 00000001 - Enter raw SPI mode, display version string + kSpi_ChipSelect = 0x02, //!< 0000001x - CS high (1) or low (0) + kSpi_Sniff = 0x0c, //!< 000011XX - Sniff SPI traffic when CS low(10)/all(01) + kSpi_BulkTransfer = 0x10, //!< 0001xxxx - Bulk SPI transfer, send/read 1-16 bytes (0=1byte!) + kSpi_ConfigPeriph = 0x40, //!< 0100wxyz - Configure peripherals w=power, x=pull-ups, y=AUX, z=CS + kSpi_SetSpeed = 0x60, //!< 01100xxx - SPI speed + kSpi_ConfigSpi = 0x80, //!< 1000wxyz - SPI config, w=HiZ/3.3v, x=CKP idle, y=CKE edge, z=SMP sample + kSpi_WriteThenRead = 0x04 //!< 00000100 - Write then read extended command +}; + +//! @brief Spi configuration shifts for the mask +enum +{ + kSpiConfigShift_Direction = 0, + kSpiConfigShift_Phase = 1, + kSpiConfigShift_Polarity = 2 +}; + +//! @brief Sai/I2s mode commands. +enum +{ + kSai_Exit = 0x00, //!< 00000000 - Exit to bit bang mode + kSai_Version = 0x01, //!< 00000001 - Enter raw SAI mode, display version string + kSai_SetSpeed = 0x60, //!< 01100000 - SAI speed + kSai_ConfigSai = 0x80, //!< 100xxxyy - SAI config, xxx=protocol, yy=stereo + kSai_WriteThenRead = 0x04 //!< 00000100 - Write then read extended command +}; + +//! @brief Sai configuration shifts for the mask +enum +{ + kSaiConfigShift_Stereo = 0, + kSaiConfigShift_Protocol = 2 +}; + +//! @brief Can mode commands. +enum +{ + kCan_Exit = 0x00, //!< 00000000 - Exit to bit bang mode + kCan_Version = 0x01, //!< 00000001 - Display mode version string, responds "CANx" + kCan_BulkWrite = 0x02, //!< 00000010 + kCan_WriteThenRead = 0x03, //!< 00000011 - Write then read extended command + kCan_ReadFrame = 0x04, //!< 00000100 - CAN read frame + kCan_WriteFrame = 0x05, //!< 00000101 - CAN write frame + kCan_AckBit = 0x06, //!< 00000110 - ACK bit + kCan_NackBit = 0x07, //!< 00000111 - NACK bit + kCan_SetRxFifoId = 0x10, //!< 00010000 - Set CAN RX FIFO id + kCan_SetRxMbId = 0x20, //!< 00100000 - Set CAN RX MB id + kCan_SetTxMbId = 0x30, //!< 00110000 - Set CAN TX MB id + kCan_SetSpeed = 0x40 //!< 010000xx - Set CAN speed, 0=125kHz, 1=250kHz, 2=500kHz, 3=750kHz, 4=1MHz +}; + +enum +{ + kCanMaxReadAttemptCount = 3 //!< CAN reads will sometimes fail, this will be the retry count +}; + +#pragma pack(1) +struct SpiWriteThenReadCommand +{ + uint8_t command; + uint16_t numBytesToWrite; + uint16_t numBytesToRead; +}; + +struct CanWriteThenReadCommand +{ + uint8_t command; + uint16_t numBytesToWrite; + uint16_t numBytesToRead; +}; + +struct SpiSetSpeedCommand +{ + uint8_t command; + uint32_t speed; +}; + +struct CanSetTxidCommand +{ + uint8_t command; + uint32_t txid; +}; + +struct CanSetRxidCommand +{ + uint8_t command; + uint32_t rxid; +}; + +struct CanSetSpeedCommand +{ + uint8_t command; + uint32_t speed; +}; + +struct I2cSetSpeedCommand +{ + uint8_t command; + uint32_t speed; +}; + +struct I2cWriteThenReadCommand +{ + uint8_t command; + uint16_t numBytesToWrite; + uint16_t numBytesToRead; +}; + +struct SaiSetSpeedCommand +{ + uint8_t command; + uint32_t speed; +}; + +struct SaiWriteThenReadCommand +{ + uint8_t command; + uint16_t numBytesToWrite; + uint16_t numBytesToRead; +}; + +struct GpioConfigCommand +{ + uint8_t command; + uint8_t port; + uint8_t pin; + uint8_t muxVal; +}; + +struct GpioSetCommand +{ + uint8_t command; + uint8_t port; + uint8_t pin; + uint8_t level; +}; + +struct SetFpgaClockCommand +{ + uint8_t command; + uint32_t speed; +}; +#pragma pack() + +//! @brief I2c mode commands. +enum +{ + kI2c_Exit = 0x00, //!< 00000000 - Exit to bit bang mode + kI2c_Version = 0x01, //!< 00000001 - Display mode version string, responds "I2Cx" + kI2c_StartBit = 0x02, //!< 00000010 - I2C start bit + kI2c_StopBit = 0x03, //!< 00000011 - I2C stop bit + kI2c_ReadByte = 0x04, //!< 00000100 - I2C read byte + kI2c_AckBit = 0x06, //!< 00000110 - ACK bit + kI2c_NackBit = 0x07, //!< 00000111 - NACK bit + kI2c_BusSniff = 0x0F, //!< 00001111 - Start bus sniffer + kI2c_BulkWrite = 0x10, //!< 0001xxxx - Bulk I2C write, send 1-16 bytes (0=1byte!) + kI2c_ConfigurePeriph = 0x40, //!< 0100wxyz - Configure peripherals w=power, x=pullups, y=AUX, z=CS + kI2c_PullUpSelect = 0x50, //!< 010100xy - Pull up voltage select (BPV4 only)- x=5v y=3.3v + kI2c_SetSpeed = + 0x60, //!< 011000xx - Set I2C speed, 3=~400kHz, 2=~100kHz, 1=~50kHz, 0=~5kHz (updated in v4.2 firmware) + kI2c_SetAddress = 0x70, //!< 11100000 - Set I2C address + kI2c_WriteThenRead = 0x08 //!< Write then read +}; + +enum +{ + kSerialReadTimeoutMs = 3000, //!< 3 second timeout + kI2cMaxReadAttemptCount = 2 //!< I2c reads will sometimes fail, this will be the retry count +}; + +//! @name Expected mode enter command responses. +//@{ + +//! @brief Response to enter bit bang mode. +const char k_responseBitBangMode[] = "BBIO1"; +const char k_responseReset[] = "BBIO1"; +const char k_responseSpiMode[] = "SPI1"; +const char k_responseCanMode[] = "CAN1"; +const char k_responseI2cMode[] = "I2C1"; +const char k_responseUartMode[] = "ART1"; +const char k_responseSaiMode[] = "SAI1"; + +//@} + +//! @brief UART settings. +//! +//! Arguments (lower 5 bits) are wxxyz: +//! w=pin output HiZ(0)/3.3v(1) +//! xx=databits and parity 8/N(0), 8/E(1), 8/O(2), 9/N(3) +//! y=stop bits 1(0)/2(1) +//! z=RX polarity idle 1 (0), idle 0 (1) +enum +{ + kUartSetting_33 = 0x10, + kUartSettings = kUartSetting_33 +}; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// +void BusPal::flushRX() +{ + uint8_t readBuf[100]; + Log::info("Flushing data...\n"); + int test; + while ((test = buspal_serial_read(readBuf, sizeof(readBuf)))) + { + if (test) + { + Log::info("Flushed %d bytes\n", test); + } + } + + Log::info("Done flushing.\n"); +} + +// See See bus_pal.h for documentation on this method. +BusPal::BusPal(int fileDescriptor) + : m_fileDescriptor(fileDescriptor) + , m_mode(kBusPalModeBitBang) + , m_spiWriteByteCount(0) + , m_canFirstReadDelay(100) + , m_canWriteByteCount(0) + , m_saiWriteByteCount(0) +{ +} + +bool BusPal::parse(const string_vector_t ¶ms, BusPal::BusPalConfigData &config) +{ + if (!params[0].compare(0, 3, "spi")) + { + config.function = BusPal::kBusPalFunction_SPI; + + if ((params.size() > 1)) + { + int32_t spiSpeed = atoi(params[1].c_str()); + if (spiSpeed <= 0) + return false; + + config.spiSpeedKHz = spiSpeed; + + if (params.size() > 2) + { + config.spiPolarity = (BusPal::spi_clock_polarity_t)atoi(params[2].c_str()); + + if (params.size() > 3) + { + config.spiPhase = (BusPal::spi_clock_phase_t)atoi(params[3].c_str()); + + if (params.size() > 4) + { + if (!strcmp(params[4].c_str(), "lsb")) + { + config.spiDirection = BusPal::kSpiLsbFirst; + } + else if (!strcmp(params[4].c_str(), "msb")) + { + config.spiDirection = BusPal::kSpiMsbFirst; + } + } + } + } + } + } + else if (!params[0].compare(0, 3, "i2c")) + { + config.function = BusPal::kBusPalFunction_I2C; + + if (params.size() > 1) + { + uint32_t i2cAddress = strtoul(params[1].c_str(), NULL, 16); + + if (i2cAddress > 0x7F) + { + i2cAddress &= 0x7F; + Log::info("Only 7-bit i2c address is supported, so the effective value is 0x%x\n", i2cAddress); + } + config.i2cAddress = (uint8_t)i2cAddress; + + if (params.size() > 2) + { + int32_t i2cSpeed = atoi(params[2].c_str()); + if (i2cSpeed <= 0) + return false; + + config.i2cSpeedKHz = i2cSpeed; + } + } + } + else if (!params[0].compare(0, 3, "can")) + { + config.function = BusPal::kBusPalFunction_CAN; + if ((params.size() > 1)) + { + uint32_t canSpeed = atoi(params[1].c_str()); + if (canSpeed > 4) + return false; + + config.canSpeed = canSpeed; + + if (params.size() > 2) + { + config.canTxid = strtoul(params[2].c_str(), NULL, 16) & 0x7ff; + } + + if (params.size() > 3) + { + config.canRxid = strtoul(params[3].c_str(), NULL, 16) & 0x7ff; + } + } + } + else if (!params[0].compare(0, 3, "sai")) + { + config.function = BusPal::kBusPalFunction_SAI; + + if ((params.size() > 1)) + { + int32_t saiSpeed = atoi(params[1].c_str()); + if (saiSpeed <= 0) + return false; + + config.saiSpeedHz = saiSpeed; + } + } + else if (!params[0].compare(0, 4, "gpio")) + { + if (!params[1].compare(0, 6, "config")) + { + if (params.size() != 5) + { + return false; + } + config.function = kBusPalFunction_GPIO_CONFIG; + config.gpioPort = (uint8_t)*params[2].c_str(); + config.gpioPinNum = atoi(params[3].c_str()); + config.gpioMux = atoi(params[4].c_str()); + } + else if (!params[1].compare(0, 3, "set")) + { + if (params.size() != 5) + { + return false; + } + config.function = kBusPalFunction_GPIO_SET; + config.gpioPort = (uint8_t)*params[2].c_str(); + config.gpioPinNum = atoi(params[3].c_str()); + config.gpioLevel = atoi(params[4].c_str()); + } + else + { + return false; + } + } + else if (!params[0].compare(0, 5, "clock")) + { + if (params.size() != 2) + { + return false; + } + config.function = kBusPalFunction_FPGA_CLOCK_SET; + config.fpgaClockMhz = atoi(params[1].c_str()); + } + else + { + // Error: Invalid BusPal function. + return false; + } + + return true; +} + +bool BusPal::enterSpiMode() +{ + bool retVal = writeCommand(kBitBang_SpiMode, k_responseSpiMode); + + if (retVal) + { + m_mode = kBusPalModeSpi; + } + + return retVal; +} + +bool BusPal::enterCanMode() +{ + bool retVal = writeCommand(kBitBang_CanMode, k_responseCanMode); + + if (retVal) + { + m_mode = kBusPalModeCan; + } + + return retVal; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::setCanTxid(unsigned int txid) +{ + CanSetTxidCommand command = { kCan_SetTxMbId, txid }; + + uint8_t respData = writeCommand(reinterpret_cast(&command), sizeof(command)); + + if (respData != kResponseOk) + { + Log::error("Error: bad response to Can Set Txid, response byte = 0x%x\n", respData); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::setCanRxid(unsigned int rxid) +{ + CanSetRxidCommand command = { kCan_SetRxMbId, rxid }; + + uint8_t respData = writeCommand(reinterpret_cast(&command), sizeof(command)); + + if (respData != kResponseOk) + { + Log::error("Error: bad response to Can Set Rxid, response byte = 0x%x\n", respData); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::setCanSpeed(unsigned int speed) +{ + CanSetSpeedCommand command = { kCan_SetSpeed, speed }; + + uint8_t respData = writeCommand(reinterpret_cast(&command), sizeof(command)); + + if (respData != kResponseOk) + { + Log::error("Error: bad response to Can Set Speed, response byte = 0x%x\n", respData); + return false; + } + + return true; +} + +bool BusPal::enterI2cMode() +{ + bool retVal = writeCommand(kBitBang_I2cMode, k_responseI2cMode); + + if (retVal) + { + m_mode = kBusPalModeI2c; + } + + return retVal; +} + +bool BusPal::enterSaiMode() +{ + bool retVal = writeCommand(kBitBang_SaiMode, k_responseSaiMode); + + if (retVal) + { + m_mode = kBusPalModeSai; + } + + return retVal; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::enterBitBangMode() +{ + Log::info("Entering bit bang mode...\n"); + + if (reset()) + { + Log::info("Entered BB mode\n"); + return true; + } + else + { + Log::error("Error: enter bit bang mode failed\n"); + Log::error("Dumping any remaining data...\n"); + + uint8_t blah; + while (buspal_serial_read(&blah, 1)) + ; + + return false; + } +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::writeCommand(uint8_t commandByte, const char *expectedResponse) +{ + assert(expectedResponse); + int actualBytes = buspal_serial_write(&commandByte, 1, true); + if (actualBytes != 1) + { + Log::error("Error: Bus Pal write command failed\n"); + return false; + } + + char *str = 0; + unsigned int count = strlen(expectedResponse); + + while (!(str = reinterpret_cast(response(count)))) + ; + + return (strncmp(str, expectedResponse, count) == 0); +} + +// See See bus_pal.h for documentation on this method. +uint8_t BusPal::writeCommand(uint8_t commandByte) +{ + int actualBytes = buspal_serial_write(&commandByte, 1, true); + if (actualBytes != 1) + { + Log::error("Error: Bus Pal write command failed\n"); + return false; + } + + uint8_t *data = 0; + + while (data == NULL) + { + data = response(); + } + + return *data; +} + +uint8_t BusPal::writeCommand(uint8_t *command, unsigned int length) +{ + int actualBytes = buspal_serial_write(command, length, true); + if (actualBytes != length) + { + Log::error("Error: Bus Pal write command failed\n"); + return false; + } + + uint8_t *data = 0; + + while (data == NULL) + { + data = response(); + } + + return *data; +} + +// See See bus_pal.h for documentation on this method. +uint8_t *BusPal::response(size_t byteCount) +{ + int remainingBytes = std::min(byteCount, sizeof(m_responseBuffer)); + uint8_t *buffer = m_responseBuffer; + + while (remainingBytes > 0) + { + int actualBytes = buspal_serial_read(buffer, remainingBytes, true); + if (actualBytes > 0) + { + remainingBytes -= actualBytes; + buffer += actualBytes; + } + else + { + // If no bytes received, it is a timeout error. + return NULL; + } + } + return m_responseBuffer; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::resetHardware() +{ + uint8_t rc = writeCommand(kBitBang_HardReset); + if (rc != kResponseOk) + { + Log::error("Error: bad response from hard reset\n"); + return false; + } + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::reset() +{ + // We need to do a non wait forever loop here to verify that bus pal is running/connected, + // so that is why we are doing this manually instead of using writeCommand + // if bus pal is not active this will return failure after the serial read timeout instead of blocking forever + // Reset is always the first command run when talking to bus pal + uint8_t commandByte = kBitBang_Reset; + int actualBytes = buspal_serial_write(&commandByte, sizeof(commandByte), true); + bool retVal = false; + if (actualBytes == sizeof(commandByte)) + { + uint8_t reply[sizeof(k_responseBitBangMode) - 1]; // reply does not send a null + actualBytes = buspal_serial_read(reply, sizeof(reply), true); + + if (actualBytes == (sizeof(k_responseBitBangMode) - 1)) + { + if (!strncmp(reinterpret_cast(reply), k_responseBitBangMode, + sizeof(k_responseBitBangMode) - 1)) + { + retVal = true; + } + } + } + + return retVal; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::setSpiConfig(spi_clock_polarity_t polarity, spi_clock_phase_t phase, spi_shift_direction_t direction) +{ + uint8_t mask = (polarity & 1) << kSpiConfigShift_Polarity; + mask |= (phase & 1) << kSpiConfigShift_Phase; + mask |= (direction & 1) << kSpiConfigShift_Direction; + + uint8_t rc = writeCommand(kSpi_ConfigSpi | mask); + + if (rc != kResponseOk) + { + Log::error("Error: bad response from Set Spi Config\n"); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::setSpiSpeed(unsigned int speed) +{ + SpiSetSpeedCommand command = { kSpi_SetSpeed, speed }; + + uint8_t respData = writeCommand(reinterpret_cast(&command), sizeof(command)); + + if (respData != kResponseOk) + { + Log::error("Error: bad response to Spi Set Speed, response byte = 0x%x\n", respData); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::setI2cAddress(uint8_t address) +{ + uint8_t command[] = { kI2c_SetAddress, address }; + + uint8_t respData = writeCommand(reinterpret_cast(&command), sizeof(command)); + + if (respData != kResponseOk) + { + Log::error("Error: bad response to Set I2c Address, response byte = 0x%x\n", respData); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::setI2cSpeed(uint32_t speed) +{ + I2cSetSpeedCommand command = { kI2c_SetSpeed, speed }; + + uint8_t respData = writeCommand(reinterpret_cast(&command), sizeof(command)); + + if (respData != kResponseOk) + { + Log::error("Error: bad response to I2c Set Speed, response byte = 0x%x\n", respData); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::setSaiConfig(sai_protocol_t protocol, sai_mono_stereo_t stereo) +{ + uint8_t mask = protocol << kSaiConfigShift_Protocol; + mask |= stereo << kSaiConfigShift_Stereo; + + uint8_t rc = writeCommand(kSai_ConfigSai | mask); + + if (rc != kResponseOk) + { + Log::error("Error: bad response from Set Sai Config\n"); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::setSaiSpeed(unsigned int speed) +{ + SaiSetSpeedCommand command = { kSai_SetSpeed, speed }; + + uint8_t respData = writeCommand(reinterpret_cast(&command), sizeof(command)); + + if (respData != kResponseOk) + { + Log::error("Error: bad response to Sai Set Speed, response byte = 0x%x\n", respData); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::rawConfigurePins(uint8_t port, uint8_t pin, uint8_t muxVal) +{ + GpioConfigCommand command = { kBitBang_CfgPins, port, pin, muxVal }; + + uint8_t respData = writeCommand(reinterpret_cast(&command), sizeof(command)); + + if (respData != kResponseOk) + { + Log::error("Error: bad response to Gpio Set, response byte = 0x%x\n", respData); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::rawSetPins(uint8_t port, uint8_t pin, uint8_t level) +{ + GpioSetCommand command = { kBitBang_SetPins, port, pin, level }; + + uint8_t respData = writeCommand(reinterpret_cast(&command), sizeof(command)); + + if (respData != kResponseOk) + { + Log::error("Error: bad response to Gpio Set, response byte = 0x%x\n", respData); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::setFPGAClock(uint32_t clock) +{ + SetFpgaClockCommand command = { kBitBang_SetFpgaClock, clock }; + + uint8_t respData = writeCommand(reinterpret_cast(&command), sizeof(command)); + + if (respData != kResponseOk) + { + Log::error("Error: bad response to FPGA Set Clock, response byte = 0x%x\n", respData); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::write(const uint8_t *data, size_t byteCount) +{ + switch (m_mode) + { + case kBusPalModeSpi: + return writeSpi(data, byteCount); + case kBusPalModeI2c: + return writeI2c(data, byteCount); + case kBusPalModeCan: + return writeCan(data, byteCount); + case kBusPalModeSai: + return writeSai(data, byteCount); + default: + return false; + } +} + +// See See bus_pal.h for documentation on this method. +int BusPal::read(uint8_t *data, size_t byteCount) +{ + switch (m_mode) + { + case kBusPalModeSpi: + return readSpi(data, byteCount); + case kBusPalModeCan: + return readCan(data, byteCount); + case kBusPalModeI2c: + { + if (!byteCount) + { + return 0; + } + + int attemptCount = 0; + int bytesRead = 0; + + while (!bytesRead && (attemptCount++ < kI2cMaxReadAttemptCount)) + { + bytesRead = readI2c(data, byteCount); + if (!bytesRead) + { + Log::info("Initial read on I2C failed, trying again...\n"); + } + } + + return bytesRead; + } + case kBusPalModeSai: + return readSai(data, byteCount); + default: + return false; + } +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::writeSpi(const uint8_t *data, size_t byteCount) +{ + assert(data); + + SpiWriteThenReadCommand command; + command.command = kSpi_WriteThenRead; + command.numBytesToWrite = std::min(byteCount, kBulkTransferMax); + command.numBytesToRead = 0; + + m_spiWriteByteCount += byteCount; + + int actualBytes = buspal_serial_write(reinterpret_cast(&command), sizeof(command), true); + if (actualBytes != sizeof(command)) + { + Log::error("Error: Bus Pal spi write command failed\n"); + return false; + } + + actualBytes = buspal_serial_write(const_cast(data), byteCount); + if (actualBytes != byteCount) + { + Log::error("Error: Bus Pal spi write command failed\n"); + return false; + } + + uint8_t *respData = 0; + + while (respData == NULL) + { + respData = response(1); + } + + if (respData[0] != kResponseOk) + { + Log::error("Error: bad response to spi write, response byte = 0x%x\n", respData[0]); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +int BusPal::readSpi(uint8_t *data, size_t byteCount) +{ + return readSpiActual(data, byteCount); +} + +// See See bus_pal.h for documentation on this method. +int BusPal::readSpiActual(uint8_t *data, size_t byteCount) +{ + assert(data); + + SpiWriteThenReadCommand command; + command.command = kSpi_WriteThenRead; + command.numBytesToWrite = 0; + command.numBytesToRead = std::min(byteCount, kBulkTransferMax); + + int actualBytes = buspal_serial_write(reinterpret_cast(&command), sizeof(command), true); + if (actualBytes != sizeof(command)) + { + Log::error("Error: Bus Pal read spi command failed\n"); + return 0; + } + + uint8_t *respData = 0; + + while (respData == NULL) + { + respData = response(1); + } + + if (respData[0] != kResponseOk) + { + Log::error("Error: bad response to spi read, response byte 0 = 0x%x\n", respData[0]); + return 0; + } + + int remainingBytes = byteCount; + uint8_t *buffer = data; + + while (remainingBytes > 0) + { + actualBytes = buspal_serial_read(buffer, remainingBytes); + if (actualBytes > 0) + { + remainingBytes -= actualBytes; + buffer += actualBytes; + } + else + { + // If no bytes received, it is a timeout error. + return 0; + } + } + + return byteCount; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::writeCan(const uint8_t *data, size_t byteCount) +{ + assert(data); + + CanWriteThenReadCommand command; + command.command = kCan_WriteThenRead; + command.numBytesToWrite = std::min(byteCount, kBulkTransferMax); + command.numBytesToRead = 0; + + m_canWriteByteCount += byteCount; + + int actualBytes = buspal_serial_write(reinterpret_cast(&command), sizeof(command), true); + if (actualBytes != sizeof(command)) + { + Log::error("Error: Bus Pal can write command failed\n"); + return false; + } + + actualBytes = buspal_serial_write(const_cast(data), byteCount); + if (actualBytes != byteCount) + { + Log::error("Error: Bus Pal can write command failed\n"); + return false; + } + + uint8_t *respData = 0; + uint8_t retry = 2; + + while (respData == NULL && (retry-- != 0)) + { + respData = response(1); + } + + if (respData == NULL || respData[0] != kResponseOk) + { + if (respData != NULL) + { + Log::error("Error: bad response to can write, response byte = 0x%x\n", respData[0]); + } + return false; + } + + return true; +} + +// See See BusPal.h for documentation on this method. +int BusPal::readCan(uint8_t *data, size_t byteCount) +{ + assert(data); + + if (m_canFirstReadDelay) + { +#if defined(LINUX) || defined(MACOSX) + sleep(m_canFirstReadDelay); +#else + Sleep(m_canFirstReadDelay); +#endif + } + m_canFirstReadDelay = 0; + + CanWriteThenReadCommand command; + command.command = kCan_WriteThenRead; + command.numBytesToWrite = 0; + command.numBytesToRead = std::min(byteCount, kBulkTransferMax); + + int actualBytes = buspal_serial_write(reinterpret_cast(&command), sizeof(command), true); + if (actualBytes != sizeof(command)) + { + Log::error("Error: Bus Pal read can command failed\n"); + return 0; + } + + uint8_t *respData = 0; + + while (respData == NULL) + { + respData = response(1); + } + + if (respData[0] != kResponseOk) + { + Log::error("Error: bad response to can read, response byte 0 = 0x%x\n", respData[0]); + return 0; + } + + int remainingBytes = byteCount; + uint8_t *buffer = data; + + while (remainingBytes > 0) + { + actualBytes = buspal_serial_read(buffer, remainingBytes); + if (actualBytes > 0) + { + remainingBytes -= actualBytes; + buffer += actualBytes; + } + else + { + // If no bytes received, it is a timeout error. + return 0; + } + } + + return byteCount; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::writeI2c(const uint8_t *data, size_t byteCount) +{ + assert(data); + + I2cWriteThenReadCommand command; + command.command = kI2c_WriteThenRead; + command.numBytesToWrite = std::min(byteCount, kBulkTransferMax); + command.numBytesToRead = 0; + + int actualBytes = buspal_serial_write(reinterpret_cast(&command), sizeof(command), true); + if (actualBytes != sizeof(command)) + { + Log::error("Error: Bus Pal i2c write command failed\n"); + return false; + } + + actualBytes = buspal_serial_write(const_cast(data), byteCount); + if (actualBytes != byteCount) + { + Log::error("Error: Bus Pal i2c write command failed\n"); + return false; + } + + uint8_t *respData = NULL; + while (respData == NULL) + { + respData = response(1); + } + + if (respData[0] != kResponseOk) + { + Log::error("Error: bad response to i2c write, response byte = 0x%x\n", respData[0]); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +int BusPal::readI2c(uint8_t *data, size_t byteCount) +{ + assert(data); + + I2cWriteThenReadCommand command; + command.command = kI2c_WriteThenRead; + command.numBytesToWrite = 0; + command.numBytesToRead = std::min(byteCount, kBulkTransferMax); + + int actualBytes = buspal_serial_write(reinterpret_cast(&command), sizeof(command), true); + if (actualBytes != sizeof(command)) + { + Log::error("Error: Bus Pal i2c read command failed\n"); + return 0; + } + + uint8_t *respData = 0; + + while (respData == NULL) + { + respData = response(1); + } + + if (respData[0] != kResponseOk) + { + Log::error("Error: bad response to i2c read, response byte 0 = 0x%x\n", respData[0]); + return 0; + } + + int remainingBytes = byteCount; + uint8_t *buffer = data; + + while (remainingBytes > 0) + { + actualBytes = buspal_serial_read(buffer, remainingBytes); + if (actualBytes > 0) + { + remainingBytes -= actualBytes; + buffer += actualBytes; + } + else + { + // If no bytes received, it is a timeout error. + return 0; + } + } + + return byteCount; +} + +// See See bus_pal.h for documentation on this method. +bool BusPal::writeSai(const uint8_t *data, size_t byteCount) +{ + assert(data); + + SaiWriteThenReadCommand command; + command.command = kSai_WriteThenRead; + command.numBytesToWrite = std::min(byteCount, kBulkTransferMax); + command.numBytesToRead = 0; + + m_saiWriteByteCount += byteCount; + + int actualBytes = buspal_serial_write(reinterpret_cast(&command), sizeof(command), true); + if (actualBytes != sizeof(command)) + { + Log::error("Error: Bus Pal sai write command failed\n"); + return false; + } + + actualBytes = buspal_serial_write(const_cast(data), byteCount); + if (actualBytes != byteCount) + { + Log::error("Error: Bus Pal sai write command failed\n"); + return false; + } + + uint8_t *respData = 0; + + while (respData == NULL) + { + respData = response(1); + } + + if (respData[0] != kResponseOk) + { + Log::error("Error: bad response to sai write, response byte = 0x%x\n", respData[0]); + return false; + } + + return true; +} + +// See See bus_pal.h for documentation on this method. +int BusPal::readSai(uint8_t *data, size_t byteCount) +{ + assert(data); + + SaiWriteThenReadCommand command; + command.command = kSai_WriteThenRead; + command.numBytesToWrite = 0; + command.numBytesToRead = std::min(byteCount, kBulkTransferMax); + + int actualBytes = buspal_serial_write(reinterpret_cast(&command), sizeof(command), true); + if (actualBytes != sizeof(command)) + { + Log::error("Error: Bus Pal read sai command failed\n"); + return 0; + } + + uint8_t *respData = 0; + + while (respData == NULL) + { + respData = response(1); + } + + if (respData[0] != kResponseOk) + { + Log::error("Error: bad response to sai read, response byte 0 = 0x%x\n", respData[0]); + return 0; + } + + int remainingBytes = byteCount; + uint8_t *buffer = data; + + while (remainingBytes > 0) + { + actualBytes = buspal_serial_read(buffer, remainingBytes); + if (actualBytes > 0) + { + remainingBytes -= actualBytes; + buffer += actualBytes; + } + else + { + // If no bytes received, it is a timeout error. + return 0; + } + } + + return byteCount; +} + +int BusPal::buspal_serial_read(uint8_t *buf, int size, bool isCommandData) +{ + int retVal = serial_read(m_fileDescriptor, (char *)buf, size); + + // If serial_read returned a 0 it is a timeout failure but doing this double + // read fixes an intermittent issue when talking to bus pal the byte is actually sent from + // the bus pal but serial_read returns empty however the byte is available on the next read + if (retVal == 0) + { + retVal = serial_read(m_fileDescriptor, (char *)buf, size); + } + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes read in hex + if (isCommandData) + { + Log::debug2("+"); + } + else + { + Log::debug2("<"); + } + + for (int i = 0; i < retVal; i++) + { + Log::debug2("%02x", (uint8_t)buf[i]); + if (i != (retVal - 1)) + { + Log::debug2(" "); + } + } + if (isCommandData) + { + Log::debug2("+\n"); + } + else + { + Log::debug2(">\n"); + } + } + + return retVal; +} + +int BusPal::buspal_serial_write(uint8_t *buf, int size, bool isCommandData) +{ + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes written in hex + if (isCommandData) + { + Log::debug2("-"); + } + else + { + Log::debug2("["); + } + + for (int i = 0; i < size; i++) + { + Log::debug2("%02x", (uint8_t)buf[i]); + if (i != (size - 1)) + { + Log::debug2(" "); + } + } + if (isCommandData) + { + Log::debug2("-\n"); + } + else + { + Log::debug2("]\n"); + } + } + + return serial_write(m_fileDescriptor, (char *)buf, size); +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/BusPalPeripheral.cpp b/src/blfwk/src/BusPalPeripheral.cpp new file mode 100644 index 0000000..6fdd6cb --- /dev/null +++ b/src/blfwk/src/BusPalPeripheral.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/BusPalPeripheral.h" +#include "blfwk/Logging.h" +#include "blfwk/format_string.h" + +using namespace blfwk; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See BusPalUartPeripheral.h for documentation of this method. +BusPalUartPeripheral::BusPalUartPeripheral(const char *port, long speed, const BusPal::BusPalConfigData &config) + : UartPeripheral(port, speed) +{ + m_busPal = new BusPal(m_fileDescriptor); + if (m_busPal->enterBitBangMode()) + { + configure(config); + } + else + { + delete m_busPal; + m_busPal = NULL; + throw std::runtime_error(format_string("Error: BusPalUartPeripheral() cannot enter BitBang Mode.")); + } +} + +// See BusPalUartPeripheral.h for documentation of this method. +BusPalUartPeripheral::~BusPalUartPeripheral() +{ + if (m_busPal) + { + free(m_busPal); + } +} + +// See BusPalUartPeripheral.h for documentation of this method. +void BusPalUartPeripheral::configure(const BusPal::BusPalConfigData &config) +{ + switch (config.function) + { + case BusPal::kBusPalFunction_SPI: + if (!m_busPal->enterSpiMode()) + { + throw std::runtime_error(format_string("Error: BusPalUartPeripheral() cannot enter SPI Mode.")); + } + + if (!m_busPal->setSpiSpeed(config.spiSpeedKHz)) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot set SPI speed(%d kHz).", config.spiSpeedKHz)); + } + + if (!m_busPal->setSpiConfig(config.spiPolarity, config.spiPhase, config.spiDirection)) + { + throw std::runtime_error(format_string( + "Error: BusPalUartPeripheral() cannot set SPI polarity(%s(%d)), phase(%s(%d)), and " + "direction(%s(%d)).", + config.spiPolarity == BusPal::kSpiClockPolarity_ActiveHigh ? "ActiveHigh" : "ActiveLow", + config.spiPolarity, + config.spiPhase == BusPal::kSpiClockPhase_FirstEdge ? "FirstEdge" : "SecondEdge", config.spiPhase, + config.spiDirection == BusPal::kSpiLsbFirst ? "LsbFirst" : "MsbFirst", config.spiDirection)); + } + break; + + case BusPal::kBusPalFunction_CAN: + if (!m_busPal->enterCanMode()) + { + throw std::runtime_error(format_string("Error: BusPalUartPeripheral() cannot enter CAN Mode.")); + } + + if (!m_busPal->setCanSpeed(config.canSpeed)) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot set CAN speed(%d).", config.canSpeed)); + } + + if (!m_busPal->setCanTxid(config.canTxid)) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot set CAN txid(0x%x).", config.canTxid)); + } + + if (!m_busPal->setCanRxid(config.canRxid)) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot set CAN rxid(0x%x).", config.canTxid)); + } + + break; + + case BusPal::kBusPalFunction_I2C: + if (!m_busPal->enterI2cMode()) + { + throw std::runtime_error(format_string("Error: BusPalUartPeripheral() cannot enter I2C Mode.")); + } + if (!m_busPal->setI2cAddress(config.i2cAddress)) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot set I2C address %02X.", config.i2cAddress)); + } + + if (!m_busPal->setI2cSpeed(config.i2cSpeedKHz)) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot set I2C speed(%d KHz).", config.i2cSpeedKHz)); + } + break; + + case BusPal::kBusPalFunction_SAI: + if (!m_busPal->enterSaiMode()) + { + throw std::runtime_error(format_string("Error: BusPalUartPeripheral() cannot enter SAI Mode.")); + } + + if (!m_busPal->setSaiSpeed(config.saiSpeedHz)) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot set SAI speed(%d Hz).", config.saiSpeedHz)); + } + break; + + case BusPal::kBusPalFunction_GPIO_CONFIG: + if (!m_busPal->enterBitBangMode()) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot enter Bitbang mode for GPIO setting.")); + } + if (!m_busPal->rawConfigurePins(config.gpioPort, config.gpioPinNum, config.gpioMux)) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot configure GPIO %c,%02X,%02X", config.gpioPort, + config.gpioPinNum, config.gpioMux)); + } + break; + + case BusPal::kBusPalFunction_GPIO_SET: + if (!m_busPal->enterBitBangMode()) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot enter Bitbang mode for GPIO setting.")); + } + if (!m_busPal->rawSetPins(config.gpioPort, config.gpioPinNum, config.gpioLevel)) + { + throw std::runtime_error(format_string("Error: BusPalUartPeripheral() cannot set GPIO %c,%02X,%02X", + config.gpioPort, config.gpioPinNum, config.gpioLevel)); + } + break; + + case BusPal::kBusPalFunction_FPGA_CLOCK_SET: + if (!m_busPal->enterBitBangMode()) + { + throw std::runtime_error( + format_string("Error: BusPalUartPeripheral() cannot enter Bitbang mode for FPGA clock setting.")); + } + if (!m_busPal->setFPGAClock(config.fpgaClockMhz)) + { + throw std::runtime_error(format_string("Error: BusPalUartPeripheral() cannot set FPGA clock to %2d MHz", + config.fpgaClockMhz)); + } + break; + + default: + throw std::runtime_error("Unsupported BusPal function type"); + } +} + +// See BusPalUartPeripheral.h for documentation of this method. +status_t BusPalUartPeripheral::read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t timeoutMs) +{ + assert(buffer); + + // Read the requested number of bytes. + int count = m_busPal->read(buffer, requestedBytes); + if (actualBytes) + { + *actualBytes = count; + } + + if (count < (int)requestedBytes) + { + // Anything less than requestedBytes is a timeout error. + Log::error("Error: Bus Pal read returned 0\n"); + return kStatus_Timeout; + } + + return kStatus_Success; +} + +// See BusPalUartPeripheral.h for documentation of this method. +status_t BusPalUartPeripheral::write(const uint8_t *buffer, uint32_t byteCount) +{ + assert(buffer); + + const int maxBulk = BusPal::kBulkTransferMax; + int numBulk = (byteCount / maxBulk); + int remaining = byteCount - (numBulk * maxBulk); + bool rc = true; + + // Send buffer in max bulk transfer size chunks. + for (int i = 0; i < numBulk; ++i) + { + rc = m_busPal->write(&buffer[i * maxBulk], maxBulk); + if (!rc) + { + return kStatus_Fail; + } + } + + // Send the last OR partial chunk. + if (rc && remaining) + { + rc = m_busPal->write(&buffer[numBulk * maxBulk], remaining); + if (!rc) + { + return kStatus_Fail; + } + } + + return kStatus_Success; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/Command.cpp b/src/blfwk/src/Command.cpp new file mode 100644 index 0000000..4af84b6 --- /dev/null +++ b/src/blfwk/src/Command.cpp @@ -0,0 +1,4495 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * Copyright 2016-2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include "blfwk/Command.h" +#include "blfwk/EndianUtilities.h" +#include "blfwk/Logging.h" +#include "blfwk/json.h" +#include "blfwk/utils.h" +#include "bootloader_common.h" +#ifdef LINUX +#include +#endif + +using namespace blfwk; +using namespace utils; +using namespace std; + +//#define TEST_SENDER_ABORT +//#define TEST_RECEIVER_ABORT + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Value of the terminator code in the g_statusCodes table. +const int32_t kStatusMessageTableTerminatorValue = 0x7fffffff; + +//! @brief Status return code descriptions. +StatusMessageTableEntry blfwk::g_statusCodes[] = { + // Generic statuses. + { kStatus_Success, "Success." }, + { kStatus_Fail, "Failure." }, + { kStatus_ReadOnly, "kStatus_ReadOnly" }, + { kStatus_OutOfRange, "kStatus_OutOfRange" }, + { kStatus_InvalidArgument, "kStatus_InvalidArgument" }, + { kStatus_Timeout, "kStatus_Timeout" }, + { kStatus_NoTransferInProgress, "kStatus_NoTransferInProgress" }, + + // Flash driver errors. + { 100 /*kStatus_FlashSizeError*/, "kStatus_FlashSizeError" }, + { 101 /*kStatus_FlashAlignmentError*/, "kStatus_FlashAlignmentError" }, + { 102 /*kStatus_FlashAddressError*/, "kStatus_FlashAddressError" }, + { 103 /*kStatus_FlashAccessError*/, "kStatus_FlashAccessError" }, + { 104 /*kStatus_FlashProtectionViolation*/, "kStatus_FlashProtectionViolation" }, + { 105 /*kStatus_FlashCommandFailure*/, "kStatus_FlashCommandFailure" }, + { 106 /*kStatus_FlashUnknownProperty*/, "kStatus_FlashUnknownProperty" }, + { 108 /*kStatus_FlashRegionExecuteOnly*/, "kStatus_FlashRegionExecuteOnly" }, + { 109 /*kStatus_FlashExecuteInRamFunctionNotReady*/, "kStatus_FlashExecuteInRamFunctionNotReady" }, + { 111 /*kStatus_FLASH_CommandNotSupported*/, "kStatus_FLASH_CommandNotSupported" }, + { 132 /*kStatus_FLASH_OutOfDateCfpaPage*/, "kStatus_FLASH_OutOfDateCfpaPage" }, + { 133 /*kStatus_FLASH_BlankIfrPageData*/, "kStatus_FLASH_BlankIfrPageData" }, + { 134 /*kStatus_FLASH_EncryptedRegionsEraseNotDoneAtOnce*/, "kStatus_FLASH_EncryptedRegionsEraseNotDoneAtOnce" }, + { 135 /*kStatus_FLASH_ProgramVerificationNotAllowed*/, "kStatus_FLASH_ProgramVerificationNotAllowed" }, + { 136 /*kStatus_FLASH_HashCheckError*/, "kStatus_FLASH_HashCheckError" }, + { 137 /*kStatus_FLASH_SealedFfrRegion*/, "kStatus_FLASH_SealedFfrRegion" }, + { 138 /*kStatus_FLASH_FfrRegionWriteBroken*/, "kStatus_FLASH_FfrRegionWriteBroken" }, + { 139 /*kStatus_FLASH_NmpaUpdateNotAllowed*/, "kStatus_FLASH_NmpaUpdateNotAllowed" }, + { 140 /*kStatus_FLASH_CmpaCfgDirectEraseNotAllowed*/, "kStatus_FLASH_CmpaCfgDirectEraseNotAllowed" }, + { 141 /*kStatus_FLASH_FfrBankIsLocked*/, "kStatus_FLASH_FfrBankIsLocked" }, + + // I2C driver errors. + { 200 /*kStatus_I2C_SlaveTxUnderrun*/, "I2C Slave TX Underrun error." }, + { 201 /*kStatus_I2C_SlaveRxOverrun*/, "I2C Slave RX Overrun error." }, + { 202 /*kStatus_I2C_AribtrationLost*/, "I2C Arbitration Lost error." }, + + // SPI driver errors. + { 300 /*kStatus_SPI_SlaveTxUnderrun*/, "SPI Slave TX Underrun error." }, + { 301 /*kStatus_SPI_SlaveRxOverrun*/, "SPI Slave RX Overrun error." }, + + // QSPI driver errors. + { 400 /*kStatus_QspiFlashSizeError*/, "QSPI Flash Size error." }, + { 401 /*kStatus_QspiFlashAlignmentError*/, "QSPI Flash Alignment error." }, + { 402 /*kStatus_QspiFlashAddressError*/, "QSPI Flash Address error." }, + { 403 /*kStatus_QspiFlashCommandFailure*/, "QSPI Flash Command Failure." }, + { 404 /*kStatus_QspiFlashUnknownProperty*/, "QSPI Flash Unknown Property." }, + { 405 /*kStatus_QspiNotConfigured*/, "QSPI Not Configured." }, + { 406 /*kStatus_QspiCommandNotSupported*/, "QSPI Command Not Supported." }, + { 407 /*kStatus_QspiCommandTimeout*/, "QSPI Command Timeout" }, + { 408 /*kStatus_QspiWriteFailure*/, "QSPI Write Failure." }, + + // OTFAD driver errors. + { 500 /*kStatus_OtfadSecurityViolation*/, "OTFAD Security Violation." }, + { 501 /*kStatus_OtfadLogicallyDisabled*/, "OTFAD Logically Disabled." }, + { 502 /*kStatus_OtfadInvalidKey*/, "OTFAD Invalid Key." }, + { 503 /*Kstatus_OtfadInvalidKeyBlob*/, "OTFAD Invalid KeyBlob." }, + + // SDMMC driver errors. + { 1800 /*kStatus_SDMMC_NotSupportYet*/, "Haven't supported" }, + { 1801 /*kStatus_SDMMC_TransferFailed*/, " Send command failed" }, + { 1802 /*kStatus_SDMMC_SetCardBlockSizeFailed*/, " Set block size failed" }, + { 1803 /*kStatus_SDMMC_HostNotSupport*/, "Host doesn't support" }, + { 1804 /*kStatus_SDMMC_CardNotSupport*/, "Card doesn't support" }, + { 1805 /*kStatus_SDMMC_AllSendCidFailed*/, "Send CID failed" }, + { 1806 /*kStatus_SDMMC_SendRelativeAddressFailed*/, "Send relative address failed" }, + { 1807 /*kStatus_SDMMC_SendCsdFailed*/, "Send CSD failed" }, + { 1808 /*kStatus_SDMMC_SelectCardFailed*/, "Select card failed" }, + { 1809 /*kStatus_SDMMC_SendScrFailed*/, "Send SCR failed" }, + { 1810 /*kStatus_SDMMC_SetDataBusWidthFailed*/, "Set bus width failed" }, + { 1811 /*kStatus_SDMMC_GoIdleFailed*/, "Go idle failed" }, + { 1812 /*kStatus_SDMMC_HandShakeOperationConditionFailed*/, "Send Operation Condition failed" }, + { 1813 /*kStatus_SDMMC_SendApplicationCommandFailed*/, "Send application command failed" }, + { 1814 /*kStatus_SDMMC_SwitchFailed*/, "Switch command failed" }, + { 1815 /*kStatus_SDMMC_StopTransmissionFailed*/, "Stop transmission failed" }, + { 1816 /*kStatus_SDMMC_WaitWriteCompleteFailed*/, "Wait write complete failed" }, + { 1817 /*kStatus_SDMMC_SetBlockCountFailed*/, "Set block count failed" }, + { 1818 /*kStatus_SDMMC_SetRelativeAddressFailed*/, "Set relative address failed" }, + { 1819 /*kStatus_SDMMC_SwitchBusTimingFailed*/, "Switch high speed failed" }, + { 1820 /*kStatus_SDMMC_SendExtendedCsdFailed*/, "Send EXT_CSD failed" }, + { 1821 /*kStatus_SDMMC_ConfigureBootFailed*/, "Configure boot failed" }, + { 1822 /*kStatus_SDMMC_ConfigureExtendedCsdFailed*/, "Configure EXT_CSD failed" }, + { 1823 /*kStatus_SDMMC_EnableHighCapacityEraseFailed*/, "Enable high capacity erase failed" }, + { 1824 /*kStatus_SDMMC_SendTestPatternFailed*/, "Send test pattern failed" }, + { 1825 /*kStatus_SDMMC_ReceiveTestPatternFailed*/, "Receive test pattern failed" }, + { 1826 /*kStatus_SDMMC_SDIO_ResponseError*/, "sdio response error" }, + { 1827 /*kStatus_SDMMC_SDIO_InvalidArgument*/, "sdio invalid argument response error" }, + { 1828 /*kStatus_SDMMC_SDIO_SendOperationConditionFail*/, "sdio send operation condition fail" }, + { 1829 /*kStatus_SDMMC_InvalidVoltage*/, "invaild voltage" }, + { 1830 /*kStatus_SDMMC_SDIO_SwitchHighSpeedFail*/, "switch to high speed fail" }, + { 1831 /*kStatus_SDMMC_SDIO_ReadCISFail*/, "read CIS fail" }, + { 1832 /*kStatus_SDMMC_SDIO_InvalidCard*/, "invaild SDIO card" }, + { 1833 /*kStatus_SDMMC_TuningFail*/, "tuning fail" }, + { 1834 /*kStatus_SDMMC_SwitchVoltageFail*/, "switch voltage fail" }, + { 1835 /*kStatus_SDMMC_ReTuningRequest*/, "retuning request" }, + { 1836 /*kStatus_SDMMC_SetDriverStrengthFail*/, "set driver strength fail" }, + { 1837 /*kStatus_SDMMC_SetPowerClassFail*/, "set power class fail" }, + + // Bootloader errors. + { kStatus_UnknownCommand, "kStatus_UnknownCommand" }, + { kStatus_SecurityViolation, "Command disallowed when security is enabled." }, + { kStatus_AbortDataPhase, "kStatus_AbortDataPhase" }, + { kStatus_Ping, "kStatus_Ping" }, + { kStatus_NoResponse, "No response packet from target device." }, + { kStatus_NoResponseExpected, "No response packet from target device was expected." }, + { kStatus_CommandUnsupported, " Command is not supported\n" }, + + // SB loader errors. + { kStatusRomLdrSectionOverrun, "kStatusRomLdrSectionOverrun" }, + { kStatusRomLdrSignature, "kStatusRomLdrSignature" }, + { kStatusRomLdrSectionLength, "kStatusRomLdrSectionLength" }, + { kStatusRomLdrUnencryptedOnly, "kStatusRomLdrUnencryptedOnly" }, + { kStatusRomLdrEOFReached, "kStatusRomLdrEOFReached" }, + { kStatusRomLdrChecksum, "kStatusRomLdrChecksum" }, + { kStatusRomLdrCrc32Error, "kStatusRomLdrCrc32Error" }, + { kStatusRomLdrUnknownCommand, "kStatusRomLdrUnknownCommand" }, + { kStatusRomLdrIdNotFound, "kStatusRomLdrIdNotFound" }, + { kStatusRomLdrDataUnderrun, "kStatusRomLdrDataUnderrun" }, + { kStatusRomLdrJumpReturned, "kStatusRomLdrJumpReturned" }, + { kStatusRomLdrCallFailed, "kStatusRomLdrCallFailed" }, + { kStatusRomLdrKeyNotFound, "kStatusRomLdrKeyNotFound" }, + { kStatusRomLdrSecureOnly, "kStatusRomLdrSecureOnly" }, + { kStatusRomLdrResetReturned, "kStatusRomLdrResetReturned" }, + { kStatusRomLdrRollbackBlocked, "kStatusRomLdrRollbackBlocked" }, + { kStatusRomLdrInvalidSectionMacCount, "kStatusRomLdrInvalidSectionMacCount" }, + { kStatusRomLdrUnexpectedCommand, "kStatusRomLdrUnexpectedCommand" }, + + // Memory interface errors. + { kStatusMemoryRangeInvalid, "kStatusMemoryRangeInvalid" }, + { kStatusMemoryReadFailed, "kStatusMemoryReadFailed" }, + { kStatusMemoryWriteFailed, "kStatusMemoryWriteFailed" }, + { kStatusMemoryCumulativeWrite, "kStatusMemoryCumulativeWrite" }, + { kStatusMemoryAppOverlapWithExecuteOnlyRegion, "kStatusMemoryAppOverlapWithExecuteOnlyRegion" }, + { kStatusMemoryNotConfigured, "kStatusMemoryNotConfigured" }, + { kStatusMemoryAlignmentError, "kStatusMemoryAlignmentError" }, + { kStatusMemoryVerifyFailed, "kStatusMemoryVerifyFailed" }, + { kStatusMemoryWriteProtected, "kStatusMemoryWriteProtected" }, + { kStatusMemoryAddressError, "kStatusMemoryAddressError" }, + { kStatusMemoryBlankCheckFailed, "kStatusMemoryBlankCheckFailed" }, + { kStatusMemoryBlankPageReadDisallowed, "kStatusMemoryBlankPageReadDisallowed" }, + { kStatusMemoryProtectedPageReadDisallowed, "kStatusMemoryProtectedPageReadDisallowed" }, + { kStatusMemoryUnsupportedCommand, "kStatusMemoryUnsupportedCommand" }, + + // Property store errors. + { kStatus_UnknownProperty, "Unknown property." }, + { kStatus_ReadOnlyProperty, "Property is read-only." }, + { kStatus_InvalidPropertyValue, "Invalid property value." }, + + /*!< Group number for FLASHIAP status codes */ + { kStatus_FLASHIAP_InvalidArgument, "kStatus_FLASHIAP_InvalidArgument" }, + { kStatus_FLASHIAP_UnknownProperty, "kStatus_FLASHIAP_UnknownProperty" }, + { kStatus_FLASHIAP_AlignmentError, "kStatus_FLASHIAP_AlignmentError" }, + { kStatus_FLASHIAP_AddressError, "kStatus_FLASHIAP_AddressError" }, + { kStatus_FLASHIAP_EraseKeyError, "kStatus_FLASHIAP_EraseKeyError" }, + { kStatus_FLASHIAP_MemoryNotblank, "kStatus_FLASHIAP_MemoryNotblank" }, + + /*!< Group number for FLASHIAP wrapper status codes */ + { kStatus_FLASHIAP_Success, "kStatus_FLASHIAP_Success" }, + { kStatus_FLASHIAP_InvalidCommand, "kStatus_FLASHIAP_InvalidCommand" }, + { kStatus_FLASHIAP_SrcAddrError, "kStatus_FLASHIAP_SrcAddrError" }, + { kStatus_FLASHIAP_DstAddrError, "kStatus_FLASHIAP_DstAddrError" }, + { kStatus_FLASHIAP_SrcAddrNotMapped, "kStatus_FLASHIAP_SrcAddrNotMapped" }, + { kStatus_FLASHIAP_DstAddrNotMapped, "kStatus_FLASHIAP_DstAddrNotMapped" }, + { kStatus_FLASHIAP_CountError, "kStatus_FLASHIAP_CountError" }, + { kStatus_FLASHIAP_InvalidSector, "kStatus_FLASHIAP_InvalidSector" }, + { kStatus_FLASHIAP_SectorNotblank, "kStatus_FLASHIAP_SectorNotblank" }, + { kStatus_FLASHIAP_NotPrepared, "kStatus_FLASHIAP_NotPrepared" }, + { kStatus_FLASHIAP_CompareError, "kStatus_FLASHIAP_CompareError" }, + { kStatus_FLASHIAP_Busy, "kStatus_FLASHIAP_Busy" }, + { kStatus_FLASHIAP_ParamError, "kStatus_FLASHIAP_ParamError" }, + { kStatus_FLASHIAP_AddrError, "kStatus_FLASHIAP_AddrError" }, + { kStatus_FLASHIAP_AddrNotMapped, "kStatus_FLASHIAP_AddrNotMapped" }, + { kStatus_FLASHIAP_NoPower, "kStatus_FLASHIAP_NoPower" }, + { kStatus_FLASHIAP_NoClock, "kStatus_FLASHIAP_NoClock" }, + + // Application crc check statuses. + { 10400, "kStatus_AppCrcCheckPassed" }, + { 10401, "kStatus_AppCrcCheckFailed" }, + { 10402, "kStatus_AppCrcCheckInactive" }, + { 10403, "kStatus_AppCrcCheckInvalid" }, + { 10404, "kStatus_AppCrcCheckOutOfRange" }, + + // Packetizer errors. + { kStatus_NoPingResponse, "No response received for ping command." }, + { kStatus_InvalidPacketType, "Invalid packet type." }, + { kStatus_InvalidCRC, "Invalid CRC value." }, + { kStatus_NoCommandResponse, "No response received for command." }, + + // Reliable Update statuses. + { 10600, "kStatus_ReliableUpdateSuccess" }, + { 10601, "kStatus_ReliableUpdateFail" }, + { 10602, "kStatus_ReliableUpdateInacive" }, + { 10603, "kStatus_ReliableUpdateBackupApplicationInvalid" }, + { 10604, "kStatus_ReliableUpdateStillInMainApplication" }, + { 10605, "kStatus_ReliableUpdateSwapSystemNotReady" }, + { 10606, "kStatus_ReliableUpdateBackupBootloaderNotReady" }, + { 10607, "kStatus_ReliableUpdateSwapIndicatorAddressInvalid" }, + { 10608, "kStatus_ReliableUpdateSwapSystemNotAvailable" }, + { 10609, "kStatus_ReliableUpdateSwapTest" }, + + // Serial NOR/EEPROM statuses. + { 10700, "kStatus_SerialNorEepromAddressInvalid" }, + { 10701, "kStatus_SerialNorEepromTransferError" }, + { 10702, "kStatus_SerialNorEepromTypeInvalid" }, + { 10703, "kStatus_SerialNorEepromSizeInvalid" }, + { 10704, "kStatus_SerialNorEepromCommandInvalid" }, + + // FlexSPI NAND statuses. + { 20000, "kStatus_FlexSPINAND_ReadPageFail" }, + { 20001, "kStatus_FlexSPINAND_ReadCacheFail" }, + { 20002, "kStatus_FlexSPINAND_EccCheckFail" }, + { 20003, "kStatus_FlexSPINAND_PageLoadFail" }, + { 20004, "kStatus_FlexSPINAND_PageExecuteFail" }, + { 20005, "kStatus_FlexSPINAND_EraseBlockFail" }, + { 20006, "kStatus_FlexSPINAND_WaitTimeout" }, + { 20007, "SPI NAND PageSize over the max supported size" }, + { 20008, "Failed to update Flash config block to SPI NAND" }, + { 20009, "Failed to update discovered bad block table to SPI NAND" }, + { 20010, "kStatus_FlexSPINAND_WriteAlignmentError" }, + { 20011, "kStatus_FlexSPINAND_NotFound" }, + + // FlexSPI NOR statuses. + { 20100, "kStatus_FLEXSPINOR_ProgramFail" }, + { 20101, "kStatus_FLEXSPINOR_EraseSectorFail" }, + { 20102, "kStatus_FLEXSPINOR_EraseAllFail" }, + { 20103, "kStatus_FLEXSPINOR_WaitTimeout" }, + { 20104, "FlexSPI NOR PageSize over the max supported size" }, + { 20105, "kStatus_FlexSPINOR_WriteAlignmentError" }, + { 20106, "kStatus_FlexSPINOR_CommandFailure" }, + { 20107, "kStatus_FlexSPINOR_SFDP_NotFound" }, + { 20108, "kStatus_FLEXSPINOR_Unsupported_SFDP_Version" }, + { 20109, "kStatus_FLEXSPINOR_Flash_NotFound" }, + { 20110, "kStatus_FLEXSPINOR_DTRRead_DummyProbeFailed" }, + + { 20200, "kStatus_OCOTP_ReadFailure" }, + { 20201, "kStatus_OCOTP_ProgramFailure" }, + { 20202, "kStatus_OCOTP_ReloadFailure" }, + { 20203, "kStatus_OCOTP_WaitTimeout" }, + + // SEMC NOR statuses. + { 21100, "kStatus_SemcNOR_DeviceTimeout" }, + { 21101, "kStatus_SemcNOR_InvalidMemoryAddress" }, + { 21102, "kStatus_SemcNOR_unmatchedCommandSet" }, + { 21103, "kStatus_SemcNOR_AddressAlignmentError" }, + { 21104, "kStatus_SemcNOR_InvalidCfiSignature" }, + { 21105, "kStatus_SemcNOR_CommandErrorNoOpToSuspend" }, + { 21106, "kStatus_SemcNOR_CommandErrorNoInfoAvailable" }, + { 21107, "kStatus_SemcNOR_BlockEraseCommandFailure" }, + { 21108, "kStatus_SemcNOR_BufferProgramCommandFailure" }, + { 21109, "kStatus_SemcNOR_ProgramVerifyFailure" }, + { 21110, "kStatus_SemcNOR_EraseVerifyFailure" }, + { 21116, "kStatus_SemcNOR_InvalidCfgTag" }, + + // SEMC NAND statuses. + { 21200, "kStatus_SemcNAND_DeviceTimeout" }, + { 21201, "kStatus_SemcNAND_InvalidMemoryAddress" }, + { 21202, "kStatus_SemcNAND_NotEqualToOnePageSize" }, + { 21203, "kStatus_SemcNAND_MoreThanOnePageSize" }, + { 21204, "kStatus_SemcNAND_EccCheckFail" }, + { 21205, "kStatus_SemcNAND_InvalidOnfiParameter" }, + { 21206, "kStatus_SemcNAND_CannotEnableDeviceEcc" }, + { 21207, "kStatus_SemcNAND_SwitchTimingModeFailure" }, + { 21208, "kStatus_SemcNAND_ProgramVerifyFailure" }, + { 21209, "kStatus_SemcNAND_EraseVerifyFailure" }, + { 21210, "kStatus_SemcNAND_InvalidReadbackBuffer" }, + { 21216, "kStatus_SemcNAND_InvalidCfgTag" }, + { 21217, "kStatus_SemcNAND_FailToUpdateFcb" }, + { 21218, "kStatus_SemcNAND_FailToUpdateDbbt" }, + { 21219, "kStatus_SemcNAND_DisallowOverwriteBcb" }, + { 21220, "kStatus_SemcNAND_OnlySupportOnfiDevice" }, + { 21221, "kStatus_SemcNAND_MoreThanMaxImageCopy" }, + { 21222, "kStatus_SemcNAND_DisorderedImageCopies" }, + + // SPIFI NOR statuses. + { 22000, "kStatus_SPIFINOR_ProgramFail" }, + { 22001, "kStatus_SPIFINOR_EraseSectorFail" }, + { 22002, "kStatus_SPIFINOR_EraseAllFail" }, + { 22003, "kStatus_SPIFINOR_WaitTimeout" }, + { 22004, "kStatus_SPIFINOR_NotSupported" }, + { 22005, "kStatus_SPIFINOR_WriteAlignmentError" }, + { 22006, "kStatus_SPIFINOR_CommandFailure" }, + { 22007, "kStatus_SPIFINOR_SFDP_NotFound" }, + + // FlexSPI statuses. + { 70000, "kStatus_FLEXSPI_SequenceExecutionTimeout" }, + { 70001, "kStatus_FLEXSPI_InvalidSequence" }, + { 70002, "kStatus_FLEXSPI_DeviceTimeout" }, + + // Terminator + { kStatusMessageTableTerminatorValue, "" } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +Command *Command::create(const string_vector_t *argv) +{ + Command *cmd; + + if (argv->at(0) == kCommand_Reset.name) + { + cmd = new Reset(argv); + } + else if (argv->at(0) == kCommand_GetProperty.name) + { + cmd = new GetProperty(argv); + } + else if (argv->at(0) == kCommand_SetProperty.name) + { + cmd = new SetProperty(argv); + } + else if (argv->at(0) == kCommand_FlashEraseRegion.name) + { + cmd = new FlashEraseRegion(argv); + } + else if (argv->at(0) == kCommand_FlashEraseAll.name) + { + cmd = new FlashEraseAll(argv); + } + else if (argv->at(0) == kCommand_FlashEraseAllUnsecure.name) + { + cmd = new FlashEraseAllUnsecure(argv); + } + else if (argv->at(0) == kCommand_GenerateKeyBlob.name) + { + cmd = new GenerateKeyBlob(argv); + } + else if (argv->at(0) == kCommand_ReadMemory.name) + { + cmd = new ReadMemory(argv); + } + else if (argv->at(0) == kCommand_WriteMemory.name) + { + cmd = new WriteMemory(argv); + } + else if (argv->at(0) == kCommand_ProgramAESKey.name) + { + // Don't need customer to input address, fix it. + (const_cast(argv))->insert(argv->begin() + 1, "0x0"); + // Add MemoryID to command, it's always 0x200 + (const_cast(argv))->insert(argv->begin() + 3, "0x200"); + cmd = new WriteMemory(argv); + } + else if (argv->at(0) == kCommand_FillMemory.name) + { + cmd = new FillMemory(argv); + } + else if (argv->at(0) == kCommand_ReceiveSbFile.name) + { + cmd = new ReceiveSbFile(argv); + } + else if (argv->at(0) == kCommand_LoadImage.name) + { + cmd = new LoadImage(argv); + } + else if (argv->at(0) == kCommand_FuseProgram.name) + { + cmd = new FuseProgram(argv); + } + else if (argv->at(0) == kCommand_FuseRead.name) + { + cmd = new FuseRead(argv); + } + else if (argv->at(0) == kCommand_Execute.name) + { + cmd = new Execute(argv); + } + else if (argv->at(0) == kCommand_Call.name) + { + cmd = new Call(argv); + } + else if (argv->at(0) == kCommand_FlashSecurityDisable.name) + { + cmd = new FlashSecurityDisable(argv); + } + else if (argv->at(0) == kCommand_FlashProgramOnce.name) + { + cmd = new FlashProgramOnce(argv); + } + else if (argv->at(0) == kCommand_EfuseProgramOnce.name) + { + // Add lenght parameter to the argv. efuse read and program are always 4bytes alignment. + (const_cast(argv))->insert(argv->begin() + 2, "4"); + cmd = new FlashProgramOnce(argv); + } + else if (argv->at(0) == kCommand_FlashReadOnce.name) + { + cmd = new FlashReadOnce(argv); + } + else if (argv->at(0) == kCommand_EfuseReadOnce.name) + { + // Add lenght parameter to the argv. efuse read and program are always 4bytes alignment. + (const_cast(argv))->insert(argv->begin() + 2, "4"); + cmd = new FlashReadOnce(argv); + } + else if (argv->at(0) == kCommand_FlashReadResource.name) + { + cmd = new FlashReadResource(argv); + } + else if (argv->at(0) == kCommand_ConfigureMemory.name) + { + cmd = new ConfigureMemory(argv); + } + else if (argv->at(0) == kCommand_ReliableUpdate.name) + { + cmd = new ReliableUpdate(argv); + } + else if (argv->at(0) == kCommand_KeyProvisioning.name) + { + cmd = new KeyProvisioning(argv); + } + else if (argv->at(0) == kCommand_TrustProvisioning.name) + { + cmd = new TrustProvisioning(argv); + } + else if (argv->at(0) == kCommand_FlashImage.name) + { + cmd = new FlashImage(argv); + } + else if (argv->at(0) == kCommand_ConfigureI2c.name) + { + cmd = new ConfigureI2c(argv); + } + else if (argv->at(0) == kCommand_ConfigureSpi.name) + { + cmd = new ConfigureSpi(argv); + } + else if (argv->at(0) == kCommand_ConfigureCan.name) + { + cmd = new ConfigureCan(argv); + } + else if (argv->at(0) == kCommand_ListMemory.name) + { + cmd = new ListMemory(argv); + } + else + { + return NULL; + } + + // Validate arguments. + if (!cmd->init()) + { + delete cmd; + return NULL; + } + + return cmd; +} + +// See host_command.h for documentation of this method. +std::string Command::getResponse() const +{ + Json::Value root; + root["command"] = getName(); + root["status"] = Json::Value(Json::objectValue); + root["status"]["value"] = static_cast(m_responseValues.at(0)); + root["status"]["description"] = format_string("%d (0x%X) %s", m_responseValues.at(0), m_responseValues.at(0), + getStatusMessage(m_responseValues.at(0)).c_str()); + root["response"] = Json::Value(Json::arrayValue); + for (int i = 1; i < (int)m_responseValues.size(); ++i) + { + root["response"].append(Json::Value(m_responseValues.at(i))); + } + + Json::StyledWriter writer; + return writer.write(root); +} + +std::string Command::getStatusMessage(status_t code) +{ + int i; + for (i = 0; g_statusCodes[i].status != kStatusMessageTableTerminatorValue; ++i) + { + if (code == g_statusCodes[i].status) + { + return g_statusCodes[i].message; + } + } + + return format_string("Unknown error code (%d)", code); +} + +// See host_command.h for documentation of this method. +void Command::logResponses() const +{ + const uint32_vector_t *respv = getResponseValues(); + size_t count = respv->size(); + + Log::info("Response status = %d (0x%x) %s\n", respv->at(0), respv->at(0), getStatusMessage(respv->at(0)).c_str()); + + for (int i = 1; i < (int)count; ++i) + { + Log::info("Response word %d = %ld (0x%lx)\n", i, respv->at(i), respv->at(i)); + } + + // Print the response details if there are any. + if (!m_responseDetails.empty()) + { + Log::info("%s\n", m_responseDetails.c_str()); + } + + Log::json(getResponse().c_str()); +} + +// See host_command.h for documentation of this method. +bool Command::processResponse(const generic_response_packet_t *packet, uint8_t commandTag) +{ + if (!packet) + { + Log::debug("processResponse: null packet\n"); + m_responseValues.push_back(kStatus_NoResponse); + return false; + } + + if (packet->commandPacket.commandTag != kCommandTag_GenericResponse) + { + Log::error("Error: expected kCommandTag_GenericResponse (0x%x), received 0x%x\n", kCommandTag_GenericResponse, + packet->commandPacket.commandTag); + m_responseValues.push_back(kStatus_UnknownCommand); + return false; + } + if (packet->commandTag != commandTag) + { + Log::error("Error: expected commandTag 0x%x, received 0x%x\n", commandTag, packet->commandTag); + m_responseValues.push_back(kStatus_UnknownCommand); + return false; + } + + // Set the status in the response vector. + m_responseValues.push_back(packet->status); + + if (packet->status != kStatus_Success) + { + return false; + } + + Log::info("Successful generic response to command '%s'\n", getName().c_str()); + + return true; +} + +//! See host_command.h for documentation on this function. +bool blfwk::DataPacket::FileDataProducer::init(string filePath, uint32_t count) +{ + m_filePointer = fopen(filePath.c_str(), "rb"); + if (!m_filePointer) + { + Log::error("Error: cannot open input data file '%s'\n", filePath.c_str()); + return false; + } + + // If the fileSize wasn't specified, get the file size. + if (count) + { + m_fileSize = count; + } + else + { + ::fseek(m_filePointer, 0, SEEK_END); + m_fileSize = ftell(m_filePointer); + ::fseek(m_filePointer, 0, SEEK_SET); + } + + Log::info("Preparing to send %d (0x%x) bytes to the target.\n", m_fileSize, m_fileSize); + return true; +} + +//! See host_command.h for documentation on this function. +uint32_t blfwk::DataPacket::FileDataProducer::getData(uint8_t *data, uint32_t size) +{ + assert(m_filePointer); + assert(data); + if ((size == 0) || !hasMoreData()) + { + return 0; + } + + return (uint32_t)fread(data, 1, size, m_filePointer); +} + +//! See host_command.h for documentation on this function. +uint32_t blfwk::DataPacket::HexDataProducer::initFromString(const string hexData) +{ + // Remove everything from string except for hex digits. + string hexString = string_hex(hexData); + + // Clear any existing data. + m_data.clear(); + + // Load data byte array from hex string. + // Two hex characters equals one byte. + // Any trailing character is ignored. + for (uint32_t i = 0; i < hexString.size(); i += 2) + { + string hexByte = hexString.substr(i, 2); + long int val = strtol(hexByte.c_str(), NULL, 16); + m_data.push_back(static_cast(val)); + } + + return m_data.size(); +} + +//! See host_command.h for documentation on this function. +uint32_t blfwk::DataPacket::HexDataProducer::getData(uint8_t *data, uint32_t size) +{ + assert(data); + + if (!hasMoreData()) + { + return 0; + } + + uint32_t remaining = m_data.size() - m_byteIndex; + size = min(size, remaining); + memcpy(data, &m_data[m_byteIndex], size); + m_byteIndex += size; + return size; +} + +//! See host_command.h for documentation on this function. +uint32_t blfwk::DataPacket::SegmentDataProducer::getData(uint8_t *data, uint32_t size) +{ + assert(data); + + if (!hasMoreData()) + { + return 0; + } + + size = m_segment->getData(m_byteIndex, size, data); + m_byteIndex += size; + return size; +} + +//! See host_command.h for documentation on this function. +bool blfwk::DataPacket::FileDataConsumer::init(string filePath) +{ + m_filePointer = fopen(filePath.c_str(), "wb"); + if (!m_filePointer) + { + Log::error("Error: cannot open output data file '%s'\n", filePath.c_str()); + return false; + } + return true; +} + +//! See host_command.h for documentation on this function. +void blfwk::DataPacket::FileDataConsumer::processData(const uint8_t *data, uint32_t size) +{ + fwrite(data, 1, size, m_filePointer); +} + +//! See host_command.h for documentation on this function. +void blfwk::DataPacket::StdOutDataConsumer::processData(const uint8_t *data, uint32_t size) +{ + // Only the size of the final packet can be smaller than 32 bytes. + // So moving the cursor to the start will not over-write the data already displayed. + printf("\r"); + + if (m_dataCacheCount) + { + for (int i = 0; i < m_dataCacheCount; ++i) + { + printf("%02x", m_dataCache[i]); + if ((m_currentCount++ % kBytesPerLine) == 0) + { + printf("\n"); + } + else + { + printf(" "); + } + } + m_dataCacheCount = 0; + } + + for (int i = 0; i < (int)size; ++i) + { + printf("%02x", data[i]); + if ((m_currentCount++ % kBytesPerLine) == 0) + { + printf("\n"); + if ((size - i - 1) < kBytesPerLine) + { + memcpy(m_dataCache, &data[i + 1], size - i - 1); + m_dataCacheCount = size - i - 1; + return; + } + } + else + { + printf(" "); + } + } +} + +//! See host_command.h for documentation on this function. +uint8_t *blfwk::DataPacket::sendTo(Packetizer &device, uint32_t *bytesWritten, Progress *progress) +{ + return sendTo(device, bytesWritten, progress, true); +} + +//! See host_command.h for documentation on this function. +uint8_t *blfwk::DataPacket::sendTo(Packetizer &device, uint32_t *bytesWritten, Progress *progress, bool hasResponse) +{ + *bytesWritten = 0; + + if (!m_dataProducer->hasMoreData()) + { + device.pumpSimulator(); + } + + while (m_dataProducer->hasMoreData() && *bytesWritten < m_dataProducer->getDataSize()) + { + uint32_t count = MIN(m_packetSize, (m_dataProducer->getDataSize() - *bytesWritten)); + count = m_dataProducer->getData(m_packet, count); + if (count) + { + status_t status = device.writePacket((const uint8_t *)m_packet, count, kPacketType_Data); + if (status != kStatus_Success) + { + Log::error("Data phase write aborted by status 0x%x %s\n", status, + Command::getStatusMessage(status).c_str()); + if ((status == kStatus_AbortDataPhase) && device.isAbortEnabled()) + { + Log::error("Possible JUMP or RESET command received.\n"); + } + break; + } + + *bytesWritten += count; + + if (progress != NULL) + { + // execute process callback function. + progress->progressCallback(*bytesWritten * 100 / m_dataProducer->getDataSize()); + if (progress->abortPhase()) + { + device.writePacket((const uint8_t *)&m_packet, 0, kPacketType_Data); + break; + } + } +#ifdef TEST_SENDER_ABORT + // Send zero length packet to abort data phase. + Log::info("Testing data phase abort\n"); + device.writePacket((const uint8_t *)&m_packet, 0, kPacketType_Data); + break; +#endif + } + } + + if (hasResponse) + { + // Read final command status + uint8_t *responsePacket; + uint32_t responseLength; + if (device.readPacket(&responsePacket, &responseLength, kPacketType_Command) != kStatus_Success) + { + return NULL; + } + else + { + return responsePacket; + } + } + else + { + return NULL; + } +} + +//! See host_command.h for documentation on this function. +uint8_t *blfwk::DataPacket::receiveFrom(Packetizer &device, uint32_t *byteCount, Progress *progress) +{ + status_t status = kStatus_Success; + uint32_t totalCount = *byteCount; + // If byte count is zero, need to pump the simulator to get the final response + if (*byteCount == 0) + { + device.pumpSimulator(); + } + + while (*byteCount > 0) + { + uint8_t *dataPacket; + uint32_t length; + + // Pump the simulator command state machine, if it is enabled. + device.pumpSimulator(); + + status = device.readPacket(&dataPacket, &length, kPacketType_Data); + + // Bail if there was an error reading the packet. + if (status != kStatus_Success) + { + Log::info("Read data packet error. Sending ACK.\n"); + m_dataConsumer->finalize(); + device.sync(); + return NULL; + } + + // Check for sender abort of data phase. + if (length == 0) + { + Log::info("Data phase aborted by sender\n"); + break; + } + + m_dataConsumer->processData(dataPacket, length); + *byteCount -= length; + + if (*byteCount <= 0) + { + m_dataConsumer->finalize(); + } + + if (progress != NULL) + { + progress->progressCallback((totalCount - *byteCount) * 100 / totalCount); + if (progress->abortPhase()) + { + device.abortPacket(); + break; + } + } +#ifdef TEST_RECEIVER_ABORT + Log::info("Testing data phase abort\n"); + device.abortPacket(); + break; +#endif + } + + // Read the final generic response packet. + uint8_t *responsePacket; + uint32_t responseLength; + if (device.readPacket(&responsePacket, &responseLength, kPacketType_Command) != kStatus_Success) + { + return NULL; + } + return responsePacket; +} + +//! See host_command.h for documentation on this function. +const uint8_t *blfwk::CommandPacket::sendCommandGetResponse(Packetizer &device) +{ + uint8_t *responsePacket = NULL; + uint32_t responseLength = 0; + + status_t status = device.writePacket(getData(), getSize(), kPacketType_Command); + if (status != kStatus_Success) + { + Log::error("sendCommandGetResponse.writePacket error %d.\n", status); + return NULL; + } + status = device.readPacket(&responsePacket, &responseLength, kPacketType_Command); + if (status == kStatus_Success) + { + return responsePacket; + } + else + { + Log::error("sendCommandGetResponse.readPacket error %d.\n", status); + return NULL; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Reset command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool Reset::init() +{ + if (getArgCount() != 1) + { + return false; + } + return true; +} + +// See host_command.h for documentation of this method. +void Reset::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_Reset, kCommandFlag_None); + const uint8_t *responsePacket = cmdPacket.sendCommandGetResponse(device); + if (responsePacket) + { + processResponse(responsePacket); + } + else + { + Log::warning("Ignoring missing response from reset command.\n"); + this->m_responseValues.push_back(kStatus_Success); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// GetProperty command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool GetProperty::init() +{ + if (getArgCount() != 2 && getArgCount() != 3) + { + return false; + } + + // Init property from tag or description. + uint32_t tag = kPropertyTag_InvalidProperty; + PropertyArray::const_iterator it; + if (utils::stringtoui(getArg(1), tag)) + { + for (it = kProperties.begin(); it != kProperties.end(); ++it) + { + if (it->value == tag) + break; + } + } + else + { + for (it = kProperties.begin(); it != kProperties.end(); ++it) + { + if (strcmp(it->description, getArg(1).c_str()) == 0) + break; + } + } + + if (it == kProperties.end()) + { + m_property = kProperty_Invalid; + } + else + { + m_property = *it; + } + + if (getArgCount() == 3) + { + if (!utils::stringtoui(getArg(2), m_memoryIdorIndex)) + { + return false; + } + } + else + { + m_memoryIdorIndex = kMemoryInternal; + } + + return true; +} + +// See host_command.h for documentation of this method. +void GetProperty::sendTo(Packetizer &device) +{ + // See if the user just wants a list of the possible properites. + if (m_property.value == kPropertyTag_ListProperties) + { + Log::info("The possible properties for the %s command are\n", getName().c_str()); + for (auto it = kProperties.begin(); it != kProperties.end(); ++it) + { + Log::info(" %d or '%s'\n", it->value, it->description); + } + + m_responseValues.push_back(kStatus_NoResponseExpected); + } + else + { + // Command Phase + blfwk::CommandPacket cmdPacket(kCommandTag_GetProperty, kCommandFlag_None, m_property.value, m_memoryIdorIndex); + const uint8_t *responsePacket = cmdPacket.sendCommandGetResponse(device); + + const get_property_response_packet_t *packet = + reinterpret_cast(responsePacket); + processResponse(packet); + } +} + +bool GetProperty::processResponse(const get_property_response_packet_t *packet) +{ + if (!packet) + { + Log::debug("processResponse: null packet\n"); + m_responseValues.push_back(kStatus_NoResponse); + return false; + } + + // Handle generic response, which would be returned if command is not supported. + if (packet->commandPacket.commandTag == kCommandTag_GenericResponse) + { + return processResponse((const uint8_t *)packet); + } + + if (packet->commandPacket.commandTag != kCommandTag_GetPropertyResponse) + { + Log::error("Error: expected kCommandTag_GetPropertyResponse (0x%x), received 0x%x\n", + kCommandTag_GetPropertyResponse, packet->commandPacket.commandTag); + m_responseValues.push_back(kStatus_UnknownCommand); + return false; + } + + // Set the status in the response vector. + m_responseValues.push_back(packet->status); + + if (packet->status != kStatus_Success) + { + return false; + } + + Log::debug("Successful response to command '%s(%s)'\n", getName().c_str(), m_property.description); + + // Currently, no properties have a data phase. + assert(!(packet->commandPacket.flags & kCommandFlag_HasDataPhase)); + + // All properties have at least one response word. + // Attention: parameterCount = 1(response status) + response words + for (uint8_t i = 0; i < (packet->commandPacket.parameterCount - 1); ++i) + { + m_responseValues.push_back(packet->propertyValue[i]); + } + + // Format the returned property details. + switch (m_property.value) + { + case kPropertyTag_BootloaderVersion: + m_responseDetails = + format_string("Current Version = %c%d.%d.%d", (m_responseValues.at(1) & 0xff000000) >> 24, + (m_responseValues.at(1) & 0x00ff0000) >> 16, (m_responseValues.at(1) & 0x0000ff00) >> 8, + m_responseValues.at(1) & 0x000000ff); + break; + case kPropertyTag_TargetVersion: + m_responseDetails = + format_string("Target Version = %c%d.%d.%d", (m_responseValues.at(1) & 0xff000000) >> 24, + (m_responseValues.at(1) & 0x00ff0000) >> 16, (m_responseValues.at(1) & 0x0000ff00) >> 8, + m_responseValues.at(1) & 0x000000ff); + break; + case kPropertyTag_AvailablePeripherals: + { + m_responseDetails = "Available Peripherals = "; + uint32_t peripherals = m_responseValues.at(1); + uint32_t position = 0; + while (peripherals) + { + if (peripherals & 0x00000001) + { + m_responseDetails.append(kPeripherals.at(position).description); + if (peripherals >> 1) + m_responseDetails.append(", "); + } + peripherals >>= 1; + ++position; + } + break; + } + case kPropertyTag_FlashStartAddress: + m_responseDetails = format_string("Flash Start Address = 0x%08X", m_responseValues.at(1)); + break; + case kPropertyTag_FlashSizeInBytes: + m_responseDetails = format_string("Flash Size = %s", utils::scale_bytes(m_responseValues.at(1)).c_str()); + break; + case kPropertyTag_FlashSectorSize: + m_responseDetails = + format_string("Flash Sector Size = %s", utils::scale_bytes(m_responseValues.at(1)).c_str()); + break; + case kPropertyTag_FlashBlockCount: + m_responseDetails = format_string("Flash Block Count = %d", m_responseValues.at(1)); + break; + case kPropertyTag_AvailableCommands: + { + m_responseDetails = "Available Commands = "; + uint32_t commands = m_responseValues.at(1); + uint32_t position = 0; + while (commands) + { + if (commands & 0x00000001) + { + m_responseDetails.append(kCommands.at(position).name); + if (commands >> 1) + m_responseDetails.append(", "); + } + commands >>= 1; + ++position; + } + break; + } + case kPropertyTag_CheckStatus: + if (m_memoryIdorIndex == kCheckStatusId_CrcStatus) + { + m_responseDetails = + format_string("CRC Check Status = %s", getStatusMessage(m_responseValues.at(1)).c_str()); + } + else if (m_memoryIdorIndex == kCheckSattusId_LastError) + { + m_responseDetails = "Last Error Content ="; + for (uint32_t i = 1; i < m_responseValues.size(); ++i) + { + m_responseDetails.append(format_string(" 0x%08X", m_responseValues.at(i))); + } + } + else + { + m_responseDetails = "Unknown Check Status"; + } + + break; + case kPropertyTag_VerifyWrites: + m_responseDetails = format_string("Verify Writes Flag = %s", m_responseValues.at(1) ? "ON" : "OFF"); + break; + case kPropertyTag_MaxPacketSize: + m_responseDetails = + format_string("Max Packet Size = %s", utils::scale_bytes(m_responseValues.at(1)).c_str()); + break; + case kPropertyTag_ReservedRegions: + { + uint32_t m_reservedRegionCount = packet->commandPacket.parameterCount / 2; + uint32_t m_validRegionIndex = 0; + m_responseDetails = "Reserved Regions = "; + for (uint32_t i = 0; i < m_reservedRegionCount; i++) + { + uint32_t m_regionStart = m_responseValues.at(1 + 2 * i); + uint32_t m_regionEnd = m_responseValues.at(2 + 2 * i); + uint32_t m_regionLength = m_regionEnd - m_regionStart + 1; + + if (m_regionLength > 1) + { + m_responseDetails.append(format_string("\n Region%d: 0x%X-0x%X (%s) ", m_validRegionIndex, + m_regionStart, m_regionEnd, + utils::scale_bytes(m_regionLength).c_str())); + ++m_validRegionIndex; + } + } + m_responseDetails.append("\n"); + + break; + } + case kPropertyTag_RAMStartAddress: + m_responseDetails = format_string("RAM Start Address = 0x%08X", m_responseValues.at(1)); + break; + case kPropertyTag_RAMSizeInBytes: + m_responseDetails = format_string("RAM Size = %s", utils::scale_bytes(m_responseValues.at(1)).c_str()); + break; + case kPropertyTag_SystemDeviceId: + m_responseDetails = format_string("System Device ID = 0x%08X", m_responseValues.at(1)); + break; + case kPropertyTag_SecurityState: + { + uint32_t securityState = m_responseValues.at(1); + const char *securityStateStr = ""; + switch (securityState) + { + case kSecurityState_Legacy_Unsecure: + securityStateStr = "UNSECURE"; + break; + case kSecurityState_Legacy_Secure: + securityStateStr = "SECURE"; + break; + case kSecurityState_SKBOOT_Secure: + securityStateStr = "SECURE"; + break; + case kSecurityState_SKBOOT_Unsecure: + securityStateStr = "UNSECURE"; + break; + default: + securityStateStr = "UNKNOWN"; + break; + } + m_responseDetails = format_string("Security State = %s", securityStateStr); + } + break; + case kPropertyTag_UniqueDeviceId: + m_responseDetails = "Unique Device ID ="; + for (uint32_t i = 1; i < m_responseValues.size(); ++i) + { + m_responseDetails.append(format_string( + " %02X %02X %02X %02X", (m_responseValues.at(i) & 0x000000ff) >> 0, + (m_responseValues.at(i) & 0x0000ff00) >> 8, (m_responseValues.at(i) & 0x00ff0000) >> 16, + (m_responseValues.at(i) & 0xff000000) >> 24)); + } + break; + case kPropertyTag_FacSupport: + m_responseDetails = format_string("Flash Access Controller (FAC) Support Flag = %s", + m_responseValues.at(1) ? "SUPPORTED" : "UNSUPPORTED"); + break; + case kPropertyTag_FlashAccessSegmentSize: + m_responseDetails = + format_string("Flash Access Segment Size = %s", utils::scale_bytes(m_responseValues.at(1)).c_str()); + break; + case kPropertyTag_FlashAccessSegmentCount: + m_responseDetails = format_string("Flash Access Segment Count = %d", m_responseValues.at(1)); + break; + case kPropertyTag_FlashReadMargin: + m_responseDetails = "Flash read margin level = "; + if (m_responseValues.at(1) == 0) + m_responseDetails.append("NORMAL"); + else if (m_responseValues.at(1) == 1) + m_responseDetails.append("USER"); + else if (m_responseValues.at(1) == 2) + m_responseDetails.append("FACTORY"); + else + m_responseDetails.append(format_string("UNKNOWN (%d)", m_responseValues.at(1))); + break; + case kPropertyTag_QspiInitStatus: + m_responseDetails = + format_string("QSPI Init Status = %s", getStatusMessage(m_responseValues.at(1)).c_str()); + break; + case kPropertyTag_ExternalMemoryAttributes: + { + uint32_t m_propertyTags = m_responseValues.at(1); + uint32_t m_memStartAddress = m_responseValues.at(2); + uint32_t m_memSizeInKBytes = m_responseValues.at(3); + uint32_t m_memPageSize = m_responseValues.at(4); + uint32_t m_memSectorSize = m_responseValues.at(5); + uint32_t m_memBlockSize = m_responseValues.at(6); + + if (m_propertyTags > 0) + { + string externMemoryDescriptor = "UNKNOWN"; + + for (MemoryArray::const_iterator it = kMemories.begin(); it != kMemories.end(); ++it) + { + if (m_memoryIdorIndex == it->memoryId) + { + externMemoryDescriptor = it->description; + } + } + m_responseDetails = format_string("%s Attributes: ", externMemoryDescriptor.c_str()); + } + + if (m_propertyTags & (1 << (kExternalMemoryPropertyTag_StartAddress - 1))) + { + m_responseDetails.append(format_string("Start Address = 0x%08x ", m_memStartAddress)); + } + + if (m_propertyTags & (1 << (kExternalMemoryPropertyTag_MemorySizeInKbytes - 1))) + { + uint64_t m_memorySizeInBytes = (uint64_t)m_memSizeInKBytes * 1024; + m_responseDetails.append( + format_string("Total Size = %s ", utils::scale_bytes(m_memorySizeInBytes).c_str())); + } + + if (m_propertyTags & (1 << (kExternalMemoryPropertyTag_PageSize - 1))) + { + m_responseDetails.append(format_string("Page Size = %s ", utils::scale_bytes(m_memPageSize).c_str())); + } + + if (m_propertyTags & (1 << (kExternalMemoryPropertyTag_SectorSize - 1))) + { + m_responseDetails.append( + format_string("Sector Size = %s ", utils::scale_bytes(m_memSectorSize).c_str())); + } + + if (m_propertyTags & (1 << (kExternalMemoryPropertyTag_BlockSize - 1))) + { + m_responseDetails.append(format_string("Block Size = %s ", utils::scale_bytes(m_memBlockSize).c_str())); + } + } + break; + case kPropertyTag_ReliableUpdateStatus: + m_responseDetails = + format_string("Reliable Update Status = %s", getStatusMessage(m_responseValues.at(1)).c_str()); + break; + case kPropertyTag_FlashPageSize: + m_responseDetails = + format_string("Flash Page Size = %s", utils::scale_bytes(m_responseValues.at(1)).c_str()); + break; + case kPropertyTag_IrqNotifierPin: + { + irq_notifier_pin_property_store_t newProperty; + newProperty.U = m_responseValues.at(1); + if (newProperty.B.enable) + { + m_responseDetails = format_string("Irq pin is enabled, using GPIO port[%d], pin[%d]", + newProperty.B.port, newProperty.B.pin); + } + else + { + m_responseDetails = format_string("Irq pin is disabled"); + } + } + break; + case kPropertyTag_FfrKeystoreUpdateOpt: + { + string optString; + if (m_responseValues.at(1) == kFfrKeystoreUpdateOpt_KeyProvisioning) + { + optString = format_string("KeyProvisioning"); + } + else if (m_responseValues.at(1) == kFfrKeystoreUpdateOpt_WriteMemory) + { + optString = format_string("WriteMemory"); + } + else + { + optString = format_string("UnKnow Option"); + } + m_responseDetails = format_string("FFR KeyStore Update is ") + optString; + } + break; + case kPropertyTag_ByteWriteTimeoutMs: + { + m_responseDetails = format_string("Byte Write Timeout is %dms", m_responseValues.at(1)); + } + break; + case kPropertyTag_InvalidProperty: + default: + break; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// SetProperty command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool SetProperty::init() +{ + if (getArgCount() != 3) + { + return false; + } + // Save property tag number and value. + if (!utils::stringtoui(getArg(1), m_propertyTag)) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_propertyValue)) + { + return false; + } + return true; +} + +// See host_command.h for documentation of this method. +void SetProperty::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_SetProperty, kCommandFlag_None, m_propertyTag, m_propertyValue); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// FlashEraseRegion command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FlashEraseRegion::init() +{ + if ((getArgCount() != 3) && (getArgCount() != 4)) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_startAddress)) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_byteCount)) + { + return false; + } + + if (getArgCount() == 4) + { + if (!utils::stringtoui(getArg(3), m_memoryId)) + { + return false; + } + // Use 0 for erasing internal 4G memory including mapped memory, such as QSPI + if (GROUPID(m_memoryId) == kGroup_Internal) + { + if (m_memoryId != kMemoryInternal) + { + Log::warning( + "Note: memoryId is not required for flash-erase-region when accessing mapped external memory. " + "Ignore this parameter.\n"); + m_memoryId = kMemoryInternal; + } + } + } + else + { + m_memoryId = kMemoryInternal; + } + + return true; +} + +// See host_command.h for documentation of this method. +void FlashEraseRegion::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_FlashEraseRegion, kCommandFlag_None, m_startAddress, m_byteCount, + m_memoryId); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// FlashEraseAll command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FlashEraseAll::init() +{ + if ((getArgCount() != 1) && (getArgCount() != 2)) + { + return false; + } + + if (getArgCount() == 1) + { + m_memoryId = kMemoryInternal; + return true; + } + else if (getArgCount() == 2) + { + if (!utils::stringtoui(getArg(1), m_memoryId)) + { + return false; + } + return true; + } + + return false; +} + +// See host_command.h for documentation of this method. +void FlashEraseAll::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_FlashEraseAll, kCommandFlag_None, m_memoryId); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// FlashEraseAllUnsecure command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FlashEraseAllUnsecure::init() +{ + if (getArgCount() != 1) + { + return false; + } + return true; +} + +// See host_command.h for documentation of this method. +void FlashEraseAllUnsecure::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_FlashEraseAllUnsecure, kCommandFlag_None); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// ReadMemory command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool ReadMemory::init() +{ + if ((getArgCount() < 3) || (getArgCount() > 5)) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_startAddress)) + { + return false; // invalid 'addr' parameter + } + if (!utils::stringtoui(getArg(2), m_byteCount)) + { + return false; // invalid 'count' parameter + } + + // File name argument is optional - will use stdout if missing. + if (getArgCount() == 4) + { + // If the argument 3 is a number, then it is m_memoryId. + // Otherwise, it is a file name. + // File name "123" is not acceptable, while "123.bin" is OK. + if (!utils::stringtoui(getArg(3), m_memoryId)) + { + m_memoryId = kMemoryInternal; + m_dataFile = getArg(3); + } + // Use 0 for reading internal 4G memory including mapped memory, such as QSPI + else if (GROUPID(m_memoryId) == kGroup_Internal) + { + if (m_memoryId != kMemoryInternal) + { + Log::warning( + "Note: memoryId is not required for read-memory when accessing mapped external memory. Ignore this " + "parameter.\n"); + m_memoryId = kMemoryInternal; + } + } + } + + if (getArgCount() == 5) + { + m_dataFile = getArg(3); + if (!utils::stringtoui(getArg(4), m_memoryId)) + { + return false; // invalid 'memory ID' parameter + } + // Use 0 for reading internal 4G memory including mapped memory, such as QSPI + if (GROUPID(m_memoryId) == kGroup_Internal) + { + if (m_memoryId != kMemoryInternal) + { + Log::warning( + "Note: memoryId is not required for read-memory when accessing mapped external memory. Ignore this " + "parameter.\n"); + m_memoryId = kMemoryInternal; + } + } + } + + return true; +} + +// See host_command.h for documentation of this method. +void ReadMemory::sendTo(Packetizer &device) +{ + DataPacket::DataConsumer *dataConsumer; + DataPacket::FileDataConsumer fileDataConsumer; + DataPacket::StdOutDataConsumer stdoutDataConsumer; + + // Setup to write to file or stdout + if (m_dataFile.size() > 0) + { + if (!fileDataConsumer.init(m_dataFile)) + { + return; + } + dataConsumer = &fileDataConsumer; + } + else + { + dataConsumer = &stdoutDataConsumer; + } + + // Send command packet. + blfwk::CommandPacket cmdPacket(kCommandTag_ReadMemory, kCommandFlag_None, m_startAddress, m_byteCount, m_memoryId); + const uint8_t *responsePacket = cmdPacket.sendCommandGetResponse(device); + + const read_memory_response_packet_t *packet = + reinterpret_cast(responsePacket); + uint32_t byteCount = m_byteCount; + if (processResponse(packet)) + { + byteCount = packet->dataByteCount; + + // Receive data packets. + blfwk::DataPacket dataPacket(dataConsumer); + uint8_t *finalResponsePacket = dataPacket.receiveFrom(device, &byteCount, m_progress); + processResponse(finalResponsePacket); + } + + // Push the number of bytes transferred response value. + m_responseValues.push_back(m_byteCount - byteCount); + + // Format the command transfer details. + m_responseDetails = format_string("Read %d of %d bytes.", m_byteCount - byteCount, m_byteCount); +} + +bool ReadMemory::processResponse(const read_memory_response_packet_t *packet) +{ + if (!packet) + { + Log::debug("processResponse: null packet\n"); + m_responseValues.push_back(kStatus_NoResponse); + return false; + } + + // Handle generic response, which would be returned if command is not supported. + if (packet->commandPacket.commandTag == kCommandTag_GenericResponse) + { + return processResponse((const uint8_t *)packet); + } + + if (packet->commandPacket.commandTag != kCommandTag_ReadMemoryResponse) + { + Log::error("Error: expected kCommandTag_ReadMemoryResponse (0x%x), received 0x%x\n", + kCommandTag_ReadMemoryResponse, packet->commandPacket.commandTag); + return false; + } + if (packet->status != kStatus_Success) + { + // Set the status in the response vector. + // If status is OK, this push will be done by final response processing + m_responseValues.push_back(packet->status); + return false; + } + + Log::info("Successful response to command '%s'\n", getName().c_str()); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// WriteMemory command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool WriteMemory::init() +{ + if (getArgCount() != 3 && getArgCount() != 4) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_startAddress)) + { + return false; + } + m_fileOrData = getArg(2); + + // Try to find the separator ','. + size_t separatorIndex = m_fileOrData.find(',', 0); + if (separatorIndex != string::npos) + { + // If found, the left string is the byte count to write. + if (!utils::stringtoui(m_fileOrData.substr(separatorIndex + 1, m_fileOrData.length() - separatorIndex - 1), + m_count)) + { + return false; + } + m_fileOrData = m_fileOrData.substr(0, separatorIndex); + } + + if ((m_fileOrData[0] == '{') && (m_fileOrData[1] == '{')) + { + DataPacket::HexDataProducer hexProducer; + // Argument string is hex data, so use hex data producer. + if (hexProducer.initFromString(m_fileOrData) == 0) + { + return false; + } + } + + if (getArgCount() == 4) + { + if (!utils::stringtoui(getArg(3), m_memoryId)) + { + return false; + } + // Use 0 for writinging internal 4G memory including mapped memory, such as QSPI + if (GROUPID(m_memoryId) == kGroup_Internal) + { + if (m_memoryId != kMemoryInternal) + { + Log::warning( + "Note: memoryId is not required for write-memory when accessing mapped external memory. Ignore " + "this parameter.\n"); + m_memoryId = kMemoryInternal; + } + } + } + else + { + m_memoryId = kMemoryInternal; + } + + return true; +} + +// See host_command.h for documentation of this method. +void WriteMemory::sendTo(Packetizer &device) +{ + DataPacket::HexDataProducer hexProducer(m_data); + DataPacket::FileDataProducer fileProducer; + DataPacket::SegmentDataProducer segmentProducer(m_segment); + DataPacket::DataProducer *dataProducer; + + if (m_segment) + { + dataProducer = &segmentProducer; + } + else if ((m_fileOrData[0] == '{') && (m_fileOrData[1] == '{')) + { + // Argument string is hex data, so use hex data producer. + if (hexProducer.initFromString(m_fileOrData) == 0) + { + return; + } + dataProducer = &hexProducer; + } + else if (m_data.size() > 0) + { + dataProducer = &hexProducer; + } + else + { + // Argument string is file name, so use file data producer. + if (!fileProducer.init(m_fileOrData, m_count)) + { + return; + } + dataProducer = &fileProducer; + } + + // Get target bootloader data packet size. + uint32_t packetSizeInBytes; + GetProperty getPacketSize(kProperty_MaxPacketSize, 0 /*Not used*/); + getPacketSize.sendTo(device); + uint32_t fw_status = getPacketSize.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + // Failed to get data packet size. + Log::warning("Warning: Failed to get packet size. Using default size(%d)", kMinPacketBufferSize); + packetSizeInBytes = kMinPacketBufferSize; // No property. Use default packet size. + } + else + { + packetSizeInBytes = getPacketSize.getResponseValues()->at(1); + if (packetSizeInBytes > device.getMaxPacketSize()) + { + Log::error("Error: Packet size(%d) is bigger than max supported size(%d).", packetSizeInBytes, + kMaxHostPacketSize); + return; + } + } + + // Send command packet. + uint32_t bytesToWrite = dataProducer->getDataSize(); + uint32_t bytesWritten; + blfwk::CommandPacket cmdPacket(kCommandTag_WriteMemory, kCommandFlag_HasDataPhase, m_startAddress, bytesToWrite, + m_memoryId); + if (!processResponse(cmdPacket.sendCommandGetResponse(device))) + { + m_responseDetails = format_string("Wrote 0 of %d bytes.", bytesToWrite); + return; + } + + // Pop the initial (successful) generic response value. + if (m_responseValues.size()) + { + m_responseValues.pop_back(); + } + + // Send data packets. + blfwk::DataPacket dataPacket(dataProducer, packetSizeInBytes); + + processResponse(dataPacket.sendTo(device, &bytesWritten, m_progress)); + + // Format the command transfer details. + m_responseDetails = format_string("Wrote %d of %d bytes.", bytesWritten, bytesToWrite); +} + +//////////////////////////////////////////////////////////////////////////////// +// GenerateKeyBlob command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool GenerateKeyBlob::init() +{ + if ((getArgCount() != 3) && (getArgCount() != 4)) + { + return false; + } + + m_fileDek = getArg(1); + + m_fileKeyBlob = getArg(2); + + if (getArgCount() == 4) + { + if (!utils::stringtoui(getArg(3), m_keySel)) + { + string keyStr = getArg(3); + if (keyStr == "OTPMK") + { + m_keySel = GenerateKeyBlob::kKeySource_SNVS_OTPMK; + } + else if (keyStr == "ZMK") + { + m_keySel = GenerateKeyBlob::kKeySource_SNVS_ZMK; + } + else if (keyStr == "CMK") + { + m_keySel = GenerateKeyBlob::kKeySource_SNVS_CMK; + } + else + { + return false; + } + } + } + + return true; +} + +// See host_command.h for documentation of this method. +void GenerateKeyBlob::sendTo(Packetizer &device) +{ + DataPacket::FileDataProducer fileProducer; + DataPacket::DataProducer *dataProducer; + + // Argument string is file name, so use file data producer. + if (!fileProducer.init(m_fileDek, m_count)) + { + return; + } + dataProducer = &fileProducer; + + m_dataPhase = 0; + // Send command packet. + uint32_t bytesToWrite = dataProducer->getDataSize(); + uint32_t bytesWritten; + blfwk::CommandPacket cmdPacket(kCommandTag_GenerateKeyBlob, kCommandFlag_HasDataPhase, m_keySel, bytesToWrite, + m_dataPhase); + const uint8_t *responsePacket = cmdPacket.sendCommandGetResponse(device); + const generate_key_blob_response_packet_t *packet = + reinterpret_cast(responsePacket); + if (!processResponse(packet)) + { + m_responseDetails = format_string("Send 0 of %d bytes.", bytesToWrite); + return; + } + + // Pop the 1st response value.(command phase in the 1st blob phase) + if (m_responseValues.size()) + { + m_responseValues.pop_back(); + } + + // Send data packets. + blfwk::DataPacket dataPacket(dataProducer); + if (!processResponse(dataPacket.sendTo(device, &bytesWritten, m_progress))) + { + m_responseDetails = format_string("Send %d of %d bytes.", bytesWritten, bytesToWrite); + return; + } + else + { + Log::info("Send %d of %d bytes.\n", bytesWritten, bytesToWrite); + } + + // Pop the 2nd response value.(data phase in the 1st blob phase) + if (m_responseValues.size()) + { + m_responseValues.pop_back(); + } + + DataPacket::DataConsumer *dataConsumer; + DataPacket::FileDataConsumer fileDataConsumer; + DataPacket::StdOutDataConsumer stdoutDataConsumer; + + // Setup to write to file or stdout + if (m_fileKeyBlob.size() > 0) + { + if (!fileDataConsumer.init(m_fileKeyBlob)) + { + return; + } + dataConsumer = &fileDataConsumer; + } + else + { + dataConsumer = &stdoutDataConsumer; + } + + m_count = 72; // Default is AES128bit` + m_dataPhase = 1; + // Send command packet. + blfwk::CommandPacket cmdPacket1(kCommandTag_GenerateKeyBlob, kCommandFlag_None, m_keySel, m_count, m_dataPhase); + const uint8_t *responsePacket1 = cmdPacket1.sendCommandGetResponse(device); + + const generate_key_blob_response_packet_t *packet1 = + reinterpret_cast(responsePacket1); + uint32_t byteCount = m_count; + if (processResponse(packet1)) + { + // Pop the 3rd response value.(command phase in the 2nd blob phase) + if (m_responseValues.size()) + { + m_responseValues.pop_back(); + } + + m_count = packet1->dataByteCount; + byteCount = m_count; + + // Receive data packets. + blfwk::DataPacket dataPacket(dataConsumer); + uint8_t *finalResponsePacket = dataPacket.receiveFrom(device, &byteCount, m_progress); + processResponse(finalResponsePacket); + } + + // Push the number of bytes transferred response value. + m_responseValues.push_back(m_count - byteCount); + + // Format the command transfer details. + m_responseDetails = format_string("Read %d of %d bytes.", m_count - byteCount, m_count); +} + +bool GenerateKeyBlob::processResponse(const generate_key_blob_response_packet_t *packet) +{ + if (!packet) + { + Log::debug("processResponse: null packet\n"); + m_responseValues.push_back(kStatus_NoResponse); + return false; + } + + // Handle generic response, which would be returned if command is not supported. + if (packet->commandPacket.commandTag == kCommandTag_GenericResponse) + { + return processResponse((const uint8_t *)packet); + } + + if (packet->commandPacket.commandTag != kCommandTag_GenerateKeyBlobResponse) + { + Log::error("Error: expected kCommandTag_GenerateKeyBlob (0x%x), received 0x%x\n", + kCommandTag_GenerateKeyBlobResponse, packet->commandPacket.commandTag); + m_responseValues.push_back(kStatus_UnknownCommand); + return false; + } + if (packet->status != kStatus_Success) + { + // Set the status in the response vector. + // If status is OK, this push will be done by final response processing + m_responseValues.push_back(packet->status); + return false; + } + + Log::info("Successful response to command '%s'\n", getName().c_str()); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// FillMemory command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FillMemory::init() +{ + if ((getArgCount() < 4) || (getArgCount() > 5)) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_startAddress)) + { + return false; // invalid 'addr' parameter + } + if (!utils::stringtoui(getArg(2), m_byteCount)) + { + return false; // invalid 'byte_count' parameter + } + if (!utils::stringtoui(getArg(3), m_patternWord)) + { + return false; // invalid 'pattern' parameter + } + + if ((getArgCount() == 5) && (getArg(4) != "word")) + { + unsigned char b1 = (unsigned char)((m_patternWord >> 8) & 0xff); + unsigned char b0 = (unsigned char)(m_patternWord & 0xff); + + if (getArg(4) == "byte") + { + // Replicate byte pattern in word. + m_patternWord = b0 + (b0 << 8) + (b0 << 16) + (b0 << 24); + } + else if (getArg(4) == "short") + { + // Replicate short pattern in word. + m_patternWord = b0 + (b1 << 8) + (b0 << 16) + (b1 << 24); + } + else + { + return false; // unknown pattern size argument + } + } + + return true; +} + +// See host_command.h for documentation of this method. +void FillMemory::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_FillMemory, kCommandFlag_None, m_startAddress, m_byteCount, + m_patternWord); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// ReceiveSbFile command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool ReceiveSbFile::init() +{ + if (getArgCount() != 2) + { + return false; + } + m_dataFile = getArg(1); + return true; +} + +// See host_command.h for documentation of this method. +void ReceiveSbFile::sendTo(Packetizer &device) +{ + DataPacket::FileDataProducer dataProducer; + if (!dataProducer.init(m_dataFile, 0)) + { + return; + } + + // Get target bootloader data packet size. + uint32_t packetSizeInBytes; + GetProperty getPacketSize(kProperty_MaxPacketSize, 0 /*Not used*/); + getPacketSize.sendTo(device); + uint32_t fw_status = getPacketSize.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + // Failed to get data packet size. + Log::warning("Warning: Failed to get packet size. Using default size(%d)", kMinPacketBufferSize); + packetSizeInBytes = kMinPacketBufferSize; // No property. Use default packet size. + } + else + { + packetSizeInBytes = getPacketSize.getResponseValues()->at(1); + if (packetSizeInBytes > device.getMaxPacketSize()) + { + Log::error("Error: Packet size(%d) is bigger than max supported size(%d).", packetSizeInBytes, + kMaxHostPacketSize); + return; + } + } + + // Send command packet. + uint32_t bytesToWrite = dataProducer.getDataSize(); + uint32_t bytesWritten; + blfwk::CommandPacket cmdPacket(kCommandTag_ReceiveSbFile, kCommandFlag_HasDataPhase, bytesToWrite); + if (!processResponse(cmdPacket.sendCommandGetResponse(device))) + { + m_responseDetails = format_string("Wrote 0 of %d bytes.", bytesToWrite); + return; + } + + // Pop the initial (successful) generic response value. + if (m_responseValues.size()) + { + m_responseValues.pop_back(); + } + + // Send data packets. +#if !(defined(BL_FEATURE_RECEIVE_SB_FILE_CMD_PERF_IMP) && (BL_FEATURE_RECEIVE_SB_FILE_CMD_PERF_IMP == 1)) + /* + * If the SB file used for the target doesn't contain any JUMP(EXECUTE), CALL or RESET command. + * The macro BL_FEATURE_RECEIVE_SB_FILE_CMD_PERF_IMP can be set to improve the transformation performance. + */ + device.setAbortEnabled(true); +#endif + blfwk::DataPacket dataPacket(&dataProducer, packetSizeInBytes); + processResponse(dataPacket.sendTo(device, &bytesWritten, m_progress)); +#if !(defined(BL_FEATURE_RECEIVE_SB_FILE_CMD_PERF_IMP) && (BL_FEATURE_RECEIVE_SB_FILE_CMD_PERF_IMP == 1)) + device.setAbortEnabled(false); +#endif + + // Format the command transfer details. + m_responseDetails = format_string("Wrote %d of %d bytes.", bytesWritten, bytesToWrite); +} + +//////////////////////////////////////////////////////////////////////////////// +// FuseProgram command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FuseProgram::init() +{ + if (getArgCount() != 3 && getArgCount() != 4) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_startAddress)) + { + return false; + } + m_fileOrData = getArg(2); + + // Try to find the separator ','. + size_t separatorIndex = m_fileOrData.find(',', 0); + if (separatorIndex != string::npos) + { + // If found, the left string is the byte count to write. + if (!utils::stringtoui(m_fileOrData.substr(separatorIndex + 1, m_fileOrData.length() - separatorIndex - 1), + m_count)) + { + return false; + } + m_fileOrData = m_fileOrData.substr(0, separatorIndex); + } + + if ((m_fileOrData[0] == '{') && (m_fileOrData[1] == '{')) + { + DataPacket::HexDataProducer hexProducer; + // Argument string is hex data, so use hex data producer. + if (hexProducer.initFromString(m_fileOrData) == 0) + { + return false; + } + } + + if (getArgCount() == 4) + { + if (!utils::stringtoui(getArg(3), m_memoryId)) + { + return false; + } + // Use 0 for writinging internal 4G memory including mapped memory, such as QSPI + if (GROUPID(m_memoryId) == kGroup_Internal) + { + if (m_memoryId != kMemoryInternal) + { + Log::warning( + "Note: memoryId is not required for write-memory when accessing mapped external memory. Ignore " + "this parameter.\n"); + m_memoryId = kMemoryInternal; + } + } + } + else + { + m_memoryId = kMemoryInternal; + } + + return true; +} + +// See host_command.h for documentation of this method. +void FuseProgram::sendTo(Packetizer &device) +{ + DataPacket::HexDataProducer hexProducer(m_data); + DataPacket::FileDataProducer fileProducer; + DataPacket::SegmentDataProducer segmentProducer(m_segment); + DataPacket::DataProducer *dataProducer; + + if (m_segment) + { + dataProducer = &segmentProducer; + } + else if ((m_fileOrData[0] == '{') && (m_fileOrData[1] == '{')) + { + // Argument string is hex data, so use hex data producer. + if (hexProducer.initFromString(m_fileOrData) == 0) + { + return; + } + dataProducer = &hexProducer; + } + else if (m_data.size() > 0) + { + dataProducer = &hexProducer; + } + else + { + // Argument string is file name, so use file data producer. + if (!fileProducer.init(m_fileOrData, m_count)) + { + return; + } + dataProducer = &fileProducer; + } + + // Get target bootloader data packet size. + uint32_t packetSizeInBytes; + GetProperty getPacketSize(kProperty_MaxPacketSize, 0 /*Not used*/); + getPacketSize.sendTo(device); + uint32_t fw_status = getPacketSize.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + // Failed to get data packet size. + Log::warning("Warning: Failed to get packet size. Using default size(%d)", kMinPacketBufferSize); + packetSizeInBytes = kMinPacketBufferSize; // No property. Use default packet size. + } + else + { + packetSizeInBytes = getPacketSize.getResponseValues()->at(1); + if (packetSizeInBytes > device.getMaxPacketSize()) + { + Log::error("Error: Packet size(%d) is bigger than max supported size(%d).", packetSizeInBytes, + kMaxHostPacketSize); + return; + } + } + + // Send command packet. + uint32_t bytesToWrite = dataProducer->getDataSize(); + uint32_t bytesWritten; + blfwk::CommandPacket cmdPacket(kCommandTag_FuseProgram, kCommandFlag_HasDataPhase, m_startAddress, bytesToWrite, + m_memoryId); + if (!processResponse(cmdPacket.sendCommandGetResponse(device))) + { + m_responseDetails = format_string("Wrote 0 of %d bytes.", bytesToWrite); + return; + } + + // Pop the initial (successful) generic response value. + if (m_responseValues.size()) + { + m_responseValues.pop_back(); + } + + // Send data packets. + blfwk::DataPacket dataPacket(dataProducer, packetSizeInBytes); + + processResponse(dataPacket.sendTo(device, &bytesWritten, m_progress)); + + // Format the command transfer details. + m_responseDetails = format_string("Wrote %d of %d bytes.", bytesWritten, bytesToWrite); +} + +//////////////////////////////////////////////////////////////////////////////// +// FuseRead command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FuseRead::init() +{ + if ((getArgCount() < 3) || (getArgCount() > 5)) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_startAddress)) + { + return false; // invalid 'addr' parameter + } + if (!utils::stringtoui(getArg(2), m_byteCount)) + { + return false; // invalid 'count' parameter + } + + // File name argument is optional - will use stdout if missing. + if (getArgCount() == 4) + { + // If the argument 3 is a number, then it is m_memoryId. + // Otherwise, it is a file name. + // File name "123" is not acceptable, while "123.bin" is OK. + if (!utils::stringtoui(getArg(3), m_memoryId)) + { + m_memoryId = kMemoryInternal; + m_dataFile = getArg(3); + } + // Use 0 for reading internal 4G memory including mapped memory, such as QSPI + else if (GROUPID(m_memoryId) == kGroup_Internal) + { + if (m_memoryId != kMemoryInternal) + { + Log::warning( + "Note: memoryId is not required for read-memory when accessing mapped external memory. Ignore this " + "parameter.\n"); + m_memoryId = kMemoryInternal; + } + } + } + + if (getArgCount() == 5) + { + m_dataFile = getArg(3); + if (!utils::stringtoui(getArg(4), m_memoryId)) + { + return false; // invalid 'memory ID' parameter + } + // Use 0 for reading internal 4G memory including mapped memory, such as QSPI + if (GROUPID(m_memoryId) == kGroup_Internal) + { + if (m_memoryId != kMemoryInternal) + { + Log::warning( + "Note: memoryId is not required for read-memory when accessing mapped external memory. Ignore this " + "parameter.\n"); + m_memoryId = kMemoryInternal; + } + } + } + + return true; +} + +// See host_command.h for documentation of this method. +void FuseRead::sendTo(Packetizer &device) +{ + DataPacket::DataConsumer *dataConsumer; + DataPacket::FileDataConsumer fileDataConsumer; + DataPacket::StdOutDataConsumer stdoutDataConsumer; + + // Setup to write to file or stdout + if (m_dataFile.size() > 0) + { + if (!fileDataConsumer.init(m_dataFile)) + { + return; + } + dataConsumer = &fileDataConsumer; + } + else + { + dataConsumer = &stdoutDataConsumer; + } + + // Send command packet. + blfwk::CommandPacket cmdPacket(kCommandTag_FuseRead, kCommandFlag_None, m_startAddress, m_byteCount, m_memoryId); + const uint8_t *responsePacket = cmdPacket.sendCommandGetResponse(device); + + const read_memory_response_packet_t *packet = + reinterpret_cast(responsePacket); + uint32_t byteCount = m_byteCount; + if (processResponse(packet)) + { + byteCount = packet->dataByteCount; + + // Receive data packets. + blfwk::DataPacket dataPacket(dataConsumer); + uint8_t *finalResponsePacket = dataPacket.receiveFrom(device, &byteCount, m_progress); + processResponse(finalResponsePacket); + } + + // Push the number of bytes transferred response value. + m_responseValues.push_back(m_byteCount - byteCount); + + // Format the command transfer details. + m_responseDetails = format_string("Read %d of %d bytes.", m_byteCount - byteCount, m_byteCount); +} + +bool FuseRead::processResponse(const read_memory_response_packet_t *packet) +{ + if (!packet) + { + Log::debug("processResponse: null packet\n"); + m_responseValues.push_back(kStatus_NoResponse); + return false; + } + + // Handle generic response, which would be returned if command is not supported. + if (packet->commandPacket.commandTag == kCommandTag_GenericResponse) + { + return processResponse((const uint8_t *)packet); + } + + if (packet->commandPacket.commandTag != kCommandTag_ReadMemoryResponse) + { + Log::error("Error: expected kCommandTag_ReadMemoryResponse (0x%x), received 0x%x\n", + kCommandTag_ReadMemoryResponse, packet->commandPacket.commandTag); + return false; + } + if (packet->status != kStatus_Success) + { + // Set the status in the response vector. + // If status is OK, this push will be done by final response processing + m_responseValues.push_back(packet->status); + return false; + } + + Log::info("Successful response to command '%s'\n", getName().c_str()); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// LoadImage command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool LoadImage::init() +{ + if (getArgCount() != 2) + { + return false; + } + m_dataFile = getArg(1); + return true; +} + +// See host_command.h for documentation of this method. +void LoadImage::sendTo(Packetizer &device) +{ + DataPacket::FileDataProducer dataProducer; + if (!dataProducer.init(m_dataFile, 0)) + { + return; + } + + // Get target bootloader data packet size. + uint32_t packetSizeInBytes; + GetProperty getPacketSize(kProperty_MaxPacketSize, 0 /*Not used*/); + getPacketSize.sendTo(device); + uint32_t fw_status = getPacketSize.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + // Failed to get data packet size. + Log::warning("Warning: Failed to get packet size. Using default size(%d)", kMinPacketBufferSize); + packetSizeInBytes = kMinPacketBufferSize; // No property. Use default packet size. + } + else + { + packetSizeInBytes = getPacketSize.getResponseValues()->at(1); + if (packetSizeInBytes > device.getMaxPacketSize()) + { + Log::error("Error: Packet size(%d) is bigger than max supported size(%d).", packetSizeInBytes, + kMaxHostPacketSize); + return; + } + } + + // Send command packet. + uint32_t bytesToWrite = dataProducer.getDataSize(); + uint32_t bytesWritten; + + // Send data packets. + blfwk::DataPacket dataPacket(&dataProducer, packetSizeInBytes); + + dataPacket.sendTo(device, &bytesWritten, m_progress, false); + + uint32_t status = kStatus_Fail; + // DO NOT call processResponse to handle the result. load-image doesn't transfer any command packet, including + // response packet. + if (bytesWritten == dataProducer.getDataSize()) + { + status = kStatus_Success; + } + else + { + status = kStatus_Fail; + } + m_responseValues.push_back(status); + // Format the command transfer details. + m_responseDetails = format_string("Wrote %d of %d bytes.", bytesWritten, bytesToWrite); + + generic_response_packet_t m_genericResponse; + m_genericResponse.commandPacket.commandTag = kCommandTag_GenericResponse; + m_genericResponse.commandPacket.flags = 0; + m_genericResponse.commandPacket.parameterCount = 2; + m_genericResponse.commandPacket.reserved = 0; + m_genericResponse.commandTag = 0; + m_genericResponse.status = status; + + processResponse((uint8_t *)&m_genericResponse); +} + +bool LoadImage::processResponse(const uint8_t *packet) +{ + return Command::processResponse((generic_response_packet_t *)packet, 0); +} + +//////////////////////////////////////////////////////////////////////////////// +// Execute command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool Execute::init() +{ + if (getArgCount() != 4) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_jumpAddress)) + { + return false; + } + + if (!utils::stringtoui(getArg(2), m_wordArgument)) + { + return false; + } + + if (!utils::stringtoui(getArg(3), m_stackpointer)) + { + return false; + } + return true; +} + +// See host_command.h for documentation of this method. +void Execute::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_Execute, kCommandFlag_None, m_jumpAddress, m_wordArgument, + m_stackpointer); + const uint8_t *responsePacket = cmdPacket.sendCommandGetResponse(device); + if (responsePacket) + { + processResponse(responsePacket); + } + else + { + Log::warning("Ignoring missing response from execute command.\n"); + this->m_responseValues.push_back(kStatus_Success); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Call command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool Call::init() +{ + if (getArgCount() != 3) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_callAddress)) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_wordArgument)) + { + return false; + } + + return true; +} + +// See host_command.h for documentation of this method. +void Call::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_Call, kCommandFlag_None, m_callAddress, m_wordArgument); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// FlashSecurityDisable command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FlashSecurityDisable::init() +{ + if (getArgCount() != 2) + { + return false; + } + if (getArg(1).length() != 16) + { + return false; + } + + // String must be hex digits with no leading 0x. + char *endPtr; + std::string s_keyLow = getArg(1).substr(0, 8); + m_keyLow = strtoul(s_keyLow.c_str(), &endPtr, 16); + if ((endPtr == NULL) || (*endPtr != 0)) + { + return false; + } + std::string s_keyHigh = getArg(1).substr(8, 8); + m_keyHigh = strtoul(s_keyHigh.c_str(), &endPtr, 16); + if ((endPtr == NULL) || (*endPtr != 0)) + { + return false; + } + + return true; +} + +// See host_command.h for documentation of this method. +void FlashSecurityDisable::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_FlashSecurityDisable, kCommandFlag_None, m_keyLow, m_keyHigh); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// FlashProgramOnce command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FlashProgramOnce::init() +{ + if ((getArgCount() != 4) && (getArgCount() != 5) && (getArgCount() != 6)) + { + return false; + } + + if ((getArg(3).length() != 8) && (getArg(3).length() != 16)) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_index)) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_byteCount)) + { + return false; + } + + if ((m_byteCount != 4) && (m_byteCount != 8)) + { + return false; + } + + char *endPtr; + std::string s_dataHigh = getArg(3).substr(0, 8); + m_dataHigh = strtoul(s_dataHigh.c_str(), &endPtr, 16); + if ((endPtr == NULL) || (*endPtr != 0)) + { + return false; + } + + if (m_byteCount == 8) + { + std::string s_dataLow = getArg(3).substr(8, 8); + m_dataLow = strtoul(s_dataLow.c_str(), &endPtr, 16); + if ((endPtr == NULL) || (*endPtr != 0)) + { + return false; + } + } + + if (getArgCount() == 5) + { + if ((getArg(4) == "LSB") || (getArg(4) == "lsb")) + { + m_lsb = true; + } + else if ((getArg(4) == "MSB") || (getArg(4) == "msb")) + { + m_lsb = false; + } + else if (strcmp(getArg(4).c_str(), "lock") == 0) + { + m_index |= (1ul << 24); + } + else + { + return false; + } + } + else + { + m_lsb = true; + } + return true; +} + +// See host_command.h for documentation of this method. +void FlashProgramOnce::sendTo(Packetizer &device) +{ + uint32_t firstWord, secondWord; + + if (m_byteCount == 4) + { + if (m_lsb) + { + firstWord = ENDIAN_HOST_TO_LITTLE_U32(m_dataHigh); + } + else + { + firstWord = ENDIAN_HOST_TO_BIG_U32(m_dataHigh); + } + blfwk::CommandPacket cmdPacket(kCommandTag_FlashProgramOnce, kCommandFlag_None, m_index, m_byteCount, + firstWord); + processResponse(cmdPacket.sendCommandGetResponse(device)); + } + else + { + if (m_lsb) + { + firstWord = ENDIAN_HOST_TO_LITTLE_U32(m_dataLow); + secondWord = ENDIAN_HOST_TO_LITTLE_U32(m_dataHigh); + } + else + { + firstWord = ENDIAN_HOST_TO_BIG_U32(m_dataHigh); + secondWord = ENDIAN_HOST_TO_BIG_U32(m_dataLow); + } + blfwk::CommandPacket cmdPacket(kCommandTag_FlashProgramOnce, kCommandFlag_None, m_index, m_byteCount, firstWord, + secondWord); + processResponse(cmdPacket.sendCommandGetResponse(device)); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// FlashReadOnce command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FlashReadOnce::init(void) +{ + if (getArgCount() != 3) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_index)) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_byteCount)) + { + return false; + } + + if (m_byteCount != 4 && m_byteCount != 8) + { + return false; + } + + return true; +} + +// See host_command.h for documentation of this method. +void FlashReadOnce::sendTo(Packetizer &device) +{ + // Command Phase + blfwk::CommandPacket cmdPacket(kCommandTag_FlashReadOnce, kCommandFlag_None, m_index, m_byteCount); + const uint8_t *responsePacket = cmdPacket.sendCommandGetResponse(device); + + const flash_read_once_response_packet_t *packet = + reinterpret_cast(responsePacket); + processResponse(packet); +} + +// See host_command.h for documentation of this method. +bool FlashReadOnce::processResponse(const flash_read_once_response_packet_t *packet) +{ + if (!packet) + { + Log::debug("processResponse: null packet\n"); + m_responseValues.push_back(kStatus_NoResponse); + return false; + } + + // Handle generic response, which would be returned if command is not supported. + if (packet->commandPacket.commandTag == kCommandTag_GenericResponse) + { + return processResponse((const uint8_t *)packet); + } + + if (packet->commandPacket.commandTag != kCommandTag_FlashReadOnceResponse) + { + Log::error("Error: expected kCommandTag_FlashReadOnceResponse (0x%x), received 0x%x\n", + kCommandTag_FlashReadOnceResponse, packet->commandPacket.commandTag); + m_responseValues.push_back(kStatus_UnknownCommand); + return false; + } + + // Set the status in the response vector. + m_responseValues.push_back(packet->status); + + if (packet->status != kStatus_Success) + { + return false; + } + + Log::debug("Successful response to command '%s'\n", getName().c_str()); + + // Currently, no properties have a data phase. + assert(!(packet->commandPacket.flags & kCommandFlag_HasDataPhase)); + + // All properties have at least one response word. + // Attention: parameterCount = 1(response status) + response words + m_responseValues.push_back(packet->byteCount); + + // two parameters(status and byte count) should be excluded from the parameterCount + for (uint8_t i = 0; i < (packet->commandPacket.parameterCount - 2); ++i) + { + m_responseValues.push_back(packet->data[i]); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// FlashReadResource command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FlashReadResource::init() +{ + if ((getArgCount() < 4) || (getArgCount() > 5)) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_startAddress)) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_byteCount)) + { + return false; + } + if (!utils::stringtoui(getArg(3), m_option)) + { + return false; + } + + // File name argument is optional - will use stdout if missing. + if (getArgCount() == 5) + { + m_dataFile = getArg(4); + } + + return true; +} + +// See host_command.h for documentation of this method. +void FlashReadResource::sendTo(Packetizer &device) +{ + DataPacket::DataConsumer *dataConsumer; + DataPacket::FileDataConsumer fileDataConsumer; + DataPacket::StdOutDataConsumer stdoutDataConsumer; + + // Setup to write to file or stdout + if (m_dataFile.size() > 0) + { + if (!fileDataConsumer.init(m_dataFile)) + { + return; + } + dataConsumer = &fileDataConsumer; + } + else + { + dataConsumer = &stdoutDataConsumer; + } + + // Send command packet. + blfwk::CommandPacket cmdPacket(kCommandTag_FlashReadResource, kCommandFlag_None, m_startAddress, m_byteCount, + m_option); + const uint8_t *responsePacket = cmdPacket.sendCommandGetResponse(device); + + const flash_read_resource_response_packet_t *packet = + reinterpret_cast(responsePacket); + uint32_t byteCount = packet->dataByteCount; + if (processResponse(packet)) + { + // Receive data packets. + blfwk::DataPacket dataPacket(dataConsumer); + uint8_t *finalResponsePacket = dataPacket.receiveFrom(device, &byteCount, m_progress); + processResponse(finalResponsePacket); + } + + // Push the number of bytes transferred response value. + m_responseValues.push_back(m_byteCount - byteCount); + + // Format the command transfer details. + m_responseDetails = format_string("Read %d of %d bytes.", m_byteCount - byteCount, m_byteCount); +} + +bool FlashReadResource::processResponse(const flash_read_resource_response_packet_t *packet) +{ + if (!packet) + { + Log::debug("processResponse: null packet\n"); + m_responseValues.push_back(kStatus_NoResponse); + return false; + } + + // Handle generic response, which would be returned if command is not supported. + if (packet->commandPacket.commandTag == kCommandTag_GenericResponse) + { + return processResponse((const uint8_t *)packet); + } + + if (packet->commandPacket.commandTag != kCommandTag_FlashReadResourceResponse) + { + Log::error("Error: expected kCommandTag_FlashReadResourceResponse (0x%x), received 0x%x\n", + kCommandTag_FlashReadResourceResponse, packet->commandPacket.commandTag); + return false; + } + if (packet->status != kStatus_Success) + { + // Set the status in the response vector. + // If status is OK, this push will be done by final response processing + m_responseValues.push_back(packet->status); + return false; + } + + Log::info("Successful response to command '%s'\n", getName().c_str()); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Configure Memory command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool ConfigureMemory::init() +{ + if (getArgCount() != 3) + { + return false; + } + if (!utils::stringtoui(getArg(1), m_memoryId)) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_configBlockAddress)) + { + return false; + } + + return true; +} + +// See host_command.h for documentation of this method. +void ConfigureMemory::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_ConfigureMemory, kCommandFlag_None, m_memoryId, m_configBlockAddress); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// ReliableUpdate command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool ReliableUpdate::init() +{ + if (getArgCount() != 2) + { + return false; + } + if (!utils::stringtoui(getArg(1), m_address)) + { + return false; + } + + return true; +} + +// See host_command.h for documentation of this method. +void ReliableUpdate::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_ReliableUpdate, kCommandFlag_None, m_address); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// Key Provisioning command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool KeyProvisioning::init() +{ + size_t separatorIndex; + + if (getArgCount() < 2) // 1 arguement at least. + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_operation)) + { + if (strcmp(getArg(1).c_str(), "enroll") == 0) + { + m_operation = kKeyProvisioning_Operation_Enroll; + } + else if (strcmp(getArg(1).c_str(), "set_user_key") == 0) + { + m_operation = kKeyProvisioning_Operation_SetUserKey; + } + else if (strcmp(getArg(1).c_str(), "set_key") == 0) + { + m_operation = kKeyProvisioning_Operation_SetIntrinsicKey; + } + else if (strcmp(getArg(1).c_str(), "write_key_nonvolatile") == 0) + { + m_operation = kKeyProvisioning_Operation_WriteNonVolatile; + } + else if (strcmp(getArg(1).c_str(), "read_key_nonvolatile") == 0) + { + m_operation = kKeyProvisioning_Operation_ReadNonVolatile; + } + else if (strcmp(getArg(1).c_str(), "write_key_store") == 0) + { + m_operation = kKeyProvisioning_Operation_WriteKeyStore; + } + else if (strcmp(getArg(1).c_str(), "read_key_store") == 0) + { + m_operation = kKeyProvisioning_Operation_ReadKeyStore; + } + else + { + return false; + } + } + + switch (m_operation) + { + case kKeyProvisioning_Operation_Enroll: + if (getArgCount() != 2) + { + return false; + } + break; + case kKeyProvisioning_Operation_SetUserKey: + if (getArgCount() != 4) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_type)) + { + return false; + } + + m_fileOrData = getArg(3); + + // Try to find the separator ','. + separatorIndex = m_fileOrData.find(',', 0); + if (separatorIndex != string::npos) + { + // If found, the left string is the byte count to write. + if (!utils::stringtoui( + m_fileOrData.substr(separatorIndex + 1, m_fileOrData.length() - separatorIndex - 1), m_size)) + { + return false; + } + m_fileOrData = m_fileOrData.substr(0, separatorIndex); + } + + if ((m_fileOrData[0] == '{') && (m_fileOrData[1] == '{')) + { + DataPacket::HexDataProducer hexProducer; + // Argument string is hex data, so use hex data producer. + if (hexProducer.initFromString(m_fileOrData) == 0) + { + return false; + } + } + break; + case kKeyProvisioning_Operation_SetIntrinsicKey: + if (getArgCount() != 4) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_type)) + { + return false; + } + if (!utils::stringtoui(getArg(3), m_size)) + { + return false; + } + break; + case kKeyProvisioning_Operation_WriteNonVolatile: + case kKeyProvisioning_Operation_ReadNonVolatile: + if ((getArgCount() != 2) && (getArgCount() != 3)) + { + return false; + } + + if (getArgCount() == 3) + { + if (!utils::stringtoui(getArg(2), m_memoryId)) + { + return false; + } + } + else + { + m_memoryId = 0; + } + + break; + case kKeyProvisioning_Operation_WriteKeyStore: + if (getArgCount() != 3) + { + return false; + } + m_fileOrData = getArg(2); + + // Try to find the separator ','. + separatorIndex = m_fileOrData.find(',', 0); + if (separatorIndex != string::npos) + { + // If found, the left string is the byte count to write. + if (!utils::stringtoui( + m_fileOrData.substr(separatorIndex + 1, m_fileOrData.length() - separatorIndex - 1), m_size)) + { + return false; + } + m_fileOrData = m_fileOrData.substr(0, separatorIndex); + } + + if ((m_fileOrData[0] == '{') && (m_fileOrData[1] == '{')) + { + DataPacket::HexDataProducer hexProducer; + // Argument string is hex data, so use hex data producer. + if (hexProducer.initFromString(m_fileOrData) == 0) + { + return false; + } + } + break; + case kKeyProvisioning_Operation_ReadKeyStore: + if ((getArgCount() != 2) && (getArgCount() != 3)) + { + return false; + } + if (getArgCount() == 3) + { + m_fileOrData = getArg(2); + } + break; + default: + return false; + } + + return true; +} + +// See host_command.h for documentation of this method. +void KeyProvisioning::sendTo(Packetizer &device) +{ + switch (m_operation) + { + case kKeyProvisioning_Operation_Enroll: + { + blfwk::CommandPacket cmdPacket(kCommandTag_KeyProvisioning, kCommandFlag_None, m_operation); + processResponse(cmdPacket.sendCommandGetResponse(device)); + break; + } + case kKeyProvisioning_Operation_SetIntrinsicKey: + { + blfwk::CommandPacket cmdPacket(kCommandTag_KeyProvisioning, kCommandFlag_None, m_operation, m_type, m_size); + processResponse(cmdPacket.sendCommandGetResponse(device)); + break; + } + case kKeyProvisioning_Operation_WriteNonVolatile: + case kKeyProvisioning_Operation_ReadNonVolatile: + { + blfwk::CommandPacket cmdPacket(kCommandTag_KeyProvisioning, kCommandFlag_None, m_operation, m_memoryId); + processResponse(cmdPacket.sendCommandGetResponse(device)); + break; + } + case kKeyProvisioning_Operation_SetUserKey: + case kKeyProvisioning_Operation_WriteKeyStore: + sendCmdAndData(device); + break; + case kKeyProvisioning_Operation_ReadKeyStore: + sendCmdAndGetData(device); + break; + default: + break; + } +} + +void KeyProvisioning::sendCmdAndGetData(Packetizer &device) +{ + DataPacket::DataConsumer *dataConsumer; + DataPacket::FileDataConsumer fileDataConsumer; + DataPacket::StdOutDataConsumer stdoutDataConsumer; + + // Setup to write to file or stdout + if (m_fileOrData.size() > 0) + { + if (!fileDataConsumer.init(m_fileOrData)) + { + return; + } + dataConsumer = &fileDataConsumer; + } + else + { + dataConsumer = &stdoutDataConsumer; + } + + // Send command packet. + blfwk::CommandPacket cmdPacket(kCommandTag_KeyProvisioning, kCommandFlag_None, m_operation); + const uint8_t *responsePacket = cmdPacket.sendCommandGetResponse(device); + + const key_provisioning_response_packet_t *packet = + reinterpret_cast(responsePacket); + uint32_t bytesToRead = m_size; + if (processResponse(packet)) + { + bytesToRead = packet->keyByteCount; + m_size = bytesToRead; + // Receive data packets. + blfwk::DataPacket dataPacket(dataConsumer); + uint8_t *finalResponsePacket = dataPacket.receiveFrom(device, &bytesToRead, m_progress); + processResponse(finalResponsePacket); + } + + // Push the number of bytes transferred response value. + m_responseValues.push_back(m_size - bytesToRead); + + // Format the command transfer details. + m_responseDetails = format_string("Read %d of %d bytes.", m_size - bytesToRead, m_size); +} + +void KeyProvisioning::sendCmdAndData(Packetizer &device) +{ + DataPacket::HexDataProducer hexProducer; + DataPacket::FileDataProducer fileProducer; + DataPacket::DataProducer *dataProducer; + + if ((m_fileOrData[0] == '{') && (m_fileOrData[1] == '{')) + { + // Argument string is hex data, so use hex data producer. + if (hexProducer.initFromString(m_fileOrData) == 0) + { + return; + } + dataProducer = &hexProducer; + } + else + { + // Argument string is file name, so use file data producer. + if (!fileProducer.init(m_fileOrData, m_size)) + { + return; + } + dataProducer = &fileProducer; + } + + // Get target bootloader data packet size. + uint32_t packetSizeInBytes; + GetProperty getPacketSize(kProperty_MaxPacketSize, 0 /*Not used*/); + getPacketSize.sendTo(device); + uint32_t fw_status = getPacketSize.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + // Failed to get data packet size. + Log::warning("Warning: Failed to get packet size. Using default size(%d)", kMinPacketBufferSize); + packetSizeInBytes = kMinPacketBufferSize; // No property. Use default packet size. + } + else + { + packetSizeInBytes = getPacketSize.getResponseValues()->at(1); + if (packetSizeInBytes > device.getMaxPacketSize()) + { + Log::error("Error: Packet size(%d) is bigger than max supported size(%d).", packetSizeInBytes, + kMaxHostPacketSize); + return; + } + } + + // Send command packet. + uint32_t bytesToWrite = dataProducer->getDataSize(); + uint32_t bytesWritten; + blfwk::CommandPacket cmdPacket(kCommandTag_KeyProvisioning, kCommandFlag_HasDataPhase, m_operation, m_type, + bytesToWrite); + const uint8_t *responsePacket = cmdPacket.sendCommandGetResponse(device); + + const key_provisioning_response_packet_t *packet = + reinterpret_cast(responsePacket); + if (processResponse(packet)) + { + if (packet->keyByteCount != bytesToWrite) + { + Log::error("Error: Incorrect key length %d(%#x) which should be %d(%#x), Abort the data phase.", m_size, + m_size, packet->keyByteCount, packet->keyByteCount); + uint8_t dummy_data = 0; + device.writePacket(&dummy_data, 0, kPacketType_Data); + uint8_t *responsePacket = NULL; + uint32_t responseLength; + if (!device.readPacket(&responsePacket, &responseLength, kPacketType_Command)) + { + processResponse(responsePacket); + } + return; + } + } + else + { + m_responseDetails = format_string("Wrote 0 of %d bytes.", bytesToWrite); + return; + } + + // Pop the initial (successful) generic response value. + if (m_responseValues.size()) + { + m_responseValues.pop_back(); + } + + // Send data packets. + blfwk::DataPacket dataPacket(dataProducer, packetSizeInBytes); + + processResponse(dataPacket.sendTo(device, &bytesWritten, m_progress)); +} + +bool KeyProvisioning::processResponse(const key_provisioning_response_packet_t *packet) +{ + if (!packet) + { + Log::debug("processResponse: null packet\n"); + m_responseValues.push_back(kStatus_NoResponse); + return false; + } + + // Handle generic response, which would be returned if command is not supported. + if (packet->commandPacket.commandTag == kCommandTag_GenericResponse) + { + return processResponse((const uint8_t *)packet); + } + + if (packet->commandPacket.commandTag != kCommandTag_KeyProvisioningResponse) + { + Log::error("Error: expected kCommandTag_KeyProvisioningResponse (0x%x), received 0x%x\n", + kCommandTag_KeyProvisioningResponse, packet->commandPacket.commandTag); + return false; + } + if (packet->status != kStatus_Success) + { + // Set the status in the response vector. + // If status is OK, this push will be done by final response processing + m_responseValues.push_back(packet->status); + return false; + } + + Log::info("Successful response to command '%s'\n", getName().c_str()); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Trust Provisioning command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool TrustProvisioning::init() +{ + if (getArgCount() < 2) // 1 arguement at least. + { + return false; + } + + // Get the operation code. + if (!utils::stringtoui(getArg(1), m_operation)) + { + if (strcmp(getArg(1).c_str(), kOperation_Tp_OemGenMasterShare.name) == 0) + { + m_operation = kOperation_Tp_OemGenMasterShare.tag; + } + else if (strcmp(getArg(1).c_str(), kOperation_Tp_OemSetMasterShare.name) == 0) + { + m_operation = kOperation_Tp_OemSetMasterShare.tag; + } + else if (strcmp(getArg(1).c_str(), kOperation_Tp_OemGetCustCertDicePuk.name) == 0) + { + m_operation = kOperation_Tp_OemGetCustCertDicePuk.tag; + } + else if (strcmp(getArg(1).c_str(), kOperation_Tp_HsmGenKey.name) == 0) + { + m_operation = kOperation_Tp_HsmGenKey.tag; + } + else if (strcmp(getArg(1).c_str(), kOperation_Tp_HsmStoreKey.name) == 0) + { + m_operation = kOperation_Tp_HsmStoreKey.tag; + } + else if (strcmp(getArg(1).c_str(), kOperation_Tp_HsmEncryptBlock.name) == 0) + { + m_operation = kOperation_Tp_HsmEncryptBlock.tag; + } + else if (strcmp(getArg(1).c_str(), kOperation_Tp_HsmEncryptSign.name) == 0) + { + m_operation = kOperation_Tp_HsmEncryptSign.tag; + } + else + { + return false; + } + } + + switch (m_operation) + { + /*!< OEM trusted facility commands. */ + case kTrustProvisioning_Operation_Oem_GenMasterShare: + if (getArgCount() != 10) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_parms.oemGenMasterShare.oemShareInputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(3), m_parms.oemGenMasterShare.oemShareInputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(4), m_parms.oemGenMasterShare.oemEncShareOutputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(5), m_parms.oemGenMasterShare.oemEncShareOutputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(6), m_parms.oemGenMasterShare.oemEncMasterShareOutputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(7), m_parms.oemGenMasterShare.oemEncMasterShareOutputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(8), m_parms.oemGenMasterShare.oemCustCertPukOutputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(9), m_parms.oemGenMasterShare.oemCustCertPukOutputSize)) + { + return false; + } + break; + case kTrustProvisioning_Operation_Oem_SetMasterShare: + if (getArgCount() != 6) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_parms.oemSetMasterShare.oemShareInputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(3), m_parms.oemSetMasterShare.oemShareInputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(4), m_parms.oemSetMasterShare.oemEncMasterShareInputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(5), m_parms.oemSetMasterShare.oemEncMasterShareInputSize)) + { + return false; + } + break; + case kTrustProvisioning_Operation_Oem_GetCustCertDicePuk: + if (getArgCount() != 6) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_parms.oemGetCustCertDicePuk.oemRkthInputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(3), m_parms.oemGetCustCertDicePuk.oemRkthInputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(4), m_parms.oemGetCustCertDicePuk.oemCustCertDicePukOutputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(5), m_parms.oemGetCustCertDicePuk.oemCustCertDicePukOutputSize)) + { + return false; + } + break; + case kTrustProvisioning_Operation_Hsm_GenKey: + if (getArgCount() != 8) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_parms.hsmGenKey.keyType)) + { + if (strcmp(getArg(2).c_str(), kKeyType_Tp_HsmGenKey_MfwIsK.name) == 0) + { + m_parms.hsmGenKey.keyType = kKeyType_Tp_HsmGenKey_MfwIsK.tag; + } + else if (strcmp(getArg(2).c_str(), kKeyType_Tp_HsmGenKey_MfwEncK.name) == 0) + { + m_parms.hsmGenKey.keyType = kKeyType_Tp_HsmGenKey_MfwEncK.tag; + } + else if (strcmp(getArg(2).c_str(), kKeyType_Tp_HsmGenKey_GenSignK.name) == 0) + { + m_parms.hsmGenKey.keyType = kKeyType_Tp_HsmGenKey_GenSignK.tag; + } + else if (strcmp(getArg(2).c_str(), kKeyType_Tp_HsmGenKey_GenCustMkSK.name) == 0) + { + m_parms.hsmGenKey.keyType = kKeyType_Tp_HsmGenKey_GenCustMkSK.tag; + } + else + { + return false; + } + } + if (!utils::stringtoui(getArg(3), m_parms.hsmGenKey.keyProp)) + { + return false; + } + if (!utils::stringtoui(getArg(4), m_parms.hsmGenKey.keyBlobOutputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(5), m_parms.hsmGenKey.keyBlobOutputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(6), m_parms.hsmGenKey.ecdsaPukOutputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(7), m_parms.hsmGenKey.ecdsaPukOutputSize)) + { + return false; + } + break; + case kTrustProvisioning_Operation_Hsm_StoreKey: + if (getArgCount() != 8) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_parms.hsmStoreKey.keyType)) + { + if (strcmp(getArg(2).c_str(), kKeyType_Tp_HsmStoreKey_CKDFK.name) == 0) + { + m_parms.hsmStoreKey.keyType = kKeyType_Tp_HsmStoreKey_CKDFK.tag; + } + else if (strcmp(getArg(2).c_str(), kKeyType_Tp_HsmStoreKey_HKDFK.name) == 0) + { + m_parms.hsmStoreKey.keyType = kKeyType_Tp_HsmStoreKey_HKDFK.tag; + } + else if (strcmp(getArg(2).c_str(), kKeyType_Tp_HsmStoreKey_HMACK.name) == 0) + { + m_parms.hsmStoreKey.keyType = kKeyType_Tp_HsmStoreKey_HMACK.tag; + } + else if (strcmp(getArg(2).c_str(), kKeyType_Tp_HsmStoreKey_CMACK.name) == 0) + { + m_parms.hsmStoreKey.keyType = kKeyType_Tp_HsmStoreKey_CMACK.tag; + } + else if (strcmp(getArg(2).c_str(), kKeyType_Tp_HsmStoreKey_AESK.name) == 0) + { + m_parms.hsmStoreKey.keyType = kKeyType_Tp_HsmStoreKey_AESK.tag; + } + else if (strcmp(getArg(2).c_str(), kKeyType_Tp_HsmStoreKey_KUOK.name) == 0) + { + m_parms.hsmStoreKey.keyType = kKeyType_Tp_HsmStoreKey_KUOK.tag; + } + else + { + return false; + } + } + if (!utils::stringtoui(getArg(3), m_parms.hsmStoreKey.keyProp)) + { + return false; + } + if (!utils::stringtoui(getArg(4), m_parms.hsmStoreKey.keyInputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(5), m_parms.hsmStoreKey.keyInputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(6), m_parms.hsmStoreKey.keyBlobOutputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(7), m_parms.hsmStoreKey.keyBlobOutputSize)) + { + return false; + } + break; + case kTrustProvisioning_Operation_Hsm_EncryptBlock: + if (getArgCount() != 10) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_parms.hsmEncBlk.mfgCustMkSk0BlobInputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(3), m_parms.hsmEncBlk.mfgCustMkSk0BlobInputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(4), m_parms.hsmEncBlk.kekId)) + { + return false; + } + if (!utils::stringtoui(getArg(5), m_parms.hsmEncBlk.sb3HeaderInputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(6), m_parms.hsmEncBlk.sb3HeaderInputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(7), m_parms.hsmEncBlk.blockNum)) + { + return false; + } + if (!utils::stringtoui(getArg(8), m_parms.hsmEncBlk.blockDataAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(9), m_parms.hsmEncBlk.blockDataSize)) + { + return false; + } + break; + case kTrustProvisioning_Operation_Hsm_EncryptSign: + if (getArgCount() != 8) + { + return false; + } + if (!utils::stringtoui(getArg(2), m_parms.hsmEncSign.keyBlobInputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(3), m_parms.hsmEncSign.keyBlobInputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(4), m_parms.hsmEncSign.blockDataInputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(5), m_parms.hsmEncSign.blockDataInputSize)) + { + return false; + } + if (!utils::stringtoui(getArg(6), m_parms.hsmEncSign.signatureOutputAddr)) + { + return false; + } + if (!utils::stringtoui(getArg(7), m_parms.hsmEncSign.signatureOutputSize)) + { + return false; + } + break; + /* + * Not supported yet. + */ + /*!< NXP factory commands. */ + case kTrustProvisioning_Operation_Nxp_RtsGetId: + case kTrustProvisioning_Operation_Nxp_RtsInsertCertificate: + case kTrustProvisioning_Operation_Nxp_SsfInsertCertificate: + /*!< OEM/CM factory commands. */ + case kTrustProvisioning_Operation_Dev_AuthChallengeNxp: + case kTrustProvisioning_Operation_Dev_AuthChallengeOem: + case kTrustProvisioning_Operation_Dev_SetWrapData: + /*!< In-field commands. */ + case kTrustProvisioning_Operation_Dev_GetUuid: + default: + return false; + } + + return true; +} + +// See host_command.h for documentation of this method. +void TrustProvisioning::sendTo(Packetizer &device) +{ + switch (m_operation) + { + /*!< OEM trusted facility commands. */ + case kTrustProvisioning_Operation_Oem_GenMasterShare: + { + blfwk::CommandPacket cmdPacket( + kCommandTag_TrustProvisioning, kCommandFlag_None, m_operation, + m_parms.oemGenMasterShare.oemShareInputAddr, m_parms.oemGenMasterShare.oemShareInputSize, + m_parms.oemGenMasterShare.oemEncShareOutputAddr, m_parms.oemGenMasterShare.oemEncShareOutputSize, + m_parms.oemGenMasterShare.oemEncMasterShareOutputAddr, + m_parms.oemGenMasterShare.oemEncMasterShareOutputSize, + m_parms.oemGenMasterShare.oemCustCertPukOutputAddr, m_parms.oemGenMasterShare.oemCustCertPukOutputSize); + processResponse(reinterpret_cast( + cmdPacket.sendCommandGetResponse(device))); + break; + } + case kTrustProvisioning_Operation_Oem_SetMasterShare: + { + blfwk::CommandPacket cmdPacket(kCommandTag_TrustProvisioning, kCommandFlag_None, m_operation, + m_parms.oemSetMasterShare.oemShareInputAddr, + m_parms.oemSetMasterShare.oemShareInputSize, + m_parms.oemSetMasterShare.oemEncMasterShareInputAddr, + m_parms.oemSetMasterShare.oemEncMasterShareInputSize); + processResponse(reinterpret_cast( + cmdPacket.sendCommandGetResponse(device))); + break; + } + case kTrustProvisioning_Operation_Oem_GetCustCertDicePuk: + { + blfwk::CommandPacket cmdPacket(kCommandTag_TrustProvisioning, kCommandFlag_None, m_operation, + m_parms.oemGetCustCertDicePuk.oemRkthInputAddr, + m_parms.oemGetCustCertDicePuk.oemRkthInputSize, + m_parms.oemGetCustCertDicePuk.oemCustCertDicePukOutputAddr, + m_parms.oemGetCustCertDicePuk.oemCustCertDicePukOutputSize); + processResponse(reinterpret_cast( + cmdPacket.sendCommandGetResponse(device))); + break; + } + case kTrustProvisioning_Operation_Hsm_GenKey: + { + blfwk::CommandPacket cmdPacket(kCommandTag_TrustProvisioning, kCommandFlag_None, m_operation, + m_parms.hsmGenKey.keyType, m_parms.hsmGenKey.keyProp, + m_parms.hsmGenKey.keyBlobOutputAddr, m_parms.hsmGenKey.keyBlobOutputSize, + m_parms.hsmGenKey.ecdsaPukOutputAddr, m_parms.hsmGenKey.ecdsaPukOutputSize); + processResponse(reinterpret_cast( + cmdPacket.sendCommandGetResponse(device))); + break; + } + case kTrustProvisioning_Operation_Hsm_StoreKey: + { + blfwk::CommandPacket cmdPacket( + kCommandTag_TrustProvisioning, kCommandFlag_None, m_operation, m_parms.hsmStoreKey.keyType, + m_parms.hsmStoreKey.keyProp, m_parms.hsmStoreKey.keyInputAddr, m_parms.hsmStoreKey.keyInputSize, + m_parms.hsmStoreKey.keyBlobOutputAddr, m_parms.hsmStoreKey.keyBlobOutputSize); + processResponse(reinterpret_cast( + cmdPacket.sendCommandGetResponse(device))); + break; + } + case kTrustProvisioning_Operation_Hsm_EncryptBlock: + { + blfwk::CommandPacket cmdPacket( + kCommandTag_TrustProvisioning, kCommandFlag_None, m_operation, + m_parms.hsmEncBlk.mfgCustMkSk0BlobInputAddr, m_parms.hsmEncBlk.mfgCustMkSk0BlobInputSize, + m_parms.hsmEncBlk.kekId, m_parms.hsmEncBlk.sb3HeaderInputAddr, m_parms.hsmEncBlk.sb3HeaderInputSize, + m_parms.hsmEncBlk.blockNum, m_parms.hsmEncBlk.blockDataAddr, m_parms.hsmEncBlk.blockDataSize); + processResponse(reinterpret_cast( + cmdPacket.sendCommandGetResponse(device))); + break; + } + case kTrustProvisioning_Operation_Hsm_EncryptSign: + { + blfwk::CommandPacket cmdPacket(kCommandTag_TrustProvisioning, kCommandFlag_None, m_operation, + m_parms.hsmEncSign.keyBlobInputAddr, m_parms.hsmEncSign.keyBlobInputSize, + m_parms.hsmEncSign.blockDataInputAddr, m_parms.hsmEncSign.blockDataInputSize, + m_parms.hsmEncSign.signatureOutputAddr, + m_parms.hsmEncSign.signatureOutputSize); + processResponse(reinterpret_cast( + cmdPacket.sendCommandGetResponse(device))); + break; + } + /* + * Not supported yet. + */ + /*!< NXP factory commands. */ + case kTrustProvisioning_Operation_Nxp_RtsGetId: + case kTrustProvisioning_Operation_Nxp_RtsInsertCertificate: + case kTrustProvisioning_Operation_Nxp_SsfInsertCertificate: + /*!< OEM/CM factory commands. */ + case kTrustProvisioning_Operation_Dev_AuthChallengeNxp: + case kTrustProvisioning_Operation_Dev_AuthChallengeOem: + case kTrustProvisioning_Operation_Dev_SetWrapData: + /*!< In-field commands. */ + case kTrustProvisioning_Operation_Dev_GetUuid: + default: + break; + } +} + +bool TrustProvisioning::processResponse(const trust_provisioning_response_packet_t *packet) +{ + if (!packet) + { + Log::debug("processResponse: null packet\n"); + m_responseValues.push_back(kStatus_NoResponse); + return false; + } + + // Handle generic response, which would be returned if command is not supported. + if (packet->commandPacket.commandTag == kCommandTag_GenericResponse) + { + return processResponse((const uint8_t *)packet); + } + + if (packet->commandPacket.commandTag != kCommandTag_TrustProvisioningResponse) + { + Log::error("Error: expected kCommandTag_TrustProvisioningResponse (0x%x), received 0x%x\n", + kCommandTag_TrustProvisioningResponse, packet->commandPacket.commandTag); + return false; + } + + // Set the status in the response vector. + m_responseValues.push_back(packet->status); + + // All operation responses have at least one response word. + // Attention: parameterCount = 1(response status) + response words + for (uint8_t i = 0; i < (packet->commandPacket.parameterCount - 1); ++i) + { + m_responseValues.push_back(packet->returnValue[i]); + } + + if ((packet->status == kStatus_InvalidArgument) || (packet->status == kStatus_Success)) + { + if (packet->status == kStatus_Success) + { + m_responseDetails = format_string("Output data size/value(s) is(are):\n"); + } + else + { + m_responseDetails = + format_string("Output buffer(s) is(are) smaller than the minimum requested which is(are):\n"); + } + switch (m_operation) + { + /*!< OEM trusted facility commands. */ + case kTrustProvisioning_Operation_Oem_GenMasterShare: + { + if (m_responseValues.size() != 4) + { + Log::error("Error: expected %d arguments, received %d\n", 4, m_responseValues.size()); + m_responseDetails.clear(); + } + else + { + m_responseDetails += format_string( + "\tOEM Share size: %d(%#x)\n\tOEM Master Share size: %d(%#x)\n\tCust Cert Puk size: %d(%#x)", + m_responseValues.at(1), m_responseValues.at(1), m_responseValues.at(2), m_responseValues.at(2), + m_responseValues.at(3), m_responseValues.at(3)); + } + break; + } + case kTrustProvisioning_Operation_Oem_SetMasterShare: + { + /* No response value. */ + m_responseDetails.clear(); + break; + } + case kTrustProvisioning_Operation_Oem_GetCustCertDicePuk: + { + if (m_responseValues.size() != 2) + { + Log::error("Error: expected %d arguments, received %d\n", 4, m_responseValues.size()); + m_responseDetails.clear(); + } + else + { + m_responseDetails += format_string("\tCust Cert Dice Puk size: %d(%#x)", m_responseValues.at(1), + m_responseValues.at(1)); + } + break; + } + case kTrustProvisioning_Operation_Hsm_GenKey: + { + if (m_responseValues.size() != 3) + { + Log::error("Error: expected %d arguments, received %d\n", 4, m_responseValues.size()); + m_responseDetails.clear(); + } + else + { + m_responseDetails += + format_string("\tKey Blob size: %d(%#x)\n\tECDSA Puk size: %d(%#x)", m_responseValues.at(1), + m_responseValues.at(1), m_responseValues.at(2), m_responseValues.at(2)); + } + break; + } + case kTrustProvisioning_Operation_Hsm_StoreKey: + { + if (m_responseValues.size() != 3) + { + Log::error("Error: expected %d arguments, received %d\n", 4, m_responseValues.size()); + m_responseDetails.clear(); + } + else + { + m_responseDetails += + format_string("\tKey Header: %d(%#x)\n\tKey Blob size: %d(%#x)", m_responseValues.at(1), + m_responseValues.at(1), m_responseValues.at(2), m_responseValues.at(2)); + } + break; + } + case kTrustProvisioning_Operation_Hsm_EncryptBlock: + { + /* No response value. */ + m_responseDetails.clear(); + break; + } + case kTrustProvisioning_Operation_Hsm_EncryptSign: + { + if (m_responseValues.size() != 2) + { + Log::error("Error: expected %d arguments, received %d\n", 4, m_responseValues.size()); + m_responseDetails.clear(); + } + else + { + m_responseDetails += + format_string("\tSignature size: %d(%#x)", m_responseValues.at(1), m_responseValues.at(1)); + } + break; + } + /* + * Not supported yet. + */ + /*!< NXP factory commands. */ + case kTrustProvisioning_Operation_Nxp_RtsGetId: + case kTrustProvisioning_Operation_Nxp_RtsInsertCertificate: + case kTrustProvisioning_Operation_Nxp_SsfInsertCertificate: + /*!< OEM/CM factory commands. */ + case kTrustProvisioning_Operation_Dev_AuthChallengeNxp: + case kTrustProvisioning_Operation_Dev_AuthChallengeOem: + case kTrustProvisioning_Operation_Dev_SetWrapData: + /*!< In-field commands. */ + case kTrustProvisioning_Operation_Dev_GetUuid: + default: + break; + } + } + + if (packet->status != kStatus_Success) + { + return false; + } + + Log::info("Successful response to command '%s'\n", getName().c_str()); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// FlashImage command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool FlashImage::init() +{ + if (getArgCount() != 2 && getArgCount() != 3 && getArgCount() != 4) + { + return false; + } + + m_fileName = getArg(1); + + if (getArgCount() == 3) + { + string strDoEraseOpt = getArg(2); + if (strDoEraseOpt == "erase") + { + m_doEraseOpt = true; + m_memoryId = kMemoryInternal; + } + else if (strDoEraseOpt == "none") + { + m_doEraseOpt = false; + m_memoryId = kMemoryInternal; + } + else + { + // If not 'erase' or 'none'. Try to parse it as memoryId + // If arg2 is a valid number, it is valid memoryId, + // otherwise is invalid arg and return false. + if (!utils::stringtoui(getArg(2), m_memoryId)) + { + return false; + } + // Use 0 for flashing image to internal 4G memory including mapped memory, such as QSPI + if (GROUPID(m_memoryId) == kGroup_Internal) + { + m_memoryId = kMemoryInternal; + } + // NO erase operation by default. + m_doEraseOpt = false; + } + } + else if (getArgCount() == 4) + { + string strDoEraseOpt = getArg(2); + if (strDoEraseOpt == "erase") + { + m_doEraseOpt = true; + } + else if (strDoEraseOpt == "none") + { + m_doEraseOpt = false; + } + else + { + return false; + } + + if (!utils::stringtoui(getArg(3), m_memoryId)) + { + return false; + } + // Use 0 for flashing image to internal 4G memory including mapped memory, such as QSPI + if (GROUPID(m_memoryId) == kGroup_Internal) + { + m_memoryId = kMemoryInternal; + } + } + + return true; +} + +// See host_command.h for documentation of this method. +void FlashImage::sendTo(Packetizer &device) +{ + uint32_t fw_status; + DataSource *dataSource; + + try + { + m_sourceFile = SourceFile::openFile(m_fileName); + } + catch (exception &e) + { + Log::error("Error: %s", e.what()); + return; + } + if (m_sourceFile->getFileType() == SourceFile::source_file_t::kBinarySourceFile) + { + Log::error("Error: please use write-memory command for binary file downloading.\n"); + return; + } + + m_sourceFile->open(); + dataSource = m_sourceFile->createDataSource(); + + m_progress->m_segmentCount = dataSource->getSegmentCount(); + + if (m_doEraseOpt) + { + for (uint32_t index = 0; index < dataSource->getSegmentCount(); ++index) + { + DataSource::Segment *segment = dataSource->getSegmentAt(index); + + // Align the start address and length to a sector boundary + uint32_t alignedStart = segment->getBaseAddress() & (~(MinEraseAlignment - 1)); + uint32_t alignedLength = + ((segment->getBaseAddress() + segment->getLength() + MinEraseAlignment) & (~(MinEraseAlignment - 1))) - + alignedStart; + + // Do erase operation to erase the necessary flash. + FlashEraseRegion cmd(alignedStart, alignedLength, m_memoryId); + cmd.sendTo(device); + + // Print and check the command response values. + fw_status = cmd.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + m_responseValues.push_back(fw_status); + delete dataSource; + return; + } + } + } + + for (uint32_t index = 0; index < dataSource->getSegmentCount(); ++index) + { + DataSource::Segment *segment = dataSource->getSegmentAt(index); + + // Write the file to the base address. + Log::info("Wrote %d bytes to address %#x\n", segment->getLength(), segment->getBaseAddress()); + WriteMemory cmd(segment, m_memoryId); + + m_progress->m_segmentIndex = index + 1; + cmd.registerProgress(m_progress); + + cmd.sendTo(device); + + // Print and check the command response values. + fw_status = cmd.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + m_responseValues.push_back(fw_status); + delete dataSource; + return; + } + } + + m_responseValues.push_back(fw_status); + delete dataSource; + m_sourceFile->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Configure I2C command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool ConfigureI2c::init() +{ + if (getArgCount() > 1) + { + i2cAddress = (uint8_t)strtoul(getArg(1).c_str(), NULL, 16); + + if (i2cAddress > 0x7F) + { + i2cAddress &= 0x7F; + Log::info("Only 7-bit i2c address is supported, so the effective value is 0x%x\n", i2cAddress); + } + + if (getArgCount() > 2) + { + int32_t i2cSpeed = atoi(getArg(2).c_str()); + if (i2cSpeed <= 0) + return false; + + i2cSpeedKHz = i2cSpeed; + } + } + + return true; +} + +// See host_command.h for documentation of this method. +void ConfigureI2c::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_ConfigureI2c, kCommandFlag_None, i2cAddress, i2cSpeedKHz); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// Configure SPI command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool ConfigureSpi::init() +{ + if ((getArgCount() > 1)) + { + int32_t spiSpeed = atoi(getArg(1).c_str()); + if (spiSpeed <= 0) + return false; + + spiSpeedKHz = spiSpeed; + + if (getArgCount() > 2) + { + spiPolarity = (BusPal::spi_clock_polarity_t)atoi(getArg(2).c_str()); + + if (getArgCount() > 3) + { + spiPhase = (BusPal::spi_clock_phase_t)atoi(getArg(3).c_str()); + + if (getArgCount() > 4) + { + if (!strcmp(getArg(4).c_str(), "lsb")) + { + spiDirection = BusPal::kSpiLsbFirst; + } + else if (!strcmp(getArg(4).c_str(), "msb")) + { + spiDirection = BusPal::kSpiMsbFirst; + } + } + } + } + } + + return true; +} + +// See host_command.h for documentation of this method. +void ConfigureSpi::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_ConfigureSpi, kCommandFlag_None, spiSpeedKHz, spiPolarity, spiPhase, + spiDirection); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// Configure CAN command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool ConfigureCan::init() +{ + if ((getArgCount() > 1)) + { + canSpeed = atoi(getArg(1).c_str()); + if (canSpeed > 4) + return false; + + if (getArgCount() > 2) + { + canTxid = strtoul(getArg(2).c_str(), NULL, 16) & 0x7ff; + } + + if (getArgCount() > 3) + { + canRxid = strtoul(getArg(3).c_str(), NULL, 16) & 0x7ff; + } + } + + return true; +} + +// See host_command.h for documentation of this method. +void ConfigureCan::sendTo(Packetizer &device) +{ + blfwk::CommandPacket cmdPacket(kCommandTag_ConfigureCan, kCommandFlag_None, canSpeed, canTxid, canRxid); + processResponse(cmdPacket.sendCommandGetResponse(device)); +} + +//////////////////////////////////////////////////////////////////////////////// +// ListMemory command +//////////////////////////////////////////////////////////////////////////////// + +// See host_command.h for documentation of this method. +bool ListMemory::init() +{ + if (getArgCount() != 1) + { + return false; + } + return true; +} + +// See host_command.h for documentation of this method. +void ListMemory::sendTo(Packetizer &device) +{ + uint32_t fw_status; + uint32_t firstStartAddress = 0; + + // List internal on-chip FLASH regions. + Log::info("Internal Flash:\n"); + for (uint32_t index = 0;; index++) + { + // Get internal on-chip FLASH start address. + uint32_t startAddress; + GetProperty getFlashStartAddress(kProperty_FlashStartAddress, index); + getFlashStartAddress.sendTo(device); + fw_status = getFlashStartAddress.getResponseValues()->at(0); + if (fw_status == kStatus_UnknownProperty) + { + // UnknownProperty means that there is no internal flash on current device. + Log::info(" No Internal Flash available\n"); + break; + } + else if (fw_status != kStatus_Success) + { + // Failed to communicate with the device. + m_responseValues.push_back(fw_status); + return; + } + startAddress = getFlashStartAddress.getResponseValues()->at(1); + + if (index == 0) + { + firstStartAddress = startAddress; + } + else if (startAddress == firstStartAddress) + { + // If a flash region's start address is the same as the first region. + // That means all flash regions are listed. + // Then break the for-loop. + break; + } + + // Get internal on-chip FLASH size. + uint32_t flashSize; + GetProperty getFlashSize(kProperty_FlashSizeInBytes, index); + getFlashSize.sendTo(device); + fw_status = getFlashSize.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + // Failed to communicate with the device. + m_responseValues.push_back(fw_status); + return; + } + flashSize = getFlashSize.getResponseValues()->at(1); + + // Get internal on-chip FLASH sector size. + uint32_t sectorSize; + GetProperty getFlashSectorSize(kProperty_FlashSectorSize, index); + getFlashSectorSize.sendTo(device); + fw_status = getFlashSectorSize.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + // Failed to communicate with the device. + m_responseValues.push_back(fw_status); + return; + } + sectorSize = getFlashSectorSize.getResponseValues()->at(1); + + Log::info(" Region %d: 0x%08x - 0x%08x; Total size: %s Sector size: %s\n", index, startAddress, + startAddress + flashSize - 1, utils::scale_bytes(flashSize).c_str(), + utils::scale_bytes(sectorSize).c_str()); + } + + // List internal on-chip RAM regions. + Log::info("Internal RAM:\n"); + for (uint32_t index = 0;; index++) + { + uint32_t startAddress; + // Get internal on-chip RAM start address. + GetProperty getRamStartAddress(kProperty_RAMStartAddress, index); + getRamStartAddress.sendTo(device); + fw_status = getRamStartAddress.getResponseValues()->at(0); + if (fw_status == kStatus_UnknownProperty) + { + // UnknownProperty means that there is no internal ram on current device. + Log::info(" No Internal RAM available\n"); + break; + } + if (fw_status != kStatus_Success) + { + // Failed to communicate with the device. + m_responseValues.push_back(fw_status); + return; + } + startAddress = getRamStartAddress.getResponseValues()->at(1); + + if (index == 0) + { + firstStartAddress = startAddress; + } + else if (startAddress == firstStartAddress) + { + // If a RAM region's start address is the same as the first region. + // That means all RAM regions are listed. + // Then break the for-loop. + break; + } + + uint32_t ramSize; + GetProperty getRamSize(kProperty_RAMSizeInBytes, index); + getRamSize.sendTo(device); + fw_status = getRamSize.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + // Failed to communicate with the device. + m_responseValues.push_back(fw_status); + return; + } + + ramSize = getRamSize.getResponseValues()->at(1); + + Log::info(" Region %d: 0x%08x - 0x%08x; Total size: %s\n", index, startAddress, startAddress + ramSize - 1, + utils::scale_bytes(ramSize).c_str()); + } + + // List external memories. + // Inject the get-property command to get the target version. + GetProperty getVersion(kProperty_CurrentVersion); + getVersion.sendTo(device); + fw_status = getVersion.getResponseValues()->at(0); + // Check the command status + if (fw_status != kStatus_Success) + { + m_responseValues.push_back(fw_status); + return; + } + uint32_t version = getVersion.getResponseValues()->at(1); + version = version & 0x00FFFFFF; // Mask the char 'K' + + // Bootloader 2.0.0 and previous versions only support internal and QSPI0. + if (version <= 0x20000 /*KBL2.0.0*/) + { + GetProperty getQspi(kProperty_ExernalMemoryAttributes, kMemory_QuadSpi.memoryId); + getQspi.sendTo(device); + fw_status = getQspi.getResponseValues()->at(0); + if (fw_status == kStatus_UnknownProperty) + { + // UnknownProperty means that no external memories are supported by current device. + } + else if (fw_status == kStatus_InvalidArgument) + { + // QSPI0 is not supported. + } + else if (fw_status == 405 /*kStatus_QspiNotConfigured*/) + { + Log::info("%s:\n %s\n", kMemory_QuadSpi.description, getStatusMessage(fw_status).c_str()); + } + else if (fw_status != kStatus_Success) + { + // Failed to get property of the device memory. + m_responseValues.push_back(fw_status); + return; + } + else // fw_status == kStatus_Success + { + uint32_t propertyTags = getQspi.getResponseValues()->at(1); + Log::info("%s:\n", kMemory_QuadSpi.description); + if (propertyTags & (1 << (kExternalMemoryPropertyTag_StartAddress - 1))) + { + Log::info(" Start Address = 0x%08x", getQspi.getResponseValues()->at(2)); + } + if (propertyTags & (1 << (kExternalMemoryPropertyTag_MemorySizeInKbytes - 1))) + { + Log::info(" Total Size = %s", utils::scale_bytes(getQspi.getResponseValues()->at(3) * 1024).c_str()); + } + if (propertyTags & (1 << (kExternalMemoryPropertyTag_PageSize - 1))) + { + Log::info(" Page Size = %s", utils::scale_bytes(getQspi.getResponseValues()->at(4)).c_str()); + } + if (propertyTags & (1 << (kExternalMemoryPropertyTag_SectorSize - 1))) + { + Log::info(" Sector Size = %s", utils::scale_bytes(getQspi.getResponseValues()->at(5)).c_str()); + } + if (propertyTags & (1 << (kExternalMemoryPropertyTag_BlockSize - 1))) + { + Log::info(" Block Size = %s", utils::scale_bytes(getQspi.getResponseValues()->at(6)).c_str()); + } + Log::info("\n"); + } + } + else + { + for (MemoryArray::const_iterator it = kMemories.begin(); it != kMemories.end(); ++it) + { + GetProperty getExernalAttri(kProperty_ExernalMemoryAttributes, it->memoryId); + getExernalAttri.sendTo(device); + fw_status = getExernalAttri.getResponseValues()->at(0); + if (fw_status == kStatus_UnknownProperty) + { + // UnknownProperty means that no external memories are supported by current device. + break; + } + else if (fw_status == kStatus_InvalidArgument) + { + // Current memory type is not supported by the device, skip to next external memory. + continue; + } + // Only un-configured QSPI will return this status. + else if (fw_status == 405 /*kStatus_QspiNotConfigured*/) + { + Log::info("%s:\n %s\n", it->description, getStatusMessage(fw_status).c_str()); + continue; + } + // Other un-configured external memory will return this status. + else if (fw_status == kStatusMemoryNotConfigured) + { + Log::info("%s:\n %s\n", it->description, getStatusMessage(fw_status).c_str()); + continue; + } + else if (fw_status != kStatus_Success) + { + // Failed to get property of the device memory. + m_responseValues.push_back(fw_status); + return; + } + else // fw_status == kStatus_Success + { + uint32_t propertyTags = getExernalAttri.getResponseValues()->at(1); + Log::info("%s:\n", it->description); + if (propertyTags & (1 << (kExternalMemoryPropertyTag_StartAddress - 1))) + { + Log::info(" Start Address = 0x%08x", getExernalAttri.getResponseValues()->at(2)); + } + if (propertyTags & (1 << (kExternalMemoryPropertyTag_MemorySizeInKbytes - 1))) + { + Log::info(" Total Size = %s", + utils::scale_bytes(getExernalAttri.getResponseValues()->at(3) * 1024).c_str()); + } + if (propertyTags & (1 << (kExternalMemoryPropertyTag_PageSize - 1))) + { + Log::info(" Page Size = %s", + utils::scale_bytes(getExernalAttri.getResponseValues()->at(4)).c_str()); + } + if (propertyTags & (1 << (kExternalMemoryPropertyTag_SectorSize - 1))) + { + Log::info(" Sector Size = %s", + utils::scale_bytes(getExernalAttri.getResponseValues()->at(5)).c_str()); + } + if (propertyTags & (1 << (kExternalMemoryPropertyTag_BlockSize - 1))) + { + Log::info(" Block Size = %s", + utils::scale_bytes(getExernalAttri.getResponseValues()->at(6)).c_str()); + } + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/Crc.cpp b/src/blfwk/src/Crc.cpp new file mode 100644 index 0000000..f58cff5 --- /dev/null +++ b/src/blfwk/src/Crc.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/Crc.h" +#include "property/property.h" +#include "crc/crc32.h" + +using namespace blfwk; + +// See Crc.h for documentation of this method. +uint32_t Crc::calculate_application_crc32(const uint8_t *start, uint32_t length) +{ + uint32_t crc32; + + // Initialize the CRC32 information + crc32_data_t crcInfo; + crc32_init(&crcInfo); + + // Run CRC, Considering skip crcExpectedValue address + const bootloader_configuration_data_t *config = + NULL; // Just used for calculate the offset, must not change the region it points to. + uint32_t bypassStartAddress = 0x3C0 + ((uint32_t)&config->crcExpectedValue - (uint32_t)&config->tag); + uint32_t bypassEndAddress = bypassStartAddress + sizeof(config->crcExpectedValue); + + if (length <= bypassStartAddress) + { + crc32_update(&crcInfo, (uint8_t *)start, length); + } + else + { + // Assume that crcExpectedValue address (4 byte) resides in crc addresses completely + crc32_update(&crcInfo, (uint8_t *)start, bypassStartAddress); + crc32_update(&crcInfo, (uint8_t *)(start + bypassEndAddress), length - bypassEndAddress); + } + + // Finalize the CRC calculations + crc32_finalize(&crcInfo, &crc32); + + return crc32; +} diff --git a/src/blfwk/src/DataSource.cpp b/src/blfwk/src/DataSource.cpp new file mode 100644 index 0000000..835c4ef --- /dev/null +++ b/src/blfwk/src/DataSource.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include "blfwk/DataSource.h" +#include "blfwk/DataTarget.h" + +using namespace blfwk; + +#pragma warning(disable : 4068) /* disable unknown pragma warnings */ +#pragma mark *** DataSource::PatternSegment *** + +DataSource::PatternSegment::PatternSegment(DataSource &source) + : DataSource::Segment(source) + , m_pattern() +{ +} + +DataSource::PatternSegment::PatternSegment(DataSource &source, const SizedIntegerValue &pattern) + : DataSource::Segment(source) + , m_pattern(pattern) +{ +} + +DataSource::PatternSegment::PatternSegment(DataSource &source, uint8_t pattern) + : DataSource::Segment(source) + , m_pattern(static_cast(pattern)) +{ +} + +DataSource::PatternSegment::PatternSegment(DataSource &source, uint16_t pattern) + : DataSource::Segment(source) + , m_pattern(static_cast(pattern)) +{ +} + +DataSource::PatternSegment::PatternSegment(DataSource &source, uint32_t pattern) + : DataSource::Segment(source) + , m_pattern(static_cast(pattern)) +{ +} + +unsigned DataSource::PatternSegment::getData(unsigned offset, unsigned maxBytes, uint8_t *buffer) +{ + memset(buffer, 0, maxBytes); + + return maxBytes; +} + +//! The pattern segment's length is a function of the data target. If the +//! target is bounded, then the segment's length is simply the target's +//! length. Otherwise, if no target has been set or the target is unbounded, +//! then the length returned is 0. +unsigned DataSource::PatternSegment::getLength() +{ + DataTarget *target = m_source.getTarget(); + if (!target) + { + return 0; + } + + uint32_t length; + if (target->isBounded()) + { + length = target->getEndAddress() - target->getBeginAddress(); + } + else + { + length = m_pattern.getSize(); + } + return length; +} + +#pragma mark *** PatternSource *** + +PatternSource::PatternSource() + : DataSource() + , DataSource::PatternSegment((DataSource &)*this) +{ +} + +PatternSource::PatternSource(const SizedIntegerValue &value) + : DataSource() + , DataSource::PatternSegment((DataSource &)*this, value) +{ +} + +#pragma mark *** UnmappedDataSource *** + +UnmappedDataSource::UnmappedDataSource() + : DataSource() + , DataSource::Segment((DataSource &)*this) + , m_data() + , m_length(0) +{ +} + +UnmappedDataSource::UnmappedDataSource(const uint8_t *data, unsigned length) + : DataSource() + , DataSource::Segment((DataSource &)*this) + , m_data() + , m_length(0) +{ + setData(data, length); +} + +//! Makes a copy of \a data that is freed when the data source is +//! destroyed. The caller does not have to maintain \a data after this call +//! returns. +void UnmappedDataSource::setData(const uint8_t *data, unsigned length) +{ + m_data.safe_delete(); + + uint8_t *dataCopy = new uint8_t[length]; + memcpy(dataCopy, data, length); + m_data = dataCopy; + m_length = length; +} + +unsigned UnmappedDataSource::getData(unsigned offset, unsigned maxBytes, uint8_t *buffer) +{ + assert(offset < m_length); + unsigned copyBytes = std::min(m_length - offset, maxBytes); + memcpy(buffer, &m_data[offset], copyBytes); + return copyBytes; +} + +//! The unmapped datasource as segment's base address is a function of the data target. +//! If the target is not NULL, then the segment's base address is simply the target's +//! beginAddress. Otherwise, if no target has been set, then the base address returned is 0. +uint32_t UnmappedDataSource::getBaseAddress() +{ + return getTarget() == NULL ? 0 : getTarget()->getBeginAddress(); +} + +#pragma mark *** MemoryImageDataSource *** + +MemoryImageDataSource::MemoryImageDataSource(StExecutableImage *image) + : DataSource() + , m_image(image) +{ + // reserve enough room for all segments + m_segments.reserve(m_image->getRegionCount()); +} + +MemoryImageDataSource::~MemoryImageDataSource() +{ + segment_array_t::iterator it = m_segments.begin(); + for (; it != m_segments.end(); ++it) + { + // delete this segment if it has been created + if (*it) + { + delete *it; + } + } +} + +unsigned MemoryImageDataSource::getSegmentCount() +{ + return m_image->getRegionCount(); +} + +DataSource::Segment *MemoryImageDataSource::getSegmentAt(unsigned index) +{ + // return previously created segment + if (index < m_segments.size() && m_segments[index]) + { + return m_segments[index]; + } + + // extend array out to this index + if (index >= m_segments.size() && index < m_image->getRegionCount()) + { + m_segments.resize(index + 1, NULL); + } + + // create the new segment object + DataSource::Segment *newSegment; + const StExecutableImage::MemoryRegion ®ion = m_image->getRegionAtIndex(index); + if (region.m_type == StExecutableImage::TEXT_REGION) + { + newSegment = new TextSegment(*this, m_image, index); + } + else if (region.m_type == StExecutableImage::FILL_REGION) + { + newSegment = new FillSegment(*this, m_image, index); + } + else + { + newSegment = NULL; + } + + m_segments[index] = newSegment; + return newSegment; +} + +#pragma mark *** MemoryImageDataSource::TextSegment *** + +MemoryImageDataSource::TextSegment::TextSegment(MemoryImageDataSource &source, StExecutableImage *image, unsigned index) + : DataSource::Segment(source) + , m_image(image) + , m_index(index) +{ +} + +unsigned MemoryImageDataSource::TextSegment::getData(unsigned offset, unsigned maxBytes, uint8_t *buffer) +{ + const StExecutableImage::MemoryRegion ®ion = m_image->getRegionAtIndex(m_index); + assert(region.m_type == StExecutableImage::TEXT_REGION); + + unsigned copyBytes = std::min(region.m_length - offset, maxBytes); + memcpy(buffer, ®ion.m_data[offset], copyBytes); + + return copyBytes; +} + +unsigned MemoryImageDataSource::TextSegment::getLength() +{ + const StExecutableImage::MemoryRegion ®ion = m_image->getRegionAtIndex(m_index); + return region.m_length; +} + +uint32_t MemoryImageDataSource::TextSegment::getBaseAddress() +{ + const StExecutableImage::MemoryRegion ®ion = m_image->getRegionAtIndex(m_index); + return region.m_address; +} + +#pragma mark *** MemoryImageDataSource::FillSegment *** + +MemoryImageDataSource::FillSegment::FillSegment(MemoryImageDataSource &source, StExecutableImage *image, unsigned index) + : DataSource::PatternSegment(source) + , m_image(image) + , m_index(index) +{ + SizedIntegerValue zero(0, kWordSize); + setPattern(zero); +} + +unsigned MemoryImageDataSource::FillSegment::getLength() +{ + const StExecutableImage::MemoryRegion ®ion = m_image->getRegionAtIndex(m_index); + return region.m_length; +} + +uint32_t MemoryImageDataSource::FillSegment::getBaseAddress() +{ + const StExecutableImage::MemoryRegion ®ion = m_image->getRegionAtIndex(m_index); + return region.m_address; +} diff --git a/src/blfwk/src/DataSourceImager.cpp b/src/blfwk/src/DataSourceImager.cpp new file mode 100644 index 0000000..0a1468b --- /dev/null +++ b/src/blfwk/src/DataSourceImager.cpp @@ -0,0 +1,142 @@ +/* + * File: DataSourceImager.cpp + * + * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. + * See included license file for license details. + */ + +#include "blfwk/DataSourceImager.h" +#include +#include + +using namespace blfwk; + +DataSourceImager::DataSourceImager() + : Blob() + , m_fill(0) + , m_baseAddress(0) + , m_isBaseAddressSet(false) +{ +} + +void DataSourceImager::setBaseAddress(uint32_t address) +{ + m_baseAddress = address; + m_isBaseAddressSet = true; +} + +void DataSourceImager::setFillPattern(uint8_t pattern) +{ + m_fill = pattern; +} + +void DataSourceImager::reset() +{ + clear(); + + m_fill = 0; + m_baseAddress = 0; + m_isBaseAddressSet = false; +} + +//! \param dataSource Pointer to an instance of a concrete data source subclass. +//! +void DataSourceImager::addDataSource(DataSource *source) +{ + unsigned segmentCount = source->getSegmentCount(); + unsigned index = 0; + for (; index < segmentCount; ++index) + { + addDataSegment(source->getSegmentAt(index)); + } +} + +//! \param segment The segment to add. May be any type of data segment, including +//! a pattern segment. +void DataSourceImager::addDataSegment(DataSource::Segment *segment) +{ + DataSource::PatternSegment *patternSegment = dynamic_cast(segment); + + unsigned segmentLength = segment->getLength(); + bool segmentHasLocation = segment->hasNaturalLocation(); + uint32_t segmentAddress = segment->getBaseAddress(); + + uint8_t *toPtr = NULL; + unsigned addressDelta; + unsigned newLength; + + // If a pattern segment's length is 0 then make it as big as the fill pattern. + // This needs to be done before the buffer is adjusted. + if (patternSegment && segmentLength == 0) + { + SizedIntegerValue &pattern = patternSegment->getPattern(); + segmentLength = pattern.getSize(); + } + + if (segmentLength) + { + if (segmentHasLocation) + { + // Make sure a base address is set. + if (!m_isBaseAddressSet) + { + m_baseAddress = segmentAddress; + m_isBaseAddressSet = true; + } + + // The segment is located before our buffer's first address. + // toPtr is not set in this if, but in the else branch of the next if. + // Unless the segment completely overwrite the current data. + if (segmentAddress < m_baseAddress) + { + addressDelta = m_baseAddress - segmentAddress; + + uint8_t *newData = (uint8_t *)malloc(m_length + addressDelta); + memcpy(&newData[addressDelta], m_data, m_length); + free(m_data); + + m_data = newData; + m_length += addressDelta; + m_baseAddress = segmentAddress; + } + + // This segment is located or extends outside of our buffer. + if (segmentAddress + segmentLength > m_baseAddress + m_length) + { + newLength = segmentAddress + segmentLength - m_baseAddress; + + m_data = (uint8_t *)realloc(m_data, newLength); + + // Clear any bytes between the old data and the new segment. + addressDelta = segmentAddress - (m_baseAddress + m_length); + if (addressDelta) + { + memset(m_data + m_length, 0, addressDelta); + } + + toPtr = m_data + (segmentAddress - m_baseAddress); + m_length = newLength; + } + else + { + toPtr = m_data + (segmentAddress - m_baseAddress); + } + } + // Segment has no natural location, so just append it to the end of our buffer. + else + { + newLength = m_length + segmentLength; + m_data = (uint8_t *)realloc(m_data, newLength); + toPtr = m_data + m_length; + m_length = newLength; + } + } + + // A loop is used because getData() may fill in less than the requested + // number of bytes per call. + unsigned offset = 0; + while (offset < segmentLength) + { + offset += segment->getData(offset, segmentLength - offset, toPtr + offset); + } +} diff --git a/src/blfwk/src/DataTarget.cpp b/src/blfwk/src/DataTarget.cpp new file mode 100644 index 0000000..328e03a --- /dev/null +++ b/src/blfwk/src/DataTarget.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/DataTarget.h" +#include "blfwk/DataSource.h" +#include "blfwk/BlfwkErrors.h" +#include + +using namespace blfwk; + +//! \exception blfwk::semantic_error Thrown if the source has multiple segments. +DataTarget::AddressRange ConstantDataTarget::getRangeForSegment(DataSource &source, DataSource::Segment &segment) +{ + // can't handle multi-segment data sources + if (source.getSegmentCount() > 1) + { + throw semantic_error("constant targets only support single-segment sources"); + } + + // always relocate the segment to our begin address + AddressRange range; + range.m_begin = m_begin; + + if (isBounded()) + { + // we have an end address. trim the result range to the segment size + // or let the end address crop the segment. + range.m_end = std::min(m_end, m_begin + segment.getLength()); + } + else + { + // we have no end address, so the segment size determines it. + range.m_end = m_begin + segment.getLength(); + } + + return range; +} + +//! If the \a segment has a natural location, the returned address range extends +//! from the segment's base address to its base address plus its length. +//! +//! \exception blfwk::semantic_error This exception is thrown if the \a segment +//! does not have a natural location associated with it. +DataTarget::AddressRange NaturalDataTarget::getRangeForSegment(DataSource &source, DataSource::Segment &segment) +{ + if (!segment.hasNaturalLocation()) + { + throw semantic_error("source has no natural location"); + } + + AddressRange range; + range.m_begin = segment.getBaseAddress(); + range.m_end = segment.getBaseAddress() + segment.getLength(); + return range; +} diff --git a/src/blfwk/src/ELFSourceFile.cpp b/src/blfwk/src/ELFSourceFile.cpp new file mode 100644 index 0000000..88997c1 --- /dev/null +++ b/src/blfwk/src/ELFSourceFile.cpp @@ -0,0 +1,627 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include "blfwk/ELFSourceFile.h" +#include "blfwk/GHSSecInfo.h" +#include "blfwk/Logging.h" +#ifdef LINUX +#include +#endif + +//! The name of the toolset option. +#define kToolsetOptionName "toolset" +#define kGHSToolsetName "GHS" +#define kGCCToolsetName "GCC" +#define kGNUToolsetName "GNU" +#define kADSToolsetName "ADS" + +//! Name of the option to control .secinfo action. +#define kSecinfoClearOptionName "secinfoClear" +#define kSecinfoDefaultName "DEFAULT" +#define kSecinfoIgnoreName "IGNORE" +#define kSecinfoROMName "ROM" +#define kSecinfoCName "C" + +using namespace blfwk; + +ELFSourceFile::ELFSourceFile(const std::string &path) + : SourceFile(path, kELFSourceFile) + , m_toolset(kUnknownToolset) + , m_secinfoOption(kSecinfoDefault) +{ +} + +ELFSourceFile::~ELFSourceFile() {} + +bool ELFSourceFile::isELFFile(std::istream &stream) +{ + try + { + StELFFile elf(stream); + return true; + } + catch (...) + { + return false; + } +} + +void ELFSourceFile::open() +{ + // Read toolset option + m_toolset = readToolsetOption(); + + // Read option and select default value + m_secinfoOption = readSecinfoClearOption(); + if (m_secinfoOption == kSecinfoDefault) + { + m_secinfoOption = kSecinfoCStartupClear; + } + + // Open the stream + SourceFile::open(); + + m_file = new StELFFile(*m_stream); + // m_file->dumpSections(); + + // Set toolset in elf file object + switch (m_toolset) + { + // default toolset is GHS + case kGHSToolset: + case kUnknownToolset: + m_file->setELFVariant(eGHSVariant); + break; + case kGCCToolset: + m_file->setELFVariant(eGCCVariant); + break; + case kADSToolset: + m_file->setELFVariant(eARMVariant); + break; + } +} + +void ELFSourceFile::close() +{ + SourceFile::close(); + + m_file.safe_delete(); +} + +elf_toolset_t ELFSourceFile::readToolsetOption() +{ + do + { + const OptionContext *options = getOptions(); + if (!options || !options->hasOption(kToolsetOptionName)) + { + break; + } + + const Value *value = options->getOption(kToolsetOptionName); + const StringValue *stringValue = dynamic_cast(value); + if (!stringValue) + { + // Not a string value, warn the user. + Log::log(Logger::kWarning, "invalid type for 'toolset' option\n"); + break; + } + + std::string toolsetName = *stringValue; + + // convert option value to uppercase + std::transform( + toolsetName.begin(), toolsetName.end(), toolsetName.begin(), toupper); + + if (toolsetName == kGHSToolsetName) + { + return kGHSToolset; + } + else if (toolsetName == kGCCToolsetName || toolsetName == kGNUToolsetName) + { + return kGCCToolset; + } + else if (toolsetName == kADSToolsetName) + { + return kADSToolset; + } + + // Unrecognized option value, log a warning. + Log::log(Logger::kWarning, "unrecognized value for 'toolset' option\n"); + } while (0); + + return kUnknownToolset; +} + +//! It is up to the caller to convert from kSecinfoDefault to the actual default +//! value. +secinfo_clear_t ELFSourceFile::readSecinfoClearOption() +{ + do + { + const OptionContext *options = getOptions(); + if (!options || !options->hasOption(kSecinfoClearOptionName)) + { + break; + } + + const Value *value = options->getOption(kSecinfoClearOptionName); + const StringValue *stringValue = dynamic_cast(value); + if (!stringValue) + { + // Not a string value, warn the user. + Log::log(Logger::kWarning, "invalid type for 'secinfoClear' option\n"); + break; + } + + std::string secinfoOption = *stringValue; + + // convert option value to uppercase + std::transform( + secinfoOption.begin(), secinfoOption.end(), secinfoOption.begin(), toupper); + + if (secinfoOption == kSecinfoDefaultName) + { + return kSecinfoDefault; + } + else if (secinfoOption == kSecinfoIgnoreName) + { + return kSecinfoIgnore; + } + else if (secinfoOption == kSecinfoROMName) + { + return kSecinfoROMClear; + } + else if (secinfoOption == kSecinfoCName) + { + return kSecinfoCStartupClear; + } + + // Unrecognized option value, log a warning. + Log::log(Logger::kWarning, "unrecognized value for 'secinfoClear' option\n"); + } while (0); + + return kSecinfoDefault; +} + +//! To create a data source for all sections of the ELF file, a WildcardMatcher +//! is instantiated and passed to createDataSource(StringMatcher&). +DataSource *ELFSourceFile::createDataSource() +{ + WildcardMatcher matcher; + return createDataSource(matcher); +} + +DataSource *ELFSourceFile::createDataSource(const std::vector &baseAddresses, bool match) +{ + assert(m_file); + ELFDataSource *source = new ELFDataSource(m_file); + source->setSecinfoOption(m_secinfoOption); + + if (match) + { + Log::log(Logger::kDebug2, "filtering sections of file that match base addresses: 0x%08X\n", baseAddresses[0]); + for (unsigned i = 1; i < baseAddresses.size(); i++) + { + Log::log(Logger::kDebug2, " 0x%08X\n", + baseAddresses[i]); + } + } + else + { + Log::log(Logger::kDebug2, "filtering sections of file that exclude base addresses: 0x%08X\n", baseAddresses[0]); + for (unsigned i = 1; i < baseAddresses.size(); i++) + { + Log::log(Logger::kDebug2, " 0x%08X\n", + baseAddresses[i]); + } + } + + try + { + // We start at section 1 to skip the null section that is always first. + unsigned index = 1; + for (; index < m_file->getSectionCount(); ++index) + { + const Elf32_Shdr &header = m_file->getSectionAtIndex(index); + std::string name = m_file->getSectionNameAtIndex(header.sh_name); + + // Ignore most section types + if (!(header.sh_type == SHT_PROGBITS || header.sh_type == SHT_NOBITS)) + { + continue; + } + + // Ignore sections that don't have the allocate flag set. + if ((header.sh_flags & SHF_ALLOC) == 0) + { + continue; + } + + bool contains = + std::find(baseAddresses.begin(), baseAddresses.end(), header.sh_addr) != baseAddresses.end(); + + // If we are not matching the supplied address then we will add any section that does not have the + // baseAddress If we are matching we will only include the section that matches the baseAddress + if ((!match && !contains) || (match && contains)) + { + Log::log(Logger::kDebug2, "creating segment for section %s with base address = 0x%08X\n", name.c_str(), + header.sh_addr); + source->addSection(index); + } + else + { + Log::log(Logger::kDebug2, "section %s with base address of 0x%08X is excluded\n", name.c_str(), + header.sh_addr); + } + } + } + catch (...) + { + delete source; + throw; + } + + Log::log(Logger::kDebug2, "\n"); + + return source; +} + +DataSource *ELFSourceFile::createDataSource(StringMatcher &matcher) +{ + assert(m_file); + ELFDataSource *source = new ELFDataSource(m_file); + source->setSecinfoOption(m_secinfoOption); + + Log::log(Logger::kDebug2, "filtering sections of file: %s\n", getPath().c_str()); + try + { + // We start at section 1 to skip the null section that is always first. + unsigned index = 1; + for (; index < m_file->getSectionCount(); ++index) + { + const Elf32_Shdr &header = m_file->getSectionAtIndex(index); + std::string name = m_file->getSectionNameAtIndex(header.sh_name); + + // Ignore most section types + if (!(header.sh_type == SHT_PROGBITS || header.sh_type == SHT_NOBITS)) + { + continue; + } + + // Ignore sections that don't have the allocate flag set. + if ((header.sh_flags & SHF_ALLOC) == 0) + { + continue; + } + + if (matcher.match(name)) + { + Log::log(Logger::kDebug2, "creating segment for section %s\n", name.c_str()); + source->addSection(index); + } + else + { + Log::log(Logger::kDebug2, "section %s did not match\n", name.c_str()); + } + } + } + catch (...) + { + delete source; + throw; + } + + return source; +} + +//! It is assumed that all ELF files have an entry point. +//! +bool ELFSourceFile::hasEntryPoint() +{ + return true; +} + +//! The StELFFile::getTypeOfSymbolAtIndex() method uses different methods of determining +//! ARM/Thumb mode depending on the toolset. +uint32_t ELFSourceFile::getEntryPointAddress() +{ + uint32_t entryPoint = 0; + + // get entry point address + const Elf32_Ehdr &header = m_file->getFileHeader(); + + // find symbol corresponding to entry point and determine if + // it is arm or thumb mode + unsigned symbolIndex = m_file->getIndexOfSymbolAtAddress(header.e_entry); + if (symbolIndex != 0) + { + ARMSymbolType_t symbolType = m_file->getTypeOfSymbolAtIndex(symbolIndex); + bool entryPointIsThumb = (symbolType == eThumbSymbol); + const Elf32_Sym &symbol = m_file->getSymbolAtIndex(symbolIndex); + std::string symbolName = m_file->getSymbolName(symbol); + + Log::log(Logger::kDebug2, "Entry point is %s@0x%08x (%s)\n", symbolName.c_str(), symbol.st_value, + entryPointIsThumb ? "Thumb" : "ARM"); + + // set entry point, setting the low bit if it is thumb mode + entryPoint = header.e_entry + (entryPointIsThumb ? 1 : 0); + } + else + { + entryPoint = header.e_entry; + } + + return entryPoint; +} + +//! \return A DataTarget that describes the named section. +//! \retval NULL There was no section with the requested name. +DataTarget *ELFSourceFile::createDataTargetForSection(const std::string §ion) +{ + assert(m_file); + unsigned index = m_file->getIndexOfSectionWithName(section); + if (index == SHN_UNDEF) + { + return NULL; + } + + const Elf32_Shdr §ionHeader = m_file->getSectionAtIndex(index); + uint32_t beginAddress = sectionHeader.sh_addr; + uint32_t endAddress = beginAddress + sectionHeader.sh_size; + ConstantDataTarget *target = new ConstantDataTarget(beginAddress, endAddress); + return target; +} + +//! \return A DataTarget instance pointing at the requested symbol. +//! \retval NULL No symbol matching the requested name was found. +DataTarget *ELFSourceFile::createDataTargetForSymbol(const std::string &symbol) +{ + assert(m_file); + unsigned symbolCount = m_file->getSymbolCount(); + unsigned i; + + for (i = 0; i < symbolCount; ++i) + { + const Elf32_Sym &symbolHeader = m_file->getSymbolAtIndex(i); + std::string symbolName = m_file->getSymbolName(symbolHeader); + if (symbolName == symbol) + { + ARMSymbolType_t symbolType = m_file->getTypeOfSymbolAtIndex(i); + bool symbolIsThumb = (symbolType == eThumbSymbol); + + uint32_t beginAddress = symbolHeader.st_value + (symbolIsThumb ? 1 : 0); + uint32_t endAddress = beginAddress + symbolHeader.st_size; + ConstantDataTarget *target = new ConstantDataTarget(beginAddress, endAddress); + return target; + } + } + + // didn't find a matching symbol + return NULL; +} + +bool ELFSourceFile::hasSymbol(const std::string &name) +{ + Elf32_Sym symbol; + return lookupSymbol(name, symbol); +} + +uint32_t ELFSourceFile::getSymbolValue(const std::string &name) +{ + unsigned symbolCount = m_file->getSymbolCount(); + unsigned i; + + for (i = 0; i < symbolCount; ++i) + { + const Elf32_Sym &symbolHeader = m_file->getSymbolAtIndex(i); + std::string symbolName = m_file->getSymbolName(symbolHeader); + if (symbolName == name) + { + // If the symbol is a function, then we check to see if it is Thumb code and set bit 0 if so. + if (ELF32_ST_TYPE(symbolHeader.st_info) == STT_FUNC) + { + ARMSymbolType_t symbolType = m_file->getTypeOfSymbolAtIndex(i); + bool symbolIsThumb = (symbolType == eThumbSymbol); + return symbolHeader.st_value + (symbolIsThumb ? 1 : 0); + } + else + { + return symbolHeader.st_value; + } + } + } + + // Couldn't find the symbol, so return 0. + return 0; +} + +unsigned ELFSourceFile::getSymbolSize(const std::string &name) +{ + Elf32_Sym symbol; + if (!lookupSymbol(name, symbol)) + { + return 0; + } + + return symbol.st_size; +} + +//! \param name The name of the symbol on which info is wanted. +//! \param[out] info Upon succssful return this is filled in with the symbol's information. +//! +//! \retval true The symbol was found and \a info is valid. +//! \retval false No symbol with \a name was found in the file. +bool ELFSourceFile::lookupSymbol(const std::string &name, Elf32_Sym &info) +{ + assert(m_file); + unsigned symbolCount = m_file->getSymbolCount(); + unsigned i; + + for (i = 0; i < symbolCount; ++i) + { + const Elf32_Sym &symbol = m_file->getSymbolAtIndex(i); + std::string thisSymbolName = m_file->getSymbolName(symbol); + + // Is this the symbol we're looking for? + if (thisSymbolName == name) + { + info = symbol; + return true; + } + } + + // Didn't file the symbol. + return false; +} + +ELFSourceFile::ELFDataSource::~ELFDataSource() +{ + segment_vector_t::iterator it = m_segments.begin(); + for (; it != m_segments.end(); ++it) + { + delete *it; + } +} + +//! Not all sections will actually result in a new segment being created. Only +//! those sections whose type is #SHT_PROGBITS or #SHT_NOBITS will create +//! a new segment. Also, only sections whose size is non-zero will actually +//! create a segment. +//! +//! In addition to this, ELF files that have been marked as being created by +//! the Green Hills Software toolset have an extra step. #SHT_NOBITS sections +//! are looked up in the .secinfo section to determine if they really +//! should be filled. If not in the .secinfo table, no segment will be +//! created for the section. +void ELFSourceFile::ELFDataSource::addSection(unsigned sectionIndex) +{ + // get section info + const Elf32_Shdr §ion = m_elf->getSectionAtIndex(sectionIndex); + if (section.sh_size == 0) + { + // empty section, so ignore it + return; + } + + // create the right segment subclass based on the section type + DataSource::Segment *segment = NULL; + if (section.sh_type == SHT_PROGBITS) + { + segment = new ProgBitsSegment(*this, m_elf, sectionIndex); + } + else if (section.sh_type == SHT_NOBITS) + { + // Always add NOBITS sections by default. + bool addNobits = true; + + // For GHS ELF files, we use the secinfoClear option to figure out what to do. + // If set to ignore, treat like a normal ELF file and always add. If set to + // ROM, then only clear if the section is listed in .secinfo. Otherwise if set + // to C startup, then let the C startup do all clearing. + if (m_elf->ELFVariant() == eGHSVariant) + { + GHSSecInfo secinfo(m_elf); + + // If there isn't a .secinfo section present then use the normal ELF rules + // and always add NOBITS sections. + if (secinfo.hasSecinfo() && m_secinfoOption != kSecinfoIgnore) + { + switch (m_secinfoOption) + { + case kSecinfoROMClear: + addNobits = secinfo.isSectionFilled(section); + break; + + case kSecinfoCStartupClear: + addNobits = false; + break; + + default: + // Do nothing. + break; + } + } + } + + if (addNobits) + { + segment = new NoBitsSegment(*this, m_elf, sectionIndex); + } + else + { + std::string name = m_elf->getSectionNameAtIndex(section.sh_name); + Log::log(Logger::kDebug2, "..section %s is not filled\n", name.c_str()); + } + } + + // add segment if one was created + if (segment) + { + m_segments.push_back(segment); + } +} + +ELFSourceFile::ELFDataSource::ProgBitsSegment::ProgBitsSegment(ELFDataSource &source, StELFFile *elf, unsigned index) + : DataSource::Segment(source) + , m_elf(elf) + , m_sectionIndex(index) +{ +} + +unsigned ELFSourceFile::ELFDataSource::ProgBitsSegment::getData(unsigned offset, unsigned maxBytes, uint8_t *buffer) +{ + const Elf32_Shdr §ion = m_elf->getSectionAtIndex(m_sectionIndex); + uint8_t *data = m_elf->getSectionDataAtIndex(m_sectionIndex); + + assert(offset < section.sh_size); + + unsigned copyBytes = std::min(section.sh_size - offset, maxBytes); + if (copyBytes) + { + memcpy(buffer, &data[offset], copyBytes); + } + delete[] data; + return copyBytes; +} + +unsigned ELFSourceFile::ELFDataSource::ProgBitsSegment::getLength() +{ + const Elf32_Shdr §ion = m_elf->getSectionAtIndex(m_sectionIndex); + return section.sh_size; +} + +uint32_t ELFSourceFile::ELFDataSource::ProgBitsSegment::getBaseAddress() +{ + const Elf32_Shdr §ion = m_elf->getSectionAtIndex(m_sectionIndex); + return section.sh_addr; +} + +ELFSourceFile::ELFDataSource::NoBitsSegment::NoBitsSegment(ELFDataSource &source, StELFFile *elf, unsigned index) + : DataSource::PatternSegment(source) + , m_elf(elf) + , m_sectionIndex(index) +{ +} + +unsigned ELFSourceFile::ELFDataSource::NoBitsSegment::getLength() +{ + const Elf32_Shdr §ion = m_elf->getSectionAtIndex(m_sectionIndex); + return section.sh_size; +} + +uint32_t ELFSourceFile::ELFDataSource::NoBitsSegment::getBaseAddress() +{ + const Elf32_Shdr §ion = m_elf->getSectionAtIndex(m_sectionIndex); + return section.sh_addr; +} diff --git a/src/blfwk/src/ExcludesListMatcher.cpp b/src/blfwk/src/ExcludesListMatcher.cpp new file mode 100644 index 0000000..a31f005 --- /dev/null +++ b/src/blfwk/src/ExcludesListMatcher.cpp @@ -0,0 +1,87 @@ +/* + * File: ExcludesListMatcher.cpp + * + * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. + * See included license file for license details. + */ + +#include "blfwk/ExcludesListMatcher.h" + +using namespace blfwk; + +ExcludesListMatcher::ExcludesListMatcher() + : GlobMatcher("") +{ +} + +ExcludesListMatcher::~ExcludesListMatcher() +{ +} + +//! \param isInclude True if this pattern is an include, false if it is an exclude. +//! \param pattern String containing the glob pattern. +void ExcludesListMatcher::addPattern(bool isInclude, const std::string &pattern) +{ + glob_list_item_t item; + item.m_isInclude = isInclude; + item.m_glob = pattern; + + // add to end of list + m_patterns.push_back(item); +} + +//! If there are no entries in the match list, the match fails. +//! +//! \param testValue The string to match against the pattern list. +//! \retval true The \a testValue argument matches. +//! \retval false No match was made against the argument. +bool ExcludesListMatcher::match(const std::string &testValue) +{ + if (!m_patterns.size()) + { + return false; + } + + // Iterate over the match list. Includes act as an OR operator, while + // excludes act as an AND operator. + bool didMatch = false; + bool isFirstItem = true; + glob_list_t::iterator it = m_patterns.begin(); + for (; it != m_patterns.end(); ++it) + { + glob_list_item_t &item = *it; + + // if this pattern is an include and it doesn't match, or + // if this pattern is an exclude and it does match, then the match fails + bool didItemMatch = globMatch(testValue.c_str(), item.m_glob.c_str()); + + if (item.m_isInclude) + { + // Include + if (isFirstItem) + { + didMatch = didItemMatch; + } + else + { + didMatch = didMatch || didItemMatch; + } + } + else + { + // Exclude + if (isFirstItem) + { + didMatch = !didItemMatch; + } + else + { + didMatch = didMatch && !didItemMatch; + } + } + + isFirstItem = false; + } + + return didMatch; +} diff --git a/src/blfwk/src/GHSSecInfo.cpp b/src/blfwk/src/GHSSecInfo.cpp new file mode 100644 index 0000000..b9b671c --- /dev/null +++ b/src/blfwk/src/GHSSecInfo.cpp @@ -0,0 +1,103 @@ +/* + * File: GHSSecInfo.cpp + * + * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. + * See included license file for license details. + */ + +#include "blfwk/GHSSecInfo.h" +#include +#include "blfwk/Logging.h" +#include "blfwk/EndianUtilities.h" + +//! The name of the GHS-specific section info table ELF section. +const char *const kSecInfoSectionName = ".secinfo"; + +using namespace blfwk; + +//! The ELF file passed into this constructor as the \a elf argument must remain +//! valid for the life of this object. +//! +//! \param elf The ELF file parser. An assertion is raised if this is NULL. +GHSSecInfo::GHSSecInfo(StELFFile *elf) + : m_elf(elf) + , m_hasInfo(false) + , m_info(0) + , m_entryCount(0) +{ + assert(elf); + + // look up the section. if it's not there just leave m_info and m_entryCount to 0 + unsigned sectionIndex = m_elf->getIndexOfSectionWithName(kSecInfoSectionName); + if (sectionIndex == SHN_UNDEF) + { + return; + } + + // get the section data + const Elf32_Shdr &secInfo = m_elf->getSectionAtIndex(sectionIndex); + if (secInfo.sh_type != SHT_PROGBITS) + { + // .secinfo section isn't the right type, so something is wrong + return; + } + + m_hasInfo = true; + m_info = (ghs_secinfo_t *)m_elf->getSectionDataAtIndex(sectionIndex); + m_entryCount = secInfo.sh_size / sizeof(ghs_secinfo_t); +} + +//! Looks up \a addr for \a length in the .secinfo array. Only if that address is in the +//! .secinfo array does this section need to be filled. If the section is found but the +//! length does not match the \a length argument, a message is logged at the +//! #Logger::kWarning level. +//! +//! If the .secinfo section is not present in the ELF file, this method always returns +//! true. +//! +//! \param addr The start address of the section to query. +//! \param length The length of the section. If a section with a start address matching +//! \a addr is found, its length must match \a length to be considered. +//! +//! \retval true The section matching \a addr and \a length was found and should be filled. +//! True is also returned when the ELF file does not have a .secinfo section. +//! \retval false The section was not found and should not be filled. +bool GHSSecInfo::isSectionFilled(uint32_t addr, uint32_t length) +{ + if (!m_hasInfo) + { + return true; + } + + unsigned i; + for (i = 0; i < m_entryCount; ++i) + { + // byte swap these values into host endianness + uint32_t clearAddr = ENDIAN_LITTLE_TO_HOST_U32(m_info[i].m_clearAddr); + uint32_t numBytesToClear = ENDIAN_LITTLE_TO_HOST_U32(m_info[i].m_numBytesToClear); + + // we only consider non-zero length clear regions + if ((addr == clearAddr) && (numBytesToClear != 0)) + { + // it is an error if the address matches but the length does not + if (length != numBytesToClear) + { + Log::log(Logger::kWarning, "ELF Error: Size mismatch @ sect=%u, .secinfo=%u at addr 0x%08X\n", length, + numBytesToClear, addr); + } + return true; + } + } + + return false; +} + +//! Simply calls through to isSectionFilled(uint32_t, uint32_t) to determine +//! if \a section should be filled. +//! +//! If the .secinfo section is not present in the ELF file, this method always returns +//! true. +bool GHSSecInfo::isSectionFilled(const Elf32_Shdr §ion) +{ + return isSectionFilled(section.sh_addr, section.sh_size); +} diff --git a/src/blfwk/src/GlobMatcher.cpp b/src/blfwk/src/GlobMatcher.cpp new file mode 100644 index 0000000..9249cd4 --- /dev/null +++ b/src/blfwk/src/GlobMatcher.cpp @@ -0,0 +1,135 @@ +/* + * File: GlobMatcher.cpp + * + * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. + * See included license file for license details. + */ + +#include "blfwk/GlobMatcher.h" + +#ifndef NEGATE +#define NEGATE '^' // std cset negation char +#endif + +using namespace blfwk; + +//! The glob pattern must match the \e entire test value argument in order +//! for the match to be considered successful. Thus, even if, for example, +//! the pattern matches all but the last character the result will be false. +//! +//! \retval true The test value does match the glob pattern. +//! \retval false The test value does not match the glob pattern. +bool GlobMatcher::match(const std::string &testValue) +{ + return globMatch(testValue.c_str(), m_pattern.c_str()); +} + +//! \note This glob implementation was originally written by ozan s. yigit in +//! December 1994. This is public domain source code. +bool GlobMatcher::globMatch(const char *str, const char *p) +{ + int negate; + int match; + int c; + + while (*p) + { + if (!*str && *p != '*') + return false; + + switch (c = *p++) + { + case '*': + while (*p == '*') + p++; + + if (!*p) + return true; + + if (*p != '?' && *p != '[' && *p != '\\') + while (*str && *p != *str) + str++; + + while (*str) + { + if (globMatch(str, p)) + return true; + str++; + } + return false; + + case '?': + if (*str) + break; + return false; + + // set specification is inclusive, that is [a-z] is a, z and + // everything in between. this means [z-a] may be interpreted + // as a set that contains z, a and nothing in between. + case '[': + if (*p != NEGATE) + negate = false; + else + { + negate = true; + p++; + } + + match = false; + + while (!match && (c = *p++)) + { + if (!*p) + return false; + if (*p == '-') + { // c-c + if (!*++p) + return false; + if (*p != ']') + { + if (*str == c || *str == *p || (*str > c && *str < *p)) + match = true; + } + else + { // c-] + if (*str >= c) + match = true; + break; + } + } + else + { // cc or c] + if (c == *str) + match = true; + if (*p != ']') + { + if (*p == *str) + match = true; + } + else + break; + } + } + + if (negate == match) + return false; + // if there is a match, skip past the cset and continue on + while (*p && *p != ']') + p++; + if (!*p++) // oops! + return false; + break; + + case '\\': + if (*p) + c = *p++; + default: + if (c != *str) + return false; + break; + } + str++; + } + + return !*str; +} diff --git a/src/blfwk/src/HexValues.cpp b/src/blfwk/src/HexValues.cpp new file mode 100644 index 0000000..82dbba4 --- /dev/null +++ b/src/blfwk/src/HexValues.cpp @@ -0,0 +1,35 @@ +/* + * File: HexValues.cpp + * + * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. + * See included license file for license details. + */ + +#include "blfwk/HexValues.h" +#include + +bool isHexDigit(char c) +{ + return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + +//! \return The integer equivalent to \a c. +//! \retval -1 The character \a c is not a hex character. +uint8_t hexCharToInt(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else + return static_cast(-1); +} + +//! \param encodedByte Must point to at least two ASCII hex characters. +//! +uint8_t hexByteToInt(const char *encodedByte) +{ + return (hexCharToInt(encodedByte[0]) << 4) | hexCharToInt(encodedByte[1]); +} diff --git a/src/blfwk/src/I2cPeripheral.cpp b/src/blfwk/src/I2cPeripheral.cpp new file mode 100644 index 0000000..672f45a --- /dev/null +++ b/src/blfwk/src/I2cPeripheral.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include "blfwk/I2cPeripheral.h" +#include "blfwk/Logging.h" +#include "blfwk/format_string.h" +#include "blfwk/i2c.h" + +using namespace blfwk; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See I2cPeripheral.h for documentation of this method. +I2cPeripheral::I2cPeripheral(const char *port, long speed, uint8_t address) + : m_fileDescriptor(-1) +{ + if (!init(port, address, speed)) + { + throw std::runtime_error(format_string("Error: Cannot open I2C port(%s), speed(%d Hz).\n", port, speed)); + } +} + +// See I2cPeripheral.h for documentation of this method. +bool I2cPeripheral::init(const char *port, long speed, uint8_t address) +{ + assert(port); + + // Open the port. + m_fileDescriptor = i2c_open(const_cast(port)); + if (m_fileDescriptor == -1) + { + return false; + } + + i2c_setup(m_fileDescriptor, speed, address); + + // Flush garbage from receive buffer before setting read timeout. + flushRX(); + + // Set host serial timeout to 10 milliseconds. Higherlevel timeouts are implemented in + // SerialPacketizer.cpp + i2c_set_timeout(m_fileDescriptor, kI2cPeripheral_DefaultReadTimeoutMs); + + return true; +} + +// See I2cPeripheral.h for documentation of this method. +I2cPeripheral::~I2cPeripheral() +{ + if (m_fileDescriptor != -1) + { + i2c_close(m_fileDescriptor); + } +} + +// See I2cPeripheral.h for documentation of this method. +status_t I2cPeripheral::read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t unused_timeoutMs) +{ + assert(buffer); + + // Read the requested number of bytes. + int count = i2c_read(m_fileDescriptor, reinterpret_cast(buffer), requestedBytes); + if (actualBytes) + { + *actualBytes = count; + } + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes read in hex + Log::debug2("<"); + for (int i = 0; i < (int)count; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (count - 1)) + { + Log::debug2(" "); + } + } + Log::debug2(">\n"); + } + + if (count < (int)requestedBytes) + { + // Anything less than requestedBytes is a timeout error. + return kStatus_Timeout; + } + + return kStatus_Success; +} + +// See I2cPeripheral.h for documentation of this method. +void I2cPeripheral::flushRX() {} + +// See I2cPeripheral.h for documentation of this method. +status_t I2cPeripheral::write(const uint8_t *buffer, uint32_t byteCount) +{ + assert(buffer); + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes written in hex + Log::debug2("["); + for (int i = 0; i < (int)byteCount; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (byteCount - 1)) + { + Log::debug2(" "); + } + } + Log::debug2("]\n"); + } + + if (i2c_write(m_fileDescriptor, reinterpret_cast(const_cast(buffer)), byteCount) == byteCount) + return kStatus_Success; + else + return kStatus_Fail; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/IntelHexSourceFile.cpp b/src/blfwk/src/IntelHexSourceFile.cpp new file mode 100644 index 0000000..0ad4759 --- /dev/null +++ b/src/blfwk/src/IntelHexSourceFile.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "blfwk/smart_ptr.h" +#include "blfwk/IntelHexSourceFile.h" +#include "blfwk/Logging.h" + +enum +{ + //! Size in bytes of the buffer used to collect Intel Hex data records + //! before adding them to the executable image. + //! The address field in each record is 2 bytes, so set the buffer to 64KB. + COLLECTION_BUFFER_SIZE = 64 * 1024 +}; + +using namespace blfwk; + +IntelHexSourceFile::IntelHexSourceFile(const std::string &path) + : SourceFile(path, kIntelHexSourceFile) + , m_file(NULL) + , m_image(0) + , m_hasEntryRecord(false) +{ + memset(&m_entryRecord, 0, sizeof(m_entryRecord)); +} + +bool IntelHexSourceFile::isIntelHexFile(std::istream &stream) +{ + StIntelHexFile hex(stream); + return hex.isIntelHexFile(); +} + +void IntelHexSourceFile::open() +{ + SourceFile::open(); + + // create file parser and examine file + m_file = new StIntelHexFile(*m_stream); + m_file->parse(); + + // build an image of the file + m_image = new StExecutableImage(); + buildMemoryImage(); + + // dispose of file parser object + delete m_file; + m_file = 0; +} + +void IntelHexSourceFile::close() +{ + assert(m_image); + + SourceFile::close(); + + // dispose of memory image + delete m_image; + m_image = 0; +} + +//! \pre The file must be open before this method can be called. +//! +DataSource *IntelHexSourceFile::createDataSource() +{ + assert(m_image); + return new MemoryImageDataSource(m_image); +} + +//! \retval true The file has an 03 or 05 record. +//! \retval false No entry point is available. +bool IntelHexSourceFile::hasEntryPoint() +{ + return m_hasEntryRecord; +} + +//! If no entry point is available then 0 is returned instead. +//! +//! \return Entry point address. +//! \retval 0 No entry point is available. +uint32_t IntelHexSourceFile::getEntryPointAddress() +{ + if (m_hasEntryRecord) + { + // the address in the record is the entry point + uint32_t address = (m_entryRecord.m_data[0] << 24) | (m_entryRecord.m_data[1] << 16) | + (m_entryRecord.m_data[2] << 8) | (m_entryRecord.m_data[3]); + Log::log(Logger::kDebug2, "entry point address is 0x%08x\n", address); + return address; + } + + return 0; +} + +//! Scans the Intel Hex of the file looking for data records(00 records). The +//! contents of these records are added to an StExecutableImage object, which +//! coalesces the individual records into contiguous regions of memory. +//! +//! Also looks for 03 and 05 records that contain the entry point. The first +//! match of one of these records is saved off into the #m_entryRecord member. +//! +//! Also looks for 02 and 04 records that contain the extended address. The +//! address in the StExecutableImage object is the combination of extended +//! address and offset in data record. +//! +//! If 01 record, the file end record is detected, the left records would not +//! be handled. +//! +//! \pre The #m_file member must be valid. +//! \pre The #m_image member variable must have been instantiated. +void IntelHexSourceFile::buildMemoryImage() +{ + assert(m_file); + assert(m_image); + + // Clear the entry point. + m_hasEntryRecord = false; + memset(&m_entryRecord, 0, sizeof(m_entryRecord)); + + // Allocate buffer to hold data before adding it to the executable image. + // Contiguous records are added to this buffer. When overflowed or when a + // non-contiguous record is encountered the buffer is added to the executable + // image where it will be coalesced further. We don't add records individually + // to the image because coalescing record by record is very slow. + smart_array_ptr buffer = new uint8_t[COLLECTION_BUFFER_SIZE]; + + unsigned baseAddress = 0; + unsigned startAddress = 0; + unsigned nextAddress = 0; + unsigned dataLength = 0; + + // process IntelHexs + StIntelHexFile::const_iterator it = m_file->getBegin(); + for (; it != m_file->getEnd(); it++) + { + const StIntelHexFile::IntelHex &theRecord = *it; + + bool isDataRecord = theRecord.m_type == INTELHEX_RECORD_DATA; + bool isEntryRecord = (theRecord.m_type == INTELHEX_RECORD_START_SEGMENT_ADDRESS) || + (theRecord.m_type == INTELHEX_RECORD_START_LINEAR_ADDRESS); + bool isAddressRecord = (theRecord.m_type == INTELHEX_RECORD_EXTENDED_SEGMENT_ADDRESS) || + (theRecord.m_type == INTELHEX_RECORD_EXTENDED_LINEAR_ADDRESS); + bool isFileEndRecord = theRecord.m_type == INTELHEX_RECORD_END_OF_FILE; + // handle 00 data record + if (isDataRecord) + { + // If this record's data would overflow the collection buffer, or if the + // record is not contiguous with the rest of the data in the collection + // buffer, then flush the buffer to the executable image and restart. + if (dataLength && + ((dataLength + theRecord.m_dataCount > COLLECTION_BUFFER_SIZE) || (theRecord.m_address != nextAddress))) + { + m_image->addTextRegion(startAddress + baseAddress, buffer, dataLength); + + dataLength = 0; + } + + // Capture addresses when starting an empty buffer. + if (dataLength == 0) + { + startAddress = theRecord.m_address; + nextAddress = startAddress; + } + + // Copy record data into place in the collection buffer and update + // size and address. + memcpy(&buffer[dataLength], theRecord.m_data, theRecord.m_dataCount); + dataLength += theRecord.m_dataCount; + nextAddress += theRecord.m_dataCount; + } + // handle 02, 04 record + else if (isAddressRecord) + { + // If there are data in the collection buffer, then flush the buffer to the executable image. + if (dataLength != 0) + { + m_image->addTextRegion(startAddress + baseAddress, buffer, dataLength); + dataLength = 0; + } + + // extended address stored at data field. + baseAddress = (theRecord.m_data[0] << 8) | theRecord.m_data[1]; + if (theRecord.m_type == INTELHEX_RECORD_EXTENDED_SEGMENT_ADDRESS) + { + baseAddress <<= 4; // Extended Segment Address Record, left shift 4 bits. + } + else + { + baseAddress <<= 16; // Extended Linear Address Record, left shitf 16 bits. + } + } + // handle 03, 05 record + else if (isEntryRecord) + { + if (!m_hasEntryRecord) + { + // save off the entry point record so we don't have to scan again + memcpy(&m_entryRecord, &theRecord, sizeof(m_entryRecord)); + m_hasEntryRecord = true; + } + else + { + // throw an exception when detecting a second entry record + throw StIntelHexParseException("multiple entry record detected"); + } + } + // handle 01 record + else if (isFileEndRecord) + { + // if the file end record is detected, stop handling the records. + break; + } + } + + // Add any leftover data in the collection buffer to the executable image. + if (dataLength) + { + m_image->addTextRegion(startAddress + baseAddress, buffer, dataLength); + } +} diff --git a/src/blfwk/src/Logging.cpp b/src/blfwk/src/Logging.cpp new file mode 100644 index 0000000..701be85 --- /dev/null +++ b/src/blfwk/src/Logging.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include "blfwk/Logging.h" + +// init global logger to null +Logger *Log::s_logger = NULL; + +void Logger::log(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + log(m_level, fmt, args); + va_end(args); +} + +void Logger::log(log_level_t level, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + log(level, fmt, args); + va_end(args); +} + +void Logger::log(const char *fmt, va_list args) +{ + log(m_level, fmt, args); +} + +//! Allocates a temporary 1KB buffer which is used to hold the +//! formatted string. +void Logger::log(log_level_t level, const char *fmt, va_list args) +{ + if (level <= m_filter) + { + char *buffer = NULL; + +#if WIN32 + int l = _vscprintf(fmt, args); // Does not include final NULL char. + buffer = reinterpret_cast(malloc(l + 1)); + if (!buffer) + { + return; + } + vsprintf(buffer, fmt, args); +#else // WIN32 + vasprintf(&buffer, fmt, args); +#endif // WIN32 + + if (buffer) + { + _log(buffer); + free(buffer); + } + } +} + +void Log::log(const char *fmt, ...) +{ + if (s_logger) + { + va_list args; + va_start(args, fmt); + s_logger->log(fmt, args); + va_end(args); + } +} + +void Log::log(const std::string &msg) +{ + if (s_logger) + { + s_logger->log(msg); + } +} + +void Log::log(Logger::log_level_t level, const char *fmt, ...) +{ + if (s_logger) + { + va_list args; + va_start(args, fmt); + s_logger->log(level, fmt, args); + va_end(args); + } +} + +void Log::log(Logger::log_level_t level, const std::string &msg) +{ + if (s_logger) + { + s_logger->log(level, msg); + } +} + +void Log::urgent(const char *fmt, ...) +{ + if (s_logger) + { + va_list args; + va_start(args, fmt); + s_logger->log(Logger::kUrgent, fmt, args); + va_end(args); + } +} + +void Log::json(const char *fmt, ...) +{ + if (s_logger && s_logger->getFilterLevel() == Logger::kJson) + { + va_list args; + va_start(args, fmt); + s_logger->log(Logger::kJson, fmt, args); + va_end(args); + } +} + +void Log::error(const char *fmt, ...) +{ + if (s_logger) + { + va_list args; + va_start(args, fmt); + s_logger->log(Logger::kError, fmt, args); + va_end(args); + } +} + +void Log::warning(const char *fmt, ...) +{ + if (s_logger) + { + va_list args; + va_start(args, fmt); + s_logger->log(Logger::kWarning, fmt, args); + va_end(args); + } +} + +void Log::info(const char *fmt, ...) +{ + if (s_logger) + { + va_list args; + va_start(args, fmt); + s_logger->log(Logger::kInfo, fmt, args); + va_end(args); + } +} + +void Log::info2(const char *fmt, ...) +{ + if (s_logger) + { + va_list args; + va_start(args, fmt); + s_logger->log(Logger::kInfo2, fmt, args); + va_end(args); + } +} + +void Log::debug(const char *fmt, ...) +{ + if (s_logger) + { + va_list args; + va_start(args, fmt); + s_logger->log(Logger::kDebug, fmt, args); + va_end(args); + } +} + +void Log::debug2(const char *fmt, ...) +{ + if (s_logger) + { + va_list args; + va_start(args, fmt); + s_logger->log(Logger::kDebug2, fmt, args); + va_end(args); + } +} + +void StdoutLogger::_log(const char *msg) +{ + printf("%s", msg); +} + +FileLogger::FileLogger(const char *file_path) + : m_file_path(file_path) + , m_logFile(file_path) +{ +} + +FileLogger::~FileLogger() +{ + try + { + m_logFile.close(); + } + catch (std::ios_base::failure &e) + { + printf("Error: Failed to close the log file(%s)", e.what()); + } +} + +void FileLogger::_log(const char *msg) +{ + m_logFile << msg; + try + { + m_logFile.flush(); + } + catch (std::ios_base::failure &e) + { + printf("Error: Failed to write the log file(%s)", e.what()); + } +} diff --git a/src/blfwk/src/LpcUsbSio.cpp b/src/blfwk/src/LpcUsbSio.cpp new file mode 100644 index 0000000..aead3b2 --- /dev/null +++ b/src/blfwk/src/LpcUsbSio.cpp @@ -0,0 +1,477 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include "blfwk/Logging.h" +#include "blfwk/LpcUsbSio.h" +#include "blfwk/format_string.h" + +using namespace blfwk; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// + +//! This macro converts a value from KHz to Hz. +#define CONVERT_KHZ_TO_HZ(KHz) (KHz * 1000) + +//! This struct maps an error code to a description of that code. +struct LpcUsbSioErrorMessage +{ + LPCUSBSIO_ERR_T code; //!< Error code value. + const std::string message; //!< Description of the status. +}; + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//! This variable containing error codes and corresponding descriptions of the codes. +LpcUsbSioErrorMessage s_errorMessage[] = { + { LPCUSBSIO_OK, "Success" }, + { LPCUSBSIO_ERR_HID_LIB, "HID library error" }, + { LPCUSBSIO_ERR_BAD_HANDLE, "Handle passed to the function is invalid" }, + { LPCUSBSIO_ERR_SYNCHRONIZATION, "Thread Synchronization error" }, + { LPCUSBSIO_ERR_MEM_ALLOC, "Memory Allocation error" }, + { LPCUSBSIO_ERR_MUTEX_CREATE, "Mutex Creation error" }, + { LPCUSBSIO_ERR_FATAL, "Fatal error occurred" }, + { LPCUSBSIO_ERR_I2C_NAK, "Transfer aborted due to NACK" }, + { LPCUSBSIO_ERR_I2C_BUS, "Transfer aborted due to bus error" }, + { LPCUSBSIO_ERR_I2C_SLAVE_NAK, "NAK received after SLA+W or SLA+R" }, + { LPCUSBSIO_ERR_I2C_ARBLOST, "I2C bus arbitration lost to other master" }, + { LPCUSBSIO_ERR_TIMEOUT, "Transaction timed out" }, + { LPCUSBSIO_ERR_INVALID_CMD, "Invalid HID_SIO Request or Request not supported in this version" }, + { LPCUSBSIO_ERR_INVALID_PARAM, "Invalid parameters are provided for the given Request" }, + { LPCUSBSIO_ERR_PARTIAL_DATA, "Partial transfer completed" }, +}; + +//////////////////////////////////////////////////////////////////////////////// +// Code +/////////////////////////////////////////////////////////////////////////////// + +// See LpcUsbSio.h for documentation of this method. +LpcUsbSio::LpcUsbSio(const LpcUsbSio::LpcUsbSioPortConfigData &config) + : m_usbVid(config.usbVid) + , m_usbPid(config.usbPid) + , m_usbString(config.usbString) + , m_usbPath(config.usbPath) + , m_portIndex(config.portIndex) +{ + m_portCount = LPCUSBSIO_GetNumPorts(m_usbVid, m_usbPid); + if (m_portCount < 1) + { + throw std::runtime_error(format_string("Error: No LPC USB Serial I/O is detected.\n")); + } + else if (m_portCount < (m_portIndex + 1)) + { + throw std::runtime_error( + format_string("Error: Invalid index %d, only %d port(s) available.\n", m_portIndex, m_portCount)); + } + + m_portHandler = LPCUSBSIO_Open(m_portIndex); + if (m_portHandler == NULL) + { + throw std::runtime_error( + format_string("Error: Failed to open the LPC USB Serial I/O port index %x.\n", m_portIndex)); + } +} + +// See LpcUsbSio.h for documentation of this method. +LpcUsbSio::~LpcUsbSio() +{ + if (m_portHandler) + { + LPCUSBSIO_Close(m_portHandler); + m_portHandler = NULL; + } +} + +// See LpcUsbSio.h for documentation of this method. +bool LpcUsbSio::parse(const string_vector_t ¶ms, LpcUsbSio::LpcUsbSioConfigData &config) +{ + if (!params[0].compare(0, 3, "spi")) + { + config.function = LpcUsbSio::kFunction_SPI; + + // Currently only support one SPI port. Can be extended in the furture. + config.spiConfig.spiPortIndex = 0; + + if (params.size() > 1) + { + int32_t port = atoi(params[1].c_str()); + if ((port < 0) || (port > 7)) + return false; + + config.spiConfig.spiSselPort = (uint8_t)port; + } + if (params.size() > 2) + { + int32_t pin = atoi(params[2].c_str()); + if ((pin < 0) || (pin > 0x1F)) + return false; + + config.spiConfig.spiSselPin = (uint8_t)pin; + } + if (params.size() > 3) + { + int32_t spiSpeed = CONVERT_KHZ_TO_HZ(atoi(params[3].c_str())); + if (spiSpeed <= 0) + return false; + + config.spiConfig.spiSpeedHz = spiSpeed; + } + if (params.size() > 4) + { + config.spiConfig.spiPolarity = (LpcUsbSio::spi_clock_polarity_t)atoi(params[4].c_str()); + } + if (params.size() > 5) + { + config.spiConfig.spiPhase = (LpcUsbSio::spi_clock_phase_t)atoi(params[5].c_str()); + } + } + else if (!params[0].compare(0, 3, "i2c")) + { + config.function = LpcUsbSio::kFunction_I2C; + + // Currently only support one I2C port. Can be extended in the furture. + config.i2cConfig.i2cPortIndex = 0; + + if (params.size() > 1) + { + uint32_t i2cAddress = strtoul(params[1].c_str(), NULL, 16); + + if (i2cAddress > 0x7F) + { + i2cAddress &= 0x7F; + Log::warning("Warning: Only 7-bit i2c address is supported, so the effective value is 0x%x\n", + i2cAddress); + } + config.i2cConfig.i2cAddress = (uint8_t)i2cAddress; + } + if (params.size() > 2) + { + int32_t i2cSpeed = CONVERT_KHZ_TO_HZ(atoi(params[2].c_str())); + if (i2cSpeed <= 0) + { + return false; + } + + if (i2cSpeed < I2C_CLOCK_FAST_MODE) + { + config.i2cConfig.i2cSpeedHz = I2C_CLOCK_STANDARD_MODE; + } + else if (i2cSpeed < I2C_CLOCK_FAST_MODE_PLUS) + { + config.i2cConfig.i2cSpeedHz = I2C_CLOCK_FAST_MODE; + } + else + { + config.i2cConfig.i2cSpeedHz = I2C_CLOCK_FAST_MODE_PLUS; + } + + if ((i2cSpeed != I2C_CLOCK_STANDARD_MODE) && (i2cSpeed != I2C_CLOCK_FAST_MODE) && + (i2cSpeed != I2C_CLOCK_FAST_MODE_PLUS)) + { + Log::warning( + "Warning: Only 100Kbps, 400Kbps and 1Mbps are supported, so the effective speed is %dbps\n", + config.i2cConfig.i2cSpeedHz); + } + } + } + else if (!params[0].compare(0, 4, "gpio")) + { + // Not supported yet. + // config.function = LpcUsbSio::kFunction_GPIO; + return false; + } + else + { + // Error: Invalid BusPal function. + return false; + } + + return true; +} + +// See LpcUsbSio.h for documentation of this method. +const std::string LpcUsbSio::getError(LPCUSBSIO_ERR_T errorCode) +{ + int i; + for (i = 0; i < (sizeof(s_errorMessage) / sizeof(s_errorMessage[0])); ++i) + { + if (errorCode == s_errorMessage[i].code) + { + return s_errorMessage[i].message; + } + } + + return format_string("Unknown error code (%d)", errorCode); +} + +// See LpcUsbSio.h for documentation of this method. +LpcUsbSioSpi::LpcUsbSioSpi(const LpcUsbSio::LpcUsbSioPortConfigData &portConfig, + const LpcUsbSio::LpcUsbSioSpiConfigData &spiConfig) + : LpcUsbSio(portConfig) + , m_spiPortIndex(spiConfig.spiPortIndex) + , m_spiSselPort(spiConfig.spiSselPort) + , m_spiSselPin(spiConfig.spiSselPin) +{ + m_spiPortCount = (int32_t)LPCUSBSIO_GetNumSPIPorts(m_portHandler); + if (m_spiPortCount < 0) + { + throw std::runtime_error( + format_string("Error: Failed to get SPI port count, due to %s.\n", LPCUSBSIO_Error(m_portHandler))); + } + else if (m_spiPortCount == 0) + { + throw std::runtime_error(format_string("Error: No SPI port is detected on current LPC USB Serial I/O.\n")); + } + else if (m_spiPortCount < (m_spiPortIndex + 1)) + { + throw std::runtime_error( + format_string("Error: Invalid index %d, only %d port(s) available.\n", m_spiPortIndex, m_spiPortCount)); + } + + HID_SPI_PORTCONFIG_T spiParam; + spiParam.busSpeed = spiConfig.spiSpeedHz; + spiParam.Options = HID_SPI_CONFIG_OPTION_DATA_SIZE_8 | HID_SPI_CONFIG_OPTION_POL_0 | HID_SPI_CONFIG_OPTION_PHA_0 | + HID_SPI_CONFIG_OPTION_PRE_DELAY(0) | HID_SPI_CONFIG_OPTION_POST_DELAY(0); + if (spiConfig.spiPolarity == LpcUsbSio::kSpiPolarity_ActiveLow) + { + spiParam.Options |= HID_SPI_CONFIG_OPTION_POL_1; + } + if (spiConfig.spiPhase == LpcUsbSio::kSpiPhase_SecondEdge) + { + spiParam.Options |= HID_SPI_CONFIG_OPTION_PHA_1; + } + + m_spiPortHandler = SPI_Open(m_portHandler, &spiParam, m_spiPortIndex); + if (m_spiPortHandler == NULL) + { + throw std::runtime_error( + format_string("Error: Failed to open SPI port, due to %s.\n", LPCUSBSIO_Error(m_portHandler))); + } +} + +// See LpcUsbSio.h for documentation of this method. +LpcUsbSioSpi::~LpcUsbSioSpi() +{ + if (m_spiPortHandler) + { + SPI_Close(m_spiPortHandler); + m_spiPortHandler = NULL; + } +} + +// See LpcUsbSio.h for documentation of this method. +uint32_t LpcUsbSioSpi::write(const uint8_t *data, uint32_t byteCount) +{ + uint32_t total = 0; + uint8_t *rxBuffer = (uint8_t *)malloc(byteCount); + + while (byteCount) + { + uint16_t byteToTransfer = 0; + if (byteCount > UINT16_MAX) + { + byteToTransfer = UINT16_MAX; + } + else + { + byteToTransfer = (uint16_t)byteCount; + } + + SPI_XFER_T xfer; + xfer.options = 0; + xfer.length = byteToTransfer; + xfer.txBuff = data + total; + xfer.rxBuff = rxBuffer + total; + xfer.device = LPCUSBSIO_GEN_SPI_DEVICE_NUM(m_spiSselPort, m_spiSselPin); + + int result = SPI_Transfer(m_spiPortHandler, &xfer); + if (result < 0) + { + Log::error("Error: Failed to write data via SPI port, due to %x.\n", + LpcUsbSio::getError((LPCUSBSIO_ERR_T)result).c_str()); + break; + } + else + { + byteCount -= result; + total += result; + } + } + + free(rxBuffer); + + return total; +} + +// See LpcUsbSio.h for documentation of this method. +uint32_t LpcUsbSioSpi::read(uint8_t *data, uint32_t byteCount) +{ + uint32_t total = 0; + uint8_t *txBuffer = (uint8_t *)malloc(byteCount); + memset(txBuffer, 0x0, byteCount); + + while (byteCount) + { + uint16_t byteToTransfer = 0; + if (byteCount > UINT16_MAX) + { + byteToTransfer = UINT16_MAX; + } + else + { + byteToTransfer = (uint16_t)byteCount; + } + + SPI_XFER_T xfer; + xfer.options = 0; + xfer.length = byteToTransfer; + xfer.txBuff = txBuffer + total; + xfer.rxBuff = data + total; + xfer.device = LPCUSBSIO_GEN_SPI_DEVICE_NUM(m_spiSselPort, m_spiSselPin); + + int result = SPI_Transfer(m_spiPortHandler, &xfer); + if (result < 0) + { + Log::error("Error: Failed to read data via SPI port, due to %s.\n", + LpcUsbSio::getError((LPCUSBSIO_ERR_T)result).c_str()); + break; + } + else + { + byteCount -= result; + total += result; + } + } + + free(txBuffer); + + return total; +} + +// See LpcUsbSio.h for documentation of this method. +LpcUsbSioI2c::LpcUsbSioI2c(const LpcUsbSio::LpcUsbSioPortConfigData &portConfig, + const LpcUsbSio::LpcUsbSioI2cConfigData &i2cConfig) + : LpcUsbSio(portConfig) + , m_i2cPortIndex(i2cConfig.i2cPortIndex) + , m_i2cAddress(i2cConfig.i2cAddress) +{ + m_i2cPortCount = (int32_t)LPCUSBSIO_GetNumSPIPorts(m_portHandler); + if (m_i2cPortCount == 0) + { + throw std::runtime_error(format_string("Error: No I2C port is detected on current LPC USB Serial I/O.\n")); + } + else if (m_i2cPortCount < 0) + { + throw std::runtime_error( + format_string("Error: Failed to get I2C port count, due to %s.\n", LPCUSBSIO_Error(m_portHandler))); + } + else if (m_i2cPortCount < (m_i2cPortIndex + 1)) + { + throw std::runtime_error( + format_string("Error: Invalid index %d, only %d port(s) available.\n", m_i2cPortIndex, m_i2cPortCount)); + } + + I2C_PORTCONFIG_T i2cParam; + i2cParam.ClockRate = i2cConfig.i2cSpeedHz; + i2cParam.Options = 0; + m_i2cPortHandler = I2C_Open(m_portHandler, &i2cParam, m_i2cPortIndex); + if (m_i2cPortHandler == NULL) + { + throw std::runtime_error( + format_string("Error: Failed to open I2C port, due to %s.\n", LPCUSBSIO_Error(m_portHandler))); + } +} + +// See LpcUsbSio.h for documentation of this method. +LpcUsbSioI2c::~LpcUsbSioI2c() +{ + if (m_i2cPortHandler) + { + I2C_Close(m_i2cPortHandler); + m_i2cPortHandler = NULL; + } +} + +// See LpcUsbSio.h for documentation of this method. +uint32_t LpcUsbSioI2c::write(const uint8_t *data, uint32_t byteCount) +{ + uint32_t total = 0; + + while (byteCount) + { + uint16_t byteToTransfer = 0; + if (byteCount > UINT16_MAX) + { + byteToTransfer = UINT16_MAX; + } + else + { + byteToTransfer = (uint16_t)byteCount; + } + + int result = I2C_DeviceWrite(m_i2cPortHandler, m_i2cAddress, (uint8_t *)data + total, byteToTransfer, + I2C_TRANSFER_OPTIONS_START_BIT | I2C_TRANSFER_OPTIONS_STOP_BIT | + I2C_TRANSFER_OPTIONS_BREAK_ON_NACK | I2C_TRANSFER_OPTIONS_NACK_LAST_BYTE); + if (result < 0) + { + Log::error("Error: Failed to write data via I2C port, due to %s.\n", + LpcUsbSio::getError((LPCUSBSIO_ERR_T)result).c_str()); + break; + } + else + { + byteCount -= result; + total += result; + } + } + + return total; +} + +// See LpcUsbSio.h for documentation of this method. +uint32_t LpcUsbSioI2c::read(uint8_t *data, uint32_t byteCount) +{ + uint32_t total = 0; + + while (byteCount) + { + uint16_t byteToTransfer = 0; + if (byteCount > UINT16_MAX) + { + byteToTransfer = UINT16_MAX; + } + else + { + byteToTransfer = (uint16_t)byteCount; + } + + int result = I2C_DeviceRead(m_i2cPortHandler, m_i2cAddress, (uint8_t *)data + total, byteToTransfer, + I2C_TRANSFER_OPTIONS_START_BIT | I2C_TRANSFER_OPTIONS_STOP_BIT | + I2C_TRANSFER_OPTIONS_BREAK_ON_NACK | I2C_TRANSFER_OPTIONS_NACK_LAST_BYTE); + if (result < 0) + { + Log::error("Error: Failed to read data via I2C port, due to %s.\n", + LpcUsbSio::getError((LPCUSBSIO_ERR_T)result).c_str()); + break; + } + else + { + byteCount -= result; + total += result; + } + } + + return total; +} +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/LpcUsbSioPeripheral.cpp b/src/blfwk/src/LpcUsbSioPeripheral.cpp new file mode 100644 index 0000000..fcdc745 --- /dev/null +++ b/src/blfwk/src/LpcUsbSioPeripheral.cpp @@ -0,0 +1,137 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include "blfwk/Logging.h" +#include "blfwk/LpcUsbSioPeripheral.h" +#include "blfwk/format_string.h" + +using namespace blfwk; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See LpcUsbSioPeripheral.h for documentation of this method. +LpcUsbSioPeripheral::LpcUsbSioPeripheral(const LpcUsbSio::LpcUsbSioConfigData &config) + : Peripheral() +{ + if (config.function == LpcUsbSio::kFunction_SPI) + { + m_lpcUsbSio = new LpcUsbSioSpi(config.portConfig, config.spiConfig); + } + else if (config.function == LpcUsbSio::kFunction_I2C) + { + m_lpcUsbSio = new LpcUsbSioI2c(config.portConfig, config.i2cConfig); + } + else if (config.function == LpcUsbSio::kFunction_GPIO) + { + // Currently, GPIO function is not supported. + throw std::runtime_error(format_string("Error: GPIO function is not supported yet.\n")); + } + else + { + throw std::runtime_error(format_string("Error: Invalid function of LPC USB Serial I/O.\n")); + } +} + +// See LpcUsbSioPeripheral.h for documentation of this method. +LpcUsbSioPeripheral::~LpcUsbSioPeripheral() +{ + if (m_lpcUsbSio) + { + delete m_lpcUsbSio; + m_lpcUsbSio = NULL; + } +} + +// See LpcUsbSioPeripheral.h for documentation of this method. +status_t LpcUsbSioPeripheral::read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t timeoutMs) +{ + assert(buffer); + + uint32_t count = 0; + if (requestedBytes) + { + // Read the requested number of bytes. + count = m_lpcUsbSio->read(buffer, requestedBytes); + } + + if (actualBytes) + { + *actualBytes = count; + } + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes read in hex + Log::debug2("<"); + for (int i = 0; i < (int)count; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (count - 1)) + { + Log::debug2(" "); + } + } + Log::debug2(">\n"); + } + + if (count == 0) + { + // Nothing got is a timeout error. + return kStatus_Timeout; + } + else if (count != requestedBytes) + { + // Fewer than requested is a failure. + return kStatus_Fail; + } + + return kStatus_Success; +} + +// See LpcUsbSioPeripheral.h for documentation of this method. +status_t LpcUsbSioPeripheral::write(const uint8_t *buffer, uint32_t byteCount) +{ + assert(buffer); + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes written in hex + Log::debug2("["); + for (int i = 0; i < (int)byteCount; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (byteCount - 1)) + { + Log::debug2(" "); + } + } + Log::debug2("]\n"); + } + + if (byteCount == 0) + { + return kStatus_Success; + } + + uint32_t count = m_lpcUsbSio->write(buffer, byteCount); + + if (count < byteCount) + { + // Fewer than requested is a failure. + return kStatus_Fail; + } + + return kStatus_Success; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/Random.cpp b/src/blfwk/src/Random.cpp new file mode 100644 index 0000000..dd22b6b --- /dev/null +++ b/src/blfwk/src/Random.cpp @@ -0,0 +1,81 @@ +/* + * File: Random.cpp + * + * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. + * See included license file for license details. + */ + +#include "blfwk/Random.h" +#include + +#ifdef WIN32 +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif +#include +#include +#else // WIN32 +#include +#include +#include +#endif // WIN32 + +#ifdef WIN32 + +MicrosoftCryptoProvider::MicrosoftCryptoProvider() +{ + if (!CryptAcquireContext(&m_hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + { + throw std::runtime_error("CryptAcquireContext"); + } +} + +MicrosoftCryptoProvider::~MicrosoftCryptoProvider() +{ + CryptReleaseContext(m_hProvider, 0); +} + +#endif // WIN32 + +RandomNumberGenerator::RandomNumberGenerator() +{ +#ifndef WIN32 + m_fd = open("/dev/urandom", O_RDONLY); + if (m_fd == -1) + { + throw std::runtime_error("open /dev/urandom"); + } +#endif // WIN32 +} + +RandomNumberGenerator::~RandomNumberGenerator() +{ +#ifndef WIN32 + close(m_fd); +#endif // WIN32 +} + +uint8_t RandomNumberGenerator::generateByte() +{ + uint8_t result; + generateBlock(&result, 1); + return result; +} + +void RandomNumberGenerator::generateBlock(uint8_t *output, unsigned count) +{ +#ifdef WIN32 +#ifdef WORKAROUND_MS_BUG_Q258000 + static MicrosoftCryptoProvider m_provider; +#endif + if (!CryptGenRandom(m_provider.GetProviderHandle(), count, output)) + { + throw std::runtime_error("CryptGenRandom"); + } +#else // WIN32 + if (read(m_fd, output, count) != count) + { + throw std::runtime_error("read /dev/urandom"); + } +#endif // WIN32 +} diff --git a/src/blfwk/src/RijndaelCTR.cpp b/src/blfwk/src/RijndaelCTR.cpp new file mode 100644 index 0000000..49e227a --- /dev/null +++ b/src/blfwk/src/RijndaelCTR.cpp @@ -0,0 +1,77 @@ +/* + * File: RijndaelCTR.cpp + * + * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. + * See included license file for license details. + */ + +#include "blfwk/RijndaelCTR.h" +#include "blfwk/rijndael.h" +#include +#include "blfwk/Logging.h" + +void logHexArray(Logger::log_level_t level, const uint8_t *bytes, unsigned count); + +//! \param key The key to use +//! \param counter The counter to use +RijndaelCTR::RijndaelCTR(const AESKey<128> &key, const AESCounter<128> &counter) + : m_key(key) + , m_counter(counter) +{ +} + +void RijndaelCTR::decrypt(const uint8_t *data, unsigned length, uint8_t *dest) +{ + baseProcess(data, length, dest); +} + +void RijndaelCTR::encrypt(const uint8_t *data, unsigned length, uint8_t *dest) +{ + baseProcess(data, length, dest); +} + +void RijndaelCTR::baseProcess(const uint8_t *data, unsigned length, uint8_t *dest) +{ + Rijndael cipher; + unsigned blocks = length / BLOCK_SIZE; + block_t remainder; + AESCounter<128>::counter_t currentCounter; + + cipher.init(Rijndael::ECB, Rijndael::Encrypt, m_key, Rijndael::Key16Bytes); + + while (blocks--) + { + m_counter.getCounter(¤tCounter); + + cipher.blockEncrypt(currentCounter, BLOCK_SIZE * 8, currentCounter); + + for (int i = 0; i < BLOCK_SIZE; i++) + { + dest[i] = data[i] ^ currentCounter[i]; + } + + data += BLOCK_SIZE; + dest += BLOCK_SIZE; + + m_counter.incrementCounter(BLOCK_SIZE); + } + + if (length % sizeof(block_t)) + { + m_counter.getCounter(¤tCounter); + + memset(remainder, 0, sizeof(remainder)); + memcpy(remainder, data, length % sizeof(block_t)); + + cipher.blockEncrypt(currentCounter, BLOCK_SIZE * 8, currentCounter); + + for (unsigned int i = 0; i < length % sizeof(block_t); i++) + { + remainder[i] = data[i] ^ currentCounter[i]; + } + + memcpy(dest, remainder, length % sizeof(block_t)); + + m_counter.incrementCounter(BLOCK_SIZE); + } +} diff --git a/src/blfwk/src/SBSourceFile.cpp b/src/blfwk/src/SBSourceFile.cpp new file mode 100644 index 0000000..75557a4 --- /dev/null +++ b/src/blfwk/src/SBSourceFile.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "string.h" +#include "blfwk/stdafx.h" +#include "blfwk/EndianUtilities.h" +#include "blfwk/GHSSecInfo.h" +#include "blfwk/Logging.h" +#include "blfwk/SBSourceFile.h" + +using namespace blfwk; + +SBSourceFile::SBSourceFile(const std::string &path) + : BinarySourceFile(path, kSBSourceFile) +{ +} + +SBSourceFile::~SBSourceFile() +{ +} + +bool SBSourceFile::isSBFile(std::istream &stream) +{ + try + { + boot_image_header_t header; //!< Header from the SB boot image. + + // readImageHeader + // seek to beginning of the stream/file and read the plaintext header + stream.seekg(0, std::ios_base::beg); + if (stream.read((char *)&header, sizeof(header)).bad()) + { + throw SBFileException("failed to read SB image header"); + } + + header.m_flags = ENDIAN_LITTLE_TO_HOST_U16(header.m_flags); + header.m_imageBlocks = ENDIAN_LITTLE_TO_HOST_U32(header.m_imageBlocks); + header.m_firstBootTagBlock = ENDIAN_LITTLE_TO_HOST_U32(header.m_firstBootTagBlock); + header.m_firstBootableSectionID = ENDIAN_LITTLE_TO_HOST_U32(header.m_firstBootableSectionID); + header.m_keyCount = ENDIAN_LITTLE_TO_HOST_U16(header.m_keyCount); + header.m_keyDictionaryBlock = ENDIAN_LITTLE_TO_HOST_U16(header.m_keyDictionaryBlock); + header.m_headerBlocks = ENDIAN_LITTLE_TO_HOST_U16(header.m_headerBlocks); + header.m_sectionCount = ENDIAN_LITTLE_TO_HOST_U16(header.m_sectionCount); + header.m_sectionHeaderSize = ENDIAN_LITTLE_TO_HOST_U16(header.m_sectionHeaderSize); + header.m_timestamp = ENDIAN_LITTLE_TO_HOST_U64(header.m_timestamp); + + // check header signature 1 + if (header.m_signature[0] != 'S' || header.m_signature[1] != 'T' || header.m_signature[2] != 'M' || + header.m_signature[3] != 'P') + { + throw SBFileException("invalid SB signature 1"); + } + + // check header signature 2 for version 1.1 and greater + if ((header.m_majorVersion > 1 || (header.m_majorVersion == 1 && header.m_minorVersion >= 1)) && + (header.m_signature2[0] != 's' || header.m_signature2[1] != 'g' || header.m_signature2[2] != 't' || + header.m_signature2[3] != 'l')) + { + Log::log(Logger::kWarning, "warning: invalid SB signature 2\n"); + } + + return true; + } + catch (...) + { + return false; + } +} + +DataSource *SBSourceFile::createDataSource() +{ + throw std::runtime_error("SBSourceFile::createDataSource() has not been implemented."); +} diff --git a/src/blfwk/src/SDPCommand.cpp b/src/blfwk/src/SDPCommand.cpp new file mode 100644 index 0000000..bfd1c56 --- /dev/null +++ b/src/blfwk/src/SDPCommand.cpp @@ -0,0 +1,1002 @@ +/* +* Copyright (c) 2015 Freescale Semiconductor, Inc. +* Copyright 2016-2018 NXP +* All rights reserved. +* +* +* SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "blfwk/SDPCommand.h" +#include "blfwk/utils.h" +#include "blfwk/Logging.h" +#include "blfwk/json.h" +#include "blfwk/format_string.h" + +#include + +using namespace blfwk; +using namespace utils; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Declarations +//////////////////////////////////////////////////////////////////////////////// + +/*! +* @brief Abstract class to consume data from data phase. +*/ +class SDPDataConsumer +{ +public: + //! @brief Process the next data chunk. + virtual void processData(const uint8_t *data, uint32_t size) = 0; + + //! @brief Finalize processing. + virtual void finalize() = 0; +}; + +/*! +* @brief Write file data for data phase receive. +*/ +class SDPFileDataConsumer : public SDPDataConsumer +{ +public: + //! @brief Default constructor. + SDPFileDataConsumer() + : m_filePath() + , m_filePointer(NULL) + { + } + + //! @brief Destructor. + virtual ~SDPFileDataConsumer() + { + if (m_filePointer) + fclose(m_filePointer); + } + + //! @brief Initialize with a file path. + bool init(std::string filePath); + + //! @brief Process the next data chunk. + virtual void processData(const uint8_t *data, uint32_t size); + + //! @brief Finalize processing. + virtual void finalize() {} +protected: + std::string m_filePath; //!< Data file path. + FILE *m_filePointer; //!< Data file pointer. +}; + +class SDPStdOutDataConsumer : public SDPDataConsumer +{ +public: + //! @brief Constants. + enum _constants + { + kBytesPerLine = 16 //!< Number of hex bytes to display per line + }; + +public: + //! @brief Constructor. + SDPStdOutDataConsumer() + : m_currentCount(1) + { + } + + //! @brief Finalize processing. + virtual void finalize() + { + if (((m_currentCount - 1) % kBytesPerLine) != 0) + { + // Fill space to clean the progress text. + for (int i = ((m_currentCount - 1) % kBytesPerLine) * 3; i < 9; i++) + { + Log::info(" "); + } + Log::info("\n"); + } + } + + //! @brief Process the next data chunk. + virtual void processData(const uint8_t *data, uint32_t size); + +protected: + uint32_t m_currentCount; //!< Current byte being processed, starts at 1 +}; + +class SDPFileDataProducer +{ +public: + //! @brief Default constructor. + SDPFileDataProducer() + : m_filePath() + , m_filePointer(NULL) + , m_fileSize(0) + { + } + + //! @brief Destructor. + virtual ~SDPFileDataProducer() + { + if (m_filePointer) + fclose(m_filePointer); + } + + //! @brief Initialize with a file path. + bool init(std::string filePath, uint32_t count); + + //! \name DataProducer + //@{ + //! @brief Query if more data is available. + virtual bool hasMoreData() const + { + assert(m_filePointer); + return (m_fileSize && !feof(m_filePointer)); + } + + //! @brief Query the total size of the data. + virtual uint32_t getDataSize() const { return (uint32_t)m_fileSize; } + //! @brief Get the next data chunk. + //! + //! Before calling getData(), call moreData() to determine if + //! data is available. + virtual uint32_t getData(uint8_t *data, uint32_t size); + //@} + + //! @brief Send next packet to packetizer. + status_t sendPacketTo(Packetizer &packetizer, uint32_t *bytesWritten, Progress *progress); + + //! @brief Send all packets to packetizer. + status_t sendTo(Packetizer &packetizer, uint32_t *bytesWritten, Progress *progress); + +protected: + std::string m_filePath; //!< Data file path. + FILE *m_filePointer; //!< Data file pointer. + long m_fileSize; //!< Size in bytes of data file. + uint8_t m_dataBuf[SDPCommand::kSendDataSizeBytes]; //!< Buffer for data bytes. +}; + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Entry in a lookup table of status messages. +//! +//! This struct maps a status value to a description of that status. +struct StatusMessageTableEntry +{ + int32_t status; //!< Status code value. + const std::string message; //!< Description of the status. +}; + +//! @brief Value of the terminator code in the g_statusCodes table. +const int32_t kStatusMessageTableTerminatorValue = 0x7fffffff; + +//! @brief Status return code descriptions. +StatusMessageTableEntry g_statusCodes[] = { + // Generic statuses. + { kStatus_Success, "Success." }, + { kStatus_Fail, "Failure." }, + { SDPCommand::kHabEnabled, "HAB enabled." }, + { SDPCommand::kHabDisabled, "HAB disabled." }, + { SDPCommand::kWriteComplete, "Write complete." }, + { SDPCommand::kWriteFileComplete, "Write File complete." }, + { SDPCommand::kHabStatusFailure, "HAB failure." }, + { SDPCommand::kHabStatusWarning, "HAB warning." }, + { SDPCommand::kHabStatusSuccess, "HAB Success." }, + { SDPCommand::kStatus_NoResponse, "No response from device." }, + { SDPCommand::kOk_Ack, "OK ACK." }, + { SDPCommand::kReEnum_Ack, "REENUM Ack" }, + + // Terminator + { kStatusMessageTableTerminatorValue, "" } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +std::string getStatusMessage(status_t code) +{ + int i; + for (i = 0; g_statusCodes[i].status != kStatusMessageTableTerminatorValue; ++i) + { + if (code == g_statusCodes[i].status) + { + return g_statusCodes[i].message; + } + } + + return format_string("Unknown error code (%u)", code); +} + +void SDPStdOutDataConsumer::processData(const uint8_t *data, uint32_t size) +{ + Log::info("\r"); + + for (int i = 0; i < (int)size; ++i) + { + Log::info("%02x", data[i]); + if ((m_currentCount++ % kBytesPerLine) == 0) + { + Log::info("\n"); + } + else + { + Log::info(" "); + } + } +} + +bool SDPFileDataProducer::init(string filePath, uint32_t count) +{ + m_filePointer = fopen(filePath.c_str(), "rb"); + if (!m_filePointer) + { + Log::error("Error: cannot open input data file '%s'.\n", filePath.c_str()); + return false; + } + + // If the fileSize wasn't specified, get the file size. + if (count) + { + m_fileSize = count; + } + else + { + ::fseek(m_filePointer, 0, SEEK_END); + m_fileSize = ftell(m_filePointer); + ::fseek(m_filePointer, 0, SEEK_SET); + } + + Log::info("Preparing to send %u (0x%x) bytes to the target.\n", m_fileSize, m_fileSize); + return true; +} + +//! See host_command.h for documentation on this function. +uint32_t SDPFileDataProducer::getData(uint8_t *data, uint32_t size) +{ + assert(m_filePointer); + assert(data); + if ((size == 0) || !hasMoreData()) + { + return 0; + } + + return (uint32_t)fread(data, 1, size, m_filePointer); +} + +status_t SDPFileDataProducer::sendPacketTo(Packetizer &packetizer, uint32_t *bytesWritten, Progress *progress) +{ + assert(bytesWritten); + *bytesWritten = 0; + + uint32_t count = getData(m_dataBuf, sizeof(m_dataBuf)); + if (count) + { + status_t status = packetizer.writePacket(m_dataBuf, count, kPacketType_Data); + if (status != kStatus_Success) + { + Log::error("SDPFileDataProducer.writePacket error %u.\n", status); + return status; + } + + *bytesWritten += count; + + if (progress != NULL) + { + // Execute process callback function. + progress->progressCallback(*bytesWritten * 100 / getDataSize()); + } + } + + return kStatus_Success; +} + +status_t SDPFileDataProducer::sendTo(Packetizer &packetizer, uint32_t *bytesWritten, Progress *progress) +{ + assert(bytesWritten); + *bytesWritten = 0; + + while (hasMoreData() && *bytesWritten < getDataSize()) + { + uint32_t packetBytesWritten; + status_t status = sendPacketTo(packetizer, &packetBytesWritten, progress); + if (status != kStatus_Success) + { + return status; + } + + *bytesWritten += packetBytesWritten; + } + + return kStatus_Success; +} + +bool SDPFileDataConsumer::init(string filePath) +{ + m_filePointer = fopen(filePath.c_str(), "wb"); + if (!m_filePointer) + { + Log::error("Error: cannot open output data file '%s'.\n", filePath.c_str()); + return false; + } + return true; +} + +void SDPFileDataConsumer::processData(const uint8_t *data, uint32_t size) +{ + fwrite(data, 1, size, m_filePointer); +} + +SDPCommand *SDPCommand::create(const string_vector_t *argv) +{ + SDPCommand *cmd; + + if (argv->at(0) == kSDPCommand_ReadRegister.name) + { + cmd = new SDPReadRegister(argv); + } + else if (argv->at(0) == kSDPCommand_WriteRegister.name) + { + cmd = new SDPWriteRegister(argv); + } + else if (argv->at(0) == kSDPCommand_WriteFile.name) + { + cmd = new SDPWriteFile(argv); + } + else if (argv->at(0) == kSDPCommand_ErrorStatus.name) + { + cmd = new SDPErrorStatus(argv); + } + else if (argv->at(0) == kSDPCommand_DcdWrite.name) + { + cmd = new SDPDcdWrite(argv); + } + else if (argv->at(0) == kSDPCommand_SkipDcdHeader.name) + { + cmd = new SDPSkipDcdHeader(argv); + } + else if (argv->at(0) == kSDPCommand_JumpAddress.name) + { + cmd = new SDPJumpAddress(argv); + } + else if (argv->at(0) == kSDPCommand_SetBaudrate.name) + { + cmd = new SDPSetBaudrate(argv); + } + else if (argv->at(0) == kSDPCommand_Ping.name) + { + cmd = new SDPPing(argv); + } + else + { + return NULL; + } + + // Validate arguments. + if (!cmd->init()) + { + delete cmd; + return NULL; + } + + return cmd; +} + +void SDPCommand::packCommand(uint16_t cmdType, uint32_t address, uint32_t format, uint32_t dataCount, uint32_t data) +{ + memset(m_cmdBuf, 0, kCmdSizeBytes); + uint32_t *cmd = (uint32_t *)m_cmdBuf; + + cmd[0] = (((address & 0x00FF0000) << 8) | ((address & 0xFF000000) >> 8) | (cmdType & 0x0000FFFF)); + + cmd[1] = ((dataCount & 0xFF000000) | ((format & 0x000000FF) << 16) | ((address & 0x000000FF) << 8) | + ((address & 0x0000FF00) >> 8)); + + cmd[2] = ((data & 0xFF000000) | ((dataCount & 0x000000FF) << 16) | (dataCount & 0x0000FF00) | + ((dataCount & 0x00FF0000) >> 16)); + + cmd[3] = + (((0x00 & 0x000000FF) << 24) | ((data & 0x00FF0000) >> 16) | (data & 0x0000FF00) | ((data & 0x000000FF) << 16)); +} + +status_t SDPCommand::sendCommand(Packetizer &packetizer) +{ + status_t status = kStatus_Success; + + status = packetizer.writePacket(m_cmdBuf, kCmdSizeBytes, kPacketType_Command); + if (status != kStatus_Success) + { + Log::error("sendCommand.writePacket error %u.\n", status); + return status; + } + + return kStatus_Success; +} + +status_t SDPCommand::sendCommandGetHabMode(Packetizer &packetizer) +{ + status_t status = kStatus_Success; + + // Send the command. + status = sendCommand(packetizer); + if (status != kStatus_Success) + { + return status; + } + + // Read and validate HAB mode response. + return isHabModeValid(packetizer) ? kStatus_Success : kStatus_Fail; +} + +uint8_t *SDPCommand::getStatusResponse(Packetizer &packetizer, packet_type_t type) +{ + status_t status; + + // Read status/response. + uint8_t *response = NULL; + uint32_t responseSize = 0; + status = packetizer.readPacket(&response, &responseSize, type); + if (status != kStatus_Success) + { + Log::error("getStatusResponse.readPacket error %u.\n", status); + return NULL; + } + + return response; +} + +bool SDPCommand::isHabModeValid(Packetizer &packetizer) +{ + uint8_t *habMode = getStatusResponse(packetizer); + if (!habMode) + { + m_responseValues.push_back(kStatus_NoResponse); + return false; + } + + uint32_t intMode = *((uint32_t *)habMode); + m_responseValues.push_back(intMode); + + if ((intMode == kHabDisabled) || (intMode == kHabEnabled)) + { + return true; + } + else + { + Log::error("unknown HAB mode %u.\n", intMode); + return false; + } + + return true; +} + +std::string SDPCommand::getResponse() const +{ + size_t count = m_responseValues.size(); + + Json::Value root; + root["command"] = getName(); + root["status"] = Json::Value(Json::objectValue); + root["status"]["value"] = static_cast(m_responseValues.at(0)); + root["status"]["description"] = format_string("%u (0x%X) %s", m_responseValues.at(0), m_responseValues.at(0), + getStatusMessage(m_responseValues.at(0)).c_str()); + root["response"] = Json::Value(Json::arrayValue); + for (int i = 1; i < (int)m_responseValues.size(); ++i) + { + root["response"].append(Json::Value(m_responseValues.at(i))); + } + + Json::StyledWriter writer; + return writer.write(root); +} + +void SDPCommand::logResponses() const +{ + const uint32_vector_t *respv = getResponseValues(); + size_t count = respv->size(); + + if (getName() == kSDPCommand_SetBaudrate.name) + { + Log::info("Status = %u (0x%x) %s\n", respv->at(0), respv->at(0), getStatusMessage(respv->at(0)).c_str()); + } + else if (getName() == kSDPCommand_Ping.name) + { + Log::info("Status (HAB mode) = %u (0x%x) %s\n", respv->at(0), respv->at(0), + getStatusMessage(respv->at(0)).c_str()); + Log::info("Ping response, Device ID = 0x%08x 0x%08x\n", respv->at(1), respv->at(2)); + } + else + { + Log::info("Status (HAB mode) = %u (0x%x) %s\n", respv->at(0), respv->at(0), + getStatusMessage(respv->at(0)).c_str()); + if (count > 1) + { + Log::info("Reponse Status = %u (0x%x) %s\n", respv->at(1), respv->at(1), + getStatusMessage(respv->at(1)).c_str()); + } + } + + Log::json(getResponse().c_str()); +} + +//////////////////////////////////////////////////////////////////////////////// +// SDP ReadRegister command +//////////////////////////////////////////////////////////////////////////////// + +bool SDPReadRegister::init() +{ + if ((getArgCount() < 2) || (getArgCount() > 5)) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_address)) + { + return false; // invalid 'addr' parameter + } + + // Default format and count. + m_format = 32; + m_dataCount = m_format / 8; + + if (getArgCount() > 2) + { + if (!utils::stringtoui(getArg(2), m_format)) + { + return false; // invalid 'format' parameter + } + if (!((m_format == 8) || (m_format == 16) || (m_format == 32))) + { + return false; // invaid 'format' parameter + } + + // Default count. + m_dataCount = m_format / 8; + + if (getArgCount() > 3) + { + if (!utils::stringtoui(getArg(3), m_dataCount)) + { + return false; // invalid 'count' parameter + } + + if (m_dataCount == 0) + { + return false; // invalid 'count' parameter + } + + if (getArgCount() == 5) + { + m_file = getArg(4); + } + } + } + + return true; +} + +void SDPReadRegister::sendTo(Packetizer &packetizer) +{ + packCommand(kSDPCommand_ReadRegister.cmdType, m_address, m_format, m_dataCount, 0); + if (sendCommandGetHabMode(packetizer) != kStatus_Success) + { + return; + } + + SDPDataConsumer *printer; + if (m_file.size() > 0) + { + SDPFileDataConsumer *filePrinter = new SDPFileDataConsumer; + if (!filePrinter->init(m_file)) + { + delete filePrinter; + return; + } + printer = filePrinter; + } + else + { + printer = new SDPStdOutDataConsumer; + } + + // Display response values. + int32_t remaining = m_dataCount; + do + { + int32_t requested = MIN(remaining, kResponseSizeBytes); + + // Tell the packetizer how many bytes to read from the peripheral. + packetizer.setReadCount(requested); + + uint8_t *data = getStatusResponse(packetizer, kPacketType_Data); + if (!data) + { + break; + } + + printer->processData(data, requested); + + remaining -= kResponseSizeBytes; + } while (remaining > 0); + + printer->finalize(); + delete printer; +} + +//////////////////////////////////////////////////////////////////////////////// +// SDP WriteRegister command +//////////////////////////////////////////////////////////////////////////////// + +bool SDPWriteRegister::init() +{ + if (getArgCount() != 4) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_address)) + { + return false; // invalid 'addr' parameter + } + if (!utils::stringtoui(getArg(2), m_format)) + { + return false; // invalid 'format' parameter + } + if (!utils::stringtoui(getArg(3), m_data)) + { + return false; // invalid 'data' parameter + } + + if (!((m_format == 8) || (m_format == 16) || (m_format == 32))) + { + return false; // invaid 'format' parameter + } + + return true; +} + +void SDPWriteRegister::sendTo(Packetizer &packetizer) +{ + packCommand(kSDPCommand_WriteRegister.cmdType, m_address, m_format, 0, m_data); + if (sendCommandGetHabMode(packetizer) != kStatus_Success) + { + return; + } + + // Check for WRITE_COMPLETE status. + uint8_t *data = getStatusResponse(packetizer); + if (!data) + { + return; + } + uint32_t intData = *((uint32_t *)data); + m_responseValues.push_back(intData); + if (intData != kWriteComplete) + { + Log::error("unexpected write response 0x%x.\n", intData); + return; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// SDP WriteFile command +//////////////////////////////////////////////////////////////////////////////// + +bool SDPWriteFile::init() +{ + if ((getArgCount() < 3) || (getArgCount() > 4)) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_address)) + { + return false; // invalid 'addr' parameter + } + + m_file = getArg(2); + + if (getArgCount() == 4) + { + if (!utils::stringtoui(getArg(3), m_dataCount)) + { + return false; // invalid 'count' parameter + } + + if (m_dataCount == 0) + { + return false; // invalid 'count' parameter + } + } + + return true; +} + +void SDPWriteFile::sendTo(Packetizer &packetizer) +{ + SDPFileDataProducer fileProducer; + + if (!fileProducer.init(m_file, m_dataCount)) + { + return; + } + + if (fileProducer.getDataSize() == 0) + { + Log::error("non-zero file size required.\n"); + return; + } + + // Send the write file command. + packCommand(kSDPCommand_WriteFile.cmdType, m_address, 0, fileProducer.getDataSize(), 0); + if (sendCommand(packetizer) != kStatus_Success) + { + return; + } + + // Write the file data. + uint32_t bytesWritten; + if (fileProducer.sendTo(packetizer, &bytesWritten, m_progress) != kStatus_Success) + { + return; + } + + // Read and verify hab mode. + if (!isHabModeValid(packetizer)) + { + return; + } + + // Check for COMPLETE status. + uint8_t *data = getStatusResponse(packetizer); + if (!data) + { + return; + } + uint32_t intData = *((uint32_t *)data); + m_responseValues.push_back(intData); + if (intData != kWriteFileComplete) + { + Log::error("unexpected write file response 0x%x.\n", intData); + return; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// SDP ErrorStatus command +//////////////////////////////////////////////////////////////////////////////// + +bool SDPErrorStatus::init() +{ + if (getArgCount() != 1) + { + return false; + } + + return true; +} + +void SDPErrorStatus::sendTo(Packetizer &packetizer) +{ + packCommand(kSDPCommand_ErrorStatus.cmdType, 0, 0, 0, 0); + if (sendCommandGetHabMode(packetizer) != kStatus_Success) + { + return; + } + + // Read status response. + uint8_t *data = getStatusResponse(packetizer); + if (!data) + { + return; + } + uint32_t intData = *((uint32_t *)data); + m_responseValues.push_back(intData); +} + +//////////////////////////////////////////////////////////////////////////////// +// SDP DcdWrite command +//////////////////////////////////////////////////////////////////////////////// + +bool SDPDcdWrite::init() +{ + if (getArgCount() != 3) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_address)) + { + return false; // invalid 'addr' parameter + } + + m_file = getArg(2); + + return true; +} + +void SDPDcdWrite::sendTo(Packetizer &packetizer) +{ + uint32_t dataCount = 0; + SDPFileDataProducer fileProducer; + + if (!fileProducer.init(m_file, dataCount)) + { + return; + } + + uint32_t dataSize = fileProducer.getDataSize(); + if ((dataSize == 0) || (dataSize > kMaxDcdSizeBytes)) + { + Log::error("DCD file size must be non-zero and <= %d bytes.\n", kMaxDcdSizeBytes); + return; + } + + // Send the DCD Write command. + packCommand(kSDPCommand_DcdWrite.cmdType, m_address, 0, dataSize, 0); + if (sendCommand(packetizer) != kStatus_Success) + { + return; + } + + // Write the file data. + uint32_t bytesWritten; + if (fileProducer.sendTo(packetizer, &bytesWritten, m_progress) != kStatus_Success) + { + return; + } + + // Read and verify hab mode. + if (!isHabModeValid(packetizer)) + { + return; + } + + // Check for COMPLETE status. + uint8_t *data = getStatusResponse(packetizer); + if (!data) + { + return; + } + uint32_t intData = *((uint32_t *)data); + m_responseValues.push_back(intData); + if (intData != kWriteComplete) + { + Log::error("unexpected write file response 0x%x.\n", intData); + return; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// SDP SkipDcdHeader command +//////////////////////////////////////////////////////////////////////////////// + +bool SDPSkipDcdHeader::init() +{ + if (getArgCount() != 1) + { + return false; + } + + return true; +} + +void SDPSkipDcdHeader::sendTo(Packetizer &packetizer) +{ + packCommand(kSDPCommand_SkipDcdHeader.cmdType, 0, 0, 0, 0); + if (sendCommandGetHabMode(packetizer) != kStatus_Success) + { + return; + } + + // Read status response. + uint8_t *data = getStatusResponse(packetizer); + if (!data) + { + return; + } + uint32_t intData = *((uint32_t *)data); + m_responseValues.push_back(intData); +} + +//////////////////////////////////////////////////////////////////////////////// +// SDP JumpAddress command +//////////////////////////////////////////////////////////////////////////////// + +bool SDPJumpAddress::init() +{ + if (getArgCount() != 2) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_address)) + { + return false; // invalid 'addr' parameter + } + + return true; +} + +void SDPJumpAddress::sendTo(Packetizer &packetizer) +{ + packCommand(kSDPCommand_JumpAddress.cmdType, m_address, 0, 0, 0); + sendCommandGetHabMode(packetizer); +} + +//////////////////////////////////////////////////////////////////////////////// +// SDP SetBaudrate command +//////////////////////////////////////////////////////////////////////////////// + +bool SDPSetBaudrate::init() +{ + if (getArgCount() != 2) + { + return false; + } + + if (!utils::stringtoui(getArg(1), m_baudrate)) + { + Log::error("unrecognized baudrate string: %s\n", getArg(1).c_str()); + return false; // invalid 'baudrate' parameter + } + + return true; +} + +void SDPSetBaudrate::sendTo(Packetizer &packetizer) +{ + packCommand(kSDPCommand_SetBaudrate.cmdType, m_baudrate, 0, 0, 0); + sendCommandGetHabMode(packetizer); +} + +//////////////////////////////////////////////////////////////////////////////// +// SDP Ping command +//////////////////////////////////////////////////////////////////////////////// +bool SDPPing::init() +{ + if (getArgCount() != 1) + { + return false; + } + + return true; +} + +void SDPPing::sendTo(Packetizer &packetizer) +{ + uint8_t ping[2] = { 0x5a, 0xa6 }; + uint8_t *response = NULL; + uint32_t responseSize = 0; + status_t status = packetizer.writePacket(ping, sizeof(ping), kPacketType_Command); + if (status != kStatus_Success) + { + Log::error("Failed to send ping packet.\n"); + return; + } + + packetizer.setReadCount(12); + status = packetizer.readPacket(&response, &responseSize, kPacketType_Data); + if (status != kStatus_Success) + { + Log::error("getPingResponse.readPacket error %u.\n", status); + return; + } + + if (response[0] == 0x5a && response[1] == 0xa8) + { + uint32_t uuid[2]; + memcpy(&uuid[0], &response[4], sizeof(uuid)); + m_responseValues.push_back(kOk_Ack); + m_responseValues.push_back(uuid[0]); + m_responseValues.push_back(uuid[1]); + } + else + { + Log::error("Invalid ping response.\n"); + } +} diff --git a/src/blfwk/src/SDPUsbHidPacketizer.cpp b/src/blfwk/src/SDPUsbHidPacketizer.cpp new file mode 100644 index 0000000..a779a5b --- /dev/null +++ b/src/blfwk/src/SDPUsbHidPacketizer.cpp @@ -0,0 +1,99 @@ +/* +* Copyright (c) 2015 Freescale Semiconductor, Inc. +* All rights reserved. +* +* SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "blfwk/SDPUsbHidPacketizer.h" +#include "blfwk/Logging.h" + +using namespace blfwk; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +status_t SDPUsbHidPacketizer::writePacket(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType) +{ + assert(m_peripheral); + assert(packetType == kPacketType_Command || packetType == kPacketType_Data); + assert(byteCount); + assert(packet); + + uint32_t numBytes = 0; + + // Construct report contents. + memset(&m_report, 0, sizeof(m_report)); + if (packetType == kPacketType_Command) + { + m_report[0] = kIdReport1; // Report 1 is used to send a command to the device. + numBytes = kReport1SizeBytes; + } + else + { + m_report[0] = kIdReport2; // Report 2 is used to send data to the device. + numBytes = kReport2SizeBytes; + } + + memcpy(&m_report[1], packet, byteCount); + + return getPeripheral()->write((uint8_t *)&m_report, numBytes, m_packetTimeoutMs); +} + +status_t SDPUsbHidPacketizer::readPacket(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType) +{ + assert(m_peripheral); + assert(packet); + assert(packetLength); + *packet = NULL; + *packetLength = 0; + // packetType is not used. + + // Read report. + uint32_t actualBytes = 0; + uint32_t retryCnt = 0; + do + { + status_t retVal = + m_peripheral->read((uint8_t *)&m_report, sizeof(m_report), &actualBytes, m_packetTimeoutMs); + if (retVal != kStatus_Success) + { + return retVal; + } + + if (actualBytes) + { + // Check the report ID. + uint8_t reportId = m_report[0]; + if (reportId == kIdReport3) + { + if (actualBytes != kReport3SizeBytes) + { + Log::error("usbhid: received unexpected number of bytes=%x\n", actualBytes); + return kStatus_Fail; + } + } + else if (reportId == kIdReport4) + { + if (actualBytes != kReport4SizeBytes) + { + Log::error("usbhid: received unexpected number of bytes=%x\n", actualBytes); + return kStatus_Fail; + } + } + else + { + Log::error("usbhid: received unexpected report=%x\n", reportId); + return kStatus_Fail; + } + } + + } while (!actualBytes && (++retryCnt < kPollPacketMaxRetryCnt)); + + // Return results. + *packet = &m_report[1]; + *packetLength = actualBytes - 1; + + return kStatus_Success; +} diff --git a/src/blfwk/src/SRecordSourceFile.cpp b/src/blfwk/src/SRecordSourceFile.cpp new file mode 100644 index 0000000..77348ba --- /dev/null +++ b/src/blfwk/src/SRecordSourceFile.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "blfwk/smart_ptr.h" +#include "blfwk/SRecordSourceFile.h" +#include "blfwk/Logging.h" + +enum +{ + //! Size in bytes of the buffer used to collect S-record data records + //! before adding them to the executable image. Currently 64KB. + COLLECTION_BUFFER_SIZE = 64 * 1024 +}; + +using namespace blfwk; + +SRecordSourceFile::SRecordSourceFile(const std::string &path) + : SourceFile(path, kSRecordSourceFile) + , m_image(0) + , m_file(NULL) + , m_hasEntryRecord(false) +{ + memset(&m_entryRecord, 0, sizeof(m_entryRecord)); +} + +bool SRecordSourceFile::isSRecordFile(std::istream &stream) +{ + StSRecordFile srec(stream); + return srec.isSRecordFile(); +} + +void SRecordSourceFile::open() +{ + SourceFile::open(); + + // create file parser and examine file + m_file = new StSRecordFile(*m_stream); + m_file->parse(); + + // build an image of the file + m_image = new StExecutableImage(); + buildMemoryImage(); + + // dispose of file parser object + delete m_file; + m_file = 0; +} + +void SRecordSourceFile::close() +{ + assert(m_image); + + SourceFile::close(); + + // dispose of memory image + delete m_image; + m_image = 0; +} + +//! \pre The file must be open before this method can be called. +//! +DataSource *SRecordSourceFile::createDataSource() +{ + assert(m_image); + return new MemoryImageDataSource(m_image); +} + +//! \retval true The file has an S7, S8, or S9 record. +//! \retval false No entry point is available. +bool SRecordSourceFile::hasEntryPoint() +{ + return m_hasEntryRecord; +} + +//! If no entry point is available then 0 is returned instead. The method scans +//! the records in the file looking for S7, S8, or S9 records. Thus, 16-bit, +//! 24-bit, and 32-bit entry point records are supported. +//! +//! \return Entry point address. +//! \retval 0 No entry point is available. +uint32_t SRecordSourceFile::getEntryPointAddress() +{ + if (m_hasEntryRecord) + { + // the address in the record is the entry point + Log::log(Logger::kDebug2, "entry point address is 0x%08x\n", m_entryRecord.m_address); + return m_entryRecord.m_address; + } + + return 0; +} + +//! Scans the S-records of the file looking for data records. These are S3, S2, or +//! S1 records. The contents of these records are added to an StExecutableImage +//! object, which coalesces the individual records into contiguous regions of +//! memory. +//! +//! Also looks for S7, S8, or S9 records that contain the entry point. The first +//! match of one of these records is saved off into the #m_entryRecord member. +//! +//! \pre The #m_file member must be valid. +//! \pre The #m_image member variable must have been instantiated. +void SRecordSourceFile::buildMemoryImage() +{ + assert(m_file); + assert(m_image); + + // Clear the entry point related members. + m_hasEntryRecord = false; + memset(&m_entryRecord, 0, sizeof(m_entryRecord)); + + // Allocate buffer to hold data before adding it to the executable image. + // Contiguous records are added to this buffer. When overflowed or when a + // non-contiguous record is encountered the buffer is added to the executable + // image where it will be coalesced further. We don't add records individually + // to the image because coalescing record by record is very slow. + smart_array_ptr buffer = new uint8_t[COLLECTION_BUFFER_SIZE]; + unsigned startAddress = 0; + unsigned nextAddress = 0; + unsigned dataLength = 0; + + // process SRecords + StSRecordFile::const_iterator it = m_file->getBegin(); + for (; it != m_file->getEnd(); it++) + { + const StSRecordFile::SRecord &theRecord = *it; + + // only handle S3,2,1 records + bool isDataRecord = theRecord.m_type == 3 || theRecord.m_type == 2 || theRecord.m_type == 1; + bool hasData = theRecord.m_data && theRecord.m_dataCount; + if (isDataRecord && hasData) + { + // If this record's data would overflow the collection buffer, or if the + // record is not contiguous with the rest of the data in the collection + // buffer, then flush the buffer to the executable image and restart. + if (dataLength && + ((dataLength + theRecord.m_dataCount > COLLECTION_BUFFER_SIZE) || (theRecord.m_address != nextAddress))) + { + m_image->addTextRegion(startAddress, buffer, dataLength); + + dataLength = 0; + } + + // Capture addresses when starting an empty buffer. + if (dataLength == 0) + { + startAddress = theRecord.m_address; + nextAddress = startAddress; + } + + // Copy record data into place in the collection buffer and update + // size and address. + memcpy(&buffer[dataLength], theRecord.m_data, theRecord.m_dataCount); + dataLength += theRecord.m_dataCount; + nextAddress += theRecord.m_dataCount; + } + else if (!m_hasEntryRecord) + { + // look for S7,8,9 records + bool isEntryPointRecord = theRecord.m_type == 7 || theRecord.m_type == 8 || theRecord.m_type == 9; + if (isEntryPointRecord) + { + // save off the entry point record so we don't have to scan again + memcpy(&m_entryRecord, &theRecord, sizeof(m_entryRecord)); + m_hasEntryRecord = true; + } + } + } + + // Add any leftover data in the collection buffer to the executable image. + if (dataLength) + { + m_image->addTextRegion(startAddress, buffer, dataLength); + } +} diff --git a/src/blfwk/src/SearchPath.cpp b/src/blfwk/src/SearchPath.cpp new file mode 100644 index 0000000..249b7ec --- /dev/null +++ b/src/blfwk/src/SearchPath.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "blfwk/SearchPath.h" + +#if defined(WIN32) +#define PATH_SEP_CHAR '\\' +#define PATH_SEP_STRING "\\" +#else +#define PATH_SEP_CHAR '/' +#define PATH_SEP_STRING "/" +#endif + +PathSearcher *PathSearcher::s_searcher = NULL; + +//! This function will create the global path search object if it has +//! not already been created. +PathSearcher &PathSearcher::getGlobalSearcher() +{ + if (!s_searcher) + { + s_searcher = new PathSearcher; + } + + return *s_searcher; +} + +void PathSearcher::addSearchPath(std::string &path) +{ + m_paths.push_back(path); +} + +//! The \a base path argument can be either a relative or absolute path. If the path +//! is relative, then it is joined with search paths one after another until a matching +//! file is located or all search paths are exhausted. If the \a base is absolute, +//! only that path is tested and if invalid false is returned. +//! +//! \param base A path to the file that is to be found. +//! \param targetType Currently ignored. In the future it will let you select whether to +//! find a file or directory. +//! \param searchCwd If set to true, the current working directory is searched before using +//! any of the search paths. Otherwise only the search paths are considered. +//! \param[out] result When true is returned this string is set to the first path at which +//! a valid file was found. +//! +//! \retval true A matching file was found among the search paths. The contents of \a result +//! are a valid path. +//! \retval false No match could be made. \a result has been left unmodified. +bool PathSearcher::search(const std::string &base, target_type_t targetType, bool searchCwd, std::string &result) +{ + FILE *tempFile; + bool absolute = isAbsolute(base); + + // Try cwd first if requested. Same process applies to absolute paths. + if (absolute || searchCwd) + { + tempFile = fopen(base.c_str(), "r"); + if (tempFile) + { + fclose(tempFile); + result = base; + return true; + } + } + + // If the base path is absolute and the previous test failed, then we don't go any further. + if (absolute) + { + return false; + } + + // Iterate over all search paths. + string_list_t::const_iterator it = m_paths.begin(); + for (; it != m_paths.end(); ++it) + { + std::string searchPath = joinPaths(*it, base); + + tempFile = fopen(searchPath.c_str(), "r"); + if (tempFile) + { + fclose(tempFile); + result = searchPath; + return true; + } + } + + // Couldn't find anything matching the base path. + return false; +} + +bool PathSearcher::isAbsolute(const std::string &path) +{ +#if __WIN32__ + return path.size() >= 3 && path[1] == ':' && path[2] == '\\'; +#else + return path.size() >= 1 && path[0] == '/'; +#endif +} + +std::string PathSearcher::joinPaths(const std::string &first, const std::string &second) +{ + // Start with first string. + std::string result = first; + + // Add path separator if needed + if ((first[first.size() - 1] != PATH_SEP_CHAR) && (second[0] != PATH_SEP_CHAR)) + { + result += PATH_SEP_STRING; + } + + // Append the second string. + result += second; + + // And return the whole mess. + return result; +} diff --git a/src/blfwk/src/SerialPacketizer.cpp b/src/blfwk/src/SerialPacketizer.cpp new file mode 100644 index 0000000..5206206 --- /dev/null +++ b/src/blfwk/src/SerialPacketizer.cpp @@ -0,0 +1,668 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/Logging.h" +#include "blfwk/SerialPacketizer.h" +#include "blfwk/utils.h" +#include "crc/crc16.h" +#if defined(WIN32) +#include +#endif +#if defined(LINUX) || defined(MACOSX) +#include +#include +#endif + +using namespace blfwk; + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// +enum +{ + kReadRetries = 10, + kReadDelayMilliseconds = 10 +}; + +//! @brief Ping response. +const ping_response_t k_PingResponse = { + 0x50010203, + // { kSerialProtocol_Version_Bugfix, + // kSerialProtocol_Version_Minor, + // kSerialProtocol_Version_Major, + // kSerialProtocol_Version_Name }, + 0, // options, recalculate crc16 if this value changes + 0xeaaa // crc16 of start byte, packet type, version and options. + // i.e. [5a a7 00 00 01 50 00 00] + // Calculated using CRC-16/XMODEM. +}; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See SerialPacketizer.h for documentation of this method. +SerialPacketizer::SerialPacketizer(Peripheral *peripheral, uint32_t packetTimeoutMs) + : Packetizer(peripheral, packetTimeoutMs) +{ + // Clear the initial serial context + memset(&m_serialContext, 0, sizeof(m_serialContext)); +} + +// See SerialPacketizer.h for documentation of this method. +SerialPacketizer::~SerialPacketizer() +{ + delete m_peripheral; +} + +// See SerialPacketizer.h for documentation of this method. +void SerialPacketizer::finalize() +{ + send_deferred_ack(); +} + +// See SerialPacketizer.h for documentation of this method. +status_t SerialPacketizer::writePacket(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType) +{ + return serial_packet_write(packet, byteCount, packetType); +} + +// See SerialPacketizer.h for documentation of this method. +status_t SerialPacketizer::readPacket(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType) +{ + return serial_packet_read(packet, packetLength, packetType); +} + +// See SerialPacketizer.h for documentation of this method. +void SerialPacketizer::abortPacket() +{ + serial_packet_abort(); +} + +// See SerialPacketizer.h for documentation of this method. +void SerialPacketizer::sync() +{ + serial_packet_send_sync(kFramingPacketType_Ack); +} + +// See SerialPacketizer.h for documentation of this method. +uint32_t SerialPacketizer::getMaxPacketSize() +{ + return serial_packet_get_max_packet_size(); +} + +// See SerialPacketizer.h for documentation of this method. +void SerialPacketizer::host_delay(uint32_t milliseconds) +{ +// @todo implement for non-win32 +#if defined(WIN32) + Sleep(milliseconds); +#elif defined(LINUX) || defined(MACOSX) + usleep(milliseconds * 1000); +#endif +} + +// See SerialPacketizer.h for documentation of this method. +status_t SerialPacketizer::ping( + int retries, unsigned int delay, ping_response_t *pingResponse, int comSpeed, int *actualComSpeed) +{ + status_t status = kStatus_NoPingResponse; + uint8_t startByte = 0; + uint32_t bytesRead = 0; + const int initialRetries = retries; + static bool retryDoubledBaudrate = true; + + if (actualComSpeed != NULL) + { + *actualComSpeed = comSpeed; + } + + framing_header_t pingPacket; + pingPacket.startByte = kFramingPacketStartByte; + pingPacket.packetType = kFramingPacketType_Ping; + + // Send ping until we receive a start byte. + do + { + // Send the ping + if (m_peripheral->write((uint8_t *)&pingPacket, sizeof(pingPacket)) == kStatus_Success) + { + double timeout = 0.500; + double duration = 0.0; +#if defined(WIN32) + clock_t start = clock(); +#else //#elif defined(LINUX) || defined(MACOSX) + struct timespec start; + clock_gettime(CLOCK_REALTIME, &start); +#endif + + // Try for half a second to get a response from the ping. + while (duration < timeout) + { + if (m_peripheral->read(&startByte, sizeof(startByte), &bytesRead, + UartPeripheral::kUartPeripheral_UnusedTimeout) == kStatus_Success) + { + if (startByte == kFramingPacketStartByte) + { + break; + } + } + + host_delay(kReadDelayMilliseconds); + +#if defined(WIN32) + // Windows: CLOCKS_PER_SEC = 1,000, Linux & MACOSX: CLOCKS_PER_SEC = 1,000,000. + duration = (double)(clock() - start) / CLOCKS_PER_SEC; +#else //#elif defined(LINUX) || defined(MACOSX) + struct timespec current; + clock_gettime(CLOCK_REALTIME, ¤t); + duration = + (double)(current.tv_sec - start.tv_sec) + (double)(current.tv_nsec - start.tv_nsec) / 1000000000; +#endif + } + + // If we got our start byte, move on to read the response packet + if (startByte == kFramingPacketStartByte) + { + break; + } + } + + host_delay(delay); + + } while (retries--); + + if (startByte == kFramingPacketStartByte) + { + Log::info("Ping responded in %d attempt(s)\n", (initialRetries - retries) + 1); + + // Wait for the rest of the ping bytes + // In the case of testing low baud rates the target needs time to respond + // 100 baud rate reply is looking for 9 more bytes = 90 bits with start/stop overhead + // 90 bits / 100 baud = .9 seconds = 900 milliseconds. The additional 20 milliseconds is to ensure + // that even high baud rates have some sort of delay and to give a little wiggle room for lower baud rates + if (comSpeed) + { + host_delay(((1000 * 90) / comSpeed) + 20); + } + + // Read response packet type. + uint8_t packetType; + status = m_peripheral->read(&packetType, sizeof(packetType), &bytesRead, + UartPeripheral::kUartPeripheral_UnusedTimeout); + if (status == kStatus_Success) + { + if (packetType == kFramingPacketType_PingResponse) + { + // Read response. + ping_response_t response; + status = m_peripheral->read((uint8_t *)&response, sizeof(response), &bytesRead, + UartPeripheral::kUartPeripheral_UnusedTimeout); + if (status == kStatus_Success) + { + // Validate reponse CRC. + + // Initialize the CRC16 information. + uint16_t crc16; + crc16_data_t crcInfo; + crc16_init(&crcInfo); + + // Include the start byte and packetType in the CRC. + crc16_update(&crcInfo, &startByte, sizeof(startByte)); + crc16_update(&crcInfo, &packetType, sizeof(packetType)); + + // Run CRC on all other bytes except the CRC field. + crc16_update(&crcInfo, (uint8_t *)&response, sizeof(response) - sizeof(uint16_t)); + + // Finalize the CRC calculations + crc16_finalize(&crcInfo, &crc16); + + if (response.crc16 == crc16) + { + Log::debug("Framing protocol version = 0x%x, options = 0x%x\n", response.version, + response.options); + m_version = response.version; + m_options = response.options; + + if (pingResponse) + { + *pingResponse = response; + } + + status = kStatus_Success; + } + else + { + Log::info("Error: ping crc16 failed, received 0x%x, expected 0x%x\n", response.crc16, crc16); + status = kStatus_InvalidCRC; + } + } + } + else + { + status = kStatus_InvalidPacketType; + } + } + } + + return status; +} + +// Private Implementation + +// See SerialPacketizer.h for documentation on this function. +status_t SerialPacketizer::serial_packet_read(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType) +{ + if (!packet || !packetLength) + { + Log::error("Error: invalid packet\r\n"); + return kStatus_InvalidArgument; + } + *packetLength = 0; + status_t status; + + m_serialContext.isBackToBackWrite = false; + + // Send ACK if needed. + status = send_deferred_ack(); + if (status != kStatus_Success) + { + return status; + } + + framing_data_packet_t framingPacket; + + bool isPacketOk; + do + { + // Clear the packet data area so unsent parameters default to zero. + memset(m_serialContext.data, 0, sizeof(m_serialContext.data)); + + // Receive the framing data packet. + isPacketOk = true; + status_t status = read_data_packet(&framingPacket, m_serialContext.data, packetType); + if (status != kStatus_Success) + { + // No packet available. + *packetLength = 0; + return status; + } + + // Verify crc. + uint16_t calculated_crc = calculate_framing_crc16(&framingPacket, m_serialContext.data); + if (framingPacket.crc16 != calculated_crc) + { + Log::error("Error: invalid crc 0x%x, expected 0x%x\r\n", framingPacket.crc16, calculated_crc); + isPacketOk = false; + } + + // Send Nak if necessary. + if (!isPacketOk) + { + serial_packet_send_sync(kFramingPacketType_Nak); + } + } while (!isPacketOk); + + // Indicate an ACK must be sent. + m_serialContext.isAckNeeded = true; + + // Set caller's data buffer and length + *packet = m_serialContext.data; + *packetLength = framingPacket.length; + + return kStatus_Success; +} + +// See SerialPacketizer.h for documentation on this function. +status_t SerialPacketizer::serial_packet_write(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType) +{ + if (!packet || (byteCount > kOutgoingPacketBufferSize)) + { + Log::error("Error: invalid packet or packet size %d\r\n", byteCount); + return kStatus_InvalidArgument; + } + + // Send ACK if needed. + status_t status = send_deferred_ack(); + if (status != kStatus_Success) + { + return status; + } + + // Back-to-back writes require delay for receiver to enter peripheral read routine. + if (m_serialContext.isBackToBackWrite) + { + m_serialContext.isBackToBackWrite = false; + + host_delay(100); + } + + // Initialize the framing data packet. + serial_framing_packet_t *framingPacket = &m_serialContext.framingPacket; + framingPacket->dataPacket.header.startByte = kFramingPacketStartByte; + framingPacket->dataPacket.header.packetType = kFramingPacketType_Command; + if (packetType != kPacketType_Command) + { + framingPacket->dataPacket.header.packetType = kFramingPacketType_Data; + } + framingPacket->dataPacket.length = (uint16_t)byteCount; + + // Copy the caller's data buffer into the framing packet. + if (byteCount) + { + memcpy(framingPacket->data, packet, byteCount); + } + + // Calculate and set the framing packet crc. + framingPacket->dataPacket.crc16 = + calculate_framing_crc16(&framingPacket->dataPacket, (uint8_t *)framingPacket->data); +#if defined(TEST_NAK) + ++framingPacket->dataPacket.crc16; +#endif // TEST_NAK + + // Send the framing data packet. + status = m_peripheral->write((uint8_t *)framingPacket, sizeof(framing_data_packet_t) + byteCount); + if (status != kStatus_Success) + { + return status; + } + + return wait_for_ack_packet(); +} + +// See SerialPacketizer.h for documentation on this function. +void SerialPacketizer::serial_packet_abort() +{ + assert(m_serialContext.isAckNeeded); + m_serialContext.isAckAbortNeeded = true; + m_serialContext.isAckNeeded = false; +} + +// See SerialPacketizer.h for documentation on this function. +uint32_t SerialPacketizer::serial_packet_get_max_packet_size() +{ + return kMaxHostPacketSize; +} + +// See SerialPacketizer.h for documentation on this function. +status_t SerialPacketizer::serial_packet_send_sync(uint8_t framingPacketType) +{ + framing_sync_packet_t sync; + sync.header.startByte = kFramingPacketStartByte; + sync.header.packetType = framingPacketType; + + // Indicate last transaction was a write. + m_serialContext.isBackToBackWrite = true; + + status_t status = m_peripheral->write((uint8_t *)&sync, sizeof(sync)); + if (status != kStatus_Success) + { + Log::error("Error: cannot send sync packet 0x%x, status = 0x%x\r\n", framingPacketType, status); + } + + return status; +} + +// See SerialPacketizer.h for documentation of this method. +status_t SerialPacketizer::wait_for_ack_packet() +{ + framing_sync_packet_t sync; + status_t status = kStatus_NoCommandResponse; + + do + { + // Receive the sync packet. + status = read_header(&sync.header); + if (status != kStatus_Success) + { + break; + } + + if ((sync.header.packetType != kFramingPacketType_Ack) && (sync.header.packetType != kFramingPacketType_Nak) && + (sync.header.packetType != kFramingPacketType_AckAbort)) + { + Log::error("Error: Unexpected sync byte 0x%x received, expected Ack, AckAbort or Nak\r\n", + sync.header.packetType); + status = kStatus_InvalidPacketType; + break; + } + + if (sync.header.packetType == kFramingPacketType_AckAbort) + { + status = kStatus_AbortDataPhase; + break; + } + + if (sync.header.packetType == kFramingPacketType_Nak) + { +// Re-transmit the last packet. +#if defined(TEST_NAK) + --g_serialContext.framingPacket.dataPacket.crc16; +#endif // TEST_NAK + status = + m_peripheral->write((uint8_t *)&m_serialContext.framingPacket, + sizeof(framing_data_packet_t) + m_serialContext.framingPacket.dataPacket.length); + if (status != kStatus_Success) + { + break; + } + } + + } while (sync.header.packetType == kFramingPacketType_Nak); + + return status; +} + +// See SerialPacketizer.h for documentation on this function. +status_t SerialPacketizer::serial_send_ping_response() +{ + assert(m_peripheral); + + // Only reply if we're in an idle state + if (!m_serialContext.isAckNeeded || !m_serialContext.isBackToBackWrite || !m_serialContext.isAckAbortNeeded) + { + const uint8_t header[] = { kFramingPacketStartByte, kFramingPacketType_PingResponse }; + m_peripheral->write((const uint8_t *)&header, sizeof(header)); + m_peripheral->write((uint8_t *)&k_PingResponse, sizeof(k_PingResponse)); + } + + return kStatus_Ping; +} + +// See SerialPacketizer.h for documentation on this function. +status_t SerialPacketizer::send_deferred_ack() +{ + if (m_serialContext.isAckNeeded) + { + // Send Ack for last received packet. + m_serialContext.isAckNeeded = false; + return serial_packet_send_sync(kFramingPacketType_Ack); + } + else if (m_serialContext.isAckAbortNeeded) + { + // Send AckAbort for last received packet. + m_serialContext.isAckAbortNeeded = false; + return serial_packet_send_sync(kFramingPacketType_AckAbort); + } + else + { + return kStatus_Success; + } +} + +// See SerialPacketizer.h for documentation on this function. +status_t SerialPacketizer::read_data_packet(framing_data_packet_t *packet, uint8_t *data, packet_type_t packetType) +{ + // Read the packet header. + status_t status = read_header(&packet->header); + if (status != kStatus_Success) + { + return status; + } + + if (packet->header.packetType == kFramingPacketType_Ping) + { + return serial_send_ping_response(); + } + + uint8_t expectedPacketType = kFramingPacketType_Command; + + if (packetType != kPacketType_Command) + { + expectedPacketType = kFramingPacketType_Data; + } + if (packet->header.packetType != expectedPacketType) + { + Log::error("Error: read_data_packet found unexpected packet type 0x%x\r\n", packet->header.packetType); + return kStatus_Fail; + } + + // Read the packet length. + status = read_length(packet); + if (status != kStatus_Success) + { + return status; + } + + // Make sure the packet doesn't exceed the allocated buffer size. + if (packet->length > getMaxPacketSize()) + { + Log::error("Error: Data packet size(%d) is bigger than max supported size(%d)\r\n", packet->length, + getMaxPacketSize()); + return kStatus_Fail; + } + + // Read the crc + status = read_crc16(packet); + if (status != kStatus_Success) + { + return status; + } + + // Read the data. + if (packet->length > 0) + { + // Clear the data area so unsent parameters default to zero. + memset(data, 0, packet->length); + + status = m_peripheral->read(data, packet->length, NULL, UartPeripheral::kUartPeripheral_UnusedTimeout); + } + + return status; +} + +// See SerialPacketizer.h for documentation on this function. +status_t SerialPacketizer::read_start_byte(framing_header_t *header) +{ + double timeout = (double)m_packetTimeoutMs / 1000; + double duration = 0.0; + clock_t start = clock(); + + // Read until start byte found. + while (duration < timeout) + { + status_t status = + m_peripheral->read(&header->startByte, 1, NULL, UartPeripheral::kUartPeripheral_UnusedTimeout); + if (status != kStatus_Success && status != kStatus_Timeout) + { + return status; + } + + if (status == kStatus_Success) + { + if (header->startByte == kFramingPacketStartByte) + { + return kStatus_Success; + } + } + + // This will keep us from doing non necessary delays in case the byte received + // is actually the start byte, this delay and retry scenario is for cases when waiting + // for a response from a device that was issued a long running command like a flash-erase-region + // that may take several seconds to complete. + host_delay(kDefaultByteReadTimeoutMs); + + // Windows: CLOCKS_PER_SEC = 1,000, Linux & MACOSX: CLOCKS_PER_SEC = 1,000,000. + duration = (double)(clock() - start) / CLOCKS_PER_SEC; + } + + Log::error("Error: read_start_byte() timeout after %2.3f seconds\n", duration); + return kStatus_Timeout; +} + +// See SerialPacketizer.h for documentation on this function. +status_t SerialPacketizer::read_header(framing_header_t *header) +{ + // Wait for start byte. + status_t status = read_start_byte(header); + if (status != kStatus_Success) + { + return status; + } + + return m_peripheral->read(&header->packetType, sizeof(header->packetType), NULL, + UartPeripheral::kUartPeripheral_UnusedTimeout); +} + +// See SerialPacketizer.h for documentation on this function. +status_t SerialPacketizer::read_length(framing_data_packet_t *packet) +{ + union + { + uint8_t bytes[sizeof(uint16_t)]; + uint16_t halfword; + } buffer; + + status_t status = m_peripheral->read((uint8_t *)&buffer.bytes, sizeof(buffer), NULL, + UartPeripheral::kUartPeripheral_UnusedTimeout); + + packet->length = buffer.halfword; + return status; +} + +// See SerialPacketizer.h for documentation on this function. +status_t SerialPacketizer::read_crc16(framing_data_packet_t *packet) +{ + union + { + uint8_t bytes[sizeof(uint16_t)]; + uint16_t halfword; + } buffer; + + status_t status = m_peripheral->read((uint8_t *)&buffer.bytes, sizeof(buffer), NULL, + UartPeripheral::kUartPeripheral_UnusedTimeout); + + packet->crc16 = buffer.halfword; + return status; +} + +// See SerialPacketizer.h for documentation on this function. +uint16_t SerialPacketizer::calculate_framing_crc16(framing_data_packet_t *packet, const uint8_t *data) +{ + uint16_t crc16; + + // Initialize the CRC16 information + crc16_data_t crcInfo; + crc16_init(&crcInfo); + + // Run CRC on all header bytes besides the CRC field + crc16_update(&crcInfo, (uint8_t *)&packet->header.startByte, sizeof(framing_data_packet_t) - sizeof(uint16_t)); + + // Continue running CRC on any payload bytes + crc16_update(&crcInfo, data, packet->length); + + // Finalize the CRC calculations + crc16_finalize(&crcInfo, &crc16); + + return crc16; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/SimPacketizer.cpp b/src/blfwk/src/SimPacketizer.cpp new file mode 100644 index 0000000..d4bc03c --- /dev/null +++ b/src/blfwk/src/SimPacketizer.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/SimPacketizer.h" +#include "blfwk/Logging.h" +#include "bootloader/bl_command.h" +#include "bootloader/bootloader.h" + +using namespace blfwk; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See SimPacketizer.h for documentation of this method. +SimPacketizer::SimPacketizer(SimPeripheral *peripheral) + : Packetizer(dynamic_cast(peripheral), kSimReadTimeoutMs) + , m_isPumpEnabled(false) + , m_isAborted(false) +{ +} + +// See SimPacketizer.h for documentation of this method. +SimPacketizer::~SimPacketizer() +{ + delete m_peripheral; +} + +// See SimPacketizer.h for documentation of this method. +void SimPacketizer::finalize() +{ +} + +// See SimPacketizer.h for documentation of this method. +status_t SimPacketizer::writePacket(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType) +{ + assert(packet); + + // Check for receiver data phase abort. + if (m_isAborted) + { + m_isAborted = false; + return kStatus_AbortDataPhase; + } + + // Write framing packet header. + uint8_t buffer = (uint8_t)byteCount; + status_t status = m_peripheral->write(&buffer, 1); + + // Write the packet. + if (byteCount && (status == kStatus_Success)) + { + status = m_peripheral->write(packet, byteCount); + } + + pumpSimulator(); + return status; +} + +// See SimPacketizer.h for documentation of this method. +status_t SimPacketizer::readPacket(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType) +{ + assert(packet); + assert(packetLength); + *packet = NULL; + *packetLength = 0; + + // Read framing packet header. + uint8_t buffer; + status_t status = m_peripheral->read(&buffer, 1, NULL, 0); + if (status != kStatus_Success) + { + // fatal error in sumulator + Log::error("Error: no data for readPacket\n"); + return status; + } + + // Read the packet. + int length = (int)(buffer); + if (length == 0) + { + // zero length packet + return kStatus_Success; + } + + status = m_peripheral->read(m_buffer, length, NULL, 0); + if (status != kStatus_Success) + { + Log::error("Error: malformed packet\n"); + return kStatus_Fail; + } + + *packet = m_buffer; + *packetLength = length; + return kStatus_Success; +} + +void SimPacketizer::pumpSimulator() +{ + if (m_isPumpEnabled) + { + bootloader_command_pump(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/SimPeripheral.cpp b/src/blfwk/src/SimPeripheral.cpp new file mode 100644 index 0000000..5b1a92d --- /dev/null +++ b/src/blfwk/src/SimPeripheral.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/SimPeripheral.h" +#include "blfwk/Logging.h" + +using namespace blfwk; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See sim_peripheral.h for documentation of this method. +status_t SimPeripheral::read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t timeoutMs) +{ + if (m_inStream->empty()) + return kStatus_Fail; + + uint32_t bytesRead = 0; + for (int i = 0; i < (int)requestedBytes; ++i) + { + if (m_inStream->empty()) + break; + unsigned char val = m_inStream->front(); + m_inStream->pop_front(); + buffer[i] = val; + ++bytesRead; + } + + if (actualBytes) + { + *actualBytes = bytesRead; + } + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes read in hex + Log::debug2("<"); + for (int i = 0; i < (int)bytesRead; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (bytesRead - 1)) + { + Log::debug2(" "); + } + } + Log::debug2(">\n"); + } + + return kStatus_Success; +} + +// See sim_peripheral.h for documentation of this method. +status_t SimPeripheral::write(const uint8_t *buffer, uint32_t byteCount) +{ + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes written in hex + Log::debug2("["); + for (int i = 0; i < (int)byteCount; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (byteCount - 1)) + { + Log::debug2(" "); + } + } + Log::debug2("]\n"); + } + + for (int i = 0; i < (int)byteCount; ++i) + { + m_outStream->push_back(buffer[i]); + } + + return kStatus_Success; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/Simulator.cpp b/src/blfwk/src/Simulator.cpp new file mode 100644 index 0000000..fe7e610 --- /dev/null +++ b/src/blfwk/src/Simulator.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/Simulator.h" + +#include "bootloader/bl_version.h" +#include "memory/memory.h" +#include "packet/serial_packet.h" +#include "property/property.h" +#ifdef WIN32 +#include +#endif + +using namespace blfwk; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See host_peripheral.h for documentation of this method. +status_t host_peripheral_read(const peripheral_descriptor_t *self, uint8_t *buffer, uint32_t requestedBytes) +{ + Simulator &sim = Simulator::getSimulator(); + Peripheral *device = sim.getHost()->getPeripheral(); + return device->read(buffer, requestedBytes, NULL, 0); +} + +// See host_peripheral.h for documentation of this method. +status_t host_peripheral_write(const peripheral_descriptor_t *self, const uint8_t *buffer, uint32_t byteCount) +{ + Simulator &sim = Simulator::getSimulator(); + Peripheral *device = sim.getHost()->getPeripheral(); + device->write(buffer, byteCount); + return kStatus_Success; +} + +// See host_peripheral.h for documentation of this method. +status_t device_peripheral_read(const peripheral_descriptor_t *self, uint8_t *buffer, uint32_t requestedBytes) +{ + Simulator &sim = Simulator::getSimulator(); + Peripheral *device = sim.getDevice()->getPeripheral(); + device->read(buffer, requestedBytes, NULL, 0); + return kStatus_Success; +} + +// See host_peripheral.h for documentation of this method. +status_t device_peripheral_write(const peripheral_descriptor_t *self, const uint8_t *buffer, uint32_t byteCount) +{ + Simulator &sim = Simulator::getSimulator(); + Peripheral *device = sim.getDevice()->getPeripheral(); + device->write(buffer, byteCount); + return kStatus_Success; +} + +// See host_packetizer.h for documentation of this method. +status_t host_packet_read(const peripheral_descriptor_t *self, + uint8_t **packet, + uint32_t *packetLength, + packet_type_t packetType) +{ + Simulator &sim = Simulator::getSimulator(); + Packetizer *device = sim.getDevice(); + return device->readPacket(packet, packetLength, packetType); +} + +// See host_packetizer.h for documentation of this method. +status_t host_packet_write(const peripheral_descriptor_t *self, + const uint8_t *packet, + uint32_t byteCount, + packet_type_t packetType) +{ + Simulator &sim = Simulator::getSimulator(); + Packetizer *device = sim.getDevice(); + return device->writePacket(packet, byteCount, packetType); +} + +// See host_packetizer.h for documentation of this method. +void host_packet_abort(const peripheral_descriptor_t *self) +{ + Simulator &sim = Simulator::getSimulator(); + Packetizer *host = sim.getHost(); + host->setAborted(true); +} + +// See host_packetizer.h for documentation of this method. +status_t host_packet_finalize(const peripheral_descriptor_t *self) +{ + Simulator &sim = Simulator::getSimulator(); + Packetizer *host = sim.getHost(); + host->finalize(); + return kStatus_Success; +} + +uint32_t host_get_max_packet_size(const peripheral_descriptor_t *self) +{ + Simulator &sim = Simulator::getSimulator(); + Packetizer *host = sim.getHost(); + return host->getMaxPacketSize(); +} + +void host_delay(uint32_t milliseconds) +{ +// @todo implement for non-win32 +#ifdef WIN32 + Sleep(milliseconds); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Bootloader global context data. +//! +//! Referenced by both the simulated host and simulated device sides. +bootloader_context_t g_bootloaderContext = { + &g_memoryInterface, // Memory interface. + g_memoryMap, // Memory map. + &g_propertyInterface, // Property store interface. + &g_commandInterface, // Command processor interface. + NULL, // Flash driver interface - not used on host + NULL, // Peripheral array - filled in at run-time. + NULL, // Active peripheral - filled in at run-time. + 0 // Flash driver state - typed to a u32 and unused for host +}; + +//! @brief Interface to host peripheral operations. +const peripheral_byte_inteface_t g_hostPeripheralInterface = { + NULL, // init + host_peripheral_read, // read + host_peripheral_write // write +}; + +//! @brief Interface to device peripheral operations. +const peripheral_byte_inteface_t g_devicePeripheralInterface = { + NULL, // init + device_peripheral_read, // read + device_peripheral_write // write +}; + +//! @brief Interface to host packet operations. +const peripheral_packet_interface_t g_hostPacketInterface = { + NULL, // init + host_packet_read, // readPacket + host_packet_write, // writePacket + host_packet_abort, // abortPacket + host_packet_finalize, // finalize + host_get_max_packet_size, // getMaxPacketSize +}; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// +// See host_bootloader.h for documentation of this method. +Simulator::Simulator() + : Bootloader() + , m_devicePacketizer(NULL) +{ + // Create host peripheral and packetizer. + SimPeripheral *hostPeripheral = new SimPeripheral(&m_responseStream, &m_commandStream); + m_hostPacketizer = new SimPacketizer(hostPeripheral); + + // Enable the host packetizer to pump the simulator state machine as necessary. + ((SimPacketizer *)m_hostPacketizer)->enableSimulatorPump(); + + // Create device peripheral and packetizer. + SimPeripheral *devicePeripheral = new SimPeripheral(&m_commandStream, &m_responseStream); + m_devicePacketizer = new SimPacketizer(devicePeripheral); + + init(); +} + +// See host_bootloader.h for documentation of this method. +Simulator::~Simulator() +{ + // Delete packetizer should close handles and free memory on Peripheral. + delete m_devicePacketizer; + + // Delete all memory stores that were new'd in init(). + m_memoryStore.clear(); +} + +// See host_bootloader.h for documentation of this method. +void Simulator::init() +{ + // Initialize the property store component. + bootloader_property_init(); + + g_bootloaderContext.activePeripheral = &m_activePeripheral; + + // Create all memory stores. + // Stores are never deleted because bootloader is a singleton. + FlashMemoryStore *flash = new FlashMemoryStore; + m_memoryStore.push_back(flash); + SramMemoryStore *sram = new SramMemoryStore; + m_memoryStore.push_back(sram); + + // Initialize the command processor component. + bootloader_command_init(); + m_commandProcessor.state = kCommandState_CommandPhase; + + // Use the host peripheral and packet interfaces. + m_activePeripheral.byteInterface = &g_hostPeripheralInterface; + m_activePeripheral.packetInterface = &g_hostPacketInterface; +} + +// See host_bootloader.h for documentation of this method. +bool Simulator::openStateFiles(const string &pathToDir, bool forceCreate) +{ + // Open all memory stores. + memory_vector_t::iterator it = m_memoryStore.begin(); + for (; it != m_memoryStore.end(); ++it) + { + if (!(*it)->open(pathToDir, forceCreate)) + { + return false; + } + } + + if (!m_optionsStore.init(pathToDir, forceCreate)) + { + return false; + } + + m_areStateFilesOpen = true; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/SimulatorMemory.cpp b/src/blfwk/src/SimulatorMemory.cpp new file mode 100644 index 0000000..7fe2fb5 --- /dev/null +++ b/src/blfwk/src/SimulatorMemory.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/Simulator.h" +#include "blfwk/SimulatorMemory.h" +#include "blfwk/Logging.h" +#include "memory/memory.h" +//#include "fsl_platform_status.h" +//#include +#ifdef WIN32 +#include +#elif LINUX +#include +#include "sys/stat.h" +#include +#else +#include "sys/stat.h" +#endif + +using namespace blfwk; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Interface to simulator memory operations. +const memory_region_interface_t g_flashMemoryInterface = { NULL, sim_flash_read, sim_flash_write, + sim_flash_fill, NULL, NULL }; + +//! @brief Interface to simulator SRAM memory operations. +const memory_region_interface_t g_sramMemoryInterface = { + NULL, sim_sram_mem_read, sim_sram_mem_write, sim_sram_mem_fill, NULL, NULL +}; + +//! @brief Interface to simulator device peripheral memory operations. +const memory_region_interface_t g_deviceMemoryInterface = { + NULL, sim_device_mem_read, sim_device_mem_write, sim_device_mem_fill, NULL, NULL +}; + +//! @brief Memory map for KL25Z128. +memory_map_entry_t g_memoryMap[] = { + { 0x00000000, 0x0001ffff, &g_flashMemoryInterface }, // Flash array + { 0x1ffff000, 0x20002fff, &g_sramMemoryInterface }, // SRAM_L + SRAM_U + { 0x40000000, 0x4007ffff, &g_deviceMemoryInterface }, // AIPS peripherals + { 0x400ff000, 0x400fffff, &g_deviceMemoryInterface }, // GPIO + { 0x44000000, 0x5fffffff, &g_deviceMemoryInterface }, // BME + { 0xe0000000, 0xe00fffff, &g_deviceMemoryInterface }, // M0+ private peripherals + { 0xf0003000, 0xf0003fff, &g_deviceMemoryInterface }, // MCM + { 0xf8000000, 0xffffffff, &g_deviceMemoryInterface }, // IOPORT (single-cycle GPIO) + { 0 } // Terminator +}; + +#ifdef WIN32 +//! @brief File separator character. +static const string k_fileSeparator("\\"); +static const unsigned k_maxPath = MAX_PATH; +#else +static const string k_fileSeparator("/"); +static const unsigned k_maxPath = PATH_MAX; +#endif + +//! @brief Options state file name. +static const string k_optionsFileName = "state_init.dat"; + +//! @brief Memory state file name. +static const string k_memoryFileName = "state_mem0.dat"; + +// Forward function declarations +static status_t sim_mem_read(int mapIndex, uint32_t address, uint32_t length, uint8_t *buffer); +static status_t sim_mem_write(int mapIndex, uint32_t address, uint32_t length, const uint8_t *buffer); +static status_t sim_mem_fill(int mapIndex, uint32_t address, uint32_t length, uint32_t pattern); + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +//@brief Create all directories in a path if they don't exist. +//! +//! Errors are ignored. +//! +//! @param path Directory path, must end with a separator. +static void createPath(const char *path) +{ + char folder[k_maxPath] = { 0 }; + const char *end = strchr(path, k_fileSeparator[0]); + + while (end != NULL) + { + strncpy(folder, path, end - path + 1); +#ifdef WIN32 + CreateDirectory(folder, NULL); +#else + mkdir(folder, 0755); +#endif + end = strchr(++end, k_fileSeparator[0]); + } +} + +// See host_memory.h for documentation of this method. +bool MemoryStore::open(const string &pathToDir, bool forceCreate) +{ + // Create all directories in the path if they don't exist. + // Errors will be caught by trying to open the file below. + string path(pathToDir); + path += k_fileSeparator; + createPath(path.c_str()); + + // Create the file name, replacing the '0' with the actual map index. + path += k_memoryFileName; + size_t pos = path.find_last_of('0'); + path.replace(pos, 1, 1, static_cast(m_mapIndex + '0')); + + close(); + m_memoryFile = NULL; + bool doFill = false; + + if (!forceCreate) + { + // Try to open existing file. + m_memoryFile = fopen(path.c_str(), "r+b"); + } + + if (!m_memoryFile) + { + // Create new file. + m_memoryFile = fopen(path.c_str(), "w+b"); + doFill = true; + } + + if (m_memoryFile) + { + m_startAddress = g_memoryMap[m_mapIndex].startAddress; + m_size = (g_memoryMap[m_mapIndex].endAddress + 1) - m_startAddress; + if (doFill) + { + erase(); + } + } + else + { + Log::error("Error: cannot open or create memory file '%s'\n", path.c_str()); + return false; + } + + return true; +} + +// See host_memory.h for documentation of this method. +size_t MemoryStore::read(long int offset, size_t size, unsigned char *buffer) +{ + if (m_memoryFile) + { + fseek(m_memoryFile, offset - m_startAddress, SEEK_SET); + return fread(buffer, 1, size, m_memoryFile); + } + return 0; +} + +// See host_memory.h for documentation of this method. +size_t MemoryStore::write(long int offset, size_t size, const unsigned char *buffer) +{ + if (m_memoryFile) + { + fseek(m_memoryFile, offset - m_startAddress, SEEK_SET); + return fwrite(buffer, 1, size, m_memoryFile); + } + return 0; +} + +// See host_memory.h for documentation of this method. +void MemoryStore::erase(long int offset, size_t size) +{ + if (m_memoryFile) + { + unsigned char *fill = new unsigned char[size]; + memset(fill, m_fillByte, size); + fseek(m_memoryFile, offset - m_startAddress, SEEK_SET); + fwrite(fill, 1, size, m_memoryFile); + delete[] fill; + } +} + +//! @brief Read from simulated memory. +//! +//! @param mapIndex Index into memory map array +//! @param address Simulated address +//! @param length Number of bytes to read +//! @param buffer Destination buffer +//! +//! @retval kStatusMemoryReadFailed +//! @retval kStatus_Success +static status_t sim_mem_read(int mapIndex, uint32_t address, uint32_t length, uint8_t *buffer) +{ + Simulator &sim = Simulator::getSimulator(); + MemoryStore *mem = sim.getMemoryStore(mapIndex); + if (mem->read(address, length, buffer) != length) + { + return kStatusMemoryReadFailed; + } + else + { + return kStatus_Success; + } +} + +//! @brief Write to simulated memory. +//! +//! @param mapIndex Index into memory map array +//! @param address Simulated address +//! @param length Number of bytes to write +//! @param buffer Source buffer +//! +//! @retval kStatusMemoryWriteFailed +//! @retval kStatus_Success +static status_t sim_mem_write(int mapIndex, uint32_t address, uint32_t length, const uint8_t *buffer) +{ + Simulator &sim = Simulator::getSimulator(); + MemoryStore *mem = sim.getMemoryStore(mapIndex); + if (mem->write(address, length, buffer) != length) + { + return kStatusMemoryWriteFailed; + } + else + { + return kStatus_Success; + } +} + +//! @brief Fill simulated memory. +//! +//! Pattern is a 4-byte pattern that can be written at any byte alignment. +//! Pattern must contain all bytes to write. To fill every byte +//! with the same value, the value must be repeat in the word. +//! For example, pattern=0x55555555. +//! +//! @param mapIndex Index into memory map array +//! @param address Simulated address +//! @param length Number of bytes to write +//! @param pattern 4-byte pattern to write +//! +//! @retval kStatusMemoryWriteFailed +//! @retval kStatus_Success +static status_t sim_mem_fill(int mapIndex, uint32_t address, uint32_t length, uint32_t pattern) +{ + Simulator &sim = Simulator::getSimulator(); + MemoryStore *mem = sim.getMemoryStore(mapIndex); + unsigned char b3 = (unsigned char)((pattern >> 24) & 0xff); + unsigned char b2 = (unsigned char)((pattern >> 16) & 0xff); + unsigned char b1 = (unsigned char)((pattern >> 8) & 0xff); + unsigned char b0 = (unsigned char)(pattern & 0xff); + while (length) + { + if (mem->write(address++, 1, &b0) != 1) + { + Log::error("Error: memory write failed\n"); + return kStatusMemoryWriteFailed; + } + if (--length == 0) + break; + + if (mem->write(address++, 1, &b1) != 1) + { + Log::error("Error: memory write failed\n"); + return kStatusMemoryWriteFailed; + } + if (--length == 0) + break; + + if (mem->write(address++, 1, &b2) != 1) + { + Log::error("Error: memory write failed\n"); + return kStatusMemoryWriteFailed; + } + if (--length == 0) + break; + + if (mem->write(address++, 1, &b3) != 1) + { + Log::error("Error: memory write failed\n"); + return kStatusMemoryWriteFailed; + } + --length; + } + + return kStatus_Success; +} + +// See host_memory.h for documentation of this method. +status_t sim_flash_read(uint32_t address, uint32_t length, uint8_t *buffer) +{ + return sim_mem_read(MemoryStore::kMapIndexFlash, address, length, buffer); +} + +// See host_memory.h for documentation of this method. +status_t sim_flash_write(uint32_t address, uint32_t length, const uint8_t *buffer) +{ + return sim_mem_write(MemoryStore::kMapIndexFlash, address, length, buffer); +} + +// See host_memory.h for documentation of this method. +status_t sim_flash_fill(uint32_t address, uint32_t length, uint32_t pattern) +{ + return sim_mem_fill(MemoryStore::kMapIndexFlash, address, length, pattern); +} + +// See host_memory.h for documentation of this method. +status_t sim_flash_erase(uint32_t address, uint32_t length) +{ + host_flash_erase_region(address, length); + return kStatus_Success; +} + +// See host_memory.h for documentation of this method. +status_t sim_flash_erase_all(void) +{ + host_flash_erase_all(); + return kStatus_Success; +} + +// See host_memory.h for documentation of this method. +status_t sim_sram_mem_read(uint32_t address, uint32_t length, uint8_t *buffer) +{ + return sim_mem_read(MemoryStore::kMapIndexSRAM, address, length, buffer); +} + +// See host_memory.h for documentation of this method. +status_t sim_sram_mem_write(uint32_t address, uint32_t length, const uint8_t *buffer) +{ + return sim_mem_write(MemoryStore::kMapIndexSRAM, address, length, buffer); +} + +// See host_memory.h for documentation of this method. +status_t sim_sram_mem_fill(uint32_t address, uint32_t length, uint32_t pattern) +{ + return sim_mem_fill(MemoryStore::kMapIndexSRAM, address, length, pattern); +} + +// See host_memory.h for documentation of this method. +status_t sim_device_mem_read(uint32_t address, uint32_t length, uint8_t *buffer) +{ + Log::warning("Warning: device peripheral memory not simulated\n"); + return kStatus_Success; +} + +// See host_memory.h for documentation of this method. +status_t sim_device_mem_write(uint32_t address, uint32_t length, const uint8_t *buffer) +{ + Log::warning("Warning: device peripheral memory not simulated\n"); + return kStatus_Success; +} + +// See host_memory.h for documentation of this method. +status_t sim_device_mem_fill(uint32_t address, uint32_t length, uint32_t pattern) +{ + Log::warning("Warning: device peripheral memory not simulated\n"); + return kStatus_Success; +} + +// See memory.h for documentation of this method. +void host_flash_erase_all() +{ + Simulator &sim = Simulator::getSimulator(); + MemoryStore *mem = sim.getMemoryStore(MemoryStore::kMapIndexFlash); + mem->erase(); +} + +// See memory.h for documentation of this method. +void host_flash_erase_all_unsecure() +{ + host_flash_erase_all(); +} + +// See memory.h for documentation of this method. +void host_flash_erase_region(uint32_t address, uint32_t count) +{ + Simulator &sim = Simulator::getSimulator(); + MemoryStore *mem = sim.getMemoryStore(MemoryStore::kMapIndexFlash); + mem->erase(address, count); +} + +//////////////////////////////////////////////////////////////////////////////// +// Options Store operations +//////////////////////////////////////////////////////////////////////////////// + +// See host_memory.h for documentation of this method. +bool OptionsStore::init(const string &pathToDir, bool forceCreate) +{ + m_optionsFile.assign(pathToDir); + m_optionsFile += k_fileSeparator + k_optionsFileName; + + // Do nothing if forceCreate requested - file will be created by persist(). + if (!forceCreate) + { + FILE *options = fopen(m_optionsFile.c_str(), "r"); + if (options) + { + // Read property store values. + fread(g_bootloaderContext.propertyInterface->store, sizeof(*(g_bootloaderContext.propertyInterface->store)), + 1, options); + + fclose(options); + } + } + + return true; +} + +// See host_memory.h for documentation of this method. +void OptionsStore::persist() +{ + FILE *options = fopen(m_optionsFile.c_str(), "w"); + if (!options) + { + Log::error("Error: cannot create options file '%s'\n", m_optionsFile.c_str()); + return; + } + + // Write property store values. + fwrite(g_bootloaderContext.propertyInterface->store, sizeof(*(g_bootloaderContext.propertyInterface->store)), 1, + options); + + fclose(options); +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/SourceFile.cpp b/src/blfwk/src/SourceFile.cpp new file mode 100644 index 0000000..e857ce6 --- /dev/null +++ b/src/blfwk/src/SourceFile.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include "blfwk/ELFSourceFile.h" +#include "blfwk/EndianUtilities.h" +#include "blfwk/IntelHexSourceFile.h" +#include "blfwk/SBSourceFile.h" +#include "blfwk/SRecordSourceFile.h" +#include "blfwk/SearchPath.h" +#include "blfwk/SourceFile.h" +#include "blfwk/format_string.h" + +using namespace blfwk; + +//! The supported file types are currently: +//! - ELF files +//! - Motorola S-record files +//! - Binary files +//! - SB files +//! - Intel Hex files +//! +//! Any file that is not picked up by the other subclasses will result in a +//! an instance of BinaryDataFile. +//! +//! \return An instance of the correct subclass of SourceFile for the given path. +//! +//! \exception std::runtime_error Thrown if the file cannot be opened. +//! +//! \see blfwk::ELFSourceFile +//! \see blfwk::SRecordSourceFile +//! \see blfwk::BinarySourceFile +//! \see blfwk::SBSourceFile +//! \see blfwk::IntelHexSourceFile +SourceFile *SourceFile::openFile(const std::string &path) +{ + // Search for file using search paths + std::string actualPath; + bool found = PathSearcher::getGlobalSearcher().search(path, PathSearcher::kFindFile, true, actualPath); + if (!found) + { + throw std::runtime_error(format_string("unable to find file %s\n", path.c_str())); + } + + std::ifstream testStream(actualPath.c_str(), std::ios_base::in | std::ios_base::binary); + if (!testStream.is_open()) + { + throw std::runtime_error(format_string("failed to open file: %s", actualPath.c_str())); + } + + // catch exceptions so we can close the file stream + try + { + if (ELFSourceFile::isELFFile(testStream)) + { + testStream.close(); + return new ELFSourceFile(actualPath); + } + else if (SRecordSourceFile::isSRecordFile(testStream)) + { + testStream.close(); + return new SRecordSourceFile(actualPath); + } + else if (SBSourceFile::isSBFile(testStream)) + { + testStream.close(); + return new SBSourceFile(actualPath); + } + else if (IntelHexSourceFile::isIntelHexFile(testStream)) + { + testStream.close(); + return new IntelHexSourceFile(actualPath); + } + + // treat it as a binary file + testStream.close(); + return new BinarySourceFile(actualPath); + } + catch (...) + { + testStream.close(); + throw; + } +} + +SourceFile::SourceFile(const std::string &path, source_file_t filetype) + : m_path(path) + , m_filetype(filetype) + , m_stream() +{ + // Initialize m_size + open(); + assert(m_stream); + m_stream->seekg(0, std::ios_base::end); + m_size = (unsigned)m_stream->tellg(); + close(); +} + +//! The file is closed if it had been left opened. +//! +SourceFile::~SourceFile() +{ + if (isOpen()) + { + m_stream->close(); + } +} + +//! \exception std::runtime_error Raised if the file could not be opened successfully. +void SourceFile::open() +{ + assert(!isOpen()); + m_stream = new std::ifstream(m_path.c_str(), std::ios_base::in | std::ios_base::binary); + if (!m_stream->is_open()) + { + throw std::runtime_error(format_string("failed to open file: %s", m_path.c_str())); + } +} + +void SourceFile::close() +{ + assert(isOpen()); + + m_stream->close(); + m_stream.safe_delete(); +} + +//! If the file does not support named sections, or if there is not a +//! section with the given name, this method may return NULL. +//! +//! This method is just a small wrapper that creates an +//! FixedMatcher string matcher instance and uses the createDataSource() +//! that takes a reference to a StringMatcher. +DataSource *SourceFile::createDataSource(const std::string §ion) +{ + FixedMatcher matcher(section); + return createDataSource(matcher); +} + +DataTarget *SourceFile::createDataTargetForEntryPoint() +{ + if (!hasEntryPoint()) + { + return NULL; + } + + return new ConstantDataTarget(getEntryPointAddress()); +} + +BinarySourceFile::BinarySourceFile(const std::string &path, source_file_t sourceFileType) + : SourceFile(path, sourceFileType) + , m_entry_point(0xffffffff) + , m_stack_pointer(0xffffffff) +{ +} + +void BinarySourceFile::guessEntryPointAndStackPointer() +{ + open(); + + uint32_t data[] = { 0xffffffff, 0xffffffff }; + + // seek to beginning of the stream/file and read the plaintext header + m_stream->seekg(0, std::ios_base::beg); + if (m_stream->read((char *)&data, sizeof(data)).bad()) + { + close(); + throw std::runtime_error(format_string("failed to read image header from file: %s", m_path.c_str())); + } + + close(); + + m_stack_pointer = ENDIAN_LITTLE_TO_HOST_U32(data[0]); + m_entry_point = ENDIAN_LITTLE_TO_HOST_U32(data[1]); +} + +DataSource *BinarySourceFile::createDataSource() +{ + std::istream *fileStream = getStream(); + assert(fileStream); + + // get stream size + fileStream->seekg(0, std::ios_base::end); + int length = (int)fileStream->tellg(); + + // allocate buffer + smart_array_ptr data = new uint8_t[length]; + + // read entire file into the buffer + fileStream->seekg(0, std::ios_base::beg); + if (fileStream->read((char *)data.get(), length).bad()) + { + throw std::runtime_error(format_string("unexpected end of file: %s", m_path.c_str())); + } + + // create the data source. the buffer is copied, so we can dispose of it. + return new UnmappedDataSource(data, length); +} diff --git a/src/blfwk/src/SpiPeripheral.cpp b/src/blfwk/src/SpiPeripheral.cpp new file mode 100644 index 0000000..0756c37 --- /dev/null +++ b/src/blfwk/src/SpiPeripheral.cpp @@ -0,0 +1,172 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include "blfwk/Logging.h" +#include "blfwk/SpiPeripheral.h" +#include "blfwk/format_string.h" +#include "blfwk/spi.h" + +using namespace blfwk; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See SpiPeripheral.h for documentation of this method. +SpiPeripheral::SpiPeripheral(const char *port, long speed, uint8_t polarity, uint8_t phase, uint8_t sequence) + : m_fileDescriptor(-1) +{ + if (!init(port, speed, polarity, phase, sequence)) + { + throw std::runtime_error( + format_string("Error: Cannot open SPI port(%s), speed(%d Hz), polarity(%d), phase(%d), %s.\n", port, speed, + polarity, phase, sequence ? "LSB" : "MSB")); + } +} + +// See SpiPeripheral.h for documentation of this method. +bool SpiPeripheral::init(const char *port, long speed, uint8_t polarity, uint8_t phase, uint8_t sequence) +{ + assert(port); + + // Open the port. + m_fileDescriptor = spi_open(const_cast(port)); + if (m_fileDescriptor == -1) + { + return false; + } + + uint32_t mode = 0x0; + if (polarity == 0) + { + mode &= ~SPI_CPOL; + } + else if (polarity == 1) + { + mode |= SPI_CPOL; + } + else + { + return false; + } + + if (phase == 0) + { + mode &= ~SPI_CPHA; + } + else if (phase == 1) + { + mode |= SPI_CPHA; + } + else + { + return false; + } + + if (sequence == 0) + { + mode &= ~SPI_LSB_FIRST; + } + else if (sequence == 1) + { + mode |= SPI_LSB_FIRST; + } + else + { + return false; + } + + spi_setup(m_fileDescriptor, speed * 1000, mode, kSpiPeripheral_DefaultBitsPerWord); + + // Flush garbage from receive buffer before setting read timeout. + flushRX(); + + // Set host serial timeout to 10 milliseconds. Higherlevel timeouts are implemented in + // SerialPacketizer.cpp + spi_set_timeout(m_fileDescriptor, kSpiPeripheral_DefaultReadTimeoutMs); + + return true; +} + +// See SpiPeripheral.h for documentation of this method. +SpiPeripheral::~SpiPeripheral() +{ + if (m_fileDescriptor != -1) + { + spi_close(m_fileDescriptor); + } +} + +// See SpiPeripheral.h for documentation of this method. +status_t SpiPeripheral::read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t unused_timeoutMs) +{ + assert(buffer); + + // Read the requested number of bytes. + int count = spi_read(m_fileDescriptor, reinterpret_cast(buffer), requestedBytes); + if (actualBytes) + { + *actualBytes = count; + } + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes read in hex + Log::debug2("<"); + for (int i = 0; i < (int)count; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (count - 1)) + { + Log::debug2(" "); + } + } + Log::debug2(">\n"); + } + + if (count < (int)requestedBytes) + { + // Anything less than requestedBytes is a timeout error. + return kStatus_Timeout; + } + + return kStatus_Success; +} + +// See SpiPeripheral.h for documentation of this method. +void SpiPeripheral::flushRX() {} + +// See SpiPeripheral.h for documentation of this method. +status_t SpiPeripheral::write(const uint8_t *buffer, uint32_t byteCount) +{ + assert(buffer); + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes written in hex + Log::debug2("["); + for (int i = 0; i < (int)byteCount; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (byteCount - 1)) + { + Log::debug2(" "); + } + } + Log::debug2("]\n"); + } + + if (spi_write(m_fileDescriptor, reinterpret_cast(const_cast(buffer)), byteCount) == byteCount) + return kStatus_Success; + else + return kStatus_Fail; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/StELFFile.cpp b/src/blfwk/src/StELFFile.cpp new file mode 100644 index 0000000..e88a01c --- /dev/null +++ b/src/blfwk/src/StELFFile.cpp @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include "blfwk/StELFFile.h" +#include "blfwk/EndianUtilities.h" + +//! \exception StELFFileException is thrown if there is a problem with the file format. +//! +StELFFile::StELFFile(std::istream &inStream) + : m_stream(inStream) + , m_elfVariant(eIllegalVariant) +{ + readFileHeaders(); +} + +//! Disposes of the string table data. +StELFFile::~StELFFile() +{ + SectionDataMap::iterator it = m_sectionDataCache.begin(); + for (; it != m_sectionDataCache.end(); ++it) + { + SectionDataInfo &info = it->second; + if (info.m_data != NULL) + { + delete[] info.m_data; + } + } +} + +//! \exception StELFFileException is thrown if the file is not an ELF file. +//! +void StELFFile::readFileHeaders() +{ + // move read head to beginning of stream + m_stream.seekg(0, std::ios_base::beg); + + // read ELF header + m_stream.read(reinterpret_cast(&m_header), sizeof(m_header)); + if (m_stream.bad()) + { + throw StELFFileException("could not read file header"); + } + + // convert endianness + m_header.e_type = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_type); + m_header.e_machine = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_machine); + m_header.e_version = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_version); + m_header.e_entry = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_entry); + m_header.e_phoff = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_phoff); + m_header.e_shoff = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_shoff); + m_header.e_flags = ENDIAN_LITTLE_TO_HOST_U32(m_header.e_flags); + m_header.e_ehsize = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_ehsize); + m_header.e_phentsize = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_phentsize); + m_header.e_phnum = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_phnum); + m_header.e_shentsize = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_shentsize); + m_header.e_shnum = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_shnum); + m_header.e_shstrndx = ENDIAN_LITTLE_TO_HOST_U16(m_header.e_shstrndx); + + // check magic number + if (!(m_header.e_ident[EI_MAG0] == ELFMAG0 && m_header.e_ident[EI_MAG1] == ELFMAG1 && + m_header.e_ident[EI_MAG2] == ELFMAG2 && m_header.e_ident[EI_MAG3] == ELFMAG3)) + { + throw StELFFileException("invalid magic number in ELF header"); + } + + try + { + int i; + + // read section headers + if (m_header.e_shoff != 0 && m_header.e_shnum > 0) + { + Elf32_Shdr sectionHeader; + for (i = 0; i < m_header.e_shnum; ++i) + { + m_stream.seekg(m_header.e_shoff + m_header.e_shentsize * i, std::ios::beg); + m_stream.read(reinterpret_cast(§ionHeader), sizeof(sectionHeader)); + if (m_stream.bad()) + { + throw StELFFileException("could not read section header"); + } + + // convert endianness + sectionHeader.sh_name = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_name); + sectionHeader.sh_type = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_type); + sectionHeader.sh_flags = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_flags); + sectionHeader.sh_addr = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_addr); + sectionHeader.sh_offset = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_offset); + sectionHeader.sh_size = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_size); + sectionHeader.sh_link = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_link); + sectionHeader.sh_info = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_info); + sectionHeader.sh_addralign = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_addralign); + sectionHeader.sh_entsize = ENDIAN_LITTLE_TO_HOST_U32(sectionHeader.sh_entsize); + + m_sectionHeaders.push_back(sectionHeader); + } + } + + // read program headers + if (m_header.e_phoff != 0 && m_header.e_phnum > 0) + { + Elf32_Phdr programHeader; + for (i = 0; i < m_header.e_phnum; ++i) + { + m_stream.seekg(m_header.e_phoff + m_header.e_phentsize * i, std::ios::beg); + m_stream.read(reinterpret_cast(&programHeader), sizeof(programHeader)); + if (m_stream.bad()) + { + throw StELFFileException("could not read program header"); + } + + // convert endianness + programHeader.p_type = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_type); + programHeader.p_offset = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_type); + programHeader.p_vaddr = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_vaddr); + programHeader.p_paddr = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_paddr); + programHeader.p_filesz = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_filesz); + programHeader.p_memsz = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_memsz); + programHeader.p_flags = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_flags); + programHeader.p_align = ENDIAN_LITTLE_TO_HOST_U32(programHeader.p_align); + + m_programHeaders.push_back(programHeader); + } + } + + // look up symbol table section index + { + std::string symtab_section_name(SYMTAB_SECTION_NAME); + m_symbolTableIndex = getIndexOfSectionWithName(symtab_section_name); + } + } + catch (...) + { + throw StELFFileException("error reading file"); + } +} + +const Elf32_Shdr &StELFFile::getSectionAtIndex(unsigned inIndex) const +{ + if (inIndex > m_sectionHeaders.size()) + throw std::invalid_argument("inIndex"); + + return m_sectionHeaders[inIndex]; +} + +//! If there is not a matching section, then #SHN_UNDEF is returned instead. +//! +unsigned StELFFile::getIndexOfSectionWithName(const std::string &inName) +{ + unsigned sectionIndex = 0; + const_section_iterator it = getSectionBegin(); + for (; it != getSectionEnd(); ++it, ++sectionIndex) + { + const Elf32_Shdr &header = *it; + if (header.sh_name != 0) + { + std::string sectionName = getSectionNameAtIndex(header.sh_name); + if (inName == sectionName) + return sectionIndex; + } + } + + // no matching section + return SHN_UNDEF; +} + +//! The pointer returned from this method must be freed with the delete array operator (i.e., delete []). +//! If either the section data offset (sh_offset) or the section size (sh_size) are 0, then NULL will +//! be returned instead. +//! +//! The data is read directly from the input stream passed into the constructor. The stream must +//! still be open, or an exception will be thrown. +//! +//! \exception StELFFileException is thrown if an error occurs while reading the file. +//! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. +uint8_t *StELFFile::getSectionDataAtIndex(unsigned inIndex) +{ + return readSectionData(m_sectionHeaders[inIndex]); +} + +//! The pointer returned from this method must be freed with the delete array operator (i.e., delete []). +//! If either the section data offset (sh_offset) or the section size (sh_size) are 0, then NULL will +//! be returned instead. +//! +//! The data is read directly from the input stream passed into the constructor. The stream must +//! still be open, or an exception will be thrown. +//! +//! \exception StELFFileException is thrown if an error occurs while reading the file. +//! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. +uint8_t *StELFFile::getSectionData(const_section_iterator inSection) +{ + return readSectionData(*inSection); +} + +//! \exception StELFFileException is thrown if an error occurs while reading the file. +//! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. +uint8_t *StELFFile::readSectionData(const Elf32_Shdr &inHeader) +{ + // check for empty data + if (inHeader.sh_offset == 0 || inHeader.sh_size == 0) + return NULL; + + uint8_t *sectionData = new uint8_t[inHeader.sh_size]; + + try + { + m_stream.seekg(inHeader.sh_offset, std::ios::beg); + m_stream.read(reinterpret_cast(sectionData), inHeader.sh_size); + if (m_stream.bad()) + throw StELFFileException("could not read entire section"); + } + catch (StELFFileException) + { + delete[] sectionData; + throw; + } + catch (...) + { + delete[] sectionData; + throw StELFFileException("error reading section data"); + } + + return sectionData; +} + +const Elf32_Phdr &StELFFile::getSegmentAtIndex(unsigned inIndex) const +{ + if (inIndex > m_programHeaders.size()) + throw std::invalid_argument("inIndex"); + + return m_programHeaders[inIndex]; +} + +//! The pointer returned from this method must be freed with the delete array operator (i.e., delete []). +//! If either the segment offset (p_offset) or the segment file size (p_filesz) are 0, then NULL will +//! be returned instead. +//! +//! The data is read directly from the input stream passed into the constructor. The stream must +//! still be open, or an exception will be thrown. +//! +//! \exception StELFFileException is thrown if an error occurs while reading the file. +//! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. +uint8_t *StELFFile::getSegmentDataAtIndex(unsigned inIndex) +{ + return readSegmentData(m_programHeaders[inIndex]); +} + +//! The pointer returned from this method must be freed with the delete array operator (i.e., delete []). +//! If either the segment offset (p_offset) or the segment file size (p_filesz) are 0, then NULL will +//! be returned instead. +//! +//! The data is read directly from the input stream passed into the constructor. The stream must +//! still be open, or an exception will be thrown. +//! +//! \exception StELFFileException is thrown if an error occurs while reading the file. +//! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. +uint8_t *StELFFile::getSegmentData(const_segment_iterator inSegment) +{ + return readSegmentData(*inSegment); +} + +//! \exception StELFFileException is thrown if an error occurs while reading the file. +//! \exception std::bad_alloc is thrown if memory for the data cannot be allocated. +uint8_t *StELFFile::readSegmentData(const Elf32_Phdr &inHeader) +{ + // check for empty data + if (inHeader.p_offset == 0 || inHeader.p_filesz == 0) + return NULL; + + uint8_t *segmentData = new uint8_t[inHeader.p_filesz]; + + try + { + m_stream.seekg(inHeader.p_offset, std::ios::beg); + m_stream.read(reinterpret_cast(segmentData), inHeader.p_filesz); + if (m_stream.bad()) + throw StELFFileException("could not read entire segment"); + } + catch (StELFFileException) + { + delete[] segmentData; + throw; + } + catch (...) + { + delete[] segmentData; + throw StELFFileException("error reading segment data"); + } + + return segmentData; +} + +//! If the index is out of range, or if there is no string table in the file, then +//! an empty string will be returned instead. This will also happen when the index +//! is either 0 or the last byte in the table, since the table begins and ends with +//! zero bytes. +std::string StELFFile::getSectionNameAtIndex(unsigned inIndex) +{ + // make sure there's a section name string table + if (m_header.e_shstrndx == SHN_UNDEF) + return std::string(""); + + return getStringAtIndex(m_header.e_shstrndx, inIndex); +} + +//! \exception std::invalid_argument is thrown if the section identified by \a +//! inStringTableSectionIndex is not actually a string table, or if \a +//! inStringIndex is out of range for the string table. +std::string StELFFile::getStringAtIndex(unsigned inStringTableSectionIndex, unsigned inStringIndex) +{ + // check section type + const Elf32_Shdr &header = getSectionAtIndex(inStringTableSectionIndex); + if (header.sh_type != SHT_STRTAB) + throw std::invalid_argument("inStringTableSectionIndex"); + + if (inStringIndex >= header.sh_size) + throw std::invalid_argument("inStringTableSectionIndex"); + + // check cache + SectionDataInfo &info = getCachedSectionData(inStringTableSectionIndex); + return std::string(&reinterpret_cast(info.m_data)[inStringIndex]); +} + +StELFFile::SectionDataInfo &StELFFile::getCachedSectionData(unsigned inSectionIndex) +{ + // check cache + SectionDataMap::iterator it = m_sectionDataCache.find(inSectionIndex); + if (it != m_sectionDataCache.end()) + return it->second; + + // not in cache, add it + const Elf32_Shdr &header = getSectionAtIndex(inSectionIndex); + uint8_t *data = getSectionDataAtIndex(inSectionIndex); + + SectionDataInfo info; + info.m_data = data; + info.m_size = header.sh_size; + info.m_swapped = false; + + m_sectionDataCache[inSectionIndex] = info; + return m_sectionDataCache[inSectionIndex]; +} + +//! The number of entries in the symbol table is the symbol table section size +//! divided by the size of each symbol entry (the #Elf32_Shdr::sh_entsize field of the +//! symbol table section header). +unsigned StELFFile::getSymbolCount() +{ + if (m_symbolTableIndex == SHN_UNDEF) + return 0; + + const Elf32_Shdr &header = getSectionAtIndex(m_symbolTableIndex); + return header.sh_size / header.sh_entsize; +} + +//! \exception std::invalid_argument is thrown if \a inIndex is out of range.] +//! +const Elf32_Sym &StELFFile::getSymbolAtIndex(unsigned inIndex) +{ + // get section data + const Elf32_Shdr &header = getSectionAtIndex(m_symbolTableIndex); + SectionDataInfo &info = getCachedSectionData(m_symbolTableIndex); + + // has the symbol table been byte swapped yet? + if (!info.m_swapped) + { + byteSwapSymbolTable(header, info); + } + + unsigned symbolOffset = header.sh_entsize * inIndex; + if (symbolOffset >= info.m_size) + { + throw std::invalid_argument("inIndex"); + } + + Elf32_Sym *symbol = reinterpret_cast(&info.m_data[symbolOffset]); + return *symbol; +} + +void StELFFile::byteSwapSymbolTable(const Elf32_Shdr &header, SectionDataInfo &info) +{ + unsigned symbolCount = getSymbolCount(); + unsigned i = 0; + unsigned symbolOffset = 0; + + for (; i < symbolCount; ++i, symbolOffset += header.sh_entsize) + { + Elf32_Sym *symbol = reinterpret_cast(&info.m_data[symbolOffset]); + symbol->st_name = ENDIAN_LITTLE_TO_HOST_U32(symbol->st_name); + symbol->st_value = ENDIAN_LITTLE_TO_HOST_U32(symbol->st_value); + symbol->st_size = ENDIAN_LITTLE_TO_HOST_U32(symbol->st_size); + symbol->st_shndx = ENDIAN_LITTLE_TO_HOST_U16(symbol->st_shndx); + } + + // remember that we've byte swapped the symbols + info.m_swapped = true; +} + +unsigned StELFFile::getSymbolNameStringTableIndex() const +{ + const Elf32_Shdr &header = getSectionAtIndex(m_symbolTableIndex); + return header.sh_link; +} + +std::string StELFFile::getSymbolName(const Elf32_Sym &inSymbol) +{ + unsigned symbolStringTableIndex = getSymbolNameStringTableIndex(); + return getStringAtIndex(symbolStringTableIndex, inSymbol.st_name); +} + +//! Returns STN_UNDEF if it cannot find a symbol at the given \a symbolAddress. +unsigned StELFFile::getIndexOfSymbolAtAddress(uint32_t symbolAddress, bool strict) +{ + unsigned symbolCount = getSymbolCount(); + unsigned symbolIndex = 0; + for (; symbolIndex < symbolCount; ++symbolIndex) + { + const Elf32_Sym &symbol = getSymbolAtIndex(symbolIndex); + + // the GHS toolchain puts in STT_FUNC symbols marking the beginning and ending of each + // file. if the entry point happens to be at the beginning of the file, the beginning- + // of-file symbol will have the same value and type. fortunately, the size of these + // symbols is 0 (or seems to be). we also ignore symbols that start with two dots just + // in case. + if (symbol.st_value == symbolAddress && + (strict && ELF32_ST_TYPE(symbol.st_info) == STT_FUNC && symbol.st_size != 0)) + { + std::string symbolName = getSymbolName(symbol); + + // ignore symbols that start with two dots + if (symbolName[0] == '.' && symbolName[1] == '.') + continue; + + // found the symbol! + return symbolIndex; + } + } + + return STN_UNDEF; +} + +ARMSymbolType_t StELFFile::getTypeOfSymbolAtIndex(unsigned symbolIndex) +{ + ARMSymbolType_t symType = eARMSymbol; + const Elf32_Sym &symbol = getSymbolAtIndex(symbolIndex); + + if (m_elfVariant == eGHSVariant) + { + if (symbol.st_other & STO_THUMB) + symType = eThumbSymbol; + } + else + { + unsigned mappingSymStart = 1; + unsigned mappingSymCount = getSymbolCount() - 1; // don't include first undefined symbol + bool mapSymsFirst = (m_header.e_flags & EF_ARM_MAPSYMSFIRST) != 0; + if (mapSymsFirst) + { + // first symbol '$m' is number of mapping syms + const Elf32_Sym &mappingSymCountSym = getSymbolAtIndex(1); + if (getSymbolName(mappingSymCountSym) == MAPPING_SYMBOL_COUNT_TAGSYM) + { + mappingSymCount = mappingSymCountSym.st_value; + mappingSymStart = 2; + } + } + + uint32_t lastMappingSymAddress = 0; + unsigned mappingSymIndex = mappingSymStart; + for (; mappingSymIndex < mappingSymCount + mappingSymStart; ++mappingSymIndex) + { + const Elf32_Sym &mappingSym = getSymbolAtIndex(mappingSymIndex); + std::string mappingSymName = getSymbolName(mappingSym); + ARMSymbolType_t nextSymType = eUnknownSymbol; + + if (mappingSymName == ARM_SEQUENCE_MAPSYM) + symType = eARMSymbol; + else if (mappingSymName == DATA_SEQUENCE_MAPSYM) + symType = eDataSymbol; + else if (mappingSymName == THUMB_SEQUENCE_MAPSYM) + symType = eThumbSymbol; + + if (nextSymType != eUnknownSymbol) + { + if (symbol.st_value >= lastMappingSymAddress && symbol.st_value < mappingSym.st_value) + break; + + symType = nextSymType; + lastMappingSymAddress = mappingSym.st_value; + } + } + } + + return symType; +} + +void StELFFile::dumpSections() +{ + unsigned count = getSectionCount(); + unsigned i = 0; + + const char *sectionTypes[12] = { "NULL", "PROGBITS", "SYMTAB", "STRTAB", "RELA", "HASH", + "DYNAMIC", "NOTE", "NOBITS", "REL", "SHLIB", "DYNSYM" }; + + for (; i < count; ++i) + { + const Elf32_Shdr &header = getSectionAtIndex(i); + std::string name = getSectionNameAtIndex(header.sh_name); + + printf("%s: %s, 0x%08x, 0x%08x, 0x%08x, %d, %d, %d\n", name.c_str(), sectionTypes[header.sh_type], + header.sh_addr, header.sh_offset, header.sh_size, header.sh_link, header.sh_info, header.sh_entsize); + } +} + +void StELFFile::dumpSymbolTable() +{ + const char *symbolTypes[5] = { "NOTYPE", "OBJECT", "FUNC", "SECTION", "FILE" }; + const char *symbolBinding[3] = { "LOCAL", "GLOBAL", "WEAK" }; + + unsigned count = getSymbolCount(); + unsigned i = 0; + + for (; i < count; ++i) + { + const Elf32_Sym &symbol = getSymbolAtIndex(i); + std::string name = getSymbolName(symbol); + + printf("'%s': %s, %s, 0x%08x, 0x%08x, %d. 0x%08x\n", name.c_str(), symbolTypes[ELF32_ST_TYPE(symbol.st_info)], + symbolBinding[ELF32_ST_BIND(symbol.st_info)], symbol.st_value, symbol.st_size, symbol.st_shndx, + symbol.st_other); + } +} diff --git a/src/blfwk/src/StExecutableImage.cpp b/src/blfwk/src/StExecutableImage.cpp new file mode 100644 index 0000000..577471d --- /dev/null +++ b/src/blfwk/src/StExecutableImage.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include + +#include "blfwk/StExecutableImage.h" + +StExecutableImage::StExecutableImage(int inAlignment) + : m_alignment(inAlignment) + , m_hasEntry(false) + , m_entry(0) +{ +} + +//! Makes a duplicate of each memory region. +StExecutableImage::StExecutableImage(const StExecutableImage &inOther) + : m_name(inOther.m_name) + , m_alignment(inOther.m_alignment) + , m_hasEntry(inOther.m_hasEntry) + , m_entry(inOther.m_entry) + , m_filters(inOther.m_filters) +{ + const_iterator it = inOther.getRegionBegin(); + for (; it != inOther.getRegionEnd(); ++it) + { + const MemoryRegion ®ion = *it; + + MemoryRegion regionCopy(region); + if (region.m_type == FILL_REGION && region.m_data != NULL) + { + regionCopy.m_data = new uint8_t[region.m_length]; + memcpy(regionCopy.m_data, region.m_data, region.m_length); + } + + m_image.push_back(regionCopy); + } +} + +//! Disposes of memory allocated for each region. +StExecutableImage::~StExecutableImage() +{ + MemoryRegionList::iterator it; + for (it = m_image.begin(); it != m_image.end(); ++it) + { + if (it->m_data) + { + delete[] it->m_data; + it->m_data = NULL; + } + } +} + +//! A copy of \a inName is made, so the original may be disposed of by the caller +//! after this method returns. +void StExecutableImage::setName(const std::string &inName) +{ + m_name = inName; +} + +std::string StExecutableImage::getName() const +{ + return m_name; +} + +// The region is added with read and write flags set. +//! \exception std::runtime_error will be thrown if the new overlaps an +//! existing region. +void StExecutableImage::addFillRegion(uint32_t inAddress, unsigned inLength) +{ + MemoryRegion region; + region.m_type = FILL_REGION; + region.m_address = inAddress; + region.m_data = NULL; + region.m_length = inLength; + region.m_flags = REGION_RW_FLAG; + + insertOrMergeRegion(region); +} + +//! A copy of \a inData is made before returning. The copy will be deleted when +//! the executable image is destructed. Currently, the text region is created with +//! read, write, and executable flags set. +//! \exception std::runtime_error will be thrown if the new overlaps an +//! existing region. +//! \exception std::bad_alloc is thrown if memory for the copy of \a inData +//! cannot be allocated. +void StExecutableImage::addTextRegion(uint32_t inAddress, const uint8_t *inData, unsigned inLength) +{ + MemoryRegion region; + region.m_type = TEXT_REGION; + region.m_address = inAddress; + region.m_flags = REGION_RW_FLAG | REGION_EXEC_FLAG; + + // copy the data + region.m_data = new uint8_t[inLength]; + region.m_length = inLength; + memcpy(region.m_data, inData, inLength); + + insertOrMergeRegion(region); +} + +//! \exception std::out_of_range is thrown if \a inIndex is out of range. +//! +const StExecutableImage::MemoryRegion &StExecutableImage::getRegionAtIndex(unsigned inIndex) const +{ + // check bounds + if (inIndex >= m_image.size()) + throw std::out_of_range("inIndex"); + + // find region by index + MemoryRegionList::const_iterator it = m_image.begin(); + unsigned i = 0; + for (; it != m_image.end(); ++it, ++i) + { + if (i == inIndex) + break; + } + return *it; +} + +//! The list of address filters is kept sorted as filters are added. +//! +void StExecutableImage::addAddressFilter(const AddressFilter &filter) +{ + m_filters.push_back(filter); + m_filters.sort(); +} + +//! +void StExecutableImage::clearAddressFilters() +{ + m_filters.clear(); +} + +//! \exception StExecutableImage::address_filter_exception Raised when a filter +//! with the type #ADDR_FILTER_ERROR or #ADDR_FILTER_WARNING is matched. +//! +//! \todo Build a list of all matching filters and then execute them at once. +//! For the warning and error filters, a single exception should be raised +//! that identifies all the overlapping errors. Currently the user will only +//! see the first (lowest address) overlap. +void StExecutableImage::applyAddressFilters() +{ +restart_loops: + // Iterate over filters. + AddressFilterList::const_iterator fit = m_filters.begin(); + for (; fit != m_filters.end(); ++fit) + { + const AddressFilter &filter = *fit; + + // Iterator over regions. + MemoryRegionList::iterator rit = m_image.begin(); + for (; rit != m_image.end(); ++rit) + { + MemoryRegion ®ion = *rit; + + if (filter.matchesMemoryRegion(region)) + { + switch (filter.m_action) + { + case ADDR_FILTER_NONE: + // Do nothing. + break; + + case ADDR_FILTER_ERROR: + // throw error exception + throw address_filter_exception(true, m_name, filter); + break; + + case ADDR_FILTER_WARNING: + // throw warning exception + throw address_filter_exception(false, m_name, filter); + break; + + case ADDR_FILTER_CROP: + // Delete the offending portion of the region and restart + // the iteration loops. + cropRegionToFilter(region, filter); + goto restart_loops; + break; + } + } + } + } +} + +//! There are several possible cases here: +//! - No overlap at all. Nothing is done. +//! +//! - All of the memory region is matched by the \a filter. The region is +//! removed from #StExecutableImage::m_image and its data memory freed. +//! +//! - The remaining portion of the region is one contiguous chunk. In this +//! case, \a region is simply modified. +//! +//! - The region is split in the middle by the filter. The original \a region +//! is modified to match the first remaining chunk. And a new #StExecutableImage::MemoryRegion +//! instance is created to hold the other leftover piece. +void StExecutableImage::cropRegionToFilter(MemoryRegion ®ion, const AddressFilter &filter) +{ + uint32_t firstByte = region.m_address; // first byte occupied by this region + uint32_t lastByte = region.endAddress(); // last used byte in this region + + // compute new address range + uint32_t cropFrom = filter.m_fromAddress; + if (cropFrom < firstByte) + { + cropFrom = firstByte; + } + + uint32_t cropTo = filter.m_toAddress; + if (cropTo > lastByte) + { + cropTo = lastByte; + } + + // is there actually a match? + if (cropFrom > filter.m_toAddress || cropTo < filter.m_fromAddress) + { + // nothing to do, so bail + return; + } + + printf("Deleting region 0x%08x-0x%08x\n", cropFrom, cropTo); + + // handle if the entire region is to be deleted + if (cropFrom == firstByte && cropTo == lastByte) + { + delete[] region.m_data; + region.m_data = NULL; + m_image.remove(region); + } + + // there is at least a little of the original region remaining + uint32_t newLength = cropTo - cropFrom + 1; + uint32_t leftoverLength = lastByte - cropTo; + uint8_t *oldData = region.m_data; + + // update the region + region.m_address = cropFrom; + region.m_length = newLength; + + // crop data buffer for text regions + if (region.m_type == TEXT_REGION && oldData) + { + region.m_data = new uint8_t[newLength]; + memcpy(region.m_data, &oldData[cropFrom - firstByte], newLength); + } + + // create a new region for any part of the original region that was past + // the crop to address. this will happen if the filter range falls in the + // middle of the region. + if (leftoverLength) + { + MemoryRegion newRegion; + newRegion.m_type = region.m_type; + newRegion.m_flags = region.m_flags; + newRegion.m_address = cropTo + 1; + newRegion.m_length = leftoverLength; + + if (region.m_type == TEXT_REGION && oldData) + { + newRegion.m_data = new uint8_t[leftoverLength]; + memcpy(newRegion.m_data, &oldData[cropTo - firstByte + 1], leftoverLength); + } + + insertOrMergeRegion(newRegion); + } + + if (region.m_type == TEXT_REGION && oldData) + { + // dispose of old data + delete[] oldData; + } +} + +//! \exception std::runtime_error will be thrown if \a inRegion overlaps an +//! existing region. +//! +//! \todo Need to investigate if we can use the STL sort algorithm at all. Even +//! though we're doing merges too, we could sort first then examine the list +//! for merges. +void StExecutableImage::insertOrMergeRegion(MemoryRegion &inRegion) +{ + uint32_t newStart = inRegion.m_address; + uint32_t newEnd = newStart + inRegion.m_length; + + MemoryRegionList::iterator it = m_image.begin(); + + for (; it != m_image.end(); ++it) + { + MemoryRegion ®ion = *it; + uint32_t thisStart = region.m_address; + uint32_t thisEnd = thisStart + region.m_length; + + // keep track of where to insert it to retain sort order + if (thisStart >= newEnd) + { + break; + } + + // region types and flags must match in order to merge + if (region.m_type == inRegion.m_type && region.m_flags == inRegion.m_flags) + { + if (newStart == thisEnd || newEnd == thisStart) + { + mergeRegions(region, inRegion); + return; + } + else if ((newStart >= thisStart && newStart < thisEnd) || (newEnd >= thisStart && newEnd < thisEnd)) + { + throw std::runtime_error("new region overlaps existing region"); + } + } + } + + // not merged, so just insert it in the sorted position + m_image.insert(it, inRegion); +} + +//! Extends \a inNewRegion to include the data in \a inOldRegion. It is +//! assumed that the two regions are compatible. The new region may come either +//! before or after the old region in memory. Note that while the two regions +//! don't necessarily have to be touching, it's probably a good idea. That's +//! because any data between the regions will be set to 0. +//! +//! For TEXT_REGION types, the two original regions will have their data deleted +//! during the merge. Thus, this method is not safe if any outside callers may +//! be accessing the region's data. +void StExecutableImage::mergeRegions(MemoryRegion &inOldRegion, MemoryRegion &inNewRegion) +{ + bool isOldBefore = inOldRegion.m_address < inNewRegion.m_address; + uint32_t oldEnd = inOldRegion.m_address + inOldRegion.m_length; + uint32_t newEnd = inNewRegion.m_address + inNewRegion.m_length; + + switch (inOldRegion.m_type) + { + case TEXT_REGION: + { + // calculate new length + unsigned newLength; + if (isOldBefore) + { + newLength = newEnd - inOldRegion.m_address; + } + else + { + newLength = oldEnd - inNewRegion.m_address; + } + + // alloc memory + uint8_t *newData = new uint8_t[newLength]; + memset(newData, 0, newLength); + + // copy data from the two regions into new block + if (isOldBefore) + { + memcpy(newData, inOldRegion.m_data, inOldRegion.m_length); + memcpy(&newData[newLength - inNewRegion.m_length], inNewRegion.m_data, inNewRegion.m_length); + } + else + { + memcpy(newData, inNewRegion.m_data, inNewRegion.m_length); + memcpy(&newData[newLength - inOldRegion.m_length], inOldRegion.m_data, inOldRegion.m_length); + + inOldRegion.m_address = inNewRegion.m_address; + } + + // replace old region's data + delete[] inOldRegion.m_data; + inOldRegion.m_data = newData; + inOldRegion.m_length = newLength; + + // delete new region's data + delete[] inNewRegion.m_data; + inNewRegion.m_data = NULL; + break; + } + + case FILL_REGION: + { + if (isOldBefore) + { + inOldRegion.m_length = newEnd - inOldRegion.m_address; + } + else + { + inOldRegion.m_length = oldEnd - inNewRegion.m_address; + inOldRegion.m_address = inNewRegion.m_address; + } + break; + } + } +} + +//! Used when we remove a region from the region list by value. Because this +//! operator compares the #m_data member, it will only return true for either an +//! exact copy or a reference to the original. +bool StExecutableImage::MemoryRegion::operator==(const MemoryRegion &other) const +{ + return (m_type == other.m_type) && (m_address == other.m_address) && (m_length == other.m_length) && + (m_flags == other.m_flags) && (m_data == other.m_data); +} + +//! Returns true if the address filter overlaps \a region. +bool StExecutableImage::AddressFilter::matchesMemoryRegion(const MemoryRegion ®ion) const +{ + uint32_t firstByte = region.m_address; // first byte occupied by this region + uint32_t lastByte = region.endAddress(); // last used byte in this region + return (firstByte >= m_fromAddress && firstByte <= m_toAddress) || + (lastByte >= m_fromAddress && lastByte <= m_toAddress); +} + +//! The comparison does \em not take the action into account. It only looks at the +//! priority and address ranges of each filter. Priority is considered only if the two +//! filters overlap. Lower priority filters will come after higher priority ones. +//! +//! \retval -1 This filter is less than filter \a b. +//! \retval 0 This filter is equal to filter \a b. +//! \retval 1 This filter is greater than filter \a b. +int StExecutableImage::AddressFilter::compare(const AddressFilter &other) const +{ + if (m_priority != other.m_priority && + ((m_fromAddress >= other.m_fromAddress && m_fromAddress <= other.m_toAddress) || + (m_toAddress >= other.m_fromAddress && m_toAddress <= other.m_toAddress))) + { + // we know the priorities are not equal + if (m_priority > other.m_priority) + { + return -1; + } + else + { + return 1; + } + } + + if (m_fromAddress == other.m_fromAddress) + { + if (m_toAddress == other.m_toAddress) + { + return 0; + } + else if (m_toAddress < other.m_toAddress) + { + return -1; + } + else + { + return 1; + } + } + else if (m_fromAddress < other.m_fromAddress) + { + return -1; + } + else + { + return 1; + } +} diff --git a/src/blfwk/src/StIntelHexFile.cpp b/src/blfwk/src/StIntelHexFile.cpp new file mode 100644 index 0000000..59155c9 --- /dev/null +++ b/src/blfwk/src/StIntelHexFile.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * Copyright 2016-2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/StIntelHexFile.h" +#include "blfwk/stdafx.h" +#ifdef LINUX +#include +#endif + +StIntelHexFile::StIntelHexFile(std::istream &inStream) + : m_stream(inStream) +{ +} + +//! Frees any data allocated as part of an Intel Hex. +StIntelHexFile::~StIntelHexFile() +{ + const_iterator it; + for (it = m_records.begin(); it != m_records.end(); it++) + { + IntelHex &theRecord = (IntelHex &)*it; + if (theRecord.m_data) + { + delete[] theRecord.m_data; + theRecord.m_data = NULL; + } + } +} + +//! Just looks for ":" as the first characters of the file followed by two digit characters. +bool StIntelHexFile::isIntelHexFile() +{ + int savePosition = (int)m_stream.tellg(); + m_stream.seekg(0, std::ios_base::beg); + + char buffer[3]; + m_stream.read(buffer, 3); + bool isIntelHex = (buffer[0] == ':' && isHexDigit(buffer[1]) && isHexDigit(buffer[2])); + + m_stream.seekg(savePosition, std::ios_base::beg); + + return isIntelHex; +} + +//! 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 or an file end record detected. +//! The parse() method must be called after the object has been constructed +//! before any of the records will become accessible. +//! \exception StIntelHexParseException will be thrown if any error occurs while +//! parsing the input. +void StIntelHexFile::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 StIntelHexFile::isHexDigit(char c) +{ + return (isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +int StIntelHexFile::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 StIntelHexParseException is thrown if either of the nibble characters +//! is not a valid hex digit. +int StIntelHexFile::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 StIntelHexParseException("invalid hex digit"); + } + + return (hexDigitToInt(nibbleCharHi) << 4) | hexDigitToInt(nibbleCharLo); +} + +//! \brief Parses individual Intel Hex. +//! +//! Takes a single Intel Hex line as input and appends a new IntelHex struct +//! to the m_records vector. +//! \exception StIntelHexParseException will be thrown if any error occurs while +//! parsing \a inLine. +void StIntelHexFile::parseLine(std::string &inLine) +{ + int checksum = 0; + IntelHex newRecord; + memset(&newRecord, 0, sizeof(newRecord)); + + // must be at least a certain length + if (inLine.length() < INTELHEX_MIN_LENGTH) + { + throw StIntelHexParseException("invalid record length"); + } + + // start char must be ':' + if (inLine[0] != INTELHEX_START_CHAR) + { + throw StIntelHexParseException("invalid record start char"); + } + + // parse count field + newRecord.m_dataCount = readHexByte(inLine, 1); + checksum += newRecord.m_dataCount; + + // verify the record length now that we know the count + if (inLine.length() != 11 + newRecord.m_dataCount * 2) + { + throw StIntelHexParseException("invalid record length"); + } + + // read address field. + int address = 0; + for (int i = 0; i < 2; ++i) + { + int addressByte = readHexByte(inLine, INTELHEX_ADDRESS_START_CHAR_INDEX + i * 2); + address = (address << 8) | addressByte; + checksum += addressByte; + } + newRecord.m_address = address; + + // handle data type.; + newRecord.m_type = readHexByte(inLine, INTELHEX_TYPE_START_CHAR_INDEX); + checksum += newRecord.m_type; + switch (newRecord.m_type) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + break; + default: + throw StIntelHexParseException("unknown Intel Hex record type"); + break; + } + + // read data + if (newRecord.m_dataCount) + { + uint8_t *data = new uint8_t[newRecord.m_dataCount]; + try + { + for (unsigned i = 0; i < newRecord.m_dataCount; ++i) + { + int dataByte = readHexByte(inLine, INTELHEX_DATA_START_CHAR_INDEX + i * 2); + data[i] = dataByte; + checksum += dataByte; + } + newRecord.m_data = data; + } + catch (...) + { + delete[] data; + throw; + } + } + + // read and compare checksum byte + checksum = (~checksum + 1) & 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 StIntelHexParseException("invalid checksum"); + } + + // now save the new Intel Hex record + m_records.push_back(newRecord); +} diff --git a/src/blfwk/src/StSRecordFile.cpp b/src/blfwk/src/StSRecordFile.cpp new file mode 100644 index 0000000..d77ab77 --- /dev/null +++ b/src/blfwk/src/StSRecordFile.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/StSRecordFile.h" +#include "blfwk/stdafx.h" +#ifdef LINUX +#include +#endif + +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 = (int)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 be at least a certain length + if (inLine.length() < SRECORD_MIN_LENGTH) + { + throw StSRecordParseException("invalid record length"); + } + + // start char must be 'S' + if (inLine[0] != SRECORD_START_CHAR) + { + throw StSRecordParseException("invalid record start char"); + } + + // parse type field + if (!isdigit(inLine[1])) + { + throw StSRecordParseException("invalid S-record type"); + } + newRecord.m_type = inLine[1] - '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 = 0; // 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]; + try + { + for (i = 0; i < dataLength; ++i) + { + int dataByte = readHexByte(inLine, dataStartCharIndex + i * 2); + data[i] = dataByte; + checksum += dataByte; + } + } + catch (...) + { + delete[] data; + throw; + } + 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); +} diff --git a/src/blfwk/src/UartPeripheral.cpp b/src/blfwk/src/UartPeripheral.cpp new file mode 100644 index 0000000..f30cbe5 --- /dev/null +++ b/src/blfwk/src/UartPeripheral.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/Logging.h" +#include "blfwk/UartPeripheral.h" +#include "blfwk/format_string.h" +#include "blfwk/serial.h" + +using namespace blfwk; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See uart_peripheral.h for documentation of this method. +UartPeripheral::UartPeripheral(const char *port, long speed) + : m_fileDescriptor(-1) +{ + if (!init(port, speed)) + { + throw std::runtime_error( + format_string("Error: UartPeripheral() cannot open PC UART port(%s), speed(%d Hz).", port, speed)); + } +} + +// See uart_peripheral.h for documentation of this method. +bool UartPeripheral::init(const char *port, long speed) +{ + assert(port); + + port_name = (char *)malloc(strlen(port) + 1 /*'\0'*/); + strcpy(port_name, const_cast(port)); + + if (m_fileDescriptor != -1) + { + serial_close(m_fileDescriptor); + } + // Open the port. + m_fileDescriptor = serial_open(port_name); + if (m_fileDescriptor == -1) + { + return false; + } + + serial_setup(m_fileDescriptor, speed); + + // Flush garbage from receive buffer before setting read timeout. + flushRX(); + + // Set host serial timeout to 10 milliseconds. Higherlevel timeouts are implemented in + // SerialPacketizer.cpp + serial_set_read_timeout(m_fileDescriptor, kUartPeripheral_DefaultReadTimeoutMs); + + return true; +} + +// See host_peripheral.h for documentation of this method. +UartPeripheral::~UartPeripheral() +{ + if (m_fileDescriptor != -1) + { + serial_close(m_fileDescriptor); + } + if (port_name) + { + free(port_name); + } +} + +// See host_peripheral.h for documentation of this method. +status_t UartPeripheral::read(uint8_t *buffer, + uint32_t requestedBytes, + uint32_t *actualBytes, + uint32_t unused_timeoutMs) +{ + assert(buffer); + + // Read the requested number of bytes. + int count = serial_read(m_fileDescriptor, reinterpret_cast(buffer), requestedBytes); + if (actualBytes) + { + *actualBytes = count; + } + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes read in hex + Log::debug2("<"); + for (int i = 0; i < (int)count; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (count - 1)) + { + Log::debug2(" "); + } + } + Log::debug2(">\n"); + } + + if (count < (int)requestedBytes) + { + // Anything less than requestedBytes is a timeout error. + return kStatus_Timeout; + } + + return kStatus_Success; +} + +// See uart_peripheral.h for documentation of this method. +void UartPeripheral::flushRX() +{ +// An attempt was made on win32 to use a serial_flush function +// which would call PurgeComm(hCom, PURGE_RXABORT | PURGE_TXABORT | PURGE_RXCLEAR | PURGE_TXCLEAR) +// even though the function returned success there were still errant data in the RX buffer. Using reads +// to empty the RX buffer has worked + +// This read loop never exits on the Mac, and doesn't appear to be necessary. +#if (defined(WIN32) || defined(LINUX)) + // Read up to the requested number of bytes. + char *readBuf = reinterpret_cast(m_buffer); + while (serial_read(m_fileDescriptor, readBuf, 16)) + { + readBuf = reinterpret_cast(m_buffer); + } +#endif // defined(WIN32) || defined(LINUX) +} + +// See host_peripheral.h for documentation of this method. +status_t UartPeripheral::write(const uint8_t *buffer, uint32_t byteCount) +{ + assert(buffer); + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes written in hex + Log::debug2("["); + for (int i = 0; i < (int)byteCount; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (byteCount - 1)) + { + Log::debug2(" "); + } + } + Log::debug2("]\n"); + } + + if (serial_write(m_fileDescriptor, reinterpret_cast(const_cast(buffer)), byteCount) == byteCount) + return kStatus_Success; + else + return kStatus_Fail; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/Updater.cpp b/src/blfwk/src/Updater.cpp new file mode 100644 index 0000000..960262e --- /dev/null +++ b/src/blfwk/src/Updater.cpp @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2015-2020 NXP. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/Updater.h" + +using namespace blfwk; + +Updater::Updater(const Peripheral::PeripheralConfigData &config) + : Bootloader(config) + , m_base_address(0) + , m_sourceFile(NULL) + , m_operation(kUpdaterOperation_Update) + , m_progressCallback(NULL) + , m_progress() + , m_memoryId(kMemoryInternal) +{ + m_version = getVersion(); +} + +Updater::~Updater() {} + +// See Updater.h for documentation of this method. +status_t Updater::flashFirmware(const char *filename, uint32_t base_address, uint32_t memoryId) +{ + m_base_address = base_address; + m_memoryId = memoryId; + // Create the SourceFile + m_sourceFile = SourceFile::openFile(filename); + + // Initialize the Operation structure. + m_operation.tasks.push_back(updater_task_t(kUpdaterTask_Erasing, m_sourceFile->getSize())); + m_operation.tasks.push_back(updater_task_t(kUpdaterTask_Flashing, m_sourceFile->getSize())); + m_operation.current_task = 0; + + if (m_sourceFile->getFileType() == SourceFile::source_file_t::kSBSourceFile) + { + return flashFromSBFile(filename); + } + else if (m_sourceFile->getFileType() == SourceFile::source_file_t::kELFSourceFile) + { + throw std::runtime_error("ELF files are not yet supported."); + } + else + { + return flashFromSourceFile(); + } +} + +// See Updater.h for documentation of this method. +void Updater::eraseAllUnsecure() +{ + if (!isCommandSupported(kCommand_FlashEraseAllUnsecure)) + { + throw std::runtime_error("FlashEraseAllUnsecure isn't supported!"); + } + + FlashEraseAllUnsecure cmd; + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +// See Updater.h for documentation of this method. +void Updater::eraseRegion(uint32_t start, uint32_t length, uint32_t memoryId) +{ + if (!isMemorySupported(memoryId)) + { + throw std::runtime_error("Selected memory isn't supported!"); + } + m_memoryId = memoryId; + return eraseFlashRegion(start, length); +} + +void Updater::eraseAll(uint32_t memoryId) +{ + if (!isMemorySupported(memoryId)) + { + throw std::runtime_error("Selected memory isn't supported!"); + } + m_memoryId = memoryId; + return eraseFlashAll(); +} + +// See Updater.h for documentation of this method. +void Updater::unlock(string backdoor_key) +{ + if (!isCommandSupported(kCommand_FlashSecurityDisable)) + { + throw std::runtime_error("FlashSecurityDisable isn't supported!"); + } + if (backdoor_key.length() != 16) + { + throw std::runtime_error("Illegal BackdoorKey"); + } + + FlashSecurityDisable cmd(strtoul(backdoor_key.substr(8, 8).c_str(), NULL, 16), + strtoul(backdoor_key.substr(0, 8).c_str(), NULL, 16)); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +uint32_t Updater::getInternalFlashSize(void) +{ + uint32_t totalSize = 0; + + uint32_t firstStartAddress = 0; + uint32_t fw_status; + for (uint32_t index = 0;; index++) + { + // Get internal on-chip Flash start address. + GetProperty startAddr(kProperty_FlashStartAddress, index); + inject(startAddr); + fw_status = startAddr.getResponseValues()->at(0); + if (fw_status == kStatus_UnknownProperty) + { + // UnknownProperty means that there is no internal flash on current device. + return 0; + } + else if (fw_status == kStatus_InvalidArgument) + { + break; + } + else if (fw_status != kStatus_Success) + { + // Failed to communicate with the device. + throw std::runtime_error(startAddr.getStatusMessage(fw_status)); + } + + if (index == 0) + { + firstStartAddress = startAddr.getResponseValues()->at(1); + } + else if (firstStartAddress == startAddr.getResponseValues()->at(1)) + { + // If a Flash region's start address is the same as the first region. + // That means all Flash regions are listed. + // Then break the for-loop. + break; + } + + GetProperty size(kProperty_FlashSizeInBytes, index); + inject(size); + fw_status = size.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + // Failed to communicate with the device. + throw std::runtime_error(size.getStatusMessage(fw_status)); + } + + totalSize += size.getResponseValues()->at(1); + } + + return totalSize; +} + +uint32_t Updater::getInternalRAMSize(void) +{ + uint32_t totalSize = 0; + + uint32_t firstStartAddress = 0; + uint32_t fw_status; + for (uint32_t index = 0;; index++) + { + // Get internal on-chip RAM start address. + GetProperty startAddr(kProperty_RAMStartAddress, index); + inject(startAddr); + fw_status = startAddr.getResponseValues()->at(0); + if (fw_status == kStatus_UnknownProperty) + { + // UnknownProperty means that there is no internal flash on current device. + return 0; + } + else if (fw_status == kStatus_InvalidArgument) + { + break; + } + else if (fw_status != kStatus_Success) + { + // Failed to communicate with the device. + throw std::runtime_error(startAddr.getStatusMessage(fw_status)); + } + + if (index == 0) + { + firstStartAddress = startAddr.getResponseValues()->at(1); + } + else if (firstStartAddress == startAddr.getResponseValues()->at(1)) + { + // If a RAM region's start address is the same as the first region. + // That means all RAM regions are listed. + // Then break the for-loop. + break; + } + + GetProperty size(kProperty_RAMSizeInBytes, index); + inject(size); + fw_status = size.getResponseValues()->at(0); + if (fw_status != kStatus_Success) + { + // Failed to communicate with the device. + throw std::runtime_error(size.getStatusMessage(fw_status)); + } + + totalSize += size.getResponseValues()->at(1); + } + + return totalSize; +} + +// See Updater.h for documentation of this method. +void Updater::programOnce(uint32_t index, uint32_t byteCount, string data, bool isLsb) +{ + if (!isCommandSupported(kCommand_FlashProgramOnce)) + { + throw std::runtime_error("FlashProgramOnce isn't supported!"); + } + if ((byteCount != 4) && (byteCount != 8)) + { + throw std::runtime_error("Illegal byteCount! Must be 4 or 8"); + } + if ((data.length() != 8) && (data.length() != 16)) + { + throw std::runtime_error("Illegal data length. Must hex digits with no leading 0x"); + } + + FlashProgramOnce cmd(index, byteCount, strtoul(data.substr(8, 8).c_str(), NULL, 16), + strtoul(data.substr(0, 8).c_str(), NULL, 16), isLsb); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +// See Updater.h for documentation of this method. +void Updater::eraseFlashRegion(uint32_t start, uint32_t length) +{ + // Align the address and length to 1K boundary. + uint32_t alignedStart = start & (~(MinEraseAlignment - 1)); + uint32_t alignedLength = ((start + length + MinEraseAlignment) & (~(MinEraseAlignment - 1))) - alignedStart; + + // Inject the flash-erase-region(start, length) command. + FlashEraseRegion cmd(alignedStart, alignedLength, m_memoryId); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +// See Updater.h for documentation of this method. +void Updater::eraseFlashAll() +{ + // Inject the flash-erase-all command. + FlashEraseAll cmd(m_memoryId); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +// See Updater.h for documentation of this method. +void Updater::writeMemory(DataSource::Segment *segment) +{ + // Inject the write-memory(segment) command. + WriteMemory cmd(segment, m_memoryId); + cmd.registerProgress(&m_progress); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +// See Updater.h for documentation of this method. +void Updater::writeMemory(uint32_t address, const uchar_vector_t &data) +{ + // Inject the write-memory(segment) command. + WriteMemory cmd(address, data, m_memoryId); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +// See Updater.h for documentation of this method. +void Updater::fuseProgram(DataSource::Segment *segment) +{ + // Inject the write-memory(segment) command. + WriteMemory cmd(segment, m_memoryId); + cmd.registerProgress(&m_progress); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +// See Updater.h for documentation of this method. +void Updater::fuseProgram(uint32_t address, const uchar_vector_t &data) +{ + // Inject the write-memory(segment) command. + WriteMemory cmd(address, data, m_memoryId); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if (fw_status != kStatus_Success) + { + throw std::runtime_error(fw_msg); + } +} + +// See Updater.h for documentation of this method. +status_t Updater::flashFromSourceFile() +{ + if (!isMemorySupported(m_memoryId)) + { + throw std::runtime_error("Selected memory isn't supported!"); + } + + m_sourceFile->open(); + + DataSource *dataSource = m_sourceFile->createDataSource(); + + m_progress.m_segmentCount = dataSource->getSegmentCount(); + + for (uint32_t index = 0; index < dataSource->getSegmentCount(); ++index) + { + DataSource::Segment *segment = dataSource->getSegmentAt(index); + + try + { + if (segment->hasNaturalLocation()) + { + dataSource->setTarget(new NaturalDataTarget()); + } + else + { + dataSource->setTarget(new ConstantDataTarget(m_base_address)); + } + + m_operation.current_task = 0; + m_operation.tasks[m_operation.current_task].current += segment->getLength(); + if (m_progressCallback) + { + m_progressCallback(&m_operation); + } + eraseFlashRegion(segment->getBaseAddress(), segment->getLength()); + } + catch (const std::runtime_error &e) + { + // If the target region is not flash, bootloader will return kStatus_FlashAddressError. + // Ignore this error, and skip to next segment + if (strcmp(e.what(), Command::getStatusMessage(102 /*kStatus_FlashAddressError*/).c_str()) != 0) + { + delete segment; + delete dataSource; + throw e; + } + } + } + for (uint32_t index = 0; index < dataSource->getSegmentCount(); ++index) + { + DataSource::Segment *segment = dataSource->getSegmentAt(index); + + // Write the file to the base address. + m_operation.current_task = 1; + m_operation.tasks[m_operation.current_task].current += segment->getLength(); + if (m_progressCallback) + { + m_progressCallback(&m_operation); + } + m_progress.m_segmentIndex = index + 1; + writeMemory(segment); + } + + delete dataSource; + m_sourceFile->close(); + + return kStatus_Success; +} + +// See Updater.h for documentation of this method. +status_t Updater::flashFromSBFile(const char *filename) +{ + if (!isCommandSupported(kCommand_ReceiveSbFile)) + { + throw std::runtime_error("ReceiveSbFile isn't supported!"); + } + // Inject the receive-sb-file command. + m_operation.current_task = 1; + if (m_progressCallback) + { + m_progressCallback(&m_operation); + } + ReceiveSbFile cmd(filename); + cmd.registerProgress(&m_progress); + Log::info("inject command '%s'\n", cmd.getName().c_str()); + inject(cmd); + + // print command response values using the Logger. + cmd.logResponses(); + + uint32_t fw_status = cmd.getResponseValues()->at(0); + std::string fw_msg = cmd.getStatusMessage(fw_status); + + // Check the command status + if ((fw_status != kStatus_Success) && (fw_status != kStatus_AbortDataPhase)) + { + throw std::runtime_error(fw_msg); + } + return fw_status; +} + +bool Updater::isMemorySupported(uint32_t memoryId) +{ + uint32_t versionNumber = (m_version.major << 16) | (m_version.minor << 8) | (m_version.bugfix << 0); + + switch (memoryId) + { + case kMemoryInternal: + { + // Internal memory is supported by all devices running Kinetis bootloader. + return true; + } + break; + case kMemoryQuadSpi0: + { + // Bootloader 2.0.0 and previous versions only support internal and QSPI0. + if (versionNumber <= 0x20000 /*KBL2.0.0*/) + { + return isCommandSupported(kCommand_ConfigureMemory); + } + else + { + GetProperty getExernalAttri(kProperty_ExernalMemoryAttributes, kMemoryQuadSpi0); + inject(getExernalAttri); + status_t fw_status = getExernalAttri.getResponseValues()->at(0); + // UnknownProperty means that no external memories are supported by current device. + // InvalidArgument means that current memory type is not supported by the device. + if ((fw_status == kStatus_UnknownProperty) || (fw_status == kStatus_InvalidArgument)) + { + return false; + } + else if ((fw_status == 405 /*kStatus_QspiNotConfigured*/) || (fw_status == kStatus_Success)) + { + return true; + } + else + { + // Failed to get property of the device memory. + throw std::runtime_error("Failed to check the memory ID!"); + } + } + } + break; + case kMemoryIFR0: + { + return isCommandSupported(kCommand_FlashProgramOnce); + } + break; + case kMemoryFlashExecuteOnly: + { + // For the devices that doesn't support erasing all execute-only regions, + // flash-erase-all command will be executed instead. + return true; + } + break; + default: + { + // Bootloader 2.0.0 and previous versions only support internal and QSPI0. + if (versionNumber <= 0x20000 /*KBL2.0.0*/) + { + return false; + } + else + { + // If ConfigureMemory is not supported, + // then no external memory devices are supported by current target. + if (!isCommandSupported(kCommand_ConfigureMemory)) + { + return false; + } + GetProperty getExernalAttri(kProperty_ExernalMemoryAttributes, memoryId); + inject(getExernalAttri); + status_t fw_status = getExernalAttri.getResponseValues()->at(0); + // UnknownProperty means that no external memories are supported by current device. + // InvalidArgument means that current memory type is not supported by the device. + if ((fw_status == kStatus_UnknownProperty) || (fw_status == kStatus_InvalidArgument)) + { + return false; + } + // Memories, except QSPI0, have a common un-configured status. + else if ((fw_status == kStatusMemoryNotConfigured) || (fw_status == kStatus_Success)) + { + // Supported. + return true; + } + else + { + // Failed to get property of the device memory. + throw std::runtime_error("Failed to check the memory ID!"); + } + } + } + break; + } +} diff --git a/src/blfwk/src/UsbHidPacketizer.cpp b/src/blfwk/src/UsbHidPacketizer.cpp new file mode 100644 index 0000000..9f6bf9b --- /dev/null +++ b/src/blfwk/src/UsbHidPacketizer.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/UsbHidPacketizer.h" +#include "blfwk/Logging.h" +#include "blfwk/smart_ptr.h" +#include "blfwk/utils.h" +#ifdef LINUX +#include +#endif + +using namespace blfwk; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See usb_hid_packetizer.h for documentation of this method. +UsbHidPacketizer::UsbHidPacketizer(UsbHidPeripheral *peripheral, uint32_t packetTimeoutMs) + : Packetizer(peripheral, packetTimeoutMs) +{ + m_continuousReadCount = 0; + memset(&m_report, 0, sizeof(m_report)); + memset(&m_abortReport, 0, sizeof(m_abortReport)); +} + +// See usb_hid_packetizer.h for documentation of this method. +UsbHidPacketizer::~UsbHidPacketizer() +{ + delete m_peripheral; +} + +// See usb_hid_packetizer.h for documentation of this method. +status_t UsbHidPacketizer::writePacket(const uint8_t *packet, uint32_t byteCount, packet_type_t packetType) +{ + assert(m_peripheral); + if (byteCount) + { + assert(packet); + } + + // Determine report ID based on packet type. + uint8_t reportID; + switch (packetType) + { + case kPacketType_Command: + reportID = kBootloaderReportID_CommandOut; + break; + case kPacketType_Data: + reportID = kBootloaderReportID_DataOut; + break; + default: + Log::error("usbhid: unsupported packet type %d\n", (int)packetType); + return kStatus_Fail; + }; + + if (m_isAbortEnabled && (reportID == kBootloaderReportID_DataOut)) + { + // Check if the target has sent an abort report. + if (pollForAbortPacket()) + { + Log::info("usb hid detected receiver data abort\n"); + return kStatus_AbortDataPhase; + } + } + + // Construct report contents. + memset(&m_report, 0, sizeof(m_report)); + m_report.header.reportID = reportID; + m_report.header.packetLengthLsb = byteCount & 0xff; + m_report.header.packetLengthMsb = (byteCount >> 8) & 0xff; + + // If not a zero-length report, copy in packet data. + if (byteCount) + { + memcpy(m_report.packet, packet, byteCount); + } + + m_continuousReadCount = 0; + + return getPeripheral()->write((uint8_t *)&m_report, sizeof(bl_hid_header_t) + byteCount, m_packetTimeoutMs); +} + +// See usb_hid_packetizer.h for documentation of this method. +status_t UsbHidPacketizer::readPacket(uint8_t **packet, uint32_t *packetLength, packet_type_t packetType) +{ + assert(m_peripheral); + assert(packet); + assert(packetLength); + *packet = NULL; + *packetLength = 0; + + // Determine report ID based on packet type. + uint8_t reportID; + switch (packetType) + { + case kPacketType_Command: + reportID = kBootloaderReportID_CommandIn; + break; + case kPacketType_Data: + reportID = kBootloaderReportID_DataIn; + break; + default: + Log::error("usbhid: unsupported packet type %d\n", (int)packetType); + return kStatus_Fail; + }; + + // Read report. + uint32_t actualBytes = 0; + uint16_t lengthInPacket = 0; + uint32_t retryCnt = 0; + do + { + status_t retVal = + m_peripheral->read((unsigned char *)&m_report, sizeof(m_report), &actualBytes, m_packetTimeoutMs); + if (retVal != kStatus_Success) + { + return retVal; + } + + if (!retryCnt) + { + m_continuousReadCount++; + } + + // Check the report ID. + if (m_report.header.reportID != reportID) + { + Log::error("usbhid: received unexpected report=%x\n", m_report.header.reportID); + return kStatus_Fail; + } + + // Extract the packet length encoded as bytes 1 and 2 of the report. The packet length + // is transferred in little endian byte order. + lengthInPacket = m_report.header.packetLengthLsb | (m_report.header.packetLengthMsb << 8); + + // See if we received the data abort packet, + // if the data abort packet was received, try to read pending generic response packet. + if (lengthInPacket == 0) + { + Log::info("usbhid: received data phase abort\n"); + + // If continuous read occurs, that means this is a read-memory command, + // host tool shouldn't wait for pending packet because there are no pending packets. + if (m_continuousReadCount >= kContinuousReadMargin) + { + break; + } + retryCnt++; + } + else if (lengthInPacket > getMaxPacketSize()) + { + Log::error("Data packet size(%d) is bigger than max supported size(%d).", lengthInPacket, + getMaxPacketSize()); + return kStatus_Fail; + } + } while (actualBytes && !lengthInPacket && retryCnt < kPollPacketMaxRetryCnt); + + // Make sure we got all of the packet. Target will send the maximum + // report size, so there may be extra trailing bytes. + if ((actualBytes - sizeof(m_report.header)) < lengthInPacket) + { + Log::error("usbhid: received only %d bytes of packet with length %d\n", actualBytes - sizeof(m_report.header), + lengthInPacket); + return kStatus_Fail; + } + + // Return results. + *packet = m_report.packet; + *packetLength = lengthInPacket; + + return kStatus_Success; +} + +// See usb_hid_packetizer.h for documentation of this method. +void UsbHidPacketizer::flushInput() +{ + uint32_t actualBytes = 0; + do + { + m_peripheral->read((unsigned char *)&m_report, sizeof(m_report), &actualBytes, m_packetTimeoutMs); + } while (actualBytes > 0); +} + +// See usb_hid_packetizer.h for documentation of this method. +void UsbHidPacketizer::abortPacket() +{ + // Abort data phase by writing a zero-length command packet. + writePacket(NULL, 0, kPacketType_Command); + flushInput(); +} + +bool UsbHidPacketizer::pollForAbortPacket() +{ + // Just check to see if there is data to be read from hid device. + // No reason to wait (milliseconds = 0), because we aren't really expecting anything. + uint32_t actualBytes = 0; + m_peripheral->read((unsigned char *)&m_abortReport, sizeof(m_abortReport), &actualBytes, kPollAbortTimeoutMs); + if (actualBytes == 0) + { + // No abort packet + return false; + } + else + { + // Got an abort packet + return true; + } +} + +// See usb_hid_packetizer.h for documentation of this method. +uint32_t UsbHidPacketizer::getMaxPacketSize() +{ + return kMaxHostPacketSize; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/UsbHidPeripheral.cpp b/src/blfwk/src/UsbHidPeripheral.cpp new file mode 100644 index 0000000..b6d545e --- /dev/null +++ b/src/blfwk/src/UsbHidPeripheral.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2019 NXP + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/UsbHidPeripheral.h" +#include "blfwk/format_string.h" +#include "blfwk/smart_ptr.h" +#include "blfwk/Logging.h" + +#include "bootloader_hid_report_ids.h" + +using namespace blfwk; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// See UsbHidPeripheral.h for documentation of this method. +UsbHidPeripheral::UsbHidPeripheral() + : m_vendor_id(kDefault_Vid) + , m_product_id(kDefault_Pid) + , m_path("") +{ + if (!init()) + { + throw std::runtime_error( + format_string("Error: UsbHidPeripheral() cannot open USB HID device (vid=0x%04x, pid=0x%04x).", m_vendor_id, + m_product_id)); + } +} + +// See UsbHidPeripheral.h for documentation of this method. +UsbHidPeripheral::UsbHidPeripheral(unsigned short vendor_id, + unsigned short product_id, + const char *serial_number, + const char *path) + : m_vendor_id(vendor_id) + , m_product_id(product_id) + , m_path(path) +{ + // Convert to a wchar_t* + std::string s(serial_number); + m_serial_number.assign(s.begin(), s.end()); + + if (!init()) + { + if (m_path.empty()) + { + throw std::runtime_error( + format_string("Error: UsbHidPeripheral() cannot open USB HID device (vid=0x%04x, pid=0x%04x, sn=%s).", + vendor_id, product_id, s.c_str())); + } + else + { + throw std::runtime_error( + format_string("Error: UsbHidPeripheral() cannot open USB HID device (path=%s).", m_path.c_str())); + } + } +} + +// See UsbHidPeripheral.h for documentation of this method. +bool UsbHidPeripheral::init() +{ + // Open the device using the VID, PID, + // and optionally the Serial number. + if (m_path.empty()) + { + m_device = hid_open(m_vendor_id, m_product_id, m_serial_number.c_str()); + } + else + { + std::string path = m_path; +#if defined(WIN32) + // if not start with string "\\\\.\\", then add it. + if ((path.find("\\\\.\\") != 0) && (path.find("\\\\?\\") != 0)) + { + // Add the prefix string. + path = "\\\\.\\" + path; + } + + // Replace all '\\' with '#', except the 4 prefix characters. + while (path.find('\\', 4) != std::string::npos) + { + path.replace(path.find('\\', 4), 1, "#"); + } + + // if not end with string "#{4d1e55b2-f16f-11cf-88cb-001111000030}", then add it. + if (path.find("#{4d1e55b2-f16f-11cf-88cb-001111000030}") != + (path.length() - sizeof("#{4d1e55b2-f16f-11cf-88cb-001111000030}") + 1 /*Add line terminator back*/)) + { + path.append("#{4d1e55b2-f16f-11cf-88cb-001111000030}"); + } +#endif +#if _DEBUG + Log::info("USB Device Path = %s.\n", path.c_str()); +#endif + m_device = hid_open_path(path.c_str()); + } + if (!m_device) + { + return false; + } + + return true; +} + +// See UsbHidPeripheral.h for documentation of this method. +UsbHidPeripheral::~UsbHidPeripheral() +{ + if (m_device) + { + hid_close(m_device); + /* Free static HIDAPI objects. */ + hid_exit(); + } +} + +// See UsbHidPeripheral.h for documentation of this method. +status_t UsbHidPeripheral::read(uint8_t *buffer, uint32_t requestedBytes, uint32_t *actualBytes, uint32_t timeout) +{ + assert(buffer); + + // Read the requested number of bytes. + int count = hid_read_timeout(m_device, buffer, requestedBytes, timeout); + if (actualBytes) + { + *actualBytes = count; + } + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes read in hex + Log::debug2("<"); + for (int i = 0; i < (int)count; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (count - 1)) + { + Log::debug2(" "); + } + } + Log::debug2(">\n"); + } + + // Bail if we got an error (-1), or if the number of bytes read was less than + // the report header. + if (count < (int)sizeof(bl_hid_header_t)) + { + if (count == -1) + { + return kStatus_Fail; + } + else + { + return kStatus_Timeout; + } + } + + return kStatus_Success; +} + +// See UsbHidPeripheral.h for documentation of this method. +status_t UsbHidPeripheral::write(const uint8_t *buffer, uint32_t byteCount, uint32_t timeoutMS) +{ + assert(buffer); + + if (Log::getLogger()->getFilterLevel() == Logger::kDebug2) + { + // Log bytes written in hex + Log::debug2("["); + for (uint32_t i = 0; i < byteCount; i++) + { + Log::debug2("%02x", buffer[i]); + if (i != (byteCount - 1)) + { + Log::debug2(" "); + } + } + Log::debug2("]\n"); + } + + int count = hid_write_timeout(m_device, buffer, byteCount, timeoutMS); + if (count < 0) + { + const wchar_t *errorMessage = hid_error(m_device); + if (errorMessage) + { + int len = wcslen(errorMessage); + char *msg = new char[len + 1]; + wcstombs(msg, errorMessage, len + 1); + Log::error("%s", msg); + delete[] msg; + } + return kStatus_Fail; + } + + return kStatus_Success; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/Value.cpp b/src/blfwk/src/Value.cpp new file mode 100644 index 0000000..5dc620d --- /dev/null +++ b/src/blfwk/src/Value.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/Value.h" + +using namespace blfwk; + +//! Returns a varying size depending on the word size attribute. +//! +size_t SizedIntegerValue::getSize() const +{ + switch (m_size) + { + case kWordSize: + return sizeof(uint32_t); + case kHalfWordSize: + return sizeof(uint16_t); + case kByteSize: + return sizeof(uint8_t); + } + return kWordSize; +} + +//! The resulting mask can be used to truncate the integer value to be +//! certain it doesn't extend beyond the associated word size. +uint32_t SizedIntegerValue::getWordSizeMask() const +{ + switch (m_size) + { + case kWordSize: + return 0xffffffff; + case kHalfWordSize: + return 0x0000ffff; + case kByteSize: + return 0x000000ff; + } + return 0; +} diff --git a/src/blfwk/src/format_string.cpp b/src/blfwk/src/format_string.cpp new file mode 100644 index 0000000..3ec3917 --- /dev/null +++ b/src/blfwk/src/format_string.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "blfwk/format_string.h" +#include +#include +#include +#include +#include +//! Size of the temporary buffer to hold the formatted output string. +#define WIN32_FMT_BUF_LEN (512) + +/*! + * \brief Simple template class to free a pointer. + */ +template +class free_ptr +{ +public: + //! \brief Constructor. + free_ptr(T ptr) + : m_p(ptr) + { + } + + //! \brief Destructor. + ~free_ptr() + { + if (m_p) + { + free(m_p); + } + } + +protected: + T m_p; //!< The value to free. +}; + +//! The purpose of this function to provide a convenient way of generating formatted +//! STL strings inline. This is especially useful when throwing exceptions that take +//! a std::string for a message. The length of the formatted output string is limited +//! only by memory. Memory temporarily allocated for the output string is disposed of +//! before returning. +//! +//! Example usage: +//! \code +//! throw std::runtime_error(format_string("error on line %d", line)); +//! \endcode +//! +//! \param fmt Format string using printf-style format markers. +//! \return An STL string object of the formatted output. +std::string format_string(const char *fmt, ...) +{ + char *buf = 0; + va_list vargs; + va_start(vargs, fmt); + int result = -1; +#if WIN32 + buf = (char *)malloc(WIN32_FMT_BUF_LEN); + if (buf) + { + result = _vsnprintf(buf, WIN32_FMT_BUF_LEN, fmt, vargs); + } +#else // WIN32 + result = vasprintf(&buf, fmt, vargs); +#endif // WIN32 + va_end(vargs); + if (result != -1 && buf) + { + free_ptr freebuf(buf); + return std::string(buf); + } + return ""; +} diff --git a/src/blfwk/src/hid-linux.c b/src/blfwk/src/hid-linux.c new file mode 100644 index 0000000..ea64dca --- /dev/null +++ b/src/blfwk/src/hid-linux.c @@ -0,0 +1,859 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + Linux Version - 6/2/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/* C */ +#include +#include +#include +#include +#include + +/* Unix */ +#include +#include +#include +#include +#include +#include +#include + +/* Linux */ +#include +#include +#include +#include + +#include "hidapi.h" + +/* Definitions from linux/hidraw.h. Since these are new, some distros + may not have header files which contain them. */ +#ifndef HIDIOCSFEATURE +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x06, len) +#endif +#ifndef HIDIOCGFEATURE +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x07, len) +#endif + +/* Definitions from linux/include/linux/usb.h. Timeouts, in milliseconds, + used for sending receiving control messages. */ +#define USB_CTRL_GET_TIMEOUT 5000 +#define USB_CTRL_SET_TIMEOUT 5000 + +/* USB HID device property names */ +const char *device_string_names[] = { + "manufacturer", "product", "serial", +}; + +/* Symbolic names for the properties above */ +enum device_string_id +{ + DEVICE_STRING_MANUFACTURER, + DEVICE_STRING_PRODUCT, + DEVICE_STRING_SERIAL, + + DEVICE_STRING_COUNT, +}; + +struct hid_device_ +{ + int device_handle; + int blocking; + int uses_numbered_reports; +}; + +static __u32 kernel_version = 0; + +static __u32 detect_kernel_version(void) +{ + struct utsname name; + int major, minor, release; + int ret; + + uname(&name); + ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); + if (ret == 3) + { + return KERNEL_VERSION(major, minor, release); + } + + ret = sscanf(name.release, "%d.%d", &major, &minor); + if (ret == 2) + { + return KERNEL_VERSION(major, minor, 0); + } + + printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); + return 0; +} + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = -1; + dev->blocking = 1; + dev->uses_numbered_reports = 0; + + return dev; +} + +/* The caller must free the returned string with free(). */ +static wchar_t *utf8_to_wchar_t(const char *utf8) +{ + wchar_t *ret = NULL; + + if (utf8) + { + size_t wlen = mbstowcs(NULL, utf8, 0); + if ((size_t)-1 == wlen) + { + return wcsdup(L""); + } + ret = calloc(wlen + 1, sizeof(wchar_t)); + mbstowcs(ret, utf8, wlen + 1); + ret[wlen] = 0x0000; + } + + return ret; +} + +/* Get an attribute value from a udev_device and return it as a whar_t + string. The returned string must be freed with free() when done.*/ +static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) +{ + return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); +} + +/* uses_numbered_reports() returns 1 if report_descriptor describes a device + which contains numbered reports. */ +static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) +{ + unsigned int i = 0; + int size_code; + int data_len, key_size; + + while (i < size) + { + int key = report_descriptor[i]; + + /* Check for the Report ID key */ + if (key == 0x85 /*Report ID*/) + { + /* This device has a Report ID, which means it uses + numbered reports. */ + return 1; + } + + // printf("key: %02hhx\n", key); + + if ((key & 0xf0) == 0xf0) + { + /* This is a Long Item. The next byte contains the + length of the data section (value) for this key. + See the HID specification, version 1.11, section + 6.2.2.3, titled "Long Items." */ + if (i + 1 < size) + data_len = report_descriptor[i + 1]; + else + data_len = 0; /* malformed report */ + key_size = 3; + } + else + { + /* This is a Short Item. The bottom two bits of the + key contain the size code for the data section + (value) for this key. Refer to the HID + specification, version 1.11, section 6.2.2.2, + titled "Short Items." */ + size_code = key & 0x3; + switch (size_code) + { + case 0: + case 1: + case 2: + data_len = size_code; + break; + case 3: + data_len = 4; + break; + default: + /* Can't ever happen since size_code is & 0x3 */ + data_len = 0; + break; + }; + key_size = 1; + } + + /* Skip over this key and it's associated data */ + i += data_len + key_size; + } + + /* Didn't find a Report ID key. Device doesn't use numbered reports. */ + return 0; +} + +/* + * The caller is responsible for free()ing the (newly-allocated) character + * strings pointed to by serial_number_utf8 and product_name_utf8 after use. + */ +static int parse_uevent_info(const char *uevent, + int *bus_type, + unsigned short *vendor_id, + unsigned short *product_id, + char **serial_number_utf8, + char **product_name_utf8) +{ + char *tmp = strdup(uevent); + char *saveptr = NULL; + char *line; + char *key; + char *value; + + int found_id = 0; + int found_serial = 0; + int found_name = 0; + + line = strtok_r(tmp, "\n", &saveptr); + while (line != NULL) + { + /* line: "KEY=value" */ + key = line; + value = strchr(line, '='); + if (!value) + { + goto next_line; + } + *value = '\0'; + value++; + + if (strcmp(key, "HID_ID") == 0) + { + /** + * type vendor product + * HID_ID=0003:000005AC:00008242 + **/ + int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); + if (ret == 3) + { + found_id = 1; + } + } + else if (strcmp(key, "HID_NAME") == 0) + { + /* The caller has to free the product name */ + *product_name_utf8 = strdup(value); + found_name = 1; + } + else if (strcmp(key, "HID_UNIQ") == 0) + { + /* The caller has to free the serial number */ + *serial_number_utf8 = strdup(value); + found_serial = 1; + } + + next_line: + line = strtok_r(NULL, "\n", &saveptr); + } + + free(tmp); + return (found_id && found_name && found_serial); +} + +static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) +{ + struct udev *udev; + struct udev_device *udev_dev, *parent, *hid_dev; + struct stat s; + int ret = -1; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; + + /* Create the udev object */ + udev = udev_new(); + if (!udev) + { + printf("Can't create udev\n"); + return -1; + } + + /* Get the dev_t (major/minor numbers) from the file handle. */ + fstat(dev->device_handle, &s); + /* Open a udev device from the dev_t. 'c' means character device. */ + udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); + if (udev_dev) + { + hid_dev = udev_device_get_parent_with_subsystem_devtype(udev_dev, "hid", NULL); + if (hid_dev) + { + unsigned short dev_vid; + unsigned short dev_pid; + int bus_type; + size_t retm; + + ret = parse_uevent_info(udev_device_get_sysattr_value(hid_dev, "uevent"), &bus_type, &dev_vid, &dev_pid, + &serial_number_utf8, &product_name_utf8); + + if (bus_type == BUS_BLUETOOTH) + { + switch (key) + { + case DEVICE_STRING_MANUFACTURER: + wcsncpy(string, L"", maxlen); + ret = 0; + break; + case DEVICE_STRING_PRODUCT: + retm = mbstowcs(string, product_name_utf8, maxlen); + ret = (retm == (size_t)-1) ? -1 : 0; + break; + case DEVICE_STRING_SERIAL: + retm = mbstowcs(string, serial_number_utf8, maxlen); + ret = (retm == (size_t)-1) ? -1 : 0; + break; + case DEVICE_STRING_COUNT: + default: + ret = -1; + break; + } + } + else + { + /* This is a USB device. Find its parent USB Device node. */ + parent = udev_device_get_parent_with_subsystem_devtype(udev_dev, "usb", "usb_device"); + if (parent) + { + const char *str; + const char *key_str = NULL; + + if (key >= 0 && key < DEVICE_STRING_COUNT) + { + key_str = device_string_names[key]; + } + else + { + ret = -1; + goto end; + } + + str = udev_device_get_sysattr_value(parent, key_str); + if (str) + { + /* Convert the string from UTF-8 to wchar_t */ + retm = mbstowcs(string, str, maxlen); + ret = (retm == (size_t)-1) ? -1 : 0; + goto end; + } + } + } + } + } + +end: + free(serial_number_utf8); + free(product_name_utf8); + + udev_device_unref(udev_dev); + /* parent and hid_dev don't need to be (and can't be) unref'd. + I'm not sure why, but they'll throw double-free() errors. */ + udev_unref(udev); + + return ret; +} + +int HID_API_EXPORT hid_init(void) +{ + const char *locale; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); + + kernel_version = detect_kernel_version(); + + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + /* Nothing to do for this in the Linux/hidraw implementation. */ + return 0; +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + struct hid_device_info *prev_dev = NULL; /* previous device */ + + hid_init(); + + /* Create the udev object */ + udev = udev_new(); + if (!udev) + { + printf("Can't create udev\n"); + return NULL; + } + + /* Create a list of the devices in the 'hidraw' subsystem. */ + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "hidraw"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + /* For each item, see if it matches the vid/pid, and if so + create a udev_device record for it */ + udev_list_entry_foreach(dev_list_entry, devices) + { + const char *sysfs_path; + const char *dev_path; + const char *str; + struct udev_device *raw_dev; /* The device's hidraw udev node. */ + struct udev_device *hid_dev; /* The device's HID udev node. */ + struct udev_device *usb_dev; /* The device's USB udev node. */ + struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ + unsigned short dev_vid; + unsigned short dev_pid; + char *serial_number_utf8 = NULL; + char *product_name_utf8 = NULL; + int bus_type; + int result; + + /* Get the filename of the /sys entry for the device + and create a udev_device object (dev) representing it */ + sysfs_path = udev_list_entry_get_name(dev_list_entry); + raw_dev = udev_device_new_from_syspath(udev, sysfs_path); + dev_path = udev_device_get_devnode(raw_dev); + + hid_dev = udev_device_get_parent_with_subsystem_devtype(raw_dev, "hid", NULL); + + if (!hid_dev) + { + /* Unable to find parent hid device. */ + goto next; + } + + result = parse_uevent_info(udev_device_get_sysattr_value(hid_dev, "uevent"), &bus_type, &dev_vid, &dev_pid, + &serial_number_utf8, &product_name_utf8); + + if (!result) + { + /* parse_uevent_info() failed for at least one field. */ + goto next; + } + + if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) + { + /* We only know how to handle USB and BT devices. */ + goto next; + } + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && (product_id == 0x0 || product_id == dev_pid)) + { + struct hid_device_info *tmp; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) + { + cur_dev->next = tmp; + } + else + { + root = tmp; + } + prev_dev = cur_dev; + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = dev_path ? strdup(dev_path) : NULL; + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Serial Number */ + cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); + + /* Release Number */ + cur_dev->release_number = 0x0; + + /* Interface Number */ + cur_dev->interface_number = -1; + + switch (bus_type) + { + case BUS_USB: + /* The device pointed to by raw_dev contains information about + the hidraw device. In order to get information about the + USB device, get the parent device with the + subsystem/devtype pair of "usb"/"usb_device". This will + be several levels up the tree, but the function will find + it. */ + usb_dev = udev_device_get_parent_with_subsystem_devtype(raw_dev, "usb", "usb_device"); + + if (!usb_dev) + { + /* Free this device */ + free(cur_dev->serial_number); + free(cur_dev->path); + free(cur_dev); + + /* Take it off the device list. */ + if (prev_dev) + { + prev_dev->next = NULL; + cur_dev = prev_dev; + } + else + { + cur_dev = root = NULL; + } + + goto next; + } + + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = + copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); + cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); + + /* Release Number */ + str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); + cur_dev->release_number = (str) ? strtol(str, NULL, 16) : 0x0; + + /* Get a handle to the interface's udev node. */ + intf_dev = udev_device_get_parent_with_subsystem_devtype(raw_dev, "usb", "usb_interface"); + if (intf_dev) + { + str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); + cur_dev->interface_number = (str) ? strtol(str, NULL, 16) : -1; + } + + break; + + case BUS_BLUETOOTH: + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string = wcsdup(L""); + cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); + + break; + + default: + /* Unknown device type - this should never happen, as we + * check for USB and Bluetooth devices above */ + break; + } + } + + next: + free(serial_number_utf8); + free(product_name_utf8); + udev_device_unref(raw_dev); + /* hid_dev, usb_dev and intf_dev don't need to be (and can't be) + unref()d. It will cause a double-free() error. I'm not + sure why. */ + } + /* Free the enumerator and udev objects. */ + udev_enumerate_unref(enumerate); + udev_unref(udev); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + struct hid_device_info *d = devs; + while (d) + { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device *hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) + { + if (cur_dev->vendor_id == vendor_id && cur_dev->product_id == product_id) + { + if (serial_number && (wcslen(serial_number) > 0)) + { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) + { + path_to_open = cur_dev->path; + break; + } + } + else + { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +hid_device *HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + + hid_init(); + + dev = new_hid_device(); + + /* OPEN HERE */ + dev->device_handle = open(path, O_RDWR); + + /* If we have a good handle, return it. */ + if (dev->device_handle > 0) + { + /* Get the report descriptor */ + int res, desc_size = 0; + struct hidraw_report_descriptor rpt_desc; + + memset(&rpt_desc, 0x0, sizeof(rpt_desc)); + + /* Get Report Descriptor Size */ + res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); + if (res < 0) + perror("HIDIOCGRDESCSIZE"); + + /* Get Report Descriptor */ + rpt_desc.size = desc_size; + res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); + if (res < 0) + { + perror("HIDIOCGRDESC"); + } + else + { + /* Determine if this device uses numbered reports. */ + dev->uses_numbered_reports = uses_numbered_reports(rpt_desc.value, rpt_desc.size); + } + + return dev; + } + else + { + /* Unable to open any devices. */ + free(dev); + return NULL; + } +} + +int HID_API_EXPORT hid_write_timeout(hid_device *dev, const unsigned char *data, size_t length, int milliseconds) +{ + int bytes_written; + + /* + * Note: + * 1. Blocking Write for USB is not real blocking. There is a build-in timeout in Linux, which + * is defined by USB_CTRL_SET_TIMEOUT in linux/include/linux/usb.h + * 2. Do not use poll()/ppoll() for timeout control. POLLOUT wouldn't be triggered by HIDRAW. + */ + if (milliseconds >= 0) + { + while (milliseconds >= 0) + { + bytes_written = write(dev->device_handle, data, length); + milliseconds -= USB_CTRL_SET_TIMEOUT; + if ((bytes_written < 0) && (errno == ETIMEDOUT) && (milliseconds > 0)) + { + // timeout for current write, but still some time left. + continue; + } + else + { + break; + } + } + } + else + { + // Infinite blocking + while (1) + { + bytes_written = write(dev->device_handle, data, length); + if ((bytes_written < 0) && (errno == ETIMEDOUT)) + { + continue; + } + else + { + break; + } + } + } + + return bytes_written; +} + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + return hid_write_timeout(dev, data, length, (dev->blocking) ? -1 : 0); +} + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read; + + if (milliseconds >= 0) + { + /* Milliseconds is either 0 (non-blocking) or > 0 (contains + a valid timeout). In both cases we want to call poll() + and wait for data to arrive. Don't rely on non-blocking + operation (O_NONBLOCK) since some kernels don't seem to + properly report device disconnection through read() when + in non-blocking mode. */ + int ret; + struct pollfd fds; + + fds.fd = dev->device_handle; + fds.events = POLLIN; + fds.revents = 0; + ret = poll(&fds, 1, milliseconds); + if (ret == -1 || ret == 0) + { + /* Error or timeout */ + return ret; + } + else + { + /* Check for errors on the file descriptor. This will + indicate a device disconnection. */ + if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) + return -1; + } + } + + bytes_read = read(dev->device_handle, data, length); + if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) + bytes_read = 0; + + if (bytes_read >= 0 && kernel_version != 0 && kernel_version < KERNEL_VERSION(2, 6, 34) && + dev->uses_numbered_reports) + { + /* Work around a kernel bug. Chop off the first byte. */ + memmove(data, data + 1, bytes_read); + bytes_read--; + } + + return bytes_read; +} + +int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking) ? -1 : 0); +} + +int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) +{ + /* Do all non-blocking in userspace using poll(), since it looks + like there's a bug in the kernel in some versions where + read() will not return -1 on disconnection of the USB device */ + + dev->blocking = !nonblock; + return 0; /* Success */ +} + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + int res; + + res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); + if (res < 0) + perror("ioctl (SFEATURE)"); + + return res; +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + int res; + + res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); + if (res < 0) + perror("ioctl (GFEATURE)"); + + return res; +} + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + close(dev->device_handle); + free(dev); +} + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + return -1; +} + +HID_API_EXPORT const wchar_t *HID_API_CALL hid_error(hid_device *dev) +{ + return NULL; +} diff --git a/src/blfwk/src/hid-mac.c b/src/blfwk/src/hid-mac.c new file mode 100644 index 0000000..630e358 --- /dev/null +++ b/src/blfwk/src/hid-mac.c @@ -0,0 +1,1158 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 2010-07-03 + + Copyright 2010, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/* See Apple Technical Note TN2187 for details on IOHidManager. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hidapi.h" + +/* Barrier implementation because Mac OSX doesn't have pthread_barrier. + It also doesn't have clock_gettime(). So much for POSIX and SUSv2. + This implementation came from Brent Priddy and was posted on + StackOverflow. It is used with his permission. */ +typedef int pthread_barrierattr_t; +typedef struct pthread_barrier +{ + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; +} pthread_barrier_t; + +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) +{ + if (count == 0) + { + errno = EINVAL; + return -1; + } + + if (pthread_mutex_init(&barrier->mutex, 0) < 0) + { + return -1; + } + if (pthread_cond_init(&barrier->cond, 0) < 0) + { + pthread_mutex_destroy(&barrier->mutex); + return -1; + } + barrier->trip_count = count; + barrier->count = 0; + + return 0; +} + +static int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +static int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if (barrier->count >= barrier->trip_count) + { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else + { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +static int return_data(hid_device *dev, unsigned char *data, size_t length); + +/* Linked List of input reports received from the device. */ +struct input_report +{ + uint8_t *data; + size_t len; + struct input_report *next; +}; + +struct hid_device_ +{ + IOHIDDeviceRef device_handle; + int blocking; + int uses_numbered_reports; + int disconnected; + CFStringRef run_loop_mode; + CFRunLoopRef run_loop; + CFRunLoopSourceRef source; + uint8_t *input_report_buf; + CFIndex max_input_report_len; + struct input_report *input_reports; + + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */ + int shutdown_thread; + + hid_device *next; +}; + +/* Static list of all the devices open. This way when a device gets + disconnected, its hid_device structure can be marked as disconnected + from hid_device_removal_callback(). */ +static hid_device *device_list = NULL; +static pthread_mutex_t device_list_mutex = PTHREAD_MUTEX_INITIALIZER; + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = NULL; + dev->blocking = 1; + dev->uses_numbered_reports = 0; + dev->disconnected = 0; + dev->run_loop_mode = NULL; + dev->run_loop = NULL; + dev->source = NULL; + dev->input_report_buf = NULL; + dev->input_reports = NULL; + dev->shutdown_thread = 0; + dev->next = NULL; + + /* Thread objects */ + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); + pthread_barrier_init(&dev->shutdown_barrier, NULL, 2); + + /* Add the new record to the device_list. */ + pthread_mutex_lock(&device_list_mutex); + if (!device_list) + device_list = dev; + else + { + hid_device *d = device_list; + while (d) + { + if (!d->next) + { + d->next = dev; + break; + } + d = d->next; + } + } + pthread_mutex_unlock(&device_list_mutex); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + if (!dev) + return; + + /* Delete any input reports still left over. */ + struct input_report *rpt = dev->input_reports; + while (rpt) + { + struct input_report *next = rpt->next; + free(rpt->data); + free(rpt); + rpt = next; + } + + /* Free the string and the report buffer. The check for NULL + is necessary here as CFRelease() doesn't handle NULL like + free() and others do. */ + if (dev->run_loop_mode) + CFRelease(dev->run_loop_mode); + if (dev->source) + CFRelease(dev->source); + free(dev->input_report_buf); + + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->shutdown_barrier); + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); + + /* Remove it from the device list. */ + pthread_mutex_lock(&device_list_mutex); + hid_device *d = device_list; + if (d == dev) + { + device_list = d->next; + } + else + { + while (d) + { + if (d->next == dev) + { + d->next = d->next->next; + break; + } + + d = d->next; + } + } + pthread_mutex_unlock(&device_list_mutex); + + /* Free the structure itself. */ + free(dev); +} + +static IOHIDManagerRef hid_mgr = 0x0; + +#if 0 +static void register_error(hid_device *device, const char *op) +{ + +} +#endif + +static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) +{ + CFTypeRef ref; + int32_t value; + + ref = IOHIDDeviceGetProperty(device, key); + if (ref) + { + if (CFGetTypeID(ref) == CFNumberGetTypeID()) + { + CFNumberGetValue((CFNumberRef)ref, kCFNumberSInt32Type, &value); + return value; + } + } + return 0; +} + +static unsigned short get_vendor_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDVendorIDKey)); +} + +static unsigned short get_product_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDProductIDKey)); +} + +static int32_t get_max_report_length(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey)); +} + +static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len) +{ + CFStringRef str = IOHIDDeviceGetProperty(device, prop); + + buf[0] = 0x0000; + + if (str) + { + CFRange range; + range.location = 0; + range.length = CFStringGetLength(str); + CFIndex used_buf_len; + CFStringGetBytes(str, range, kCFStringEncodingUTF32LE, (char)'?', FALSE, (UInt8 *)buf, len, &used_buf_len); + buf[used_buf_len] = 0x00000000; + return used_buf_len; + } + else + return 0; +} + +static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, size_t len) +{ + CFStringRef str = IOHIDDeviceGetProperty(device, prop); + + buf[0] = 0x0000; + + if (str) + { + CFRange range; + range.location = 0; + range.length = CFStringGetLength(str); + CFIndex used_buf_len; + CFStringGetBytes(str, range, kCFStringEncodingUTF8, (char)'?', FALSE, (UInt8 *)buf, len, &used_buf_len); + buf[used_buf_len] = 0x00000000; + return used_buf_len; + } + else + return 0; +} + +static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len); +} + +static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len); +} + +static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len); +} + +/* Implementation of wcsdup() for Mac. */ +static wchar_t *dup_wcs(const wchar_t *s) +{ + size_t len = wcslen(s); + wchar_t *ret = malloc((len + 1) * sizeof(wchar_t)); + wcscpy(ret, s); + + return ret; +} + +static int make_path(IOHIDDeviceRef device, char *buf, size_t len) +{ + int res; + unsigned short vid, pid; + char transport[32]; + + buf[0] = '\0'; + + res = get_string_property_utf8(device, CFSTR(kIOHIDTransportKey), transport, sizeof(transport)); + + if (!res) + return -1; + + vid = get_vendor_id(device); + pid = get_product_id(device); + + res = snprintf(buf, len, "%s_%04hx_%04hx_%p", transport, vid, pid, device); + + buf[len - 1] = '\0'; + return res + 1; +} + +static int init_hid_manager(void) +{ + IOReturn res; + + /* Initialize all the HID Manager Objects */ + hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (!hid_mgr) + return -1; + + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + return 0; +} + +int HID_API_EXPORT hid_init(void) +{ + if (!hid_mgr) + { + if (init_hid_manager() < 0) + { + hid_exit(); + return -1; + } + } + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + if (hid_mgr) + { + /* Close the HID manager. */ + CFRelease(hid_mgr); + hid_mgr = NULL; + } + + return 0; +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + struct hid_device_info *root = NULL; // return object + struct hid_device_info *cur_dev = NULL; + CFIndex num_devices; + int i; + + setlocale(LC_ALL, ""); + + /* Set up the HID Manager if it hasn't been done */ + if (hid_init() < 0) + { + return NULL; + } + + /* Get a list of the Devices */ + CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); + + /* Convert the list into a C array so we can iterate easily. */ + num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **)device_array); + + /* Iterate over each device, making an entry for it. */ + for (i = 0; i < num_devices; i++) + { + unsigned short dev_vid; + unsigned short dev_pid; +#define BUF_LEN 256 + wchar_t buf[BUF_LEN]; + char cbuf[BUF_LEN]; + + IOHIDDeviceRef dev = device_array[i]; + + if (!dev) + { + continue; + } + dev_vid = get_vendor_id(dev); + dev_pid = get_product_id(dev); + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 && product_id == 0x0) || (vendor_id == dev_vid && product_id == dev_pid)) + { + struct hid_device_info *tmp; + size_t len; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) + { + cur_dev->next = tmp; + } + else + { + root = tmp; + } + cur_dev = tmp; + + // Get the Usage Page and Usage for this device. + cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey)); + cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey)); + + /* Fill out the record */ + cur_dev->next = NULL; + len = make_path(dev, cbuf, sizeof(cbuf)); + cur_dev->path = strdup(cbuf); + + /* Serial Number */ + get_serial_number(dev, buf, BUF_LEN); + cur_dev->serial_number = dup_wcs(buf); + + /* Manufacturer and Product strings */ + get_manufacturer_string(dev, buf, BUF_LEN); + cur_dev->manufacturer_string = dup_wcs(buf); + get_product_string(dev, buf, BUF_LEN); + cur_dev->product_string = dup_wcs(buf); + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); + + /* Interface Number (Unsupported on Mac)*/ + cur_dev->interface_number = -1; + } + } + + free(device_array); + CFRelease(device_set); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + /* This function is identical to the Linux version. Platform independent. */ + struct hid_device_info *d = devs; + while (d) + { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device *HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + /* This function is identical to the Linux version. Platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) + { + if (cur_dev->vendor_id == vendor_id && cur_dev->product_id == product_id) + { + if (serial_number && (wcslen(serial_number) > 0)) + { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) + { + path_to_open = cur_dev->path; + break; + } + } + else + { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +static void hid_device_removal_callback(void *context, IOReturn result, void *sender, IOHIDDeviceRef dev_ref) +{ + /* Stop the Run Loop for this device. */ + pthread_mutex_lock(&device_list_mutex); + hid_device *d = device_list; + while (d) + { + if (d->device_handle == dev_ref) + { + d->disconnected = 1; + CFRunLoopStop(d->run_loop); + } + + d = d->next; + } + pthread_mutex_unlock(&device_list_mutex); +} + +/* The Run Loop calls this function for each input report received. + This function puts the data into a linked list to be picked up by + hid_read(). */ +static void hid_report_callback(void *context, + IOReturn result, + void *sender, + IOHIDReportType report_type, + uint32_t report_id, + uint8_t *report, + CFIndex report_length) +{ + struct input_report *rpt; + hid_device *dev = context; + + /* Make a new Input Report object */ + rpt = calloc(1, sizeof(struct input_report)); + rpt->data = calloc(1, report_length); + memcpy(rpt->data, report, report_length); + rpt->len = report_length; + rpt->next = NULL; + + /* Lock this section */ + pthread_mutex_lock(&dev->mutex); + + /* Attach the new report object to the end of the list. */ + if (dev->input_reports == NULL) + { + /* The list is empty. Put it at the root. */ + dev->input_reports = rpt; + } + else + { + /* Find the end of the list and attach. */ + struct input_report *cur = dev->input_reports; + int num_queued = 0; + while (cur->next != NULL) + { + cur = cur->next; + num_queued++; + } + cur->next = rpt; + + /* Pop one off if we've reached 30 in the queue. This + way we don't grow forever if the user never reads + anything from the device. */ + if (num_queued > 30) + { + return_data(dev, NULL, 0); + } + } + + /* Signal a waiting thread that there is data. */ + pthread_cond_signal(&dev->condition); + + /* Unlock */ + pthread_mutex_unlock(&dev->mutex); +} + +/* This gets called when the read_thred's run loop gets signaled by + hid_close(), and serves to stop the read_thread's run loop. */ +static void perform_signal_callback(void *context) +{ + hid_device *dev = context; + CFRunLoopStop(dev->run_loop); // TODO: CFRunLoopGetCurrent() +} + +static void *read_thread(void *param) +{ + hid_device *dev = param; + + /* Move the device's run loop to this thread. */ + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); + + /* Create the RunLoopSource which is used to signal the + event loop to stop when hid_close() is called. */ + CFRunLoopSourceContext ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.version = 0; + ctx.info = dev; + ctx.perform = &perform_signal_callback; + dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 /*order*/, &ctx); + CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode); + + /* Store off the Run Loop so it can be stopped from hid_close() + and on device disconnection. */ + dev->run_loop = CFRunLoopGetCurrent(); + + /* Notify the main thread that the read thread is up and running. */ + pthread_barrier_wait(&dev->barrier); + + /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input + reports into the hid_report_callback(). */ + SInt32 code; + while (!dev->shutdown_thread && !dev->disconnected) + { + code = CFRunLoopRunInMode(dev->run_loop_mode, 1000 /*sec*/, FALSE); + /* Return if the device has been disconnected */ + if (code == kCFRunLoopRunFinished) + { + dev->disconnected = 1; + break; + } + + /* Break if The Run Loop returns Finished or Stopped. */ + if (code != kCFRunLoopRunTimedOut && code != kCFRunLoopRunHandledSource) + { + /* There was some kind of error. Setting + shutdown seems to make sense, but + there may be something else more appropriate */ + dev->shutdown_thread = 1; + break; + } + } + + /* Now that the read thread is stopping, Wake any threads which are + waiting on data (in hid_read_timeout()). Do this under a mutex to + make sure that a thread which is about to go to sleep waiting on + the condition acutally will go to sleep before the condition is + signaled. */ + pthread_mutex_lock(&dev->mutex); + pthread_cond_broadcast(&dev->condition); + pthread_mutex_unlock(&dev->mutex); + + /* Close the OS handle to the device, but only if it's not + been unplugged. If it's been unplugged, then calling + IOHIDDeviceClose() will crash. */ + if (!dev->disconnected) + { + IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone); + } + + /* Wait here until hid_close() is called and makes it past + the call to CFRunLoopWakeUp(). This thread still needs to + be valid when that function is called on the other thread. */ + pthread_barrier_wait(&dev->shutdown_barrier); + + return NULL; +} + +hid_device *HID_API_EXPORT hid_open_path(const char *path) +{ + int i; + hid_device *dev = NULL; + CFIndex num_devices; + + dev = new_hid_device(); + + /* Set up the HID Manager if it hasn't been done */ + if (hid_init() < 0) + { + return NULL; + } + + CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); + + num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **)device_array); + for (i = 0; i < num_devices; i++) + { + char cbuf[BUF_LEN]; + size_t len; + IOHIDDeviceRef os_dev = device_array[i]; + + len = make_path(os_dev, cbuf, sizeof(cbuf)); + if (!strcmp(cbuf, path)) + { + // Matched Paths. Open this Device. + IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeNone); + if (ret == kIOReturnSuccess) + { + char str[32]; + + free(device_array); + CFRelease(device_set); + dev->device_handle = os_dev; + + /* Create the buffers for receiving data */ + dev->max_input_report_len = (CFIndex)get_max_report_length(os_dev); + dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); + + /* Create the Run Loop Mode for this device. + printing the reference seems to work. */ + sprintf(str, "HIDAPI_%p", os_dev); + dev->run_loop_mode = CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); + + /* Attach the device to a Run Loop */ + IOHIDDeviceRegisterInputReportCallback(os_dev, dev->input_report_buf, dev->max_input_report_len, + &hid_report_callback, dev); + IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, hid_device_removal_callback, NULL); + + /* Start the read thread */ + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + return dev; + } + else + { + goto return_error; + } + } + } + +return_error: + free(device_array); + CFRelease(device_set); + free_hid_device(dev); + return NULL; +} + +static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length) +{ + const unsigned char *data_to_send; + size_t length_to_send; + IOReturn res; + + /* Return if the device has been disconnected. */ + if (dev->disconnected) + return -1; + + if (data[0] == 0x0) + { + /* Not using numbered Reports. + Don't send the report number. */ + data_to_send = data + 1; + length_to_send = length - 1; + } + else + { + /* Using numbered Reports. + Send the Report Number */ + data_to_send = data; + length_to_send = length; + } + + if (!dev->disconnected) + { + res = IOHIDDeviceSetReport(dev->device_handle, type, data[0], /* Report ID*/ + data_to_send, length_to_send); + + if (res == kIOReturnSuccess) + { + return length; + } + else + return -1; + } + + return -1; +} + +int HID_API_EXPORT hid_write_timeout(hid_device *dev, const unsigned char *data, size_t length, int milliseconds) +{ + return set_report(dev, kIOHIDReportTypeOutput, data, length); +} + +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + return hid_write_timeout(dev, data, length, (dev->blocking) ? -1 : 0); +} + +/* Helper function, so that this isn't duplicated in hid_read(). */ +static int return_data(hid_device *dev, unsigned char *data, size_t length) +{ + /* Copy the data out of the linked list item (rpt) into the + return buffer (data), and delete the liked list item. */ + struct input_report *rpt = dev->input_reports; + size_t len = (length < rpt->len) ? length : rpt->len; + memcpy(data, rpt->data, len); + dev->input_reports = rpt->next; + free(rpt->data); + free(rpt); + return len; +} + +static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + while (!dev->input_reports) + { + int res = pthread_cond_wait(cond, mutex); + if (res != 0) + return res; + + /* A res of 0 means we may have been signaled or it may + be a spurious wakeup. Check to see that there's acutally + data in the queue before returning, and if not, go back + to sleep. See the pthread_cond_timedwait() man page for + details. */ + + if (dev->shutdown_thread || dev->disconnected) + return -1; + } + + return 0; +} + +static int cond_timedwait(const hid_device *dev, + pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + while (!dev->input_reports) + { + int res = pthread_cond_timedwait(cond, mutex, abstime); + if (res != 0) + return res; + + /* A res of 0 means we may have been signaled or it may + be a spurious wakeup. Check to see that there's acutally + data in the queue before returning, and if not, go back + to sleep. See the pthread_cond_timedwait() man page for + details. */ + + if (dev->shutdown_thread || dev->disconnected) + return -1; + } + + return 0; +} + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read = -1; + + /* Lock the access to the report list. */ + pthread_mutex_lock(&dev->mutex); + + /* There's an input report queued up. Return it. */ + if (dev->input_reports) + { + /* Return the first one */ + bytes_read = return_data(dev, data, length); + goto ret; + } + + /* Return if the device has been disconnected. */ + if (dev->disconnected) + { + bytes_read = -1; + goto ret; + } + + if (dev->shutdown_thread) + { + /* This means the device has been closed (or there + has been an error. An error code of -1 should + be returned. */ + bytes_read = -1; + goto ret; + } + + /* There is no data. Go to sleep and wait for data. */ + + if (milliseconds == -1) + { + /* Blocking */ + int res; + res = cond_wait(dev, &dev->condition, &dev->mutex); + if (res == 0) + bytes_read = return_data(dev, data, length); + else + { + /* There was an error, or a device disconnection. */ + bytes_read = -1; + } + } + else if (milliseconds > 0) + { + /* Non-blocking, but called with timeout. */ + int res; + struct timespec ts; + struct timeval tv; + gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000L) + { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts); + if (res == 0) + bytes_read = return_data(dev, data, length); + else if (res == ETIMEDOUT) + bytes_read = 0; + else + bytes_read = -1; + } + else + { + /* Purely non-blocking */ + bytes_read = 0; + } + +ret: + /* Unlock */ + pthread_mutex_unlock(&dev->mutex); + return bytes_read; +} + +int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking) ? -1 : 0); +} + +int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) +{ + /* All Nonblocking operation is handled by the library. */ + dev->blocking = !nonblock; + + return 0; +} + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + return set_report(dev, kIOHIDReportTypeFeature, data, length); +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + CFIndex len = length; + IOReturn res; + + /* Return if the device has been unplugged. */ + if (dev->disconnected) + return -1; + + res = IOHIDDeviceGetReport(dev->device_handle, kIOHIDReportTypeFeature, data[0], /* Report ID */ + data, &len); + if (res == kIOReturnSuccess) + return len; + else + return -1; +} + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + + /* Disconnect the report callback before close. */ + if (!dev->disconnected) + { + IOHIDDeviceRegisterInputReportCallback(dev->device_handle, dev->input_report_buf, dev->max_input_report_len, + NULL, dev); + IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, NULL, dev); + IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); + } + + /* Cause read_thread() to stop. */ + dev->shutdown_thread = 1; + + /* Wake up the run thread's event loop so that the thread can exit. */ + CFRunLoopSourceSignal(dev->source); + CFRunLoopWakeUp(dev->run_loop); + + /* Notify the read thread that it can shut down now. */ + pthread_barrier_wait(&dev->shutdown_barrier); + + /* Wait for read_thread() to end. */ + pthread_join(dev->thread, NULL); + + /* Close the OS handle to the device, but only if it's not + been unplugged. If it's been unplugged, then calling + IOHIDDeviceClose() will crash. */ + if (!dev->disconnected) + { + IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone); + } + + /* Clear out the queue of received reports. */ + pthread_mutex_lock(&dev->mutex); + while (dev->input_reports) + { + return_data(dev, NULL, 0); + } + pthread_mutex_unlock(&dev->mutex); + + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_manufacturer_string(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_product_string(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_serial_number(dev->device_handle, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + // TODO: + + return 0; +} + +HID_API_EXPORT const wchar_t *HID_API_CALL hid_error(hid_device *dev) +{ + // TODO: + + return NULL; +} + +#if 0 +static int32_t get_location_id(IOHIDDeviceRef device) +{ + return get_int_property(device, CFSTR(kIOHIDLocationIDKey)); +} + +static int32_t get_usage(IOHIDDeviceRef device) +{ + int32_t res; + res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey)); + if (!res) + res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); + return res; +} + +static int32_t get_usage_page(IOHIDDeviceRef device) +{ + int32_t res; + res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey)); + if (!res) + res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); + return res; +} + +static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len) +{ + return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len); +} + + +int main(void) +{ + IOHIDManagerRef mgr; + int i; + + mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + IOHIDManagerSetDeviceMatching(mgr, NULL); + IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone); + + CFSetRef device_set = IOHIDManagerCopyDevices(mgr); + + CFIndex num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + + setlocale(LC_ALL, ""); + + for (i = 0; i < num_devices; i++) { + IOHIDDeviceRef dev = device_array[i]; + printf("Device: %p\n", dev); + printf(" %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev)); + + wchar_t serial[256], buf[256]; + char cbuf[256]; + get_serial_number(dev, serial, 256); + + + printf(" Serial: %ls\n", serial); + printf(" Loc: %ld\n", get_location_id(dev)); + get_transport(dev, buf, 256); + printf(" Trans: %ls\n", buf); + make_path(dev, cbuf, 256); + printf(" Path: %s\n", cbuf); + + } + + return 0; +} +#endif diff --git a/src/blfwk/src/hid-windows.c b/src/blfwk/src/hid-windows.c new file mode 100644 index 0000000..39af8d2 --- /dev/null +++ b/src/blfwk/src/hid-windows.c @@ -0,0 +1,982 @@ +/******************************************************* +HIDAPI - Multi-Platform library for +communication with HID devices. + +Alan Ott +Signal 11 Software + +8/22/2009 + +Copyright 2009, All Rights Reserved. + +At the discretion of the user of this library, +this software may be licensed under the terms of the +GNU Public License v3, a BSD-Style license, or the +original HIDAPI license as outlined in the LICENSE.txt, +LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt +files located at the root of the source distribution. +These files may also be found in the public source +code repository located at: +http://github.com/signal11/hidapi . +********************************************************/ + +#include + +#ifndef _NTDEF_ +#pragma warning(suppress : 28251) +typedef LONG NTSTATUS; +#endif + +#ifdef __MINGW32__ +#include +#include +#endif + +#ifdef __CYGWIN__ +#include +#define _wcsdup wcsdup +#endif + +/*#define HIDAPI_USE_DDK*/ + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef HIDAPI_USE_DDK +#include +#endif + +/* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */ +#define HID_OUT_CTL_CODE(id) CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) +#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include +#include +#include "blfwk/hidapi.h" + +#ifdef _MSC_VER +/* Thanks Microsoft, but I know how to use strncpy(). */ +#pragma warning(disable : 4996) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define WSTR_LEN 512 + +#ifndef HIDAPI_USE_DDK +/* Since we're not building with the DDK, and the HID header +files aren't part of the SDK, we have to define all this +stuff here. In lookup_functions(), the function pointers +defined below are set. */ +#define HIDP_STATUS_SUCCESS 0x110000 + +typedef struct _HIDD_ATTRIBUTES +{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + +typedef USHORT USAGE; +typedef struct _HIDP_CAPS +{ + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT fields_not_used_by_hidapi[10]; +} HIDP_CAPS, *PHIDP_CAPS; +typedef void *PHIDP_PREPARSED_DATA; + +typedef BOOLEAN(__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); +typedef BOOLEAN(__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); +typedef BOOLEAN(__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); +typedef BOOLEAN(__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); +typedef BOOLEAN(__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); +typedef BOOLEAN(__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); +typedef BOOLEAN(__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); +typedef BOOLEAN(__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); +typedef BOOLEAN(__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); +typedef NTSTATUS(__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); + +static HidD_GetAttributes_ HidD_GetAttributes; +static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; +static HidD_GetManufacturerString_ HidD_GetManufacturerString; +static HidD_GetProductString_ HidD_GetProductString; +static HidD_SetFeature_ HidD_SetFeature; +static HidD_GetFeature_ HidD_GetFeature; +static HidD_GetIndexedString_ HidD_GetIndexedString; +static HidD_GetPreparsedData_ HidD_GetPreparsedData; +static HidD_FreePreparsedData_ HidD_FreePreparsedData; +static HidP_GetCaps_ HidP_GetCaps; + +static HMODULE lib_handle = NULL; +static BOOLEAN initialized = FALSE; +#endif /* HIDAPI_USE_DDK */ + +struct hid_device_ +{ + HANDLE device_handle; + BOOL blocking; + USHORT output_report_length; + size_t input_report_length; + void *last_error_str; + DWORD last_error_num; + BOOL read_pending; + char *read_buf; + OVERLAPPED read_ol; + OVERLAPPED write_ol; +}; + +static hid_device *new_hid_device() +{ + hid_device *dev = (hid_device *)calloc(1, sizeof(hid_device)); + dev->device_handle = INVALID_HANDLE_VALUE; + dev->blocking = TRUE; + dev->output_report_length = 0; + dev->input_report_length = 0; + dev->last_error_str = NULL; + dev->last_error_num = 0; + dev->read_pending = FALSE; + dev->read_buf = NULL; + memset(&dev->read_ol, 0, sizeof(dev->read_ol)); + dev->read_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL); + memset(&dev->write_ol, 0, sizeof(dev->write_ol)); + dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + CloseHandle(dev->read_ol.hEvent); + CloseHandle(dev->write_ol.hEvent); + CloseHandle(dev->device_handle); + LocalFree(dev->last_error_str); + free(dev->read_buf); + free(dev); +} + +static void register_error(hid_device *device, const wchar_t *op) +{ + // WCHAR *ptr; + WCHAR *sysMsg, *msg; + DWORD err = GetLastError(); + + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&sysMsg, 0 /*sz*/, NULL); + + /* Get rid of the CR and LF that FormatMessage() sticks at the + end of the message. Thanks Microsoft! */ + /* ptr = sysMsg; + while (*ptr) + { + if (*ptr == '\r') + { + *ptr = 0x0000; + break; + } + ptr++; + } + */ + msg = (WCHAR *)LocalAlloc(LMEM_ZEROINIT, (wcslen((LPCWSTR)sysMsg) + wcslen((LPCWSTR)op) + 40) * sizeof(WCHAR)); + swprintf((wchar_t *)msg, (size_t)LocalSize(msg), L"%s failed with error %d: %s", op, err, sysMsg); + + /* Store the message off in the Device entry so that + the hid_error() function can pick it up. */ + LocalFree(device->last_error_str); + LocalFree(sysMsg); + device->last_error_str = msg; +} + +#ifndef HIDAPI_USE_DDK +static int lookup_functions() +{ + lib_handle = LoadLibraryA("hid.dll"); + if (lib_handle) + { +#define RESOLVE(x) \ + x = (x##_)GetProcAddress(lib_handle, #x); \ + if (!x) \ + return -1; + RESOLVE(HidD_GetAttributes); + RESOLVE(HidD_GetSerialNumberString); + RESOLVE(HidD_GetManufacturerString); + RESOLVE(HidD_GetProductString); + RESOLVE(HidD_SetFeature); + RESOLVE(HidD_GetFeature); + RESOLVE(HidD_GetIndexedString); + RESOLVE(HidD_GetPreparsedData); + RESOLVE(HidD_FreePreparsedData); + RESOLVE(HidP_GetCaps); +#undef RESOLVE + } + else + return -1; + + return 0; +} +#endif /* HIDAPI_USE_DDK */ + +static HANDLE open_device(const char *path, BOOL enumerate) +{ + HANDLE handle; + DWORD desired_access = (enumerate) ? 0 : (GENERIC_WRITE | GENERIC_READ); + DWORD share_mode = (enumerate) ? FILE_SHARE_READ | FILE_SHARE_WRITE : FILE_SHARE_READ; + + handle = CreateFileA(path, desired_access, share_mode, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, /*FILE_ATTRIBUTE_NORMAL,*/ + 0); + + return handle; +} + +int HID_API_EXPORT hid_init(void) +{ +#ifndef HIDAPI_USE_DDK + if (!initialized) + { + if (lookup_functions() < 0) + { + hid_exit(); + return -1; + } + initialized = TRUE; + } +#endif + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ +#ifndef HIDAPI_USE_DDK + if (lib_handle) + { + FreeLibrary(lib_handle); + } + lib_handle = NULL; + initialized = FALSE; +#endif + return 0; +} + +struct hid_device_info HID_API_EXPORT *HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + BOOL res; + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + + /* Windows objects for interacting with the driver. */ + GUID InterfaceClassGuid = { 0x4d1e55b2, 0xf16f, 0x11cf, { 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }; + SP_DEVINFO_DATA devinfo_data; + SP_DEVICE_INTERFACE_DATA device_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; + HDEVINFO device_info_set = INVALID_HANDLE_VALUE; + int device_index = 0; + int i; + + if (hid_init() < 0) + return NULL; + + /* Initialize the Windows objects. */ + memset(&devinfo_data, 0x0, sizeof(devinfo_data)); + devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); + device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + /* Get information for all the devices belonging to the HID class. */ + device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + /* Iterate over each device in the HID class, looking for the right one. */ + for (;;) + { + HANDLE write_handle = INVALID_HANDLE_VALUE; + DWORD required_size = 0; + HIDD_ATTRIBUTES attrib; + + res = SetupDiEnumDeviceInterfaces(device_info_set, NULL, &InterfaceClassGuid, device_index, + &device_interface_data); + + if (!res) + { + /* A return of FALSE from this function means that + there are no more devices. */ + break; + } + + /* Call with 0-sized detail size, and let the function + tell us how long the detail struct needs to be. The + size is put in &required_size. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, &device_interface_data, NULL, 0, &required_size, NULL); + + /* Allocate a long enough structure for device_interface_detail_data. */ + device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A *)malloc(required_size); + device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + + /* Get the detailed data for this device. The detail data gives us + the device path for this device, which is then passed into + CreateFile() to get a handle to the device. */ + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, &device_interface_data, device_interface_detail_data, + required_size, NULL, NULL); + + if (!res) + { + /* register_error(dev, L"Unable to call SetupDiGetDeviceInterfaceDetail"); + Continue to the next device. */ + goto cont; + } + + /* Make sure this device is of Setup Class "HIDClass" and has a + driver bound to it. */ + for (i = 0;; i++) + { + char driver_name[256]; + + /* Populate devinfo_data. This function will return failure + when there are no more interfaces left. */ + res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); + if (!res) + { + goto cont; + } + + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, SPDRP_CLASS, NULL, + (PBYTE)driver_name, sizeof(driver_name), NULL); + if (!res) + { + goto cont; + } + + if (strcmp(driver_name, "HIDClass") == 0) + { + /* See if there's a driver bound. */ + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, SPDRP_DRIVER, NULL, + (PBYTE)driver_name, sizeof(driver_name), NULL); + if (res) + { + break; + } + } + } + + // wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); + + /* Open a handle to the device */ + write_handle = open_device(device_interface_detail_data->DevicePath, TRUE); + + /* Check validity of write_handle. */ + if (write_handle == INVALID_HANDLE_VALUE) + { + /* Unable to open the device. */ + // register_error(dev, L"CreateFile"); + goto cont_close; + } + + /* Get the Vendor ID and Product ID for this device. */ + attrib.Size = sizeof(HIDD_ATTRIBUTES); + HidD_GetAttributes(write_handle, &attrib); + // wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); + + /* Check the VID/PID to see if we should add this + device to the enumeration list. */ + if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && (product_id == 0x0 || attrib.ProductID == product_id)) + { + const char *str; + struct hid_device_info *tmp; + PHIDP_PREPARSED_DATA pp_data = NULL; + HIDP_CAPS caps; + BOOLEAN res; + NTSTATUS nt_res; + wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ + size_t len; + + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) + { + cur_dev->next = tmp; + } + else + { + root = tmp; + } + cur_dev = tmp; + + /* Get the Usage Page and Usage for this device. */ + res = HidD_GetPreparsedData(write_handle, &pp_data); + if (res) + { + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res == HIDP_STATUS_SUCCESS) + { + cur_dev->usage_page = caps.UsagePage; + cur_dev->usage = caps.Usage; + } + + HidD_FreePreparsedData(pp_data); + } + + /* Fill out the record */ + cur_dev->next = NULL; + str = device_interface_detail_data->DevicePath; + if (str) + { + len = strlen(str); + cur_dev->path = (char *)calloc(len + 1, sizeof(char)); + strncpy(cur_dev->path, str, len + 1); + cur_dev->path[len] = '\0'; + } + else + cur_dev->path = NULL; + + /* Serial Number */ + res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN - 1] = 0x0000; + if (res) + { + cur_dev->serial_number = _wcsdup(wstr); + } + + /* Manufacturer String */ + res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN - 1] = 0x0000; + if (res) + { + cur_dev->manufacturer_string = _wcsdup(wstr); + } + + /* Product String */ + res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN - 1] = 0x0000; + if (res) + { + cur_dev->product_string = _wcsdup(wstr); + } + + /* VID/PID */ + cur_dev->vendor_id = attrib.VendorID; + cur_dev->product_id = attrib.ProductID; + + /* Release Number */ + cur_dev->release_number = attrib.VersionNumber; + + /* Interface Number. It can sometimes be parsed out of the path + on Windows if a device has multiple interfaces. See + http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or + search for "Hardware IDs for HID Devices" at MSDN. If it's not + in the path, it's set to -1. */ + cur_dev->interface_number = -1; + if (cur_dev->path) + { + char *interface_component = strstr(cur_dev->path, "&mi_"); + if (interface_component) + { + char *hex_str = interface_component + 4; + char *endptr = NULL; + cur_dev->interface_number = strtol(hex_str, &endptr, 16); + if (endptr == hex_str) + { + /* The parsing failed. Set interface_number to -1. */ + cur_dev->interface_number = -1; + } + } + } + } + + cont_close: + CloseHandle(write_handle); + cont: + /* We no longer need the detail data. It can be freed */ + free(device_interface_detail_data); + + device_index++; + } + + /* Close the device information handle. */ + SetupDiDestroyDeviceInfoList(device_info_set); + + return root; +} + +void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) +{ + /* TODO: Merge this with the Linux version. This function is platform-independent. */ + struct hid_device_info *d = devs; + while (d) + { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +HID_API_EXPORT hid_device *HID_API_CALL hid_open(unsigned short vendor_id, + unsigned short product_id, + const wchar_t *serial_number) +{ + /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) + { + if (cur_dev->vendor_id == vendor_id && cur_dev->product_id == product_id) + { + if (serial_number && (wcslen(serial_number) > 0)) + { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) + { + path_to_open = cur_dev->path; + break; + } + } + else + { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +HID_API_EXPORT hid_device *HID_API_CALL hid_open_path(const char *path) +{ + hid_device *dev; + HIDP_CAPS caps; + PHIDP_PREPARSED_DATA pp_data = NULL; + BOOLEAN res; + NTSTATUS nt_res; + + if (hid_init() < 0) + { + return NULL; + } + + dev = new_hid_device(); + + /* Open a handle to the device */ + dev->device_handle = open_device(path, FALSE); + + /* Check validity of write_handle. */ + if (dev->device_handle == INVALID_HANDLE_VALUE) + { + /* Unable to open the device. */ + register_error(dev, L"CreateFile"); + goto err; + } + + /* Get the Input Report length for the device. */ + res = HidD_GetPreparsedData(dev->device_handle, &pp_data); + if (!res) + { + register_error(dev, L"HidD_GetPreparsedData"); + goto err; + } + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res != HIDP_STATUS_SUCCESS) + { + register_error(dev, L"HidP_GetCaps"); + goto err_pp_data; + } + dev->output_report_length = caps.OutputReportByteLength; + dev->input_report_length = caps.InputReportByteLength; + HidD_FreePreparsedData(pp_data); + + dev->read_buf = (char *)malloc(dev->input_report_length); + + return dev; + +err_pp_data: + HidD_FreePreparsedData(pp_data); +err: + free_hid_device(dev); + return NULL; +} + +int HID_API_EXPORT HID_API_CALL hid_write_timeout(hid_device *dev, + const unsigned char *data, + size_t length, + int milliseconds) +{ + DWORD bytes_written; + BOOL res; + + unsigned char *buf; + dev->write_ol.Offset = 0; + dev->write_ol.OffsetHigh = 0; + + /* Make sure the right number of bytes are passed to WriteFile. Windows + expects the number of bytes which are in the _longest_ report (plus + one for the report number) bytes even if the data is a report + which is shorter than that. Windows gives us this value in + caps.OutputReportByteLength. If a user passes in fewer bytes than this, + create a temporary buffer which is the proper size. */ + if (length >= dev->output_report_length) + { + /* The user passed the right number of bytes. Use the buffer as-is. */ + buf = (unsigned char *)data; + } + else + { + /* Create a temporary buffer and copy the user's data + into it, padding the rest with zeros. */ + buf = (unsigned char *)malloc(dev->output_report_length); + memcpy(buf, data, length); + memset(buf + length, 0, dev->output_report_length - length); + length = dev->output_report_length; + } + + res = WriteFile(dev->device_handle, buf, length, NULL, &dev->write_ol); + + if (!res) + { + if (GetLastError() != ERROR_IO_PENDING) + { + /* WriteFile() failed. Return error. */ + CancelIo(dev->device_handle); + register_error(dev, L"WriteFile"); + bytes_written = -1; + goto end_of_function; + } + } + + /* Wait for the write to complete. */ + res = WaitForSingleObject(dev->write_ol.hEvent, milliseconds); + if (res != WAIT_OBJECT_0) + { + /* The WaitForSingleObject operation failed. */ + CancelIo(dev->device_handle); + register_error(dev, L"WaitForSingleObject"); + bytes_written = -1; + goto end_of_function; + } + + /* WaitForSingleObject() told us that WriteFile has completed.*/ + res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, TRUE /*wait*/); + if (!res) + { + /* The GetOverlappedResult operation failed. */ + register_error(dev, L"GetOverlappedResult"); + bytes_written = -1; + goto end_of_function; + } + +end_of_function: + if (buf != data) + free(buf); + + return bytes_written; +} + +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + return hid_write_timeout(dev, data, length, (dev->blocking) ? -1 : 0); +} + +int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + DWORD bytes_read = 0; + BOOL res; + + if (!dev->read_pending) + { + /* Start an Overlapped I/O read. */ + dev->read_pending = TRUE; + memset(dev->read_buf, 0, dev->input_report_length); + res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->read_ol); + + if (!res) + { + if (GetLastError() != ERROR_IO_PENDING) + { + /* ReadFile() has failed. + Clean up and return error. */ + CancelIo(dev->device_handle); + dev->read_pending = FALSE; + goto end_of_function; + } + } + } + + if (milliseconds >= 0) + { + /* See if there is any data yet. */ + res = WaitForSingleObject(dev->read_ol.hEvent, milliseconds); + if (res != WAIT_OBJECT_0) + { + /* There was no data this time. Return zero bytes available, + but leave the Overlapped I/O running. */ + return 0; + } + } + + /* Either WaitForSingleObject() told us that ReadFile has completed, or + we are in non-blocking mode. Get the number of bytes read. The actual + data has been copied to the data[] array which was passed to ReadFile(). */ + res = GetOverlappedResult(dev->device_handle, &dev->read_ol, &bytes_read, TRUE /*wait*/); + + /* Set pending back to false, even if GetOverlappedResult() returned error. */ + dev->read_pending = FALSE; + + if (res && bytes_read > 0) + { + if (dev->read_buf[0] == 0x0) + { + /* If report numbers aren't being used, but Windows sticks a report + number (0x0) on the beginning of the report anyway. To make this + work like the other platforms, and to make it work more like the + HID spec, we'll skip over this byte. */ + size_t copy_len; + bytes_read--; + copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf + 1, copy_len); + } + else + { + /* Copy the whole buffer, report number and all. */ + size_t copy_len = length > bytes_read ? bytes_read : length; + memcpy(data, dev->read_buf, copy_len); + } + } + +end_of_function: + if (!res) + { + register_error(dev, L"GetOverlappedResult"); + return -1; + } + + return bytes_read; +} + +int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking) ? -1 : 0); +} + +int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + return 0; /* Success */ +} + +int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); + if (!res) + { + register_error(dev, L"HidD_SetFeature"); + return -1; + } + + return length; +} + +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + BOOL res; +#if 0 + res = HidD_GetFeature(dev->device_handle, data, length); + if (!res) { + register_error(dev, L"HidD_GetFeature"); + return -1; + } + return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ +#else + DWORD bytes_returned; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = DeviceIoControl(dev->device_handle, IOCTL_HID_GET_FEATURE, data, length, data, length, &bytes_returned, &ol); + + if (!res) + { + if (GetLastError() != ERROR_IO_PENDING) + { + /* DeviceIoControl() failed. Return error. */ + register_error(dev, L"Send Feature Report DeviceIoControl"); + return -1; + } + } + + /* Wait here until the write is done. This makes + hid_get_feature_report() synchronous. */ + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE /*wait*/); + if (!res) + { + /* The operation failed. */ + register_error(dev, L"Send Feature Report GetOverLappedResult"); + return -1; + } + return bytes_returned; +#endif +} + +void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) +{ + if (!dev) + return; + CancelIo(dev->device_handle); + free_hid_device(dev); +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * maxlen); + if (!res) + { + register_error(dev, L"HidD_GetManufacturerString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * maxlen); + if (!res) + { + register_error(dev, L"HidD_GetProductString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * maxlen); + if (!res) + { + register_error(dev, L"HidD_GetSerialNumberString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, + int string_index, + wchar_t *string, + size_t maxlen) +{ + BOOL res; + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * maxlen); + if (!res) + { + register_error(dev, L"HidD_GetIndexedString"); + return -1; + } + + return 0; +} + +HID_API_EXPORT const wchar_t *HID_API_CALL hid_error(hid_device *dev) +{ + return (wchar_t *)dev->last_error_str; +} + +/*#define PICPGM*/ +/*#define S11*/ +#define P32 +#ifdef S11 +unsigned short VendorID = 0xa0a0; +unsigned short ProductID = 0x0001; +#endif + +#ifdef P32 +unsigned short VendorID = 0x04d8; +unsigned short ProductID = 0x3f; +#endif + +#ifdef PICPGM +unsigned short VendorID = 0x04d8; +unsigned short ProductID = 0x0033; +#endif + +#if 0 + int __cdecl main(int argc, char* argv[]) + { + int res; + unsigned char buf[65]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + /* Set up the command buffer. */ + memset(buf,0x00,sizeof(buf)); + buf[0] = 0; + buf[1] = 0x81; + + + /* Open the device. */ + int handle = open(VendorID, ProductID, L"12345"); + if (handle < 0) + printf("unable to open device\n"); + + + /* Toggle LED (cmd 0x80) */ + buf[1] = 0x80; + res = write(handle, buf, 65); + if (res < 0) + printf("Unable to write()\n"); + + /* Request state (cmd 0x81) */ + buf[1] = 0x81; + write(handle, buf, 65); + if (res < 0) + printf("Unable to write() (2)\n"); + + /* Read requested state */ + read(handle, buf, 65); + if (res < 0) + printf("Unable to read()\n"); + + /* Print out the returned buffer. */ + for (int i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + return 0; + } +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/src/blfwk/src/i2c.c b/src/blfwk/src/i2c.c new file mode 100644 index 0000000..8a803cf --- /dev/null +++ b/src/blfwk/src/i2c.c @@ -0,0 +1,170 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include "blfwk/i2c.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/******************************************************************************* + * Variables + ******************************************************************************/ + +static struct i2c_rdwr_ioctl_data i2c_data = { 0 }; + +/******************************************************************************* + * Codes + ******************************************************************************/ + +// See i2c.h for documentation of this method. +static unsigned long i2c_check_functionality(int fd) +{ + unsigned long funcs = 0; + + if (fd < 0) + { + return 0; + } + + if (ioctl(fd, I2C_FUNCS, &funcs) < 0) + { + return 0; + } + + return funcs & I2C_FUNC_I2C; +} + +// See i2c.h for documentation of this method. +int i2c_setup(int fd, uint32_t speed, uint8_t address) +{ + if (fd < 0) + { + return -1; + } + + i2c_data.nmsgs = 1; + i2c_data.msgs = (struct i2c_msg *)malloc(i2c_data.nmsgs * sizeof(struct i2c_msg)); + if (i2c_data.msgs == NULL) + { + return -1; + } + i2c_data.msgs[0].addr = (uint16_t)address; + + /* + * I2C speed setting is not supported. + */ + + return 0; +} + +// See i2c.h for documentation of this method. +int i2c_set_timeout(int fd, uint32_t milliseconds) +{ + if (fd < 0) + { + return -1; + } + + /* For historical reasons, user-space sets the timeout + * value in units of 10 ms. + */ + return ioctl(fd, I2C_TIMEOUT, (unsigned long)milliseconds / 10); +} + +// See i2c.h for documentation of this method. +int i2c_write(int fd, char *buf, int size) +{ + int ret = -1; + + if ((fd < 0) || (i2c_data.msgs == NULL)) + { + return 0; + } + + i2c_data.msgs[0].flags = 0; // write command + i2c_data.msgs[0].buf = (unsigned char *)buf; + i2c_data.msgs[0].len = size; + ret = ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data); + if (ret < 0) + { + return 0; + } + + return size; +} + +// See i2c.h for documentation of this method. +int i2c_read(int fd, char *buf, int size) +{ + int ret = -1; + + if ((fd < 0) || (i2c_data.msgs == NULL)) + { + return 0; + } + + i2c_data.msgs[0].flags = I2C_M_RD; // read command + i2c_data.msgs[0].buf = (unsigned char *)buf; + i2c_data.msgs[0].len = size; + ret = ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data); + if (ret < 0) + { + return 0; + } + + return size; +} + +// See i2c.h for documentation of this method. +int i2c_open(char *port) +{ + int fd; + + fd = open(port, O_RDWR); + if (fd < 0) + { + fprintf(stderr, "Failed to open I2C port(%s).\n", port); + return fd; + } + + if (!i2c_check_functionality(fd)) + { + fprintf(stderr, "Not an I2C port(%s).\n", port); + return -1; + } + + return fd; +} + +// See i2c.h for documentation of this method. +int i2c_close(int fd) +{ + int ret; + if (i2c_data.msgs != NULL) + { + free(i2c_data.msgs); + i2c_data.msgs = NULL; + } + + ret = close(fd); + if (ret < 0) + { + fprintf(stderr, "Failed to close I2C port.\n"); + } + else + { + fd = -1; + } + + return ret; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/jsoncpp.cpp b/src/blfwk/src/jsoncpp.cpp new file mode 100644 index 0000000..14daa98 --- /dev/null +++ b/src/blfwk/src/jsoncpp.cpp @@ -0,0 +1,3785 @@ +/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). +/// It is intented to be used with #include + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +#include "blfwk/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json +{ +/// Converts a unicode code-point to UTF-8. +static inline std::string codePointToUTF8(unsigned int cp) +{ + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) + { + result.resize(1); + result[0] = static_cast(cp); + } + else if (cp <= 0x7FF) + { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } + else if (cp <= 0xFFFF) + { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); + result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); + } + else if (cp <= 0x10FFFF) + { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +/// Returns true if ch is a control character (in range [0,32[). +static inline bool isControlCharacter(char ch) +{ + return ch > 0 && ch <= 0x1F; +} + +enum +{ + /// Constant that specify the size of the buffer that must be passed to uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + +/** Converts an unsigned integer to string. + * @param value Unsigned interger to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void uintToString(LargestUInt value, char *¤t) +{ + *--current = 0; + do + { + *--current = char(value % 10) + '0'; + value /= 10; + } while (value != 0); +} + +} // namespace Json { + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGATED) +#include +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGATED) +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning(disable : 4996) // disable warning about strdup being deprecated. +#endif + +namespace Json +{ +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_(true) + , strictRoot_(false) +{ +} + +Features Features::all() +{ + return Features(); +} + +Features Features::strictMode() +{ + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +static inline bool in(Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4) +{ + return c == c1 || c == c2 || c == c3 || c == c4; +} + +static inline bool in( + Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5) +{ + return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; +} + +static bool containsNewLine(Reader::Location begin, Reader::Location end) +{ + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : features_(Features::all()) +{ +} + +Reader::Reader(const Features &features) + : features_(features) +{ +} + +bool Reader::parse(const std::string &document, Value &root, bool collectComments) +{ + document_ = document; + const char *begin = document_.c_str(); + const char *end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream &sin, Value &root, bool collectComments) +{ + // std::istream_iterator begin(sin); + // std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char)EOF); + return parse(doc, root, collectComments); +} + +bool Reader::parse(const char *beginDoc, const char *endDoc, Value &root, bool collectComments) +{ + if (!features_.allowComments_) + { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) + { + if (!root.isArray() && !root.isObject()) + { + // Set error location to start of doc, ideally should be first token found in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError("A valid JSON document must be either an array or an object value.", token); + return false; + } + } + return successful; +} + +bool Reader::readValue() +{ + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) + { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) + { + case tokenObjectBegin: + successful = readObject(token); + break; + case tokenArrayBegin: + successful = readArray(token); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + currentValue() = true; + break; + case tokenFalse: + currentValue() = false; + break; + case tokenNull: + currentValue() = Value(); + break; + default: + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) + { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void Reader::skipCommentTokens(Token &token) +{ + if (features_.allowComments_) + { + do + { + readToken(token); + } while (token.type_ == tokenComment); + } + else + { + readToken(token); + } +} + +bool Reader::expectToken(TokenType type, Token &token, const char *message) +{ + readToken(token); + if (token.type_ != type) + return addError(message, token); + return true; +} + +bool Reader::readToken(Token &token) +{ + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) + { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void Reader::skipSpaces() +{ + while (current_ != end_) + { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(Location pattern, int patternLength) +{ + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() +{ + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) + { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) + { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +void Reader::addComment(Location begin, Location end, CommentPlacement placement) +{ + assert(collectComments_); + if (placement == commentAfterOnSameLine) + { + assert(lastValue_ != 0); + lastValue_->setComment(std::string(begin, end), placement); + } + else + { + if (!commentsBefore_.empty()) + commentsBefore_ += "\n"; + commentsBefore_ += std::string(begin, end); + } +} + +bool Reader::readCStyleComment() +{ + while (current_ != end_) + { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() +{ + while (current_ != end_) + { + Char c = getNextChar(); + if (c == '\r' || c == '\n') + break; + } + return true; +} + +void Reader::readNumber() +{ + while (current_ != end_) + { + if (!(*current_ >= '0' && *current_ <= '9') && !in(*current_, '.', 'e', 'E', '+', '-')) + break; + ++current_; + } +} + +bool Reader::readString() +{ + Char c = 0; + while (current_ != end_) + { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token & /*tokenStart*/) +{ + Token tokenName; + std::string name; + currentValue() = Value(objectValue); + while (readToken(tokenName)) + { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + if (tokenName.type_ != tokenString) + break; + + name = ""; + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) + { + return addErrorAndRecover("Missing ':' after object member name", colon, tokenObjectEnd); + } + Value &value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) + { + return addErrorAndRecover("Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover("Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool Reader::readArray(Token & /*tokenStart*/) +{ + currentValue() = Value(arrayValue); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) + { + Value &value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) + { + ok = readToken(token); + } + bool badTokenType = (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) + { + return addErrorAndRecover("Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token &token) +{ + bool isDouble = false; + for (Location inspect = token.start_; inspect != token.end_; ++inspect) + { + isDouble = isDouble || in(*inspect, '.', 'e', 'E', '+') || (*inspect == '-' && inspect != token.start_); + } + if (isDouble) + return decodeDouble(token); + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::UInt lastDigitThreshold = Value::UInt(maxIntegerValue % 10); + assert(lastDigitThreshold >= 0 && lastDigitThreshold <= 9); + Value::LargestUInt value = 0; + while (current < token.end_) + { + Char c = *current++; + if (c < '0' || c > '9') + return addError("'" + std::string(token.start_, token.end_) + "' is not a number.", token); + Value::UInt digit(c - '0'); + if (value >= threshold) + { + // If the current digit is not the last one, or if it is + // greater than the last digit of the maximum integer value, + // the parse the number as a double. + if (current != token.end_ || digit > lastDigitThreshold) + { + return decodeDouble(token); + } + } + value = value * 10 + digit; + } + if (isNegative) + currentValue() = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + currentValue() = Value::LargestInt(value); + else + currentValue() = value; + return true; +} + +bool Reader::decodeDouble(Token &token) +{ + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + if (length <= bufferSize) + { + Char buffer[bufferSize + 1]; + memcpy(buffer, token.start_, length); + buffer[length] = 0; + count = sscanf(buffer, "%lf", &value); + } + else + { + std::string buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), "%lf", &value); + } + + if (count != 1) + return addError("'" + std::string(token.start_, token.end_) + "' is not a number.", token); + currentValue() = value; + return true; +} + +bool Reader::decodeString(Token &token) +{ + std::string decoded; + if (!decodeString(token, decoded)) + return false; + currentValue() = decoded; + return true; +} + +bool Reader::decodeString(Token &token, std::string &decoded) +{ + decoded.reserve(token.end_ - token.start_ - 2); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) + { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') + { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) + { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': + { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } + break; + default: + return addError("Bad escape sequence in string", token, current); + } + } + else + { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token &token, Location ¤t, Location end, unsigned int &unicode) +{ + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) + { + // surrogate pairs + if (end - current < 6) + return addError("additional six characters expected to parse unicode surrogate pair.", token, current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') + { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) + { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } + else + return false; + } + else + return addError("expecting another \\u token to begin the second half of a unicode surrogate pair", token, + current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token &token, Location ¤t, Location end, unsigned int &unicode) +{ + if (end - current < 4) + return addError("Bad unicode escape sequence in string: four digits expected.", token, current); + unicode = 0; + for (int index = 0; index < 4; ++index) + { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError("Bad unicode escape sequence in string: hexadecimal digit expected.", token, current); + } + return true; +} + +bool Reader::addError(const std::string &message, Token &token, Location extra) +{ + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) +{ + int errorCount = int(errors_.size()); + Token skip; + for (;;) + { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const std::string &message, Token &token, TokenType skipUntilToken) +{ + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value &Reader::currentValue() +{ + return *(nodes_.top()); +} + +Reader::Char Reader::getNextChar() +{ + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, int &line, int &column) const +{ + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) + { + Char c = *current++; + if (c == '\r') + { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } + else if (c == '\n') + { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +std::string Reader::getLocationLineAndColumn(Location location) const +{ + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + sprintf(buffer, "Line %d, Column %d", line, column); + return buffer; +} + +// Deprecated. Preserved for backward compatibility +std::string Reader::getFormatedErrorMessages() const +{ + return getFormattedErrorMessages(); +} + +std::string Reader::getFormattedErrorMessages() const +{ + std::string formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) + { + const ErrorInfo &error = *itError; + formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::istream &operator>>(std::istream &sin, Value &root) +{ + Json::Reader reader; + bool ok = reader.parse(sin, root, true); + // JSON_ASSERT( ok ); + if (!ok) + throw std::runtime_error(reader.getFormattedErrorMessages()); + return sin; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_batchallocator.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED +#define JSONCPP_BATCHALLOCATOR_H_INCLUDED + +#include +#include + +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +namespace Json +{ +/* Fast memory allocator. + * + * This memory allocator allocates memory for a batch of object (specified by + * the page size, the number of object in each page). + * + * It does not allow the destruction of a single object. All the allocated objects + * can be destroyed at once. The memory can be either released or reused for future + * allocation. + * + * The in-place new operator must be used to construct the object using the pointer + * returned by allocate. + */ +template +class BatchAllocator +{ +public: + typedef AllocatedType Type; + + BatchAllocator(unsigned int objectsPerPage = 255) + : freeHead_(0) + , objectsPerPage_(objectsPerPage) + { + // printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); + assert(sizeof(AllocatedType) * objectPerAllocation >= + sizeof(AllocatedType *)); // We must be able to store a slist in the object free space. + assert(objectsPerPage >= 16); + batches_ = allocateBatch(0); // allocated a dummy page + currentBatch_ = batches_; + } + + ~BatchAllocator() + { + for (BatchInfo *batch = batches_; batch;) + { + BatchInfo *nextBatch = batch->next_; + free(batch); + batch = nextBatch; + } + } + + /// allocate space for an array of objectPerAllocation object. + /// @warning it is the responsability of the caller to call objects constructors. + AllocatedType *allocate() + { + if (freeHead_) // returns node from free list. + { + AllocatedType *object = freeHead_; + freeHead_ = *(AllocatedType **)object; + return object; + } + if (currentBatch_->used_ == currentBatch_->end_) + { + currentBatch_ = currentBatch_->next_; + while (currentBatch_ && currentBatch_->used_ == currentBatch_->end_) + currentBatch_ = currentBatch_->next_; + + if (!currentBatch_) // no free batch found, allocate a new one + { + currentBatch_ = allocateBatch(objectsPerPage_); + currentBatch_->next_ = batches_; // insert at the head of the list + batches_ = currentBatch_; + } + } + AllocatedType *allocated = currentBatch_->used_; + currentBatch_->used_ += objectPerAllocation; + return allocated; + } + + /// Release the object. + /// @warning it is the responsability of the caller to actually destruct the object. + void release(AllocatedType *object) + { + assert(object != 0); + *(AllocatedType **)object = freeHead_; + freeHead_ = object; + } + +private: + struct BatchInfo + { + BatchInfo *next_; + AllocatedType *used_; + AllocatedType *end_; + AllocatedType buffer_[objectPerAllocation]; + }; + + // disabled copy constructor and assignement operator. + BatchAllocator(const BatchAllocator &); + void operator=(const BatchAllocator &); + + static BatchInfo *allocateBatch(unsigned int objectsPerPage) + { + const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType) * objectPerAllocation + + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; + BatchInfo *batch = static_cast(malloc(mallocSize)); + batch->next_ = 0; + batch->used_ = batch->buffer_; + batch->end_ = batch->buffer_ + objectsPerPage; + return batch; + } + + BatchInfo *batches_; + BatchInfo *currentBatch_; + /// Head of a single linked list within the allocated space of freeed object + AllocatedType *freeHead_; + unsigned int objectsPerPage_; +}; + +} // namespace Json + +#endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION + +#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_batchallocator.h +// ////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json +{ +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() +#ifndef JSON_VALUE_USE_INTERNAL_MAP + : current_() + , isNull_(true) +{ +} +#else + : isArray_(true) + , isNull_(true) +{ + iterator_.array_ = ValueInternalArray::IteratorState(); +} +#endif + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIteratorBase::ValueIteratorBase(const Value::ObjectValues::iterator ¤t) + : current_(current) + , isNull_(false) +{ +} +#else +ValueIteratorBase::ValueIteratorBase(const ValueInternalArray::IteratorState &state) + : isArray_(true) +{ + iterator_.array_ = state; +} + +ValueIteratorBase::ValueIteratorBase(const ValueInternalMap::IteratorState &state) + : isArray_(false) +{ + iterator_.map_ = state; +} +#endif + +Value &ValueIteratorBase::deref() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + return current_->second; +#else + if (isArray_) + return ValueInternalArray::dereference(iterator_.array_); + return ValueInternalMap::value(iterator_.map_); +#endif +} + +void ValueIteratorBase::increment() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ++current_; +#else + if (isArray_) + ValueInternalArray::increment(iterator_.array_); + ValueInternalMap::increment(iterator_.map_); +#endif +} + +void ValueIteratorBase::decrement() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + --current_; +#else + if (isArray_) + ValueInternalArray::decrement(iterator_.array_); + ValueInternalMap::decrement(iterator_.map_); +#endif +} + +ValueIteratorBase::difference_type ValueIteratorBase::computeDistance(const SelfType &other) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP +#ifdef JSON_USE_CPPTL_SMALLMAP + return current_ - other.current_; +#else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) + { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::ObjectValues::iterator it = current_; it != other.current_; ++it) + { + ++myDistance; + } + return myDistance; +#endif +#else + if (isArray_) + return ValueInternalArray::distance(iterator_.array_, other.iterator_.array_); + return ValueInternalMap::distance(iterator_.map_, other.iterator_.map_); +#endif +} + +bool ValueIteratorBase::isEqual(const SelfType &other) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + if (isNull_) + { + return other.isNull_; + } + return current_ == other.current_; +#else + if (isArray_) + return ValueInternalArray::equals(iterator_.array_, other.iterator_.array_); + return ValueInternalMap::equals(iterator_.map_, other.iterator_.map_); +#endif +} + +void ValueIteratorBase::copy(const SelfType &other) +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + current_ = other.current_; +#else + if (isArray_) + iterator_.array_ = other.iterator_.array_; + iterator_.map_ = other.iterator_.map_; +#endif +} + +Value ValueIteratorBase::key() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if (czstring.c_str()) + { + if (czstring.isStaticString()) + return Value(StaticString(czstring.c_str())); + return Value(czstring.c_str()); + } + return Value(czstring.index()); +#else + if (isArray_) + return Value(ValueInternalArray::indexOf(iterator_.array_)); + bool isStatic; + const char *memberName = ValueInternalMap::key(iterator_.map_, isStatic); + if (isStatic) + return Value(StaticString(memberName)); + return Value(memberName); +#endif +} + +UInt ValueIteratorBase::index() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if (!czstring.c_str()) + return czstring.index(); + return Value::UInt(-1); +#else + if (isArray_) + return Value::UInt(ValueInternalArray::indexOf(iterator_.array_)); + return Value::UInt(-1); +#endif +} + +const char *ValueIteratorBase::memberName() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const char *name = (*current_).first.c_str(); + return name ? name : ""; +#else + if (!isArray_) + return ValueInternalMap::key(iterator_.map_); + return ""; +#endif +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() +{ +} + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueConstIterator::ValueConstIterator(const Value::ObjectValues::iterator ¤t) + : ValueIteratorBase(current) +{ +} +#else +ValueConstIterator::ValueConstIterator(const ValueInternalArray::IteratorState &state) + : ValueIteratorBase(state) +{ +} + +ValueConstIterator::ValueConstIterator(const ValueInternalMap::IteratorState &state) + : ValueIteratorBase(state) +{ +} +#endif + +ValueConstIterator &ValueConstIterator::operator=(const ValueIteratorBase &other) +{ + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() +{ +} + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIterator::ValueIterator(const Value::ObjectValues::iterator ¤t) + : ValueIteratorBase(current) +{ +} +#else +ValueIterator::ValueIterator(const ValueInternalArray::IteratorState &state) + : ValueIteratorBase(state) +{ +} + +ValueIterator::ValueIterator(const ValueInternalMap::IteratorState &state) + : ValueIteratorBase(state) +{ +} +#endif + +ValueIterator::ValueIterator(const ValueConstIterator &other) + : ValueIteratorBase(other) +{ +} + +ValueIterator::ValueIterator(const ValueIterator &other) + : ValueIteratorBase(other) +{ +} + +ValueIterator &ValueIterator::operator=(const SelfType &other) +{ + copy(other); + return *this; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGATED) +#include +#include +#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +#include "json_batchallocator.h" +#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +#endif // if !defined(JSON_IS_AMALGATED) +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +#include +#endif +#include // size_t + +#define JSON_ASSERT_UNREACHABLE assert(false) +#define JSON_ASSERT(condition) assert(condition); // @todo <= change this into an exception throw +#define JSON_FAIL_MESSAGE(message) throw std::runtime_error(message); +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) \ + JSON_FAIL_MESSAGE(message) + +namespace Json +{ +const Value Value::null; +const Int Value::minInt = Int(~(UInt(-1) / 2)); +const Int Value::maxInt = Int(UInt(-1) / 2); +const UInt Value::maxUInt = UInt(-1); +const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); +const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); +const UInt64 Value::maxUInt64 = UInt64(-1); +const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); +const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + +/// Unknown size marker +static const unsigned int unknown = (unsigned)-1; + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char *duplicateStringValue(const char *value, unsigned int length = unknown) +{ + if (length == unknown) + length = (unsigned int)strlen(value); + char *newString = static_cast(malloc(length + 1)); + JSON_ASSERT_MESSAGE(newString != 0, "Failed to allocate string value buffer"); + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/** Free the string duplicated by duplicateStringValue(). + */ +static inline void releaseStringValue(char *value) +{ + if (value) + free(value); +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGATED) +#ifdef JSON_VALUE_USE_INTERNAL_MAP +#include "json_internalarray.inl" +#include "json_internalmap.inl" +#endif // JSON_VALUE_USE_INTERNAL_MAP + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGATED) + +namespace Json +{ +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() + : comment_(0) +{ +} + +Value::CommentInfo::~CommentInfo() +{ + if (comment_) + releaseStringValue(comment_); +} + +void Value::CommentInfo::setComment(const char *text) +{ + if (comment_) + releaseStringValue(comment_); + JSON_ASSERT(text != 0); + JSON_ASSERT_MESSAGE(text[0] == '\0' || text[0] == '/', "Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue(text); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#ifndef JSON_VALUE_USE_INTERNAL_MAP + +// Notes: index_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(ArrayIndex index) + : cstr_(0) + , index_(index) +{ +} + +Value::CZString::CZString(const char *cstr, DuplicationPolicy allocate) + : cstr_(allocate == duplicate ? duplicateStringValue(cstr) : cstr) + , index_(allocate) +{ +} + +Value::CZString::CZString(const CZString &other) + : cstr_(other.index_ != noDuplication && other.cstr_ != 0 ? duplicateStringValue(other.cstr_) : other.cstr_) + , index_(other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) : other.index_) +{ +} + +Value::CZString::~CZString() +{ + if (cstr_ && index_ == duplicate) + releaseStringValue(const_cast(cstr_)); +} + +void Value::CZString::swap(CZString &other) +{ + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString &Value::CZString::operator=(const CZString &other) +{ + CZString temp(other); + swap(temp); + return *this; +} + +bool Value::CZString::operator<(const CZString &other) const +{ + if (cstr_) + return strcmp(cstr_, other.cstr_) < 0; + return index_ < other.index_; +} + +bool Value::CZString::operator==(const CZString &other) const +{ + if (cstr_) + return strcmp(cstr_, other.cstr_) == 0; + return index_ == other.index_; +} + +ArrayIndex Value::CZString::index() const +{ + return index_; +} + +const char *Value::CZString::c_str() const +{ + return cstr_; +} + +bool Value::CZString::isStaticString() const +{ + return index_ == noDuplication; +} + +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType type) + : type_(type) + , allocated_(0) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + switch (type) + { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArray(); + break; + case objectValue: + value_.map_ = mapAllocator()->newMap(); + break; +#endif + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +#if defined(JSON_HAS_INT64) +Value::Value(UInt value) + : type_(uintValue) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.uint_ = value; +} + +Value::Value(Int value) + : type_(intValue) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.int_ = value; +} + +#endif // if defined(JSON_HAS_INT64) + +Value::Value(Int64 value) + : type_(intValue) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.int_ = value; +} + +Value::Value(UInt64 value) + : type_(uintValue) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.uint_ = value; +} + +Value::Value(double value) + : type_(realValue) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.real_ = value; +} + +Value::Value(const char *value) + : type_(stringValue) + , allocated_(true) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.string_ = duplicateStringValue(value); +} + +Value::Value(const char *beginValue, const char *endValue) + : type_(stringValue) + , allocated_(true) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.string_ = duplicateStringValue(beginValue, (unsigned int)(endValue - beginValue)); +} + +Value::Value(const std::string &value) + : type_(stringValue) + , allocated_(true) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.string_ = duplicateStringValue(value.c_str(), (unsigned int)value.length()); +} + +Value::Value(const StaticString &value) + : type_(stringValue) + , allocated_(false) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.string_ = const_cast(value.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value::Value(const CppTL::ConstString &value) + : type_(stringValue) + , allocated_(true) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.string_ = duplicateStringValue(value, value.length()); +} +#endif + +Value::Value(bool value) + : type_(booleanValue) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + value_.bool_ = value; +} + +Value::Value(const Value &other) + : type_(other.type_) + , comments_(0) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_(0) +#endif +{ + switch (type_) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_) + { + value_.string_ = duplicateStringValue(other.value_.string_); + allocated_ = true; + } + else + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArrayCopy(*other.value_.array_); + break; + case objectValue: + value_.map_ = mapAllocator()->newMapCopy(*other.value_.map_); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + if (other.comments_) + { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for (int comment = 0; comment < numberOfCommentPlacement; ++comment) + { + const CommentInfo &otherComment = other.comments_[comment]; + if (otherComment.comment_) + comments_[comment].setComment(otherComment.comment_); + } + } +} + +Value::~Value() +{ + switch (type_) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (allocated_) + releaseStringValue(value_.string_); + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + delete value_.map_; + break; +#else + case arrayValue: + arrayAllocator()->destructArray(value_.array_); + break; + case objectValue: + mapAllocator()->destructMap(value_.map_); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + + if (comments_) + delete[] comments_; +} + +Value &Value::operator=(const Value &other) +{ + Value temp(other); + swap(temp); + return *this; +} + +void Value::swap(Value &other) +{ + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap(value_, other.value_); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2; +} + +ValueType Value::type() const +{ + return type_; +} + +int Value::compare(const Value &other) const +{ + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value &other) const +{ + int typeDelta = type_ - other.type_; + if (typeDelta) + return typeDelta < 0 ? true : false; + switch (type_) + { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + return (value_.string_ == 0 && other.value_.string_) || + (other.value_.string_ && value_.string_ && strcmp(value_.string_, other.value_.string_) < 0); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + { + int delta = int(value_.map_->size() - other.value_.map_->size()); + if (delta) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } +#else + case arrayValue: + return value_.array_->compare(*(other.value_.array_)) < 0; + case objectValue: + return value_.map_->compare(*(other.value_.map_)) < 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value &other) const +{ + return !(other < *this); +} + +bool Value::operator>=(const Value &other) const +{ + return !(*this < other); +} + +bool Value::operator>(const Value &other) const +{ + return other < *this; +} + +bool Value::operator==(const Value &other) const +{ + // if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if (type_ != temp) + return false; + switch (type_) + { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + return (value_.string_ == other.value_.string_) || + (other.value_.string_ && value_.string_ && strcmp(value_.string_, other.value_.string_) == 0); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && (*value_.map_) == (*other.value_.map_); +#else + case arrayValue: + return value_.array_->compare(*(other.value_.array_)) == 0; + case objectValue: + return value_.map_->compare(*(other.value_.map_)) == 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value &other) const +{ + return !(*this == other); +} + +const char *Value::asCString() const +{ + JSON_ASSERT(type_ == stringValue); + return value_.string_; +} + +std::string Value::asString() const +{ + switch (type_) + { + case nullValue: + return ""; + case stringValue: + return value_.string_ ? value_.string_ : ""; + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + case uintValue: + case realValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + default: + JSON_ASSERT_UNREACHABLE; + } + return ""; // unreachable +} + +#ifdef JSON_USE_CPPTL +CppTL::ConstString Value::asConstString() const +{ + return CppTL::ConstString(asString().c_str()); +} +#endif + +Value::Int Value::asInt() const +{ + switch (type_) + { + case nullValue: + return 0; + case intValue: + JSON_ASSERT_MESSAGE(value_.int_ >= minInt && value_.int_ <= maxInt, + "unsigned integer out of signed int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(value_.uint_ <= UInt(maxInt), "unsigned integer out of signed int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range"); + return Int(value_.real_); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE("Type is not convertible to int"); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +Value::UInt Value::asUInt() const +{ + switch (type_) + { + case nullValue: + return 0; + case intValue: + JSON_ASSERT_MESSAGE(value_.int_ >= 0, "Negative integer can not be converted to unsigned integer"); + JSON_ASSERT_MESSAGE(value_.int_ <= maxUInt, "signed integer out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(value_.uint_ <= maxUInt, "unsigned integer out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range"); + return UInt(value_.real_); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE("Type is not convertible to uint"); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const +{ + switch (type_) + { + case nullValue: + return 0; + case intValue: + return value_.int_; + case uintValue: + JSON_ASSERT_MESSAGE(value_.uint_ <= UInt64(maxInt64), "unsigned integer out of Int64 range"); + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE(value_.real_ >= minInt64 && value_.real_ <= maxInt64, "Real out of Int64 range"); + return Int(value_.real_); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE("Type is not convertible to Int64"); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +Value::UInt64 Value::asUInt64() const +{ + switch (type_) + { + case nullValue: + return 0; + case intValue: + JSON_ASSERT_MESSAGE(value_.int_ >= 0, "Negative integer can not be converted to UInt64"); + return value_.int_; + case uintValue: + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE(value_.real_ >= 0 && value_.real_ <= maxUInt64, "Real out of UInt64 range"); + return UInt(value_.real_); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE("Type is not convertible to UInt64"); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const +{ +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +LargestUInt Value::asLargestUInt() const +{ +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +double Value::asDouble() const +{ + switch (type_) + { + case nullValue: + return 0.0; + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(Int(value_.uint_ / 2)) * 2 + Int(value_.uint_ & 1); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE("Type is not convertible to double"); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +float Value::asFloat() const +{ + switch (type_) + { + case nullValue: + return 0.0f; + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(Int(value_.uint_ / 2)) * 2 + Int(value_.uint_ & 1); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast(value_.real_); + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE("Type is not convertible to float"); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0.0f; // unreachable; +} + +bool Value::asBool() const +{ + switch (type_) + { + case nullValue: + return false; + case intValue: + case uintValue: + return value_.int_ != 0; + case realValue: + return value_.real_ != 0.0; + case booleanValue: + return value_.bool_; + case stringValue: + return value_.string_ && value_.string_[0] != 0; + case arrayValue: + case objectValue: + return value_.map_->size() != 0; + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable; +} + +bool Value::isConvertibleTo(ValueType other) const +{ + switch (type_) + { + case nullValue: + return true; + case intValue: + return (other == nullValue && value_.int_ == 0) || other == intValue || + (other == uintValue && value_.int_ >= 0) || other == realValue || other == stringValue || + other == booleanValue; + case uintValue: + return (other == nullValue && value_.uint_ == 0) || + (other == intValue && value_.uint_ <= (unsigned)maxInt) || other == uintValue || + other == realValue || other == stringValue || other == booleanValue; + case realValue: + return (other == nullValue && value_.real_ == 0.0) || + (other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt) || + (other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt) || other == realValue || + other == stringValue || other == booleanValue; + case booleanValue: + return (other == nullValue && value_.bool_ == false) || other == intValue || other == uintValue || + other == realValue || other == stringValue || other == booleanValue; + case stringValue: + return other == stringValue || (other == nullValue && (!value_.string_ || value_.string_[0] == 0)); + case arrayValue: + return other == arrayValue || (other == nullValue && value_.map_->size() == 0); + case objectValue: + return other == objectValue || (other == nullValue && value_.map_->size() == 0); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable; +} + +/// Number of values in array or object +ArrayIndex Value::size() const +{ + switch (type_) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) + { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); +#else + case arrayValue: + return Int(value_.array_->size()); + case objectValue: + return Int(value_.map_->size()); +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +bool Value::empty() const +{ + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +bool Value::operator!() const +{ + return isNull(); +} + +void Value::clear() +{ + JSON_ASSERT(type_ == nullValue || type_ == arrayValue || type_ == objectValue); + + switch (type_) + { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_->clear(); + break; +#else + case arrayValue: + value_.array_->clear(); + break; + case objectValue: + value_.map_->clear(); + break; +#endif + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) +{ + JSON_ASSERT(type_ == nullValue || type_ == arrayValue); + if (type_ == nullValue) + *this = Value(arrayValue); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + (*this)[newSize - 1]; + else + { + for (ArrayIndex index = newSize; index < oldSize; ++index) + { + value_.map_->erase(index); + } + assert(size() == newSize); + } +#else + value_.array_->resize(newSize); +#endif +} + +Value &Value::operator[](ArrayIndex index) +{ + JSON_ASSERT(type_ == nullValue || type_ == arrayValue); + if (type_ == nullValue) + *this = Value(arrayValue); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key(index); + ObjectValues::iterator it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, null); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +#else + return value_.array_->resolveReference(index); +#endif +} + +Value &Value::operator[](int index) +{ + JSON_ASSERT(index >= 0); + return (*this)[ArrayIndex(index)]; +} + +const Value &Value::operator[](ArrayIndex index) const +{ + JSON_ASSERT(type_ == nullValue || type_ == arrayValue); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return null; + return (*it).second; +#else + Value *value = value_.array_->find(index); + return value ? *value : null; +#endif +} + +const Value &Value::operator[](int index) const +{ + JSON_ASSERT(index >= 0); + return (*this)[ArrayIndex(index)]; +} + +Value &Value::operator[](const char *key) +{ + return resolveReference(key, false); +} + +Value &Value::resolveReference(const char *key, bool isStatic) +{ + JSON_ASSERT(type_ == nullValue || type_ == objectValue); + if (type_ == nullValue) + *this = Value(objectValue); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey(key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy); + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, null); + it = value_.map_->insert(it, defaultValue); + Value &value = (*it).second; + return value; +#else + return value_.map_->resolveReference(key, isStatic); +#endif +} + +Value Value::get(ArrayIndex index, const Value &defaultValue) const +{ + const Value *value = &((*this)[index]); + return value == &null ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const +{ + return index < size(); +} + +const Value &Value::operator[](const char *key) const +{ + JSON_ASSERT(type_ == nullValue || type_ == objectValue); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey(key, CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return null; + return (*it).second; +#else + const Value *value = value_.map_->find(key); + return value ? *value : null; +#endif +} + +Value &Value::operator[](const std::string &key) +{ + return (*this)[key.c_str()]; +} + +const Value &Value::operator[](const std::string &key) const +{ + return (*this)[key.c_str()]; +} + +Value &Value::operator[](const StaticString &key) +{ + return resolveReference(key, true); +} + +#ifdef JSON_USE_CPPTL +Value &Value::operator[](const CppTL::ConstString &key) +{ + return (*this)[key.c_str()]; +} + +const Value &Value::operator[](const CppTL::ConstString &key) const +{ + return (*this)[key.c_str()]; +} +#endif + +Value &Value::append(const Value &value) +{ + return (*this)[size()] = value; +} + +Value Value::get(const char *key, const Value &defaultValue) const +{ + const Value *value = &((*this)[key]); + return value == &null ? defaultValue : *value; +} + +Value Value::get(const std::string &key, const Value &defaultValue) const +{ + return get(key.c_str(), defaultValue); +} + +Value Value::removeMember(const char *key) +{ + JSON_ASSERT(type_ == nullValue || type_ == objectValue); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey(key, CZString::noDuplication); + ObjectValues::iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return null; + Value old(it->second); + value_.map_->erase(it); + return old; +#else + Value *value = value_.map_->find(key); + if (value) + { + Value old(*value); + value_.map_.remove(key); + return old; + } + else + { + return null; + } +#endif +} + +Value Value::removeMember(const std::string &key) +{ + return removeMember(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value Value::get(const CppTL::ConstString &key, const Value &defaultValue) const +{ + return get(key.c_str(), defaultValue); +} +#endif + +bool Value::isMember(const char *key) const +{ + const Value *value = &((*this)[key]); + return value != &null; +} + +bool Value::isMember(const std::string &key) const +{ + return isMember(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +bool Value::isMember(const CppTL::ConstString &key) const +{ + return isMember(key.c_str()); +} +#endif + +Value::Members Value::getMemberNames() const +{ + JSON_ASSERT(type_ == nullValue || type_ == objectValue); + if (type_ == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) + members.push_back(std::string((*it).first.c_str())); +#else + ValueInternalMap::IteratorState it; + ValueInternalMap::IteratorState itEnd; + value_.map_->makeBeginIterator(it); + value_.map_->makeEndIterator(itEnd); + for (; !ValueInternalMap::equals(it, itEnd); ValueInternalMap::increment(it)) + members.push_back(std::string(ValueInternalMap::key(it))); +#endif + return members; +} +// +//# ifdef JSON_USE_CPPTL +// EnumMemberNames +// Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +// EnumValues +// Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + +bool Value::isNull() const +{ + return type_ == nullValue; +} + +bool Value::isBool() const +{ + return type_ == booleanValue; +} + +bool Value::isInt() const +{ + return type_ == intValue; +} + +bool Value::isUInt() const +{ + return type_ == uintValue; +} + +bool Value::isIntegral() const +{ + return type_ == intValue || type_ == uintValue || type_ == booleanValue; +} + +bool Value::isDouble() const +{ + return type_ == realValue; +} + +bool Value::isNumeric() const +{ + return isIntegral() || isDouble(); +} + +bool Value::isString() const +{ + return type_ == stringValue; +} + +bool Value::isArray() const +{ + return type_ == nullValue || type_ == arrayValue; +} + +bool Value::isObject() const +{ + return type_ == nullValue || type_ == objectValue; +} + +void Value::setComment(const char *comment, CommentPlacement placement) +{ + if (!comments_) + comments_ = new CommentInfo[numberOfCommentPlacement]; + comments_[placement].setComment(comment); +} + +void Value::setComment(const std::string &comment, CommentPlacement placement) +{ + setComment(comment.c_str(), placement); +} + +bool Value::hasComment(CommentPlacement placement) const +{ + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string Value::getComment(CommentPlacement placement) const +{ + if (hasComment(placement)) + return comments_[placement].comment_; + return ""; +} + +std::string Value::toStyledString() const +{ + StyledWriter writer; + return writer.write(*this); +} + +Value::const_iterator Value::begin() const +{ + switch (type_) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator(it); + return const_iterator(it); + } + break; + case objectValue: + if (value_.map_) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator(it); + return const_iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::const_iterator Value::end() const +{ + switch (type_) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator(it); + return const_iterator(it); + } + break; + case objectValue: + if (value_.map_) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator(it); + return const_iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::iterator Value::begin() +{ + switch (type_) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator(it); + return iterator(it); + } + break; + case objectValue: + if (value_.map_) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator(it); + return iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; +#endif + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() +{ + switch (type_) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator(it); + return iterator(it); + } + break; + case objectValue: + if (value_.map_) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator(it); + return iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; +#endif + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() + : kind_(kindNone) +{ +} + +PathArgument::PathArgument(ArrayIndex index) + : index_(index) + , kind_(kindIndex) +{ +} + +PathArgument::PathArgument(const char *key) + : key_(key) + , kind_(kindKey) +{ +} + +PathArgument::PathArgument(const std::string &key) + : key_(key.c_str()) + , kind_(kindKey) +{ +} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const std::string &path, + const PathArgument &a1, + const PathArgument &a2, + const PathArgument &a3, + const PathArgument &a4, + const PathArgument &a5) +{ + InArgs in; + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const std::string &path, const InArgs &in) +{ + const char *current = path.c_str(); + const char *end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while (current != end) + { + if (*current == '[') + { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else + { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *current++ != ']') + invalidPath(path, int(current - path.c_str())); + } + else if (*current == '%') + { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } + else if (*current == '.') + { + ++current; + } + else + { + const char *beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(std::string(beginName, current)); + } + } +} + +void Path::addPathInArg(const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind) +{ + if (itInArg == in.end()) + { + // Error: missing argument %d + } + else if ((*itInArg)->kind_ != kind) + { + // Error: bad argument type + } + else + { + args_.push_back(**itInArg); + } +} + +void Path::invalidPath(const std::string &path, int location) +{ + // Error: invalid path. +} + +const Value &Path::resolve(const Value &root) const +{ + const Value *node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) + { + const PathArgument &arg = *it; + if (arg.kind_ == PathArgument::kindIndex) + { + if (!node->isArray() || node->isValidIndex(arg.index_)) + { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } + else if (arg.kind_ == PathArgument::kindKey) + { + if (!node->isObject()) + { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if (node == &Value::null) + { + // Error: unable to resolve path (object has no member named '' at position...) + } + } + } + return *node; +} + +Value Path::resolve(const Value &root, const Value &defaultValue) const +{ + const Value *node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) + { + const PathArgument &arg = *it; + if (arg.kind_ == PathArgument::kindIndex) + { + if (!node->isArray() || node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } + else if (arg.kind_ == PathArgument::kindKey) + { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::null) + return defaultValue; + } + } + return *node; +} + +Value &Path::make(Value &root) const +{ + Value *node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) + { + const PathArgument &arg = *it; + if (arg.kind_ == PathArgument::kindIndex) + { + if (!node->isArray()) + { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } + else if (arg.kind_ == PathArgument::kindKey) + { + if (!node->isObject()) + { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGATED) +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGATED) +#include +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning(disable : 4996) // disable warning about strdup being deprecated. +#endif + +namespace Json +{ +static bool containsControlCharacter(const char *str) +{ + while (*str) + { + if (isControlCharacter(*(str++))) + return true; + } + return false; +} + +std::string valueToString(LargestInt value) +{ + UIntToStringBuffer buffer; + char *current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if (isNegative) + value = -value; + uintToString(LargestUInt(value), current); + if (isNegative) + *--current = '-'; + assert(current >= buffer); + return current; +} + +std::string valueToString(LargestUInt value) +{ + UIntToStringBuffer buffer; + char *current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +std::string valueToString(Int value) +{ + return valueToString(LargestInt(value)); +} + +std::string valueToString(UInt value) +{ + return valueToString(LargestUInt(value)); +} + +#endif // # if defined(JSON_HAS_INT64) + +std::string valueToString(double value) +{ + char buffer[32]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. + sprintf_s(buffer, sizeof(buffer), "%#.16g", value); +#else + sprintf(buffer, "%#.16g", value); +#endif + char *ch = buffer + strlen(buffer) - 1; + if (*ch != '0') + return buffer; // nothing to truncate, so save time + while (ch > buffer && *ch == '0') + { + --ch; + } + char *last_nonzero = ch; + while (ch >= buffer) + { + switch (*ch) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + --ch; + continue; + case '.': + // Truncate zeroes to save bytes in output, but keep one. + *(last_nonzero + 2) = '\0'; + return buffer; + default: + return buffer; + } + } + return buffer; +} + +std::string valueToString(bool value) +{ + return value ? "true" : "false"; +} + +std::string valueToQuotedString(const char *value) +{ + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter(value)) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = strlen(value) * 2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char *c = value; *c != 0; ++c) + { + switch (*c) + { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } + else + { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() +{ +} + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_(false) +{ +} + +void FastWriter::enableYAMLCompatibility() +{ + yamlCompatiblityEnabled_ = true; +} + +std::string FastWriter::write(const Value &root) +{ + document_ = ""; + writeValue(root); + document_ += "\n"; + return document_; +} + +void FastWriter::writeValue(const Value &value) +{ + switch (value.type()) + { + case nullValue: + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: + document_ += valueToQuotedString(value.asCString()); + break; + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: + { + document_ += "["; + int size = value.size(); + for (int index = 0; index < size; ++index) + { + if (index > 0) + document_ += ","; + writeValue(value[index]); + } + document_ += "]"; + } + break; + case objectValue: + { + Value::Members members(value.getMemberNames()); + document_ += "{"; + for (Value::Members::iterator it = members.begin(); it != members.end(); ++it) + { + const std::string &name = *it; + if (it != members.begin()) + document_ += ","; + document_ += valueToQuotedString(name.c_str()); + document_ += yamlCompatiblityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += "}"; + } + break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_(74) + , indentSize_(3) +{ +} + +std::string StyledWriter::write(const Value &root) +{ + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + +void StyledWriter::writeValue(const Value &value) +{ + switch (value.type()) + { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + pushValue(valueToQuotedString(value.asCString())); + break; + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: + { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else + { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) + { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } + break; + } +} + +void StyledWriter::writeArrayValue(const Value &value) +{ + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else + { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) + { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) + { + const Value &childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else + { + writeIndent(); + writeValue(childValue); + } + if (++index == size) + { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } + else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (unsigned index = 0; index < size; ++index) + { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultineArray(const Value &value) +{ + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) + { + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const std::string &value) +{ + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() +{ + if (!document_.empty()) + { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const std::string &value) +{ + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() +{ + indentString_ += std::string(indentSize_, ' '); +} + +void StyledWriter::unindent() +{ + assert(int(indentString_.size()) >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value &root) +{ + if (!root.hasComment(commentBefore)) + return; + document_ += normalizeEOL(root.getComment(commentBefore)); + document_ += "\n"; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value &root) +{ + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + + if (root.hasComment(commentAfter)) + { + document_ += "\n"; + document_ += normalizeEOL(root.getComment(commentAfter)); + document_ += "\n"; + } +} + +bool StyledWriter::hasCommentForValue(const Value &value) +{ + return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +std::string StyledWriter::normalizeEOL(const std::string &text) +{ + std::string normalized; + normalized.reserve(text.length()); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while (current != end) + { + char c = *current++; + if (c == '\r') // mac or dos EOL + { + if (*current == '\n') // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(std::string indentation) + : document_(NULL) + , rightMargin_(74) + , indentation_(indentation) +{ +} + +void StyledStreamWriter::write(std::ostream &out, const Value &root) +{ + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value &value) +{ + switch (value.type()) + { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + pushValue(valueToQuotedString(value.asCString())); + break; + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: + { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else + { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) + { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } + break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value &value) +{ + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else + { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) + { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) + { + const Value &childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else + { + writeIndent(); + writeValue(childValue); + } + if (++index == size) + { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } + else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) + { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultineArray(const Value &value) +{ + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) + { + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const std::string &value) +{ + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() +{ + /* + Some comments in this method would have been nice. ;-) + + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + *document_ << '\n'; + } + */ + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const std::string &value) +{ + writeIndent(); + *document_ << value; +} + +void StyledStreamWriter::indent() +{ + indentString_ += indentation_; +} + +void StyledStreamWriter::unindent() +{ + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value &root) +{ + if (!root.hasComment(commentBefore)) + return; + *document_ << normalizeEOL(root.getComment(commentBefore)); + *document_ << "\n"; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value &root) +{ + if (root.hasComment(commentAfterOnSameLine)) + *document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + + if (root.hasComment(commentAfter)) + { + *document_ << "\n"; + *document_ << normalizeEOL(root.getComment(commentAfter)); + *document_ << "\n"; + } +} + +bool StyledStreamWriter::hasCommentForValue(const Value &value) +{ + return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +std::string StyledStreamWriter::normalizeEOL(const std::string &text) +{ + std::string normalized; + normalized.reserve(text.length()); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while (current != end) + { + char c = *current++; + if (c == '\r') // mac or dos EOL + { + if (*current == '\n') // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + +std::ostream &operator<<(std::ostream &sout, const Value &root) +{ + Json::StyledStreamWriter writer; + writer.write(sout, root); + return sout; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/options.cpp b/src/blfwk/src/options.cpp new file mode 100644 index 0000000..d83ffda --- /dev/null +++ b/src/blfwk/src/options.cpp @@ -0,0 +1,1276 @@ +// COPY/REUSE POLICY +// ================= +// Permission is hereby granted to freely copy and redistribute this +// software, provided that the author is clearly credited in all copies +// and derivations. Neither the names of the authors nor that of their +// employers may be used to endorse or promote products derived from this +// software without specific written permission. +// +// +// DISCLAIMER +// ========== +// This software is provided ``As Is'' and without any express or implied +// warranties. Neither the authors nor any of their employers (including +// any of their subsidiaries and subdivisions) are responsible for maintaining +// or supporting this software or for any consequences resulting from the +// use of this software, no matter how awful, even if they arise from flaws +// in the software. +// **************************************************************************** +// ^FILE: options.c - implement the functions defined in +// +// ^HISTORY: +// 01/16/92 Brad Appleton Created +// +// 03/23/93 Brad Appleton +// - Added OptIstreamIter class +// +// 10/08/93 Brad Appleton +// - Added "hidden" options +// +// 02/08/94 Brad Appleton +// - Added "OptionSpec" class +// - Permitted use of stdio instead of iostreams via #ifdef USE_STDIO +// +// 03/08/94 Brad Appleton +// - completed support for USE_STDIO +// - added #ifdef NO_USAGE for people who always want to print their own +// - Fixed stupid NULL pointer error in OptionsSpec class +// +// 07/31/97 Brad Appleton +// - Added PARSE_POS control flag and POSITIONAL return value. +// ^^************************************************************************** + +#include +#include +#include + +#include "blfwk/options.h" + +using namespace std; + +// static const char ident[] = "@(#)Options 1.05" ; + +// I need a portable version of "tolower" that does NOT modify +// non-uppercase characters. +// +#define TOLOWER(c) (isupper(c) ? tolower(c) : c) + +// Use this to shut the compiler up about NULL strings +#define NULLSTR (char *) NULL + +// ******************************************************** insertion operators + +// If you are using then you need this stuff! +// If you are using then #ifdef this stuff out +// + +#ifdef USE_STDIO + +// Implement just enough of ostream to get this file to compile +// + +static const char endl = '\n'; + +class ostream +{ +public: + ostream(FILE *fileptr) + : fp(fileptr) + { + } + + ostream &operator<<(char ch); + + ostream &operator<<(const char *str); + + ostream &write(const char *buf, unsigned bufsize); + +private: + FILE *fp; +}; + +ostream &ostream::operator<<(char ch) +{ + fputc(ch, fp); + return *this; +} + +ostream &ostream::operator<<(const char *str) +{ + fputs(str, fp); + return *this; +} + +ostream &ostream::write(const char *buf, unsigned) +{ + fputs(buf, fp); + return *this; +} + +static ostream cerr(stderr); +static ostream cout(stdout); + +#endif /* USE_STDIO */ + +// ************************************************************** OptIter + +OptIter::~OptIter(void) +{ +} + +const char *OptIter::operator()(void) +{ + const char *elt = curr(); + (void)next(); + return elt; +} + +// ************************************************************** OptIterRwd + +OptIterRwd::OptIterRwd(void) +{ +} + +OptIterRwd::~OptIterRwd(void) +{ +} + +// ************************************************************** OptArgvIter + +OptArgvIter::~OptArgvIter(void) +{ +} + +const char *OptArgvIter::curr(void) +{ + return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx]; +} + +void OptArgvIter::next(void) +{ + if ((ndx != ac) && av[ndx]) + ++ndx; +} + +const char *OptArgvIter::operator()(void) +{ + return ((ndx == ac) || (av[ndx] == NULL)) ? NULLSTR : av[ndx++]; +} + +void OptArgvIter::rewind(void) +{ + ndx = 0; +} + +// ************************************************************** OptStrTokIter + +static const char WHITESPACE[] = " \t\n\r\v\f"; +const char *OptStrTokIter::default_delims = WHITESPACE; + +OptStrTokIter::OptStrTokIter(const char *tokens, const char *delimiters) + : len(unsigned(strlen(tokens))) + , str(tokens) + , seps(delimiters) + , cur(NULLSTR) + , tokstr(NULLSTR) +{ + if (seps == NULL) + seps = default_delims; + tokstr = new char[len + 1]; + (void)::strcpy(tokstr, str); + cur = ::strtok(tokstr, seps); +} + +OptStrTokIter::~OptStrTokIter(void) +{ + delete[] tokstr; +} + +const char *OptStrTokIter::curr(void) +{ + return cur; +} + +void OptStrTokIter::next(void) +{ + if (cur) + cur = ::strtok(NULL, seps); +} + +const char *OptStrTokIter::operator()(void) +{ + const char *elt = cur; + if (cur) + cur = ::strtok(NULL, seps); + return elt; +} + +void OptStrTokIter::rewind(void) +{ + (void)::strcpy(tokstr, str); + cur = ::strtok(tokstr, seps); +} + +// ************************************************************* OptIstreamIter + +#ifdef vms +enum +{ + c_COMMENT = '!' +}; +#else +enum +{ + c_COMMENT = '#' +}; +#endif + +const unsigned OptIstreamIter::MAX_LINE_LEN = 1024; + +// Constructor +OptIstreamIter::OptIstreamIter(istream &input) + : is(input) + , tok_iter(NULL) +{ +#ifdef USE_STDIO + fprintf(stderr, "%s: Can't use OptIstreamIter class:\n", "OptIstreamIter::OptIstreamIter"); + fprintf(stderr, "\tOptions(3C++) was compiled with USE_STDIO #defined.\n"); + exit(-1); +#endif /* USE_STDIO */ +} + +// Destructor +OptIstreamIter::~OptIstreamIter(void) +{ + delete tok_iter; +} + +const char *OptIstreamIter::curr(void) +{ +#ifdef USE_STDIO + return NULLSTR; +#else + const char *result = NULLSTR; + if (tok_iter) + result = tok_iter->curr(); + if (result) + return result; + fill(); + return (!is) ? NULLSTR : tok_iter->curr(); +#endif /* USE_STDIO */ +} + +void OptIstreamIter::next(void) +{ +#ifdef USE_STDIO + return; +#else + const char *result = NULLSTR; + if (tok_iter) + result = tok_iter->operator()(); + if (result) + return; + fill(); + if (!is) + tok_iter->next(); +#endif /* USE_STDIO */ +} + +const char *OptIstreamIter::operator()(void) +{ +#ifdef USE_STDIO + return NULLSTR; +#else + const char *result = NULLSTR; + if (tok_iter) + result = tok_iter->operator()(); + if (result) + return result; + fill(); + return (!is) ? NULLSTR : tok_iter->operator()(); +#endif /* USE_STDIO */ +} + +// What we do is this: for each line of text in the istream, we use +// a OptStrTokIter to iterate over each token on the line. +// +// If the first non-white character on a line is c_COMMENT, then we +// consider the line to be a comment and we ignore it. +// +void OptIstreamIter::fill(void) +{ +#ifdef USE_STDIO + return; +#else + char buf[OptIstreamIter::MAX_LINE_LEN]; + do + { + *buf = '\0'; + is.getline(buf, sizeof(buf)); + char *ptr = buf; + while (isspace(*ptr)) + ++ptr; + if (*ptr && (*ptr != c_COMMENT)) + { + delete tok_iter; + tok_iter = new OptStrTokIter(ptr); + return; + } + } while (is); +#endif /* USE_STDIO */ +} + +// **************************************************** Options class utilities + +// Is this option-char null? +inline static int isNullOpt(char optchar) +{ + return ((!optchar) || isspace(optchar) || (!isprint(optchar))); +} + +// Check for explicit "end-of-options" +inline static int isEndOpts(const char *token) +{ + return ((token == NULL) || (!::strcmp(token, "--"))); +} + +// See if an argument is an option +inline static int isOption(unsigned flags, const char *arg) +{ + return (((*arg != '\0') || (arg[1] != '\0')) && ((*arg == '-') || ((flags & Options::PLUS) && (*arg == '+')))); +} + +// See if we should be parsing only options or if we also need to +// parse positional arguments +inline static int isOptsOnly(unsigned flags) +{ + return (flags & Options::PARSE_POS) ? 0 : 1; +} + +// return values for a keyword matching function +enum kwdmatch_t +{ + NO_MATCH, + PARTIAL_MATCH, + EXACT_MATCH +}; + +// --------------------------------------------------------------------------- +// ^FUNCTION: kwdmatch - match a keyword +// +// ^SYNOPSIS: +// static kwdmatch_t kwdmatch(src, attempt, len) +// +// ^PARAMETERS: +// char * src -- the actual keyword to match +// char * attempt -- the possible keyword to compare against "src" +// int len -- number of character of "attempt" to consider +// (if 0 then we should use all of "attempt") +// +// ^DESCRIPTION: +// See if "attempt" matches some prefix of "src" (case insensitive). +// +// ^REQUIREMENTS: +// - attempt should be non-NULL and non-empty +// +// ^SIDE-EFFECTS: +// None. +// +// ^RETURN-VALUE: +// An enumeration value of type kwdmatch_t corresponding to whether +// We had an exact match, a partial match, or no match. +// +// ^ALGORITHM: +// Trivial +// ^^------------------------------------------------------------------------- +static kwdmatch_t kwdmatch(const char *src, const char *attempt, int len = 0) +{ + int i; + + if (src == attempt) + return EXACT_MATCH; + if ((src == NULL) || (attempt == NULL)) + return NO_MATCH; + if ((!*src) && (!*attempt)) + return EXACT_MATCH; + if ((!*src) || (!*attempt)) + return NO_MATCH; + + for (i = 0; ((i < len) || (len == 0)) && (attempt[i]) && (attempt[i] != ' '); i++) + { + if (TOLOWER(src[i]) != TOLOWER(attempt[i])) + return NO_MATCH; + } + + return (src[i]) ? PARTIAL_MATCH : EXACT_MATCH; +} + +// **************************************************************** OptionSpec + +// Class that represents an option-specification +// *NOTE*:: Assumes that the char-ptr given to the constructor points +// to storage that will NOT be modified and whose lifetime will +// be as least as long as the OptionSpec object we construct. +// +class OptionSpec +{ +public: + OptionSpec(const char *decl = NULLSTR) + : hidden(0) + , spec(decl) + { + if (spec == NULL) + spec = NULL_spec; + CheckHidden(); + } + + OptionSpec(const OptionSpec &cp) + : hidden(cp.hidden) + , spec(cp.spec) + { + } + + // NOTE: use default destructor! + + // Assign to another OptionSpec + OptionSpec &operator=(const OptionSpec &cp) + { + if (this != &cp) + { + spec = cp.spec; + hidden = cp.hidden; + } + return *this; + } + + // Assign to a string + OptionSpec &operator=(const char *decl) + { + if (spec != decl) + { + spec = decl; + hidden = 0; + CheckHidden(); + } + return *this; + } + + // Convert to char-ptr by returning the original declaration-string + operator const char *() { return isHiddenOpt() ? (spec - 1) : spec; } + // Is this option NULL? + int isNULL(void) const { return ((spec == NULL) || (spec == NULL_spec)); } + // Is this options incorrectly specified? + int isSyntaxError(const char *name) const; + + // See if this is a Hidden option + int isHiddenOpt(void) const { return hidden; } + // Get the corresponding option-character + char OptChar(void) const { return *spec; } + // Get the corresponding long-option string + const char *LongOpt(void) const { return (spec[1] && spec[2] && (!isspace(spec[2]))) ? (spec + 2) : NULLSTR; } + // Does this option require an argument? + int isValRequired(void) const { return ((spec[1] == ':') || (spec[1] == '+')); } + // Does this option take an optional argument? + int isValOptional(void) const { return ((spec[1] == '?') || (spec[1] == '*')); } + // Does this option take no arguments? + int isNoArg(void) const { return ((spec[1] == '|') || (!spec[1])); } + // Can this option take more than one argument? + int isList(void) const { return ((spec[1] == '+') || (spec[1] == '*')); } + // Does this option take any arguments? + int isValTaken(void) const { return (isValRequired() || isValOptional()); } + // Format this option in the given buffer + unsigned Format(char *buf, unsigned optctrls) const; + +private: + void CheckHidden(void) + { + if ((!hidden) && (*spec == '-')) + { + ++hidden; + ++spec; + } + } + + unsigned hidden : 1; // hidden-flag + const char *spec; // string specification + + static const char NULL_spec[]; +}; + +const char OptionSpec::NULL_spec[] = "\0\0\0"; + +int OptionSpec::isSyntaxError(const char *name) const +{ + int error = 0; + if ((!spec) || (!*spec)) + { + cerr << name << ": empty option specifier." << endl; + cerr << "\tmust be at least 1 character long." << endl; + ++error; + } + else if (spec[1] && (strchr("|?:*+", spec[1]) == NULL)) + { + cerr << name << ": bad option specifier \"" << spec << "\"." << endl; + cerr << "\t2nd character must be in the set \"|?:*+\"." << endl; + ++error; + } + return error; +} + +// --------------------------------------------------------------------------- +// ^FUNCTION: OptionSpec::Format - format an option-spec for a usage message +// +// ^SYNOPSIS: +// unsigned OptionSpec::Format(buf, optctrls) const +// +// ^PARAMETERS: +// char * buf -- where to print the formatted option +// unsigned optctrls -- option-parsing configuration flags +// +// ^DESCRIPTION: +// Self-explanatory. +// +// ^REQUIREMENTS: +// - buf must be large enough to hold the result +// +// ^SIDE-EFFECTS: +// - writes to buf. +// +// ^RETURN-VALUE: +// Number of characters written to buf. +// +// ^ALGORITHM: +// Follow along in the source - it's not hard but it is tedious! +// ^^------------------------------------------------------------------------- +unsigned OptionSpec::Format(char *buf, unsigned optctrls) const +{ +#ifdef NO_USAGE + return (*buf = '\0'); +#else + static char default_value[] = ""; + if (isHiddenOpt()) + return (unsigned)(*buf = '\0'); + char optchar = OptChar(); + const char *longopt = LongOpt(); + char *p = buf; + + const char *value = NULLSTR; + int longopt_len = 0; + int value_len = 0; + + if (longopt) + { + value = ::strchr(longopt, ' '); + longopt_len = (value) ? (int)(value - longopt) : (int)::strlen(longopt); + } + else + { + value = ::strchr(spec + 1, ' '); + } + while (value && (*value == ' ')) + ++value; + if (value && *value) + { + value_len = (int)::strlen(value); + } + else + { + value = default_value; + value_len = sizeof(default_value) - 1; + } + + if ((optctrls & Options::SHORT_ONLY) && ((!isNullOpt(optchar)) || (optctrls & Options::NOGUESSING))) + { + longopt = NULLSTR; + } + if ((optctrls & Options::LONG_ONLY) && (longopt || (optctrls & Options::NOGUESSING))) + { + optchar = '\0'; + } + if (isNullOpt(optchar) && (longopt == NULL)) + { + *buf = '\0'; + return 0; + } + + *(p++) = '['; + + // print the single character option + if (!isNullOpt(optchar)) + { + *(p++) = '-'; + *(p++) = optchar; + } + + if ((!isNullOpt(optchar)) && (longopt)) + *(p++) = '|'; + + // print the long option + if (longopt) + { + *(p++) = '-'; + if (!(optctrls & (Options::LONG_ONLY | Options::SHORT_ONLY))) + { + *(p++) = '-'; + } + strncpy(p, longopt, longopt_len); + p += longopt_len; + } + + // print any argument the option takes + if (isValTaken()) + { + *(p++) = ' '; + if (isValOptional()) + *(p++) = '['; + strcpy(p, value); + p += value_len; + if (isList()) + { + strcpy(p, " ..."); + p += 4; + } + if (isValOptional()) + *(p++) = ']'; + } + + *(p++) = ']'; + *p = '\0'; + + return (unsigned)strlen(buf); +#endif /* USE_STDIO */ +} + +// ******************************************************************* Options + +#if (defined(MSWIN) || defined(OS2) || defined(MSDOS)) +#define DIR_SEP_CHAR '\\' +#else +#define DIR_SEP_CHAR '/' +#endif + +Options::Options(const char *name, const char *const optv[]) + : cmdname(name) + , optvec(optv) + , explicit_end(0) + , optctrls(DEFAULT) + , nextchar(NULLSTR) + , listopt(NULLSTR) +{ + const char *basename = ::strrchr(cmdname, DIR_SEP_CHAR); + if (basename) + cmdname = basename + 1; + check_syntax(); +} + +Options::~Options(void) +{ +} + +// Make sure each option-specifier has correct syntax. +// +// If there is even one invalid specifier, then exit ungracefully! +// +void Options::check_syntax(void) const +{ + int errors = 0; + if ((optvec == NULL) || (!*optvec)) + return; + + for (const char *const *optv = optvec; *optv; optv++) + { + OptionSpec optspec = *optv; + errors += optspec.isSyntaxError(cmdname); + } + if (errors) + exit(127); +} + +// --------------------------------------------------------------------------- +// ^FUNCTION: Options::match_opt - match an option +// +// ^SYNOPSIS: +// const char * match_opt(opt, int ignore_case) const +// +// ^PARAMETERS: +// char opt -- the option-character to match +// int ignore_case -- should we ignore character-case? +// +// ^DESCRIPTION: +// See if "opt" is found in "optvec" +// +// ^REQUIREMENTS: +// - optvec should be non-NULL and terminated by a NULL pointer. +// +// ^SIDE-EFFECTS: +// None. +// +// ^RETURN-VALUE: +// NULL if no match is found, +// otherwise a pointer to the matching option-spec. +// +// ^ALGORITHM: +// foreach option-spec +// - see if "opt" is a match, if so return option-spec +// end-for +// ^^------------------------------------------------------------------------- +const char *Options::match_opt(char opt, int ignore_case) const +{ + if ((optvec == NULL) || (!*optvec)) + return NULLSTR; + + for (const char *const *optv = optvec; *optv; optv++) + { + OptionSpec optspec = *optv; + char optchar = optspec.OptChar(); + if (isNullOpt(optchar)) + continue; + if (opt == optchar) + { + return optspec; + } + else if (ignore_case && (TOLOWER(opt) == TOLOWER(optchar))) + { + return optspec; + } + } + + return NULLSTR; // not found +} + +// --------------------------------------------------------------------------- +// ^FUNCTION: Options::match_longopt - match a long-option +// +// ^SYNOPSIS: +// const char * Options::match_longopt(opt, len, ambiguous) +// +// ^PARAMETERS: +// char * opt -- the long-option to match +// int len -- the number of character of "opt" to match +// int & ambiguous -- set by this routine before returning. +// +// ^DESCRIPTION: +// Try to match "opt" against some unique prefix of a long-option +// (case insensitive). +// +// ^REQUIREMENTS: +// - optvec should be non-NULL and terminated by a NULL pointer. +// +// ^SIDE-EFFECTS: +// - *ambiguous is set to '1' if "opt" matches >1 long-option +// (otherwise it is set to 0). +// +// ^RETURN-VALUE: +// NULL if no match is found, +// otherwise a pointer to the matching option-spec. +// +// ^ALGORITHM: +// ambiguous is FALSE +// foreach option-spec +// if we have an EXACT-MATCH, return the option-spec +// if we have a partial-match then +// if we already had a previous partial match then +// set ambiguous = TRUE and return NULL +// else +// remember this options spec and continue matching +// end-if +// end-if +// end-for +// if we had exactly 1 partial match return it, else return NULL +// ^^------------------------------------------------------------------------- +const char *Options::match_longopt(const char *opt, int len, int &ambiguous) const +{ + kwdmatch_t result; + const char *matched = NULLSTR; + + ambiguous = 0; + if ((optvec == NULL) || (!*optvec)) + return NULLSTR; + + for (const char *const *optv = optvec; *optv; optv++) + { + OptionSpec optspec = *optv; + const char *longopt = optspec.LongOpt(); + if (longopt == NULL) + continue; + result = kwdmatch(longopt, opt, len); + if (result == EXACT_MATCH) + { + return optspec; + } + else if (result == PARTIAL_MATCH) + { + if (matched) + { + ++ambiguous; + return NULLSTR; + } + else + { + matched = optspec; + } + } + } // for + + return matched; +} + +// --------------------------------------------------------------------------- +// ^FUNCTION: Options::parse_opt - parse an option +// +// ^SYNOPSIS: +// int Options::parse_opt(iter, optarg) +// +// ^PARAMETERS: +// OptIter & iter -- option iterator +// const char * & optarg -- where to store any option-argument +// +// ^DESCRIPTION: +// Parse the next option in iter (advancing as necessary). +// Make sure we update the nextchar pointer along the way. Any option +// we find should be returned and optarg should point to its argument. +// +// ^REQUIREMENTS: +// - nextchar must point to the prospective option character +// +// ^SIDE-EFFECTS: +// - iter is advanced when an argument completely parsed +// - optarg is modified to point to any option argument +// - if Options::QUIET is not set, error messages are printed on cerr +// +// ^RETURN-VALUE: +// 'c' if the -c option was matched (optarg points to its argument) +// BADCHAR if the option is invalid (optarg points to the bad +// option-character). +// +// ^ALGORITHM: +// It gets complicated -- follow the comments in the source. +// ^^------------------------------------------------------------------------- +int Options::parse_opt(OptIter &iter, const char *&optarg) +{ + listopt = NULLSTR; // reset the list pointer + + if ((optvec == NULL) || (!*optvec)) + return Options::ENDOPTS; + + // Try to match a known option + OptionSpec optspec = match_opt(*(nextchar++), (optctrls & Options::ANYCASE)); + + // Check for an unknown option + if (optspec.isNULL()) + { + // See if this was a long-option in disguise + if (!(optctrls & Options::NOGUESSING)) + { + unsigned save_ctrls = optctrls; + const char *save_nextchar = nextchar; + nextchar -= 1; + optctrls |= (Options::QUIET | Options::NOGUESSING); + int optchar = parse_longopt(iter, optarg); + optctrls = save_ctrls; + if (optchar > 0) + { + return optchar; + } + else + { + nextchar = save_nextchar; + } + } + if (!(optctrls & Options::QUIET)) + { + cerr << cmdname << ": unknown option -" << *(nextchar - 1) << "." << endl; + } + optarg = (nextchar - 1); // record the bad option in optarg + return Options::BADCHAR; + } + + // If no argument is taken, then leave now + if (optspec.isNoArg()) + { + optarg = NULLSTR; + return optspec.OptChar(); + } + + // Check for argument in this arg + if (*nextchar) + { + optarg = nextchar; // the argument is right here + nextchar = NULLSTR; // we've exhausted this arg + if (optspec.isList()) + listopt = optspec; // save the list-spec + return optspec.OptChar(); + } + + // Check for argument in next arg + const char *nextarg = iter.curr(); + if ((nextarg != NULL) && (optspec.isValRequired() || (!isOption(optctrls, nextarg)))) + { + optarg = nextarg; // the argument is here + iter.next(); // end of arg - advance + if (optspec.isList()) + listopt = optspec; // save the list-spec + return optspec.OptChar(); + } + + // No argument given - if its required, thats an error + optarg = NULLSTR; + if (optspec.isValRequired() && !(optctrls & Options::QUIET)) + { + cerr << cmdname << ": argument required for -" << optspec.OptChar() << " option." << endl; + } + return optspec.OptChar(); +} + +// --------------------------------------------------------------------------- +// ^FUNCTION: Options::parse_longopt - parse a long-option +// +// ^SYNOPSIS: +// int Options::parse_longopt(iter, optarg) +// +// ^PARAMETERS: +// OptIter & iter -- option iterator +// const char * & optarg -- where to store any option-argument +// +// ^DESCRIPTION: +// Parse the next long-option in iter (advancing as necessary). +// Make sure we update the nextchar pointer along the way. Any option +// we find should be returned and optarg should point to its argument. +// +// ^REQUIREMENTS: +// - nextchar must point to the prospective option character +// +// ^SIDE-EFFECTS: +// - iter is advanced when an argument completely parsed +// - optarg is modified to point to any option argument +// - if Options::QUIET is not set, error messages are printed on cerr +// +// ^RETURN-VALUE: +// 'c' if the the long-option corresponding to the -c option was matched +// (optarg points to its argument) +// BADKWD if the option is invalid (optarg points to the bad long-option +// name). +// +// ^ALGORITHM: +// It gets complicated -- follow the comments in the source. +// ^^------------------------------------------------------------------------- +int Options::parse_longopt(OptIter &iter, const char *&optarg) +{ + int len = 0, ambiguous = 0; + + listopt = NULLSTR; // reset the list-spec + + if ((optvec == NULL) || (!*optvec)) + return Options::ENDOPTS; + + // if a value is supplied in this argv element, get it now + const char *val = strpbrk(nextchar, ":="); + if (val) + { + len = (int)(val - nextchar); + ++val; + } + + // Try to match a known long-option + OptionSpec optspec = match_longopt(nextchar, len, ambiguous); + + // Check for an unknown long-option + if (optspec.isNULL()) + { + // See if this was a short-option in disguise + if ((!ambiguous) && (!(optctrls & Options::NOGUESSING))) + { + unsigned save_ctrls = optctrls; + const char *save_nextchar = nextchar; + optctrls |= (Options::QUIET | Options::NOGUESSING); + int optchar = parse_opt(iter, optarg); + optctrls = save_ctrls; + if (optchar > 0) + { + return optchar; + } + else + { + nextchar = save_nextchar; + } + } + if (!(optctrls & Options::QUIET)) + { + cerr << cmdname << ": " << ((ambiguous) ? "ambiguous" : "unknown") << " option " + << ((optctrls & Options::LONG_ONLY) ? "-" : "--") << nextchar << "." << endl; + } + optarg = nextchar; // record the bad option in optarg + nextchar = NULLSTR; // we've exhausted this argument + return (ambiguous) ? Options::AMBIGUOUS : Options::BADKWD; + } + + // If no argument is taken, then leave now + if (optspec.isNoArg()) + { + if ((val) && !(optctrls & Options::QUIET)) + { + cerr << cmdname << ": option " << ((optctrls & Options::LONG_ONLY) ? "-" : "--") << optspec.LongOpt() + << " does NOT take an argument." << endl; + } + optarg = val; // record the unexpected argument + nextchar = NULLSTR; // we've exhausted this argument + return optspec.OptChar(); + } + + // Check for argument in this arg + if (val) + { + optarg = val; // the argument is right here + nextchar = NULLSTR; // we exhausted the rest of this arg + if (optspec.isList()) + listopt = optspec; // save the list-spec + return optspec.OptChar(); + } + + // Check for argument in next arg + const char *nextarg = iter.curr(); // find the next argument to parse + if ((nextarg != NULL) && (optspec.isValRequired() || (!isOption(optctrls, nextarg)))) + { + optarg = nextarg; // the argument is right here + iter.next(); // end of arg - advance + nextchar = NULLSTR; // we exhausted the rest of this arg + if (optspec.isList()) + listopt = optspec; // save the list-spec + return optspec.OptChar(); + } + + // No argument given - if its required, thats an error + optarg = NULLSTR; + if (optspec.isValRequired() && !(optctrls & Options::QUIET)) + { + const char *longopt = optspec.LongOpt(); + const char *spc = ::strchr(longopt, ' '); + int longopt_len; + if (spc) + { + longopt_len = (int)(spc - longopt); + } + else + { + longopt_len = (int)::strlen(longopt); + } + cerr << cmdname << ": argument required for " << ((optctrls & Options::LONG_ONLY) ? "-" : "--"); + cerr.write(longopt, longopt_len) << " option." << endl; + } + nextchar = NULLSTR; // we exhausted the rest of this arg + return optspec.OptChar(); +} + +// --------------------------------------------------------------------------- +// ^FUNCTION: Options::usage - print usage +// +// ^SYNOPSIS: +// void Options::usage(os, positionals) +// +// ^PARAMETERS: +// ostream & os -- where to print the usage +// char * positionals -- command-line syntax for any positional args +// +// ^DESCRIPTION: +// Print command-usage (using either option or long-option syntax) on os. +// +// ^REQUIREMENTS: +// os should correspond to an open output file. +// +// ^SIDE-EFFECTS: +// Prints on os +// +// ^RETURN-VALUE: +// None. +// +// ^ALGORITHM: +// Print usage on os, wrapping long lines where necessary. +// ^^------------------------------------------------------------------------- +void Options::usage(ostream &os, const char *positionals) const +{ +#ifdef NO_USAGE + return; +#else + const char *const *optv = optvec; + unsigned cols = 79; + int first, nloop; + char buf[256]; + + if ((optv == NULL) || (!*optv)) + return; + + // print first portion "usage: progname" + os << "usage: " << cmdname; + unsigned ll = (unsigned)strlen(cmdname) + 7; + + // save the current length so we know how much space to skip for + // subsequent lines. + // + unsigned margin = ll + 1; + + // print the options and the positional arguments + for (nloop = 0, first = 1; !nloop; optv++, first = 0) + { + unsigned len; + OptionSpec optspec = *optv; + + // figure out how wide this parameter is (for printing) + if (!*optv) + { + len = (unsigned)strlen(positionals); + ++nloop; // terminate this loop + } + else + { + if (optspec.isHiddenOpt()) + continue; + len = optspec.Format(buf, optctrls); + } + + // Will this fit? + if ((ll + len + 1) > (cols - first)) + { + os << endl; // No - start a new line; +#ifdef USE_STDIO + for (int _i_ = 0; _i_ < margin; ++_i_) + os << " "; +#else + os.width(margin); + os << ""; +#endif + ll = margin; + } + else + { + os << " "; // Yes - just throw in a space + ++ll; + } + ll += len; + os << ((nloop) ? positionals : buf); + } // for each ad + + os << endl; +#endif /* NO_USAGE */ +} + +// --------------------------------------------------------------------------- +// ^FUNCTION: Options::operator() - get options from the command-line +// +// ^SYNOPSIS: +// int Options::operator()(iter, optarg) +// +// ^PARAMETERS: +// OptIter & iter -- option iterator +// const char * & optarg -- where to store any option-argument +// +// ^DESCRIPTION: +// Parse the next option in iter (advancing as necessary). +// Make sure we update the nextchar pointer along the way. Any option +// we find should be returned and optarg should point to its argument. +// +// ^REQUIREMENTS: +// None. +// +// ^SIDE-EFFECTS: +// - iter is advanced when an argument is completely parsed +// - optarg is modified to point to any option argument +// - if Options::QUIET is not set, error messages are printed on cerr +// +// ^RETURN-VALUE: +// 0 if all options have been parsed. +// 'c' if the the option or long-option corresponding to the -c option was +// matched (optarg points to its argument). +// BADCHAR if the option is invalid (optarg points to the bad option char). +// BADKWD if the option is invalid (optarg points to the bad long-opt name). +// AMBIGUOUS if an ambiguous keyword name was given (optarg points to the +// ambiguous keyword name). +// POSITIONAL if PARSE_POS was set and the current argument is a positional +// parameter (in which case optarg points to the positional argument). +// +// ^ALGORITHM: +// It gets complicated -- follow the comments in the source. +// ^^------------------------------------------------------------------------- +int Options::operator()(OptIter &iter, const char *&optarg) +{ + int parse_opts_only = isOptsOnly(optctrls); + if (parse_opts_only) + explicit_end = 0; + + // See if we have an option left over from before ... + if ((nextchar) && *nextchar) + { + return parse_opt(iter, optarg); + } + + // Check for end-of-options ... + const char *arg = NULLSTR; + int get_next_arg = 0; + do + { + arg = iter.curr(); + get_next_arg = 0; + if (arg == NULL) + { + listopt = NULLSTR; + return Options::ENDOPTS; + } + else if ((!explicit_end) && isEndOpts(arg)) + { + iter.next(); // advance past end-of-options arg + listopt = NULLSTR; + explicit_end = 1; + if (parse_opts_only) + return Options::ENDOPTS; + get_next_arg = 1; // make sure we look at the next argument. + } + } while (get_next_arg); + + // Do we have a positional arg? + if (explicit_end || (!isOption(optctrls, arg))) + { + if (parse_opts_only) + { + return Options::ENDOPTS; + } + else + { + optarg = arg; // set optarg to the positional argument + iter.next(); // advance iterator to the next argument + return Options::POSITIONAL; + } + } + + iter.next(); // pass the argument that arg already points to + + // See if we have a long option ... + if (!(optctrls & Options::SHORT_ONLY)) + { + if ((*arg == '-') && (arg[1] == '-')) + { + nextchar = arg + 2; + return parse_longopt(iter, optarg); + } + else if ((optctrls & Options::PLUS) && (*arg == '+')) + { + nextchar = arg + 1; + return parse_longopt(iter, optarg); + } + } + if (*arg == '-') + { + nextchar = arg + 1; + if (optctrls & Options::LONG_ONLY) + { + return parse_longopt(iter, optarg); + } + else + { + return parse_opt(iter, optarg); + } + } + + // If we get here - it is because we have a list value + OptionSpec optspec = listopt; + optarg = arg; // record the list value + return optspec.OptChar(); +} diff --git a/src/blfwk/src/rijndael.cpp b/src/blfwk/src/rijndael.cpp new file mode 100644 index 0000000..96192fd --- /dev/null +++ b/src/blfwk/src/rijndael.cpp @@ -0,0 +1,1410 @@ +// +// File : rijndael.cpp +// Creation date : Sun Nov 5 2000 03:22:10 CEST +// Author : Szymon Stefanek (stefanek@tin.it) +// +// Another implementation of the Rijndael cipher. +// This is intended to be an easily usable library file. +// This code is public domain. +// Based on the Vincent Rijmen and K.U.Leuven implementation 2.4. +// + +// +// Original Copyright notice: +// +// rijndael-alg-fst.c v2.4 April '2000 +// rijndael-alg-fst.h +// rijndael-api-fst.c +// rijndael-api-fst.h +// +// Optimised ANSI C code +// +// authors: v1.0: Antoon Bosselaers +// v2.0: Vincent Rijmen, K.U.Leuven +// v2.3: Paulo Barreto +// v2.4: Vincent Rijmen, K.U.Leuven +// +// This code is placed in the public domain. +// + +// +// This implementation works on 128 , 192 , 256 bit keys +// and on 128 bit blocks +// + +#define _RIJNDAEL_CPP_ + +#include "blfwk/rijndael.h" + +#include +#include +#include + +static uint8_t S[256] = { 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, + 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, + 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, + 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, + 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, + 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, + 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, + 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, + 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, + 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, + 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, + 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, + 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, + 104, 65, 153, 45, 15, 176, 84, 187, 22 }; + +static uint8_t T1[256][4] = { + 0xc6, 0x63, 0x63, 0xa5, 0xf8, 0x7c, 0x7c, 0x84, 0xee, 0x77, 0x77, 0x99, 0xf6, 0x7b, 0x7b, 0x8d, 0xff, 0xf2, 0xf2, + 0x0d, 0xd6, 0x6b, 0x6b, 0xbd, 0xde, 0x6f, 0x6f, 0xb1, 0x91, 0xc5, 0xc5, 0x54, 0x60, 0x30, 0x30, 0x50, 0x02, 0x01, + 0x01, 0x03, 0xce, 0x67, 0x67, 0xa9, 0x56, 0x2b, 0x2b, 0x7d, 0xe7, 0xfe, 0xfe, 0x19, 0xb5, 0xd7, 0xd7, 0x62, 0x4d, + 0xab, 0xab, 0xe6, 0xec, 0x76, 0x76, 0x9a, 0x8f, 0xca, 0xca, 0x45, 0x1f, 0x82, 0x82, 0x9d, 0x89, 0xc9, 0xc9, 0x40, + 0xfa, 0x7d, 0x7d, 0x87, 0xef, 0xfa, 0xfa, 0x15, 0xb2, 0x59, 0x59, 0xeb, 0x8e, 0x47, 0x47, 0xc9, 0xfb, 0xf0, 0xf0, + 0x0b, 0x41, 0xad, 0xad, 0xec, 0xb3, 0xd4, 0xd4, 0x67, 0x5f, 0xa2, 0xa2, 0xfd, 0x45, 0xaf, 0xaf, 0xea, 0x23, 0x9c, + 0x9c, 0xbf, 0x53, 0xa4, 0xa4, 0xf7, 0xe4, 0x72, 0x72, 0x96, 0x9b, 0xc0, 0xc0, 0x5b, 0x75, 0xb7, 0xb7, 0xc2, 0xe1, + 0xfd, 0xfd, 0x1c, 0x3d, 0x93, 0x93, 0xae, 0x4c, 0x26, 0x26, 0x6a, 0x6c, 0x36, 0x36, 0x5a, 0x7e, 0x3f, 0x3f, 0x41, + 0xf5, 0xf7, 0xf7, 0x02, 0x83, 0xcc, 0xcc, 0x4f, 0x68, 0x34, 0x34, 0x5c, 0x51, 0xa5, 0xa5, 0xf4, 0xd1, 0xe5, 0xe5, + 0x34, 0xf9, 0xf1, 0xf1, 0x08, 0xe2, 0x71, 0x71, 0x93, 0xab, 0xd8, 0xd8, 0x73, 0x62, 0x31, 0x31, 0x53, 0x2a, 0x15, + 0x15, 0x3f, 0x08, 0x04, 0x04, 0x0c, 0x95, 0xc7, 0xc7, 0x52, 0x46, 0x23, 0x23, 0x65, 0x9d, 0xc3, 0xc3, 0x5e, 0x30, + 0x18, 0x18, 0x28, 0x37, 0x96, 0x96, 0xa1, 0x0a, 0x05, 0x05, 0x0f, 0x2f, 0x9a, 0x9a, 0xb5, 0x0e, 0x07, 0x07, 0x09, + 0x24, 0x12, 0x12, 0x36, 0x1b, 0x80, 0x80, 0x9b, 0xdf, 0xe2, 0xe2, 0x3d, 0xcd, 0xeb, 0xeb, 0x26, 0x4e, 0x27, 0x27, + 0x69, 0x7f, 0xb2, 0xb2, 0xcd, 0xea, 0x75, 0x75, 0x9f, 0x12, 0x09, 0x09, 0x1b, 0x1d, 0x83, 0x83, 0x9e, 0x58, 0x2c, + 0x2c, 0x74, 0x34, 0x1a, 0x1a, 0x2e, 0x36, 0x1b, 0x1b, 0x2d, 0xdc, 0x6e, 0x6e, 0xb2, 0xb4, 0x5a, 0x5a, 0xee, 0x5b, + 0xa0, 0xa0, 0xfb, 0xa4, 0x52, 0x52, 0xf6, 0x76, 0x3b, 0x3b, 0x4d, 0xb7, 0xd6, 0xd6, 0x61, 0x7d, 0xb3, 0xb3, 0xce, + 0x52, 0x29, 0x29, 0x7b, 0xdd, 0xe3, 0xe3, 0x3e, 0x5e, 0x2f, 0x2f, 0x71, 0x13, 0x84, 0x84, 0x97, 0xa6, 0x53, 0x53, + 0xf5, 0xb9, 0xd1, 0xd1, 0x68, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xed, 0xed, 0x2c, 0x40, 0x20, 0x20, 0x60, 0xe3, 0xfc, + 0xfc, 0x1f, 0x79, 0xb1, 0xb1, 0xc8, 0xb6, 0x5b, 0x5b, 0xed, 0xd4, 0x6a, 0x6a, 0xbe, 0x8d, 0xcb, 0xcb, 0x46, 0x67, + 0xbe, 0xbe, 0xd9, 0x72, 0x39, 0x39, 0x4b, 0x94, 0x4a, 0x4a, 0xde, 0x98, 0x4c, 0x4c, 0xd4, 0xb0, 0x58, 0x58, 0xe8, + 0x85, 0xcf, 0xcf, 0x4a, 0xbb, 0xd0, 0xd0, 0x6b, 0xc5, 0xef, 0xef, 0x2a, 0x4f, 0xaa, 0xaa, 0xe5, 0xed, 0xfb, 0xfb, + 0x16, 0x86, 0x43, 0x43, 0xc5, 0x9a, 0x4d, 0x4d, 0xd7, 0x66, 0x33, 0x33, 0x55, 0x11, 0x85, 0x85, 0x94, 0x8a, 0x45, + 0x45, 0xcf, 0xe9, 0xf9, 0xf9, 0x10, 0x04, 0x02, 0x02, 0x06, 0xfe, 0x7f, 0x7f, 0x81, 0xa0, 0x50, 0x50, 0xf0, 0x78, + 0x3c, 0x3c, 0x44, 0x25, 0x9f, 0x9f, 0xba, 0x4b, 0xa8, 0xa8, 0xe3, 0xa2, 0x51, 0x51, 0xf3, 0x5d, 0xa3, 0xa3, 0xfe, + 0x80, 0x40, 0x40, 0xc0, 0x05, 0x8f, 0x8f, 0x8a, 0x3f, 0x92, 0x92, 0xad, 0x21, 0x9d, 0x9d, 0xbc, 0x70, 0x38, 0x38, + 0x48, 0xf1, 0xf5, 0xf5, 0x04, 0x63, 0xbc, 0xbc, 0xdf, 0x77, 0xb6, 0xb6, 0xc1, 0xaf, 0xda, 0xda, 0x75, 0x42, 0x21, + 0x21, 0x63, 0x20, 0x10, 0x10, 0x30, 0xe5, 0xff, 0xff, 0x1a, 0xfd, 0xf3, 0xf3, 0x0e, 0xbf, 0xd2, 0xd2, 0x6d, 0x81, + 0xcd, 0xcd, 0x4c, 0x18, 0x0c, 0x0c, 0x14, 0x26, 0x13, 0x13, 0x35, 0xc3, 0xec, 0xec, 0x2f, 0xbe, 0x5f, 0x5f, 0xe1, + 0x35, 0x97, 0x97, 0xa2, 0x88, 0x44, 0x44, 0xcc, 0x2e, 0x17, 0x17, 0x39, 0x93, 0xc4, 0xc4, 0x57, 0x55, 0xa7, 0xa7, + 0xf2, 0xfc, 0x7e, 0x7e, 0x82, 0x7a, 0x3d, 0x3d, 0x47, 0xc8, 0x64, 0x64, 0xac, 0xba, 0x5d, 0x5d, 0xe7, 0x32, 0x19, + 0x19, 0x2b, 0xe6, 0x73, 0x73, 0x95, 0xc0, 0x60, 0x60, 0xa0, 0x19, 0x81, 0x81, 0x98, 0x9e, 0x4f, 0x4f, 0xd1, 0xa3, + 0xdc, 0xdc, 0x7f, 0x44, 0x22, 0x22, 0x66, 0x54, 0x2a, 0x2a, 0x7e, 0x3b, 0x90, 0x90, 0xab, 0x0b, 0x88, 0x88, 0x83, + 0x8c, 0x46, 0x46, 0xca, 0xc7, 0xee, 0xee, 0x29, 0x6b, 0xb8, 0xb8, 0xd3, 0x28, 0x14, 0x14, 0x3c, 0xa7, 0xde, 0xde, + 0x79, 0xbc, 0x5e, 0x5e, 0xe2, 0x16, 0x0b, 0x0b, 0x1d, 0xad, 0xdb, 0xdb, 0x76, 0xdb, 0xe0, 0xe0, 0x3b, 0x64, 0x32, + 0x32, 0x56, 0x74, 0x3a, 0x3a, 0x4e, 0x14, 0x0a, 0x0a, 0x1e, 0x92, 0x49, 0x49, 0xdb, 0x0c, 0x06, 0x06, 0x0a, 0x48, + 0x24, 0x24, 0x6c, 0xb8, 0x5c, 0x5c, 0xe4, 0x9f, 0xc2, 0xc2, 0x5d, 0xbd, 0xd3, 0xd3, 0x6e, 0x43, 0xac, 0xac, 0xef, + 0xc4, 0x62, 0x62, 0xa6, 0x39, 0x91, 0x91, 0xa8, 0x31, 0x95, 0x95, 0xa4, 0xd3, 0xe4, 0xe4, 0x37, 0xf2, 0x79, 0x79, + 0x8b, 0xd5, 0xe7, 0xe7, 0x32, 0x8b, 0xc8, 0xc8, 0x43, 0x6e, 0x37, 0x37, 0x59, 0xda, 0x6d, 0x6d, 0xb7, 0x01, 0x8d, + 0x8d, 0x8c, 0xb1, 0xd5, 0xd5, 0x64, 0x9c, 0x4e, 0x4e, 0xd2, 0x49, 0xa9, 0xa9, 0xe0, 0xd8, 0x6c, 0x6c, 0xb4, 0xac, + 0x56, 0x56, 0xfa, 0xf3, 0xf4, 0xf4, 0x07, 0xcf, 0xea, 0xea, 0x25, 0xca, 0x65, 0x65, 0xaf, 0xf4, 0x7a, 0x7a, 0x8e, + 0x47, 0xae, 0xae, 0xe9, 0x10, 0x08, 0x08, 0x18, 0x6f, 0xba, 0xba, 0xd5, 0xf0, 0x78, 0x78, 0x88, 0x4a, 0x25, 0x25, + 0x6f, 0x5c, 0x2e, 0x2e, 0x72, 0x38, 0x1c, 0x1c, 0x24, 0x57, 0xa6, 0xa6, 0xf1, 0x73, 0xb4, 0xb4, 0xc7, 0x97, 0xc6, + 0xc6, 0x51, 0xcb, 0xe8, 0xe8, 0x23, 0xa1, 0xdd, 0xdd, 0x7c, 0xe8, 0x74, 0x74, 0x9c, 0x3e, 0x1f, 0x1f, 0x21, 0x96, + 0x4b, 0x4b, 0xdd, 0x61, 0xbd, 0xbd, 0xdc, 0x0d, 0x8b, 0x8b, 0x86, 0x0f, 0x8a, 0x8a, 0x85, 0xe0, 0x70, 0x70, 0x90, + 0x7c, 0x3e, 0x3e, 0x42, 0x71, 0xb5, 0xb5, 0xc4, 0xcc, 0x66, 0x66, 0xaa, 0x90, 0x48, 0x48, 0xd8, 0x06, 0x03, 0x03, + 0x05, 0xf7, 0xf6, 0xf6, 0x01, 0x1c, 0x0e, 0x0e, 0x12, 0xc2, 0x61, 0x61, 0xa3, 0x6a, 0x35, 0x35, 0x5f, 0xae, 0x57, + 0x57, 0xf9, 0x69, 0xb9, 0xb9, 0xd0, 0x17, 0x86, 0x86, 0x91, 0x99, 0xc1, 0xc1, 0x58, 0x3a, 0x1d, 0x1d, 0x27, 0x27, + 0x9e, 0x9e, 0xb9, 0xd9, 0xe1, 0xe1, 0x38, 0xeb, 0xf8, 0xf8, 0x13, 0x2b, 0x98, 0x98, 0xb3, 0x22, 0x11, 0x11, 0x33, + 0xd2, 0x69, 0x69, 0xbb, 0xa9, 0xd9, 0xd9, 0x70, 0x07, 0x8e, 0x8e, 0x89, 0x33, 0x94, 0x94, 0xa7, 0x2d, 0x9b, 0x9b, + 0xb6, 0x3c, 0x1e, 0x1e, 0x22, 0x15, 0x87, 0x87, 0x92, 0xc9, 0xe9, 0xe9, 0x20, 0x87, 0xce, 0xce, 0x49, 0xaa, 0x55, + 0x55, 0xff, 0x50, 0x28, 0x28, 0x78, 0xa5, 0xdf, 0xdf, 0x7a, 0x03, 0x8c, 0x8c, 0x8f, 0x59, 0xa1, 0xa1, 0xf8, 0x09, + 0x89, 0x89, 0x80, 0x1a, 0x0d, 0x0d, 0x17, 0x65, 0xbf, 0xbf, 0xda, 0xd7, 0xe6, 0xe6, 0x31, 0x84, 0x42, 0x42, 0xc6, + 0xd0, 0x68, 0x68, 0xb8, 0x82, 0x41, 0x41, 0xc3, 0x29, 0x99, 0x99, 0xb0, 0x5a, 0x2d, 0x2d, 0x77, 0x1e, 0x0f, 0x0f, + 0x11, 0x7b, 0xb0, 0xb0, 0xcb, 0xa8, 0x54, 0x54, 0xfc, 0x6d, 0xbb, 0xbb, 0xd6, 0x2c, 0x16, 0x16, 0x3a +}; + +static uint8_t T2[256][4] = { + 0xa5, 0xc6, 0x63, 0x63, 0x84, 0xf8, 0x7c, 0x7c, 0x99, 0xee, 0x77, 0x77, 0x8d, 0xf6, 0x7b, 0x7b, 0x0d, 0xff, 0xf2, + 0xf2, 0xbd, 0xd6, 0x6b, 0x6b, 0xb1, 0xde, 0x6f, 0x6f, 0x54, 0x91, 0xc5, 0xc5, 0x50, 0x60, 0x30, 0x30, 0x03, 0x02, + 0x01, 0x01, 0xa9, 0xce, 0x67, 0x67, 0x7d, 0x56, 0x2b, 0x2b, 0x19, 0xe7, 0xfe, 0xfe, 0x62, 0xb5, 0xd7, 0xd7, 0xe6, + 0x4d, 0xab, 0xab, 0x9a, 0xec, 0x76, 0x76, 0x45, 0x8f, 0xca, 0xca, 0x9d, 0x1f, 0x82, 0x82, 0x40, 0x89, 0xc9, 0xc9, + 0x87, 0xfa, 0x7d, 0x7d, 0x15, 0xef, 0xfa, 0xfa, 0xeb, 0xb2, 0x59, 0x59, 0xc9, 0x8e, 0x47, 0x47, 0x0b, 0xfb, 0xf0, + 0xf0, 0xec, 0x41, 0xad, 0xad, 0x67, 0xb3, 0xd4, 0xd4, 0xfd, 0x5f, 0xa2, 0xa2, 0xea, 0x45, 0xaf, 0xaf, 0xbf, 0x23, + 0x9c, 0x9c, 0xf7, 0x53, 0xa4, 0xa4, 0x96, 0xe4, 0x72, 0x72, 0x5b, 0x9b, 0xc0, 0xc0, 0xc2, 0x75, 0xb7, 0xb7, 0x1c, + 0xe1, 0xfd, 0xfd, 0xae, 0x3d, 0x93, 0x93, 0x6a, 0x4c, 0x26, 0x26, 0x5a, 0x6c, 0x36, 0x36, 0x41, 0x7e, 0x3f, 0x3f, + 0x02, 0xf5, 0xf7, 0xf7, 0x4f, 0x83, 0xcc, 0xcc, 0x5c, 0x68, 0x34, 0x34, 0xf4, 0x51, 0xa5, 0xa5, 0x34, 0xd1, 0xe5, + 0xe5, 0x08, 0xf9, 0xf1, 0xf1, 0x93, 0xe2, 0x71, 0x71, 0x73, 0xab, 0xd8, 0xd8, 0x53, 0x62, 0x31, 0x31, 0x3f, 0x2a, + 0x15, 0x15, 0x0c, 0x08, 0x04, 0x04, 0x52, 0x95, 0xc7, 0xc7, 0x65, 0x46, 0x23, 0x23, 0x5e, 0x9d, 0xc3, 0xc3, 0x28, + 0x30, 0x18, 0x18, 0xa1, 0x37, 0x96, 0x96, 0x0f, 0x0a, 0x05, 0x05, 0xb5, 0x2f, 0x9a, 0x9a, 0x09, 0x0e, 0x07, 0x07, + 0x36, 0x24, 0x12, 0x12, 0x9b, 0x1b, 0x80, 0x80, 0x3d, 0xdf, 0xe2, 0xe2, 0x26, 0xcd, 0xeb, 0xeb, 0x69, 0x4e, 0x27, + 0x27, 0xcd, 0x7f, 0xb2, 0xb2, 0x9f, 0xea, 0x75, 0x75, 0x1b, 0x12, 0x09, 0x09, 0x9e, 0x1d, 0x83, 0x83, 0x74, 0x58, + 0x2c, 0x2c, 0x2e, 0x34, 0x1a, 0x1a, 0x2d, 0x36, 0x1b, 0x1b, 0xb2, 0xdc, 0x6e, 0x6e, 0xee, 0xb4, 0x5a, 0x5a, 0xfb, + 0x5b, 0xa0, 0xa0, 0xf6, 0xa4, 0x52, 0x52, 0x4d, 0x76, 0x3b, 0x3b, 0x61, 0xb7, 0xd6, 0xd6, 0xce, 0x7d, 0xb3, 0xb3, + 0x7b, 0x52, 0x29, 0x29, 0x3e, 0xdd, 0xe3, 0xe3, 0x71, 0x5e, 0x2f, 0x2f, 0x97, 0x13, 0x84, 0x84, 0xf5, 0xa6, 0x53, + 0x53, 0x68, 0xb9, 0xd1, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xc1, 0xed, 0xed, 0x60, 0x40, 0x20, 0x20, 0x1f, 0xe3, + 0xfc, 0xfc, 0xc8, 0x79, 0xb1, 0xb1, 0xed, 0xb6, 0x5b, 0x5b, 0xbe, 0xd4, 0x6a, 0x6a, 0x46, 0x8d, 0xcb, 0xcb, 0xd9, + 0x67, 0xbe, 0xbe, 0x4b, 0x72, 0x39, 0x39, 0xde, 0x94, 0x4a, 0x4a, 0xd4, 0x98, 0x4c, 0x4c, 0xe8, 0xb0, 0x58, 0x58, + 0x4a, 0x85, 0xcf, 0xcf, 0x6b, 0xbb, 0xd0, 0xd0, 0x2a, 0xc5, 0xef, 0xef, 0xe5, 0x4f, 0xaa, 0xaa, 0x16, 0xed, 0xfb, + 0xfb, 0xc5, 0x86, 0x43, 0x43, 0xd7, 0x9a, 0x4d, 0x4d, 0x55, 0x66, 0x33, 0x33, 0x94, 0x11, 0x85, 0x85, 0xcf, 0x8a, + 0x45, 0x45, 0x10, 0xe9, 0xf9, 0xf9, 0x06, 0x04, 0x02, 0x02, 0x81, 0xfe, 0x7f, 0x7f, 0xf0, 0xa0, 0x50, 0x50, 0x44, + 0x78, 0x3c, 0x3c, 0xba, 0x25, 0x9f, 0x9f, 0xe3, 0x4b, 0xa8, 0xa8, 0xf3, 0xa2, 0x51, 0x51, 0xfe, 0x5d, 0xa3, 0xa3, + 0xc0, 0x80, 0x40, 0x40, 0x8a, 0x05, 0x8f, 0x8f, 0xad, 0x3f, 0x92, 0x92, 0xbc, 0x21, 0x9d, 0x9d, 0x48, 0x70, 0x38, + 0x38, 0x04, 0xf1, 0xf5, 0xf5, 0xdf, 0x63, 0xbc, 0xbc, 0xc1, 0x77, 0xb6, 0xb6, 0x75, 0xaf, 0xda, 0xda, 0x63, 0x42, + 0x21, 0x21, 0x30, 0x20, 0x10, 0x10, 0x1a, 0xe5, 0xff, 0xff, 0x0e, 0xfd, 0xf3, 0xf3, 0x6d, 0xbf, 0xd2, 0xd2, 0x4c, + 0x81, 0xcd, 0xcd, 0x14, 0x18, 0x0c, 0x0c, 0x35, 0x26, 0x13, 0x13, 0x2f, 0xc3, 0xec, 0xec, 0xe1, 0xbe, 0x5f, 0x5f, + 0xa2, 0x35, 0x97, 0x97, 0xcc, 0x88, 0x44, 0x44, 0x39, 0x2e, 0x17, 0x17, 0x57, 0x93, 0xc4, 0xc4, 0xf2, 0x55, 0xa7, + 0xa7, 0x82, 0xfc, 0x7e, 0x7e, 0x47, 0x7a, 0x3d, 0x3d, 0xac, 0xc8, 0x64, 0x64, 0xe7, 0xba, 0x5d, 0x5d, 0x2b, 0x32, + 0x19, 0x19, 0x95, 0xe6, 0x73, 0x73, 0xa0, 0xc0, 0x60, 0x60, 0x98, 0x19, 0x81, 0x81, 0xd1, 0x9e, 0x4f, 0x4f, 0x7f, + 0xa3, 0xdc, 0xdc, 0x66, 0x44, 0x22, 0x22, 0x7e, 0x54, 0x2a, 0x2a, 0xab, 0x3b, 0x90, 0x90, 0x83, 0x0b, 0x88, 0x88, + 0xca, 0x8c, 0x46, 0x46, 0x29, 0xc7, 0xee, 0xee, 0xd3, 0x6b, 0xb8, 0xb8, 0x3c, 0x28, 0x14, 0x14, 0x79, 0xa7, 0xde, + 0xde, 0xe2, 0xbc, 0x5e, 0x5e, 0x1d, 0x16, 0x0b, 0x0b, 0x76, 0xad, 0xdb, 0xdb, 0x3b, 0xdb, 0xe0, 0xe0, 0x56, 0x64, + 0x32, 0x32, 0x4e, 0x74, 0x3a, 0x3a, 0x1e, 0x14, 0x0a, 0x0a, 0xdb, 0x92, 0x49, 0x49, 0x0a, 0x0c, 0x06, 0x06, 0x6c, + 0x48, 0x24, 0x24, 0xe4, 0xb8, 0x5c, 0x5c, 0x5d, 0x9f, 0xc2, 0xc2, 0x6e, 0xbd, 0xd3, 0xd3, 0xef, 0x43, 0xac, 0xac, + 0xa6, 0xc4, 0x62, 0x62, 0xa8, 0x39, 0x91, 0x91, 0xa4, 0x31, 0x95, 0x95, 0x37, 0xd3, 0xe4, 0xe4, 0x8b, 0xf2, 0x79, + 0x79, 0x32, 0xd5, 0xe7, 0xe7, 0x43, 0x8b, 0xc8, 0xc8, 0x59, 0x6e, 0x37, 0x37, 0xb7, 0xda, 0x6d, 0x6d, 0x8c, 0x01, + 0x8d, 0x8d, 0x64, 0xb1, 0xd5, 0xd5, 0xd2, 0x9c, 0x4e, 0x4e, 0xe0, 0x49, 0xa9, 0xa9, 0xb4, 0xd8, 0x6c, 0x6c, 0xfa, + 0xac, 0x56, 0x56, 0x07, 0xf3, 0xf4, 0xf4, 0x25, 0xcf, 0xea, 0xea, 0xaf, 0xca, 0x65, 0x65, 0x8e, 0xf4, 0x7a, 0x7a, + 0xe9, 0x47, 0xae, 0xae, 0x18, 0x10, 0x08, 0x08, 0xd5, 0x6f, 0xba, 0xba, 0x88, 0xf0, 0x78, 0x78, 0x6f, 0x4a, 0x25, + 0x25, 0x72, 0x5c, 0x2e, 0x2e, 0x24, 0x38, 0x1c, 0x1c, 0xf1, 0x57, 0xa6, 0xa6, 0xc7, 0x73, 0xb4, 0xb4, 0x51, 0x97, + 0xc6, 0xc6, 0x23, 0xcb, 0xe8, 0xe8, 0x7c, 0xa1, 0xdd, 0xdd, 0x9c, 0xe8, 0x74, 0x74, 0x21, 0x3e, 0x1f, 0x1f, 0xdd, + 0x96, 0x4b, 0x4b, 0xdc, 0x61, 0xbd, 0xbd, 0x86, 0x0d, 0x8b, 0x8b, 0x85, 0x0f, 0x8a, 0x8a, 0x90, 0xe0, 0x70, 0x70, + 0x42, 0x7c, 0x3e, 0x3e, 0xc4, 0x71, 0xb5, 0xb5, 0xaa, 0xcc, 0x66, 0x66, 0xd8, 0x90, 0x48, 0x48, 0x05, 0x06, 0x03, + 0x03, 0x01, 0xf7, 0xf6, 0xf6, 0x12, 0x1c, 0x0e, 0x0e, 0xa3, 0xc2, 0x61, 0x61, 0x5f, 0x6a, 0x35, 0x35, 0xf9, 0xae, + 0x57, 0x57, 0xd0, 0x69, 0xb9, 0xb9, 0x91, 0x17, 0x86, 0x86, 0x58, 0x99, 0xc1, 0xc1, 0x27, 0x3a, 0x1d, 0x1d, 0xb9, + 0x27, 0x9e, 0x9e, 0x38, 0xd9, 0xe1, 0xe1, 0x13, 0xeb, 0xf8, 0xf8, 0xb3, 0x2b, 0x98, 0x98, 0x33, 0x22, 0x11, 0x11, + 0xbb, 0xd2, 0x69, 0x69, 0x70, 0xa9, 0xd9, 0xd9, 0x89, 0x07, 0x8e, 0x8e, 0xa7, 0x33, 0x94, 0x94, 0xb6, 0x2d, 0x9b, + 0x9b, 0x22, 0x3c, 0x1e, 0x1e, 0x92, 0x15, 0x87, 0x87, 0x20, 0xc9, 0xe9, 0xe9, 0x49, 0x87, 0xce, 0xce, 0xff, 0xaa, + 0x55, 0x55, 0x78, 0x50, 0x28, 0x28, 0x7a, 0xa5, 0xdf, 0xdf, 0x8f, 0x03, 0x8c, 0x8c, 0xf8, 0x59, 0xa1, 0xa1, 0x80, + 0x09, 0x89, 0x89, 0x17, 0x1a, 0x0d, 0x0d, 0xda, 0x65, 0xbf, 0xbf, 0x31, 0xd7, 0xe6, 0xe6, 0xc6, 0x84, 0x42, 0x42, + 0xb8, 0xd0, 0x68, 0x68, 0xc3, 0x82, 0x41, 0x41, 0xb0, 0x29, 0x99, 0x99, 0x77, 0x5a, 0x2d, 0x2d, 0x11, 0x1e, 0x0f, + 0x0f, 0xcb, 0x7b, 0xb0, 0xb0, 0xfc, 0xa8, 0x54, 0x54, 0xd6, 0x6d, 0xbb, 0xbb, 0x3a, 0x2c, 0x16, 0x16 +}; + +static uint8_t T3[256][4] = { + 0x63, 0xa5, 0xc6, 0x63, 0x7c, 0x84, 0xf8, 0x7c, 0x77, 0x99, 0xee, 0x77, 0x7b, 0x8d, 0xf6, 0x7b, 0xf2, 0x0d, 0xff, + 0xf2, 0x6b, 0xbd, 0xd6, 0x6b, 0x6f, 0xb1, 0xde, 0x6f, 0xc5, 0x54, 0x91, 0xc5, 0x30, 0x50, 0x60, 0x30, 0x01, 0x03, + 0x02, 0x01, 0x67, 0xa9, 0xce, 0x67, 0x2b, 0x7d, 0x56, 0x2b, 0xfe, 0x19, 0xe7, 0xfe, 0xd7, 0x62, 0xb5, 0xd7, 0xab, + 0xe6, 0x4d, 0xab, 0x76, 0x9a, 0xec, 0x76, 0xca, 0x45, 0x8f, 0xca, 0x82, 0x9d, 0x1f, 0x82, 0xc9, 0x40, 0x89, 0xc9, + 0x7d, 0x87, 0xfa, 0x7d, 0xfa, 0x15, 0xef, 0xfa, 0x59, 0xeb, 0xb2, 0x59, 0x47, 0xc9, 0x8e, 0x47, 0xf0, 0x0b, 0xfb, + 0xf0, 0xad, 0xec, 0x41, 0xad, 0xd4, 0x67, 0xb3, 0xd4, 0xa2, 0xfd, 0x5f, 0xa2, 0xaf, 0xea, 0x45, 0xaf, 0x9c, 0xbf, + 0x23, 0x9c, 0xa4, 0xf7, 0x53, 0xa4, 0x72, 0x96, 0xe4, 0x72, 0xc0, 0x5b, 0x9b, 0xc0, 0xb7, 0xc2, 0x75, 0xb7, 0xfd, + 0x1c, 0xe1, 0xfd, 0x93, 0xae, 0x3d, 0x93, 0x26, 0x6a, 0x4c, 0x26, 0x36, 0x5a, 0x6c, 0x36, 0x3f, 0x41, 0x7e, 0x3f, + 0xf7, 0x02, 0xf5, 0xf7, 0xcc, 0x4f, 0x83, 0xcc, 0x34, 0x5c, 0x68, 0x34, 0xa5, 0xf4, 0x51, 0xa5, 0xe5, 0x34, 0xd1, + 0xe5, 0xf1, 0x08, 0xf9, 0xf1, 0x71, 0x93, 0xe2, 0x71, 0xd8, 0x73, 0xab, 0xd8, 0x31, 0x53, 0x62, 0x31, 0x15, 0x3f, + 0x2a, 0x15, 0x04, 0x0c, 0x08, 0x04, 0xc7, 0x52, 0x95, 0xc7, 0x23, 0x65, 0x46, 0x23, 0xc3, 0x5e, 0x9d, 0xc3, 0x18, + 0x28, 0x30, 0x18, 0x96, 0xa1, 0x37, 0x96, 0x05, 0x0f, 0x0a, 0x05, 0x9a, 0xb5, 0x2f, 0x9a, 0x07, 0x09, 0x0e, 0x07, + 0x12, 0x36, 0x24, 0x12, 0x80, 0x9b, 0x1b, 0x80, 0xe2, 0x3d, 0xdf, 0xe2, 0xeb, 0x26, 0xcd, 0xeb, 0x27, 0x69, 0x4e, + 0x27, 0xb2, 0xcd, 0x7f, 0xb2, 0x75, 0x9f, 0xea, 0x75, 0x09, 0x1b, 0x12, 0x09, 0x83, 0x9e, 0x1d, 0x83, 0x2c, 0x74, + 0x58, 0x2c, 0x1a, 0x2e, 0x34, 0x1a, 0x1b, 0x2d, 0x36, 0x1b, 0x6e, 0xb2, 0xdc, 0x6e, 0x5a, 0xee, 0xb4, 0x5a, 0xa0, + 0xfb, 0x5b, 0xa0, 0x52, 0xf6, 0xa4, 0x52, 0x3b, 0x4d, 0x76, 0x3b, 0xd6, 0x61, 0xb7, 0xd6, 0xb3, 0xce, 0x7d, 0xb3, + 0x29, 0x7b, 0x52, 0x29, 0xe3, 0x3e, 0xdd, 0xe3, 0x2f, 0x71, 0x5e, 0x2f, 0x84, 0x97, 0x13, 0x84, 0x53, 0xf5, 0xa6, + 0x53, 0xd1, 0x68, 0xb9, 0xd1, 0x00, 0x00, 0x00, 0x00, 0xed, 0x2c, 0xc1, 0xed, 0x20, 0x60, 0x40, 0x20, 0xfc, 0x1f, + 0xe3, 0xfc, 0xb1, 0xc8, 0x79, 0xb1, 0x5b, 0xed, 0xb6, 0x5b, 0x6a, 0xbe, 0xd4, 0x6a, 0xcb, 0x46, 0x8d, 0xcb, 0xbe, + 0xd9, 0x67, 0xbe, 0x39, 0x4b, 0x72, 0x39, 0x4a, 0xde, 0x94, 0x4a, 0x4c, 0xd4, 0x98, 0x4c, 0x58, 0xe8, 0xb0, 0x58, + 0xcf, 0x4a, 0x85, 0xcf, 0xd0, 0x6b, 0xbb, 0xd0, 0xef, 0x2a, 0xc5, 0xef, 0xaa, 0xe5, 0x4f, 0xaa, 0xfb, 0x16, 0xed, + 0xfb, 0x43, 0xc5, 0x86, 0x43, 0x4d, 0xd7, 0x9a, 0x4d, 0x33, 0x55, 0x66, 0x33, 0x85, 0x94, 0x11, 0x85, 0x45, 0xcf, + 0x8a, 0x45, 0xf9, 0x10, 0xe9, 0xf9, 0x02, 0x06, 0x04, 0x02, 0x7f, 0x81, 0xfe, 0x7f, 0x50, 0xf0, 0xa0, 0x50, 0x3c, + 0x44, 0x78, 0x3c, 0x9f, 0xba, 0x25, 0x9f, 0xa8, 0xe3, 0x4b, 0xa8, 0x51, 0xf3, 0xa2, 0x51, 0xa3, 0xfe, 0x5d, 0xa3, + 0x40, 0xc0, 0x80, 0x40, 0x8f, 0x8a, 0x05, 0x8f, 0x92, 0xad, 0x3f, 0x92, 0x9d, 0xbc, 0x21, 0x9d, 0x38, 0x48, 0x70, + 0x38, 0xf5, 0x04, 0xf1, 0xf5, 0xbc, 0xdf, 0x63, 0xbc, 0xb6, 0xc1, 0x77, 0xb6, 0xda, 0x75, 0xaf, 0xda, 0x21, 0x63, + 0x42, 0x21, 0x10, 0x30, 0x20, 0x10, 0xff, 0x1a, 0xe5, 0xff, 0xf3, 0x0e, 0xfd, 0xf3, 0xd2, 0x6d, 0xbf, 0xd2, 0xcd, + 0x4c, 0x81, 0xcd, 0x0c, 0x14, 0x18, 0x0c, 0x13, 0x35, 0x26, 0x13, 0xec, 0x2f, 0xc3, 0xec, 0x5f, 0xe1, 0xbe, 0x5f, + 0x97, 0xa2, 0x35, 0x97, 0x44, 0xcc, 0x88, 0x44, 0x17, 0x39, 0x2e, 0x17, 0xc4, 0x57, 0x93, 0xc4, 0xa7, 0xf2, 0x55, + 0xa7, 0x7e, 0x82, 0xfc, 0x7e, 0x3d, 0x47, 0x7a, 0x3d, 0x64, 0xac, 0xc8, 0x64, 0x5d, 0xe7, 0xba, 0x5d, 0x19, 0x2b, + 0x32, 0x19, 0x73, 0x95, 0xe6, 0x73, 0x60, 0xa0, 0xc0, 0x60, 0x81, 0x98, 0x19, 0x81, 0x4f, 0xd1, 0x9e, 0x4f, 0xdc, + 0x7f, 0xa3, 0xdc, 0x22, 0x66, 0x44, 0x22, 0x2a, 0x7e, 0x54, 0x2a, 0x90, 0xab, 0x3b, 0x90, 0x88, 0x83, 0x0b, 0x88, + 0x46, 0xca, 0x8c, 0x46, 0xee, 0x29, 0xc7, 0xee, 0xb8, 0xd3, 0x6b, 0xb8, 0x14, 0x3c, 0x28, 0x14, 0xde, 0x79, 0xa7, + 0xde, 0x5e, 0xe2, 0xbc, 0x5e, 0x0b, 0x1d, 0x16, 0x0b, 0xdb, 0x76, 0xad, 0xdb, 0xe0, 0x3b, 0xdb, 0xe0, 0x32, 0x56, + 0x64, 0x32, 0x3a, 0x4e, 0x74, 0x3a, 0x0a, 0x1e, 0x14, 0x0a, 0x49, 0xdb, 0x92, 0x49, 0x06, 0x0a, 0x0c, 0x06, 0x24, + 0x6c, 0x48, 0x24, 0x5c, 0xe4, 0xb8, 0x5c, 0xc2, 0x5d, 0x9f, 0xc2, 0xd3, 0x6e, 0xbd, 0xd3, 0xac, 0xef, 0x43, 0xac, + 0x62, 0xa6, 0xc4, 0x62, 0x91, 0xa8, 0x39, 0x91, 0x95, 0xa4, 0x31, 0x95, 0xe4, 0x37, 0xd3, 0xe4, 0x79, 0x8b, 0xf2, + 0x79, 0xe7, 0x32, 0xd5, 0xe7, 0xc8, 0x43, 0x8b, 0xc8, 0x37, 0x59, 0x6e, 0x37, 0x6d, 0xb7, 0xda, 0x6d, 0x8d, 0x8c, + 0x01, 0x8d, 0xd5, 0x64, 0xb1, 0xd5, 0x4e, 0xd2, 0x9c, 0x4e, 0xa9, 0xe0, 0x49, 0xa9, 0x6c, 0xb4, 0xd8, 0x6c, 0x56, + 0xfa, 0xac, 0x56, 0xf4, 0x07, 0xf3, 0xf4, 0xea, 0x25, 0xcf, 0xea, 0x65, 0xaf, 0xca, 0x65, 0x7a, 0x8e, 0xf4, 0x7a, + 0xae, 0xe9, 0x47, 0xae, 0x08, 0x18, 0x10, 0x08, 0xba, 0xd5, 0x6f, 0xba, 0x78, 0x88, 0xf0, 0x78, 0x25, 0x6f, 0x4a, + 0x25, 0x2e, 0x72, 0x5c, 0x2e, 0x1c, 0x24, 0x38, 0x1c, 0xa6, 0xf1, 0x57, 0xa6, 0xb4, 0xc7, 0x73, 0xb4, 0xc6, 0x51, + 0x97, 0xc6, 0xe8, 0x23, 0xcb, 0xe8, 0xdd, 0x7c, 0xa1, 0xdd, 0x74, 0x9c, 0xe8, 0x74, 0x1f, 0x21, 0x3e, 0x1f, 0x4b, + 0xdd, 0x96, 0x4b, 0xbd, 0xdc, 0x61, 0xbd, 0x8b, 0x86, 0x0d, 0x8b, 0x8a, 0x85, 0x0f, 0x8a, 0x70, 0x90, 0xe0, 0x70, + 0x3e, 0x42, 0x7c, 0x3e, 0xb5, 0xc4, 0x71, 0xb5, 0x66, 0xaa, 0xcc, 0x66, 0x48, 0xd8, 0x90, 0x48, 0x03, 0x05, 0x06, + 0x03, 0xf6, 0x01, 0xf7, 0xf6, 0x0e, 0x12, 0x1c, 0x0e, 0x61, 0xa3, 0xc2, 0x61, 0x35, 0x5f, 0x6a, 0x35, 0x57, 0xf9, + 0xae, 0x57, 0xb9, 0xd0, 0x69, 0xb9, 0x86, 0x91, 0x17, 0x86, 0xc1, 0x58, 0x99, 0xc1, 0x1d, 0x27, 0x3a, 0x1d, 0x9e, + 0xb9, 0x27, 0x9e, 0xe1, 0x38, 0xd9, 0xe1, 0xf8, 0x13, 0xeb, 0xf8, 0x98, 0xb3, 0x2b, 0x98, 0x11, 0x33, 0x22, 0x11, + 0x69, 0xbb, 0xd2, 0x69, 0xd9, 0x70, 0xa9, 0xd9, 0x8e, 0x89, 0x07, 0x8e, 0x94, 0xa7, 0x33, 0x94, 0x9b, 0xb6, 0x2d, + 0x9b, 0x1e, 0x22, 0x3c, 0x1e, 0x87, 0x92, 0x15, 0x87, 0xe9, 0x20, 0xc9, 0xe9, 0xce, 0x49, 0x87, 0xce, 0x55, 0xff, + 0xaa, 0x55, 0x28, 0x78, 0x50, 0x28, 0xdf, 0x7a, 0xa5, 0xdf, 0x8c, 0x8f, 0x03, 0x8c, 0xa1, 0xf8, 0x59, 0xa1, 0x89, + 0x80, 0x09, 0x89, 0x0d, 0x17, 0x1a, 0x0d, 0xbf, 0xda, 0x65, 0xbf, 0xe6, 0x31, 0xd7, 0xe6, 0x42, 0xc6, 0x84, 0x42, + 0x68, 0xb8, 0xd0, 0x68, 0x41, 0xc3, 0x82, 0x41, 0x99, 0xb0, 0x29, 0x99, 0x2d, 0x77, 0x5a, 0x2d, 0x0f, 0x11, 0x1e, + 0x0f, 0xb0, 0xcb, 0x7b, 0xb0, 0x54, 0xfc, 0xa8, 0x54, 0xbb, 0xd6, 0x6d, 0xbb, 0x16, 0x3a, 0x2c, 0x16 +}; + +static uint8_t T4[256][4] = { + 0x63, 0x63, 0xa5, 0xc6, 0x7c, 0x7c, 0x84, 0xf8, 0x77, 0x77, 0x99, 0xee, 0x7b, 0x7b, 0x8d, 0xf6, 0xf2, 0xf2, 0x0d, + 0xff, 0x6b, 0x6b, 0xbd, 0xd6, 0x6f, 0x6f, 0xb1, 0xde, 0xc5, 0xc5, 0x54, 0x91, 0x30, 0x30, 0x50, 0x60, 0x01, 0x01, + 0x03, 0x02, 0x67, 0x67, 0xa9, 0xce, 0x2b, 0x2b, 0x7d, 0x56, 0xfe, 0xfe, 0x19, 0xe7, 0xd7, 0xd7, 0x62, 0xb5, 0xab, + 0xab, 0xe6, 0x4d, 0x76, 0x76, 0x9a, 0xec, 0xca, 0xca, 0x45, 0x8f, 0x82, 0x82, 0x9d, 0x1f, 0xc9, 0xc9, 0x40, 0x89, + 0x7d, 0x7d, 0x87, 0xfa, 0xfa, 0xfa, 0x15, 0xef, 0x59, 0x59, 0xeb, 0xb2, 0x47, 0x47, 0xc9, 0x8e, 0xf0, 0xf0, 0x0b, + 0xfb, 0xad, 0xad, 0xec, 0x41, 0xd4, 0xd4, 0x67, 0xb3, 0xa2, 0xa2, 0xfd, 0x5f, 0xaf, 0xaf, 0xea, 0x45, 0x9c, 0x9c, + 0xbf, 0x23, 0xa4, 0xa4, 0xf7, 0x53, 0x72, 0x72, 0x96, 0xe4, 0xc0, 0xc0, 0x5b, 0x9b, 0xb7, 0xb7, 0xc2, 0x75, 0xfd, + 0xfd, 0x1c, 0xe1, 0x93, 0x93, 0xae, 0x3d, 0x26, 0x26, 0x6a, 0x4c, 0x36, 0x36, 0x5a, 0x6c, 0x3f, 0x3f, 0x41, 0x7e, + 0xf7, 0xf7, 0x02, 0xf5, 0xcc, 0xcc, 0x4f, 0x83, 0x34, 0x34, 0x5c, 0x68, 0xa5, 0xa5, 0xf4, 0x51, 0xe5, 0xe5, 0x34, + 0xd1, 0xf1, 0xf1, 0x08, 0xf9, 0x71, 0x71, 0x93, 0xe2, 0xd8, 0xd8, 0x73, 0xab, 0x31, 0x31, 0x53, 0x62, 0x15, 0x15, + 0x3f, 0x2a, 0x04, 0x04, 0x0c, 0x08, 0xc7, 0xc7, 0x52, 0x95, 0x23, 0x23, 0x65, 0x46, 0xc3, 0xc3, 0x5e, 0x9d, 0x18, + 0x18, 0x28, 0x30, 0x96, 0x96, 0xa1, 0x37, 0x05, 0x05, 0x0f, 0x0a, 0x9a, 0x9a, 0xb5, 0x2f, 0x07, 0x07, 0x09, 0x0e, + 0x12, 0x12, 0x36, 0x24, 0x80, 0x80, 0x9b, 0x1b, 0xe2, 0xe2, 0x3d, 0xdf, 0xeb, 0xeb, 0x26, 0xcd, 0x27, 0x27, 0x69, + 0x4e, 0xb2, 0xb2, 0xcd, 0x7f, 0x75, 0x75, 0x9f, 0xea, 0x09, 0x09, 0x1b, 0x12, 0x83, 0x83, 0x9e, 0x1d, 0x2c, 0x2c, + 0x74, 0x58, 0x1a, 0x1a, 0x2e, 0x34, 0x1b, 0x1b, 0x2d, 0x36, 0x6e, 0x6e, 0xb2, 0xdc, 0x5a, 0x5a, 0xee, 0xb4, 0xa0, + 0xa0, 0xfb, 0x5b, 0x52, 0x52, 0xf6, 0xa4, 0x3b, 0x3b, 0x4d, 0x76, 0xd6, 0xd6, 0x61, 0xb7, 0xb3, 0xb3, 0xce, 0x7d, + 0x29, 0x29, 0x7b, 0x52, 0xe3, 0xe3, 0x3e, 0xdd, 0x2f, 0x2f, 0x71, 0x5e, 0x84, 0x84, 0x97, 0x13, 0x53, 0x53, 0xf5, + 0xa6, 0xd1, 0xd1, 0x68, 0xb9, 0x00, 0x00, 0x00, 0x00, 0xed, 0xed, 0x2c, 0xc1, 0x20, 0x20, 0x60, 0x40, 0xfc, 0xfc, + 0x1f, 0xe3, 0xb1, 0xb1, 0xc8, 0x79, 0x5b, 0x5b, 0xed, 0xb6, 0x6a, 0x6a, 0xbe, 0xd4, 0xcb, 0xcb, 0x46, 0x8d, 0xbe, + 0xbe, 0xd9, 0x67, 0x39, 0x39, 0x4b, 0x72, 0x4a, 0x4a, 0xde, 0x94, 0x4c, 0x4c, 0xd4, 0x98, 0x58, 0x58, 0xe8, 0xb0, + 0xcf, 0xcf, 0x4a, 0x85, 0xd0, 0xd0, 0x6b, 0xbb, 0xef, 0xef, 0x2a, 0xc5, 0xaa, 0xaa, 0xe5, 0x4f, 0xfb, 0xfb, 0x16, + 0xed, 0x43, 0x43, 0xc5, 0x86, 0x4d, 0x4d, 0xd7, 0x9a, 0x33, 0x33, 0x55, 0x66, 0x85, 0x85, 0x94, 0x11, 0x45, 0x45, + 0xcf, 0x8a, 0xf9, 0xf9, 0x10, 0xe9, 0x02, 0x02, 0x06, 0x04, 0x7f, 0x7f, 0x81, 0xfe, 0x50, 0x50, 0xf0, 0xa0, 0x3c, + 0x3c, 0x44, 0x78, 0x9f, 0x9f, 0xba, 0x25, 0xa8, 0xa8, 0xe3, 0x4b, 0x51, 0x51, 0xf3, 0xa2, 0xa3, 0xa3, 0xfe, 0x5d, + 0x40, 0x40, 0xc0, 0x80, 0x8f, 0x8f, 0x8a, 0x05, 0x92, 0x92, 0xad, 0x3f, 0x9d, 0x9d, 0xbc, 0x21, 0x38, 0x38, 0x48, + 0x70, 0xf5, 0xf5, 0x04, 0xf1, 0xbc, 0xbc, 0xdf, 0x63, 0xb6, 0xb6, 0xc1, 0x77, 0xda, 0xda, 0x75, 0xaf, 0x21, 0x21, + 0x63, 0x42, 0x10, 0x10, 0x30, 0x20, 0xff, 0xff, 0x1a, 0xe5, 0xf3, 0xf3, 0x0e, 0xfd, 0xd2, 0xd2, 0x6d, 0xbf, 0xcd, + 0xcd, 0x4c, 0x81, 0x0c, 0x0c, 0x14, 0x18, 0x13, 0x13, 0x35, 0x26, 0xec, 0xec, 0x2f, 0xc3, 0x5f, 0x5f, 0xe1, 0xbe, + 0x97, 0x97, 0xa2, 0x35, 0x44, 0x44, 0xcc, 0x88, 0x17, 0x17, 0x39, 0x2e, 0xc4, 0xc4, 0x57, 0x93, 0xa7, 0xa7, 0xf2, + 0x55, 0x7e, 0x7e, 0x82, 0xfc, 0x3d, 0x3d, 0x47, 0x7a, 0x64, 0x64, 0xac, 0xc8, 0x5d, 0x5d, 0xe7, 0xba, 0x19, 0x19, + 0x2b, 0x32, 0x73, 0x73, 0x95, 0xe6, 0x60, 0x60, 0xa0, 0xc0, 0x81, 0x81, 0x98, 0x19, 0x4f, 0x4f, 0xd1, 0x9e, 0xdc, + 0xdc, 0x7f, 0xa3, 0x22, 0x22, 0x66, 0x44, 0x2a, 0x2a, 0x7e, 0x54, 0x90, 0x90, 0xab, 0x3b, 0x88, 0x88, 0x83, 0x0b, + 0x46, 0x46, 0xca, 0x8c, 0xee, 0xee, 0x29, 0xc7, 0xb8, 0xb8, 0xd3, 0x6b, 0x14, 0x14, 0x3c, 0x28, 0xde, 0xde, 0x79, + 0xa7, 0x5e, 0x5e, 0xe2, 0xbc, 0x0b, 0x0b, 0x1d, 0x16, 0xdb, 0xdb, 0x76, 0xad, 0xe0, 0xe0, 0x3b, 0xdb, 0x32, 0x32, + 0x56, 0x64, 0x3a, 0x3a, 0x4e, 0x74, 0x0a, 0x0a, 0x1e, 0x14, 0x49, 0x49, 0xdb, 0x92, 0x06, 0x06, 0x0a, 0x0c, 0x24, + 0x24, 0x6c, 0x48, 0x5c, 0x5c, 0xe4, 0xb8, 0xc2, 0xc2, 0x5d, 0x9f, 0xd3, 0xd3, 0x6e, 0xbd, 0xac, 0xac, 0xef, 0x43, + 0x62, 0x62, 0xa6, 0xc4, 0x91, 0x91, 0xa8, 0x39, 0x95, 0x95, 0xa4, 0x31, 0xe4, 0xe4, 0x37, 0xd3, 0x79, 0x79, 0x8b, + 0xf2, 0xe7, 0xe7, 0x32, 0xd5, 0xc8, 0xc8, 0x43, 0x8b, 0x37, 0x37, 0x59, 0x6e, 0x6d, 0x6d, 0xb7, 0xda, 0x8d, 0x8d, + 0x8c, 0x01, 0xd5, 0xd5, 0x64, 0xb1, 0x4e, 0x4e, 0xd2, 0x9c, 0xa9, 0xa9, 0xe0, 0x49, 0x6c, 0x6c, 0xb4, 0xd8, 0x56, + 0x56, 0xfa, 0xac, 0xf4, 0xf4, 0x07, 0xf3, 0xea, 0xea, 0x25, 0xcf, 0x65, 0x65, 0xaf, 0xca, 0x7a, 0x7a, 0x8e, 0xf4, + 0xae, 0xae, 0xe9, 0x47, 0x08, 0x08, 0x18, 0x10, 0xba, 0xba, 0xd5, 0x6f, 0x78, 0x78, 0x88, 0xf0, 0x25, 0x25, 0x6f, + 0x4a, 0x2e, 0x2e, 0x72, 0x5c, 0x1c, 0x1c, 0x24, 0x38, 0xa6, 0xa6, 0xf1, 0x57, 0xb4, 0xb4, 0xc7, 0x73, 0xc6, 0xc6, + 0x51, 0x97, 0xe8, 0xe8, 0x23, 0xcb, 0xdd, 0xdd, 0x7c, 0xa1, 0x74, 0x74, 0x9c, 0xe8, 0x1f, 0x1f, 0x21, 0x3e, 0x4b, + 0x4b, 0xdd, 0x96, 0xbd, 0xbd, 0xdc, 0x61, 0x8b, 0x8b, 0x86, 0x0d, 0x8a, 0x8a, 0x85, 0x0f, 0x70, 0x70, 0x90, 0xe0, + 0x3e, 0x3e, 0x42, 0x7c, 0xb5, 0xb5, 0xc4, 0x71, 0x66, 0x66, 0xaa, 0xcc, 0x48, 0x48, 0xd8, 0x90, 0x03, 0x03, 0x05, + 0x06, 0xf6, 0xf6, 0x01, 0xf7, 0x0e, 0x0e, 0x12, 0x1c, 0x61, 0x61, 0xa3, 0xc2, 0x35, 0x35, 0x5f, 0x6a, 0x57, 0x57, + 0xf9, 0xae, 0xb9, 0xb9, 0xd0, 0x69, 0x86, 0x86, 0x91, 0x17, 0xc1, 0xc1, 0x58, 0x99, 0x1d, 0x1d, 0x27, 0x3a, 0x9e, + 0x9e, 0xb9, 0x27, 0xe1, 0xe1, 0x38, 0xd9, 0xf8, 0xf8, 0x13, 0xeb, 0x98, 0x98, 0xb3, 0x2b, 0x11, 0x11, 0x33, 0x22, + 0x69, 0x69, 0xbb, 0xd2, 0xd9, 0xd9, 0x70, 0xa9, 0x8e, 0x8e, 0x89, 0x07, 0x94, 0x94, 0xa7, 0x33, 0x9b, 0x9b, 0xb6, + 0x2d, 0x1e, 0x1e, 0x22, 0x3c, 0x87, 0x87, 0x92, 0x15, 0xe9, 0xe9, 0x20, 0xc9, 0xce, 0xce, 0x49, 0x87, 0x55, 0x55, + 0xff, 0xaa, 0x28, 0x28, 0x78, 0x50, 0xdf, 0xdf, 0x7a, 0xa5, 0x8c, 0x8c, 0x8f, 0x03, 0xa1, 0xa1, 0xf8, 0x59, 0x89, + 0x89, 0x80, 0x09, 0x0d, 0x0d, 0x17, 0x1a, 0xbf, 0xbf, 0xda, 0x65, 0xe6, 0xe6, 0x31, 0xd7, 0x42, 0x42, 0xc6, 0x84, + 0x68, 0x68, 0xb8, 0xd0, 0x41, 0x41, 0xc3, 0x82, 0x99, 0x99, 0xb0, 0x29, 0x2d, 0x2d, 0x77, 0x5a, 0x0f, 0x0f, 0x11, + 0x1e, 0xb0, 0xb0, 0xcb, 0x7b, 0x54, 0x54, 0xfc, 0xa8, 0xbb, 0xbb, 0xd6, 0x6d, 0x16, 0x16, 0x3a, 0x2c +}; + +static uint8_t T5[256][4] = { + 0x51, 0xf4, 0xa7, 0x50, 0x7e, 0x41, 0x65, 0x53, 0x1a, 0x17, 0xa4, 0xc3, 0x3a, 0x27, 0x5e, 0x96, 0x3b, 0xab, 0x6b, + 0xcb, 0x1f, 0x9d, 0x45, 0xf1, 0xac, 0xfa, 0x58, 0xab, 0x4b, 0xe3, 0x03, 0x93, 0x20, 0x30, 0xfa, 0x55, 0xad, 0x76, + 0x6d, 0xf6, 0x88, 0xcc, 0x76, 0x91, 0xf5, 0x02, 0x4c, 0x25, 0x4f, 0xe5, 0xd7, 0xfc, 0xc5, 0x2a, 0xcb, 0xd7, 0x26, + 0x35, 0x44, 0x80, 0xb5, 0x62, 0xa3, 0x8f, 0xde, 0xb1, 0x5a, 0x49, 0x25, 0xba, 0x1b, 0x67, 0x45, 0xea, 0x0e, 0x98, + 0x5d, 0xfe, 0xc0, 0xe1, 0xc3, 0x2f, 0x75, 0x02, 0x81, 0x4c, 0xf0, 0x12, 0x8d, 0x46, 0x97, 0xa3, 0x6b, 0xd3, 0xf9, + 0xc6, 0x03, 0x8f, 0x5f, 0xe7, 0x15, 0x92, 0x9c, 0x95, 0xbf, 0x6d, 0x7a, 0xeb, 0x95, 0x52, 0x59, 0xda, 0xd4, 0xbe, + 0x83, 0x2d, 0x58, 0x74, 0x21, 0xd3, 0x49, 0xe0, 0x69, 0x29, 0x8e, 0xc9, 0xc8, 0x44, 0x75, 0xc2, 0x89, 0x6a, 0xf4, + 0x8e, 0x79, 0x78, 0x99, 0x58, 0x3e, 0x6b, 0x27, 0xb9, 0x71, 0xdd, 0xbe, 0xe1, 0x4f, 0xb6, 0xf0, 0x88, 0xad, 0x17, + 0xc9, 0x20, 0xac, 0x66, 0x7d, 0xce, 0x3a, 0xb4, 0x63, 0xdf, 0x4a, 0x18, 0xe5, 0x1a, 0x31, 0x82, 0x97, 0x51, 0x33, + 0x60, 0x62, 0x53, 0x7f, 0x45, 0xb1, 0x64, 0x77, 0xe0, 0xbb, 0x6b, 0xae, 0x84, 0xfe, 0x81, 0xa0, 0x1c, 0xf9, 0x08, + 0x2b, 0x94, 0x70, 0x48, 0x68, 0x58, 0x8f, 0x45, 0xfd, 0x19, 0x94, 0xde, 0x6c, 0x87, 0x52, 0x7b, 0xf8, 0xb7, 0xab, + 0x73, 0xd3, 0x23, 0x72, 0x4b, 0x02, 0xe2, 0xe3, 0x1f, 0x8f, 0x57, 0x66, 0x55, 0xab, 0x2a, 0xb2, 0xeb, 0x28, 0x07, + 0x2f, 0xb5, 0xc2, 0x03, 0x86, 0xc5, 0x7b, 0x9a, 0xd3, 0x37, 0x08, 0xa5, 0x30, 0x28, 0x87, 0xf2, 0x23, 0xbf, 0xa5, + 0xb2, 0x02, 0x03, 0x6a, 0xba, 0xed, 0x16, 0x82, 0x5c, 0x8a, 0xcf, 0x1c, 0x2b, 0xa7, 0x79, 0xb4, 0x92, 0xf3, 0x07, + 0xf2, 0xf0, 0x4e, 0x69, 0xe2, 0xa1, 0x65, 0xda, 0xf4, 0xcd, 0x06, 0x05, 0xbe, 0xd5, 0xd1, 0x34, 0x62, 0x1f, 0xc4, + 0xa6, 0xfe, 0x8a, 0x34, 0x2e, 0x53, 0x9d, 0xa2, 0xf3, 0x55, 0xa0, 0x05, 0x8a, 0xe1, 0x32, 0xa4, 0xf6, 0xeb, 0x75, + 0x0b, 0x83, 0xec, 0x39, 0x40, 0x60, 0xef, 0xaa, 0x5e, 0x71, 0x9f, 0x06, 0xbd, 0x6e, 0x10, 0x51, 0x3e, 0x21, 0x8a, + 0xf9, 0x96, 0xdd, 0x06, 0x3d, 0xdd, 0x3e, 0x05, 0xae, 0x4d, 0xe6, 0xbd, 0x46, 0x91, 0x54, 0x8d, 0xb5, 0x71, 0xc4, + 0x5d, 0x05, 0x04, 0x06, 0xd4, 0x6f, 0x60, 0x50, 0x15, 0xff, 0x19, 0x98, 0xfb, 0x24, 0xd6, 0xbd, 0xe9, 0x97, 0x89, + 0x40, 0x43, 0xcc, 0x67, 0xd9, 0x9e, 0x77, 0xb0, 0xe8, 0x42, 0xbd, 0x07, 0x89, 0x8b, 0x88, 0xe7, 0x19, 0x5b, 0x38, + 0x79, 0xc8, 0xee, 0xdb, 0xa1, 0x7c, 0x0a, 0x47, 0x7c, 0x42, 0x0f, 0xe9, 0xf8, 0x84, 0x1e, 0xc9, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x80, 0x86, 0x83, 0x32, 0x2b, 0xed, 0x48, 0x1e, 0x11, 0x70, 0xac, 0x6c, 0x5a, 0x72, 0x4e, 0xfd, 0x0e, + 0xff, 0xfb, 0x0f, 0x85, 0x38, 0x56, 0x3d, 0xae, 0xd5, 0x1e, 0x36, 0x2d, 0x39, 0x27, 0x0a, 0x0f, 0xd9, 0x64, 0x68, + 0x5c, 0xa6, 0x21, 0x9b, 0x5b, 0x54, 0xd1, 0x24, 0x36, 0x2e, 0x3a, 0x0c, 0x0a, 0x67, 0xb1, 0x93, 0x57, 0xe7, 0x0f, + 0xb4, 0xee, 0x96, 0xd2, 0x1b, 0x9b, 0x91, 0x9e, 0x80, 0xc0, 0xc5, 0x4f, 0x61, 0xdc, 0x20, 0xa2, 0x5a, 0x77, 0x4b, + 0x69, 0x1c, 0x12, 0x1a, 0x16, 0xe2, 0x93, 0xba, 0x0a, 0xc0, 0xa0, 0x2a, 0xe5, 0x3c, 0x22, 0xe0, 0x43, 0x12, 0x1b, + 0x17, 0x1d, 0x0e, 0x09, 0x0d, 0x0b, 0xf2, 0x8b, 0xc7, 0xad, 0x2d, 0xb6, 0xa8, 0xb9, 0x14, 0x1e, 0xa9, 0xc8, 0x57, + 0xf1, 0x19, 0x85, 0xaf, 0x75, 0x07, 0x4c, 0xee, 0x99, 0xdd, 0xbb, 0xa3, 0x7f, 0x60, 0xfd, 0xf7, 0x01, 0x26, 0x9f, + 0x5c, 0x72, 0xf5, 0xbc, 0x44, 0x66, 0x3b, 0xc5, 0x5b, 0xfb, 0x7e, 0x34, 0x8b, 0x43, 0x29, 0x76, 0xcb, 0x23, 0xc6, + 0xdc, 0xb6, 0xed, 0xfc, 0x68, 0xb8, 0xe4, 0xf1, 0x63, 0xd7, 0x31, 0xdc, 0xca, 0x42, 0x63, 0x85, 0x10, 0x13, 0x97, + 0x22, 0x40, 0x84, 0xc6, 0x11, 0x20, 0x85, 0x4a, 0x24, 0x7d, 0xd2, 0xbb, 0x3d, 0xf8, 0xae, 0xf9, 0x32, 0x11, 0xc7, + 0x29, 0xa1, 0x6d, 0x1d, 0x9e, 0x2f, 0x4b, 0xdc, 0xb2, 0x30, 0xf3, 0x0d, 0x86, 0x52, 0xec, 0x77, 0xc1, 0xe3, 0xd0, + 0x2b, 0xb3, 0x16, 0x6c, 0xa9, 0x70, 0xb9, 0x99, 0x11, 0x94, 0x48, 0xfa, 0x47, 0xe9, 0x64, 0x22, 0xa8, 0xfc, 0x8c, + 0xc4, 0xa0, 0xf0, 0x3f, 0x1a, 0x56, 0x7d, 0x2c, 0xd8, 0x22, 0x33, 0x90, 0xef, 0x87, 0x49, 0x4e, 0xc7, 0xd9, 0x38, + 0xd1, 0xc1, 0x8c, 0xca, 0xa2, 0xfe, 0x98, 0xd4, 0x0b, 0x36, 0xa6, 0xf5, 0x81, 0xcf, 0xa5, 0x7a, 0xde, 0x28, 0xda, + 0xb7, 0x8e, 0x26, 0x3f, 0xad, 0xbf, 0xa4, 0x2c, 0x3a, 0x9d, 0xe4, 0x50, 0x78, 0x92, 0x0d, 0x6a, 0x5f, 0xcc, 0x9b, + 0x54, 0x7e, 0x46, 0x62, 0xf6, 0x8d, 0x13, 0xc2, 0x90, 0xd8, 0xb8, 0xe8, 0x2e, 0x39, 0xf7, 0x5e, 0x82, 0xc3, 0xaf, + 0xf5, 0x9f, 0x5d, 0x80, 0xbe, 0x69, 0xd0, 0x93, 0x7c, 0x6f, 0xd5, 0x2d, 0xa9, 0xcf, 0x25, 0x12, 0xb3, 0xc8, 0xac, + 0x99, 0x3b, 0x10, 0x18, 0x7d, 0xa7, 0xe8, 0x9c, 0x63, 0x6e, 0xdb, 0x3b, 0xbb, 0x7b, 0xcd, 0x26, 0x78, 0x09, 0x6e, + 0x59, 0x18, 0xf4, 0xec, 0x9a, 0xb7, 0x01, 0x83, 0x4f, 0x9a, 0xa8, 0xe6, 0x95, 0x6e, 0x65, 0xaa, 0xff, 0xe6, 0x7e, + 0x21, 0xbc, 0xcf, 0x08, 0xef, 0x15, 0xe8, 0xe6, 0xba, 0xe7, 0x9b, 0xd9, 0x4a, 0x6f, 0x36, 0xce, 0xea, 0x9f, 0x09, + 0xd4, 0x29, 0xb0, 0x7c, 0xd6, 0x31, 0xa4, 0xb2, 0xaf, 0x2a, 0x3f, 0x23, 0x31, 0xc6, 0xa5, 0x94, 0x30, 0x35, 0xa2, + 0x66, 0xc0, 0x74, 0x4e, 0xbc, 0x37, 0xfc, 0x82, 0xca, 0xa6, 0xe0, 0x90, 0xd0, 0xb0, 0x33, 0xa7, 0xd8, 0x15, 0xf1, + 0x04, 0x98, 0x4a, 0x41, 0xec, 0xda, 0xf7, 0x7f, 0xcd, 0x50, 0x0e, 0x17, 0x91, 0xf6, 0x2f, 0x76, 0x4d, 0xd6, 0x8d, + 0x43, 0xef, 0xb0, 0x4d, 0xcc, 0xaa, 0x4d, 0x54, 0xe4, 0x96, 0x04, 0xdf, 0x9e, 0xd1, 0xb5, 0xe3, 0x4c, 0x6a, 0x88, + 0x1b, 0xc1, 0x2c, 0x1f, 0xb8, 0x46, 0x65, 0x51, 0x7f, 0x9d, 0x5e, 0xea, 0x04, 0x01, 0x8c, 0x35, 0x5d, 0xfa, 0x87, + 0x74, 0x73, 0xfb, 0x0b, 0x41, 0x2e, 0xb3, 0x67, 0x1d, 0x5a, 0x92, 0xdb, 0xd2, 0x52, 0xe9, 0x10, 0x56, 0x33, 0x6d, + 0xd6, 0x47, 0x13, 0x9a, 0xd7, 0x61, 0x8c, 0x37, 0xa1, 0x0c, 0x7a, 0x59, 0xf8, 0x14, 0x8e, 0xeb, 0x13, 0x3c, 0x89, + 0xce, 0xa9, 0x27, 0xee, 0xb7, 0x61, 0xc9, 0x35, 0xe1, 0x1c, 0xe5, 0xed, 0x7a, 0x47, 0xb1, 0x3c, 0x9c, 0xd2, 0xdf, + 0x59, 0x55, 0xf2, 0x73, 0x3f, 0x18, 0x14, 0xce, 0x79, 0x73, 0xc7, 0x37, 0xbf, 0x53, 0xf7, 0xcd, 0xea, 0x5f, 0xfd, + 0xaa, 0x5b, 0xdf, 0x3d, 0x6f, 0x14, 0x78, 0x44, 0xdb, 0x86, 0xca, 0xaf, 0xf3, 0x81, 0xb9, 0x68, 0xc4, 0x3e, 0x38, + 0x24, 0x34, 0x2c, 0xc2, 0xa3, 0x40, 0x5f, 0x16, 0x1d, 0xc3, 0x72, 0xbc, 0xe2, 0x25, 0x0c, 0x28, 0x3c, 0x49, 0x8b, + 0xff, 0x0d, 0x95, 0x41, 0x39, 0xa8, 0x01, 0x71, 0x08, 0x0c, 0xb3, 0xde, 0xd8, 0xb4, 0xe4, 0x9c, 0x64, 0x56, 0xc1, + 0x90, 0x7b, 0xcb, 0x84, 0x61, 0xd5, 0x32, 0xb6, 0x70, 0x48, 0x6c, 0x5c, 0x74, 0xd0, 0xb8, 0x57, 0x42 +}; + +static uint8_t T6[256][4] = { + 0x50, 0x51, 0xf4, 0xa7, 0x53, 0x7e, 0x41, 0x65, 0xc3, 0x1a, 0x17, 0xa4, 0x96, 0x3a, 0x27, 0x5e, 0xcb, 0x3b, 0xab, + 0x6b, 0xf1, 0x1f, 0x9d, 0x45, 0xab, 0xac, 0xfa, 0x58, 0x93, 0x4b, 0xe3, 0x03, 0x55, 0x20, 0x30, 0xfa, 0xf6, 0xad, + 0x76, 0x6d, 0x91, 0x88, 0xcc, 0x76, 0x25, 0xf5, 0x02, 0x4c, 0xfc, 0x4f, 0xe5, 0xd7, 0xd7, 0xc5, 0x2a, 0xcb, 0x80, + 0x26, 0x35, 0x44, 0x8f, 0xb5, 0x62, 0xa3, 0x49, 0xde, 0xb1, 0x5a, 0x67, 0x25, 0xba, 0x1b, 0x98, 0x45, 0xea, 0x0e, + 0xe1, 0x5d, 0xfe, 0xc0, 0x02, 0xc3, 0x2f, 0x75, 0x12, 0x81, 0x4c, 0xf0, 0xa3, 0x8d, 0x46, 0x97, 0xc6, 0x6b, 0xd3, + 0xf9, 0xe7, 0x03, 0x8f, 0x5f, 0x95, 0x15, 0x92, 0x9c, 0xeb, 0xbf, 0x6d, 0x7a, 0xda, 0x95, 0x52, 0x59, 0x2d, 0xd4, + 0xbe, 0x83, 0xd3, 0x58, 0x74, 0x21, 0x29, 0x49, 0xe0, 0x69, 0x44, 0x8e, 0xc9, 0xc8, 0x6a, 0x75, 0xc2, 0x89, 0x78, + 0xf4, 0x8e, 0x79, 0x6b, 0x99, 0x58, 0x3e, 0xdd, 0x27, 0xb9, 0x71, 0xb6, 0xbe, 0xe1, 0x4f, 0x17, 0xf0, 0x88, 0xad, + 0x66, 0xc9, 0x20, 0xac, 0xb4, 0x7d, 0xce, 0x3a, 0x18, 0x63, 0xdf, 0x4a, 0x82, 0xe5, 0x1a, 0x31, 0x60, 0x97, 0x51, + 0x33, 0x45, 0x62, 0x53, 0x7f, 0xe0, 0xb1, 0x64, 0x77, 0x84, 0xbb, 0x6b, 0xae, 0x1c, 0xfe, 0x81, 0xa0, 0x94, 0xf9, + 0x08, 0x2b, 0x58, 0x70, 0x48, 0x68, 0x19, 0x8f, 0x45, 0xfd, 0x87, 0x94, 0xde, 0x6c, 0xb7, 0x52, 0x7b, 0xf8, 0x23, + 0xab, 0x73, 0xd3, 0xe2, 0x72, 0x4b, 0x02, 0x57, 0xe3, 0x1f, 0x8f, 0x2a, 0x66, 0x55, 0xab, 0x07, 0xb2, 0xeb, 0x28, + 0x03, 0x2f, 0xb5, 0xc2, 0x9a, 0x86, 0xc5, 0x7b, 0xa5, 0xd3, 0x37, 0x08, 0xf2, 0x30, 0x28, 0x87, 0xb2, 0x23, 0xbf, + 0xa5, 0xba, 0x02, 0x03, 0x6a, 0x5c, 0xed, 0x16, 0x82, 0x2b, 0x8a, 0xcf, 0x1c, 0x92, 0xa7, 0x79, 0xb4, 0xf0, 0xf3, + 0x07, 0xf2, 0xa1, 0x4e, 0x69, 0xe2, 0xcd, 0x65, 0xda, 0xf4, 0xd5, 0x06, 0x05, 0xbe, 0x1f, 0xd1, 0x34, 0x62, 0x8a, + 0xc4, 0xa6, 0xfe, 0x9d, 0x34, 0x2e, 0x53, 0xa0, 0xa2, 0xf3, 0x55, 0x32, 0x05, 0x8a, 0xe1, 0x75, 0xa4, 0xf6, 0xeb, + 0x39, 0x0b, 0x83, 0xec, 0xaa, 0x40, 0x60, 0xef, 0x06, 0x5e, 0x71, 0x9f, 0x51, 0xbd, 0x6e, 0x10, 0xf9, 0x3e, 0x21, + 0x8a, 0x3d, 0x96, 0xdd, 0x06, 0xae, 0xdd, 0x3e, 0x05, 0x46, 0x4d, 0xe6, 0xbd, 0xb5, 0x91, 0x54, 0x8d, 0x05, 0x71, + 0xc4, 0x5d, 0x6f, 0x04, 0x06, 0xd4, 0xff, 0x60, 0x50, 0x15, 0x24, 0x19, 0x98, 0xfb, 0x97, 0xd6, 0xbd, 0xe9, 0xcc, + 0x89, 0x40, 0x43, 0x77, 0x67, 0xd9, 0x9e, 0xbd, 0xb0, 0xe8, 0x42, 0x88, 0x07, 0x89, 0x8b, 0x38, 0xe7, 0x19, 0x5b, + 0xdb, 0x79, 0xc8, 0xee, 0x47, 0xa1, 0x7c, 0x0a, 0xe9, 0x7c, 0x42, 0x0f, 0xc9, 0xf8, 0x84, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x83, 0x09, 0x80, 0x86, 0x48, 0x32, 0x2b, 0xed, 0xac, 0x1e, 0x11, 0x70, 0x4e, 0x6c, 0x5a, 0x72, 0xfb, 0xfd, + 0x0e, 0xff, 0x56, 0x0f, 0x85, 0x38, 0x1e, 0x3d, 0xae, 0xd5, 0x27, 0x36, 0x2d, 0x39, 0x64, 0x0a, 0x0f, 0xd9, 0x21, + 0x68, 0x5c, 0xa6, 0xd1, 0x9b, 0x5b, 0x54, 0x3a, 0x24, 0x36, 0x2e, 0xb1, 0x0c, 0x0a, 0x67, 0x0f, 0x93, 0x57, 0xe7, + 0xd2, 0xb4, 0xee, 0x96, 0x9e, 0x1b, 0x9b, 0x91, 0x4f, 0x80, 0xc0, 0xc5, 0xa2, 0x61, 0xdc, 0x20, 0x69, 0x5a, 0x77, + 0x4b, 0x16, 0x1c, 0x12, 0x1a, 0x0a, 0xe2, 0x93, 0xba, 0xe5, 0xc0, 0xa0, 0x2a, 0x43, 0x3c, 0x22, 0xe0, 0x1d, 0x12, + 0x1b, 0x17, 0x0b, 0x0e, 0x09, 0x0d, 0xad, 0xf2, 0x8b, 0xc7, 0xb9, 0x2d, 0xb6, 0xa8, 0xc8, 0x14, 0x1e, 0xa9, 0x85, + 0x57, 0xf1, 0x19, 0x4c, 0xaf, 0x75, 0x07, 0xbb, 0xee, 0x99, 0xdd, 0xfd, 0xa3, 0x7f, 0x60, 0x9f, 0xf7, 0x01, 0x26, + 0xbc, 0x5c, 0x72, 0xf5, 0xc5, 0x44, 0x66, 0x3b, 0x34, 0x5b, 0xfb, 0x7e, 0x76, 0x8b, 0x43, 0x29, 0xdc, 0xcb, 0x23, + 0xc6, 0x68, 0xb6, 0xed, 0xfc, 0x63, 0xb8, 0xe4, 0xf1, 0xca, 0xd7, 0x31, 0xdc, 0x10, 0x42, 0x63, 0x85, 0x40, 0x13, + 0x97, 0x22, 0x20, 0x84, 0xc6, 0x11, 0x7d, 0x85, 0x4a, 0x24, 0xf8, 0xd2, 0xbb, 0x3d, 0x11, 0xae, 0xf9, 0x32, 0x6d, + 0xc7, 0x29, 0xa1, 0x4b, 0x1d, 0x9e, 0x2f, 0xf3, 0xdc, 0xb2, 0x30, 0xec, 0x0d, 0x86, 0x52, 0xd0, 0x77, 0xc1, 0xe3, + 0x6c, 0x2b, 0xb3, 0x16, 0x99, 0xa9, 0x70, 0xb9, 0xfa, 0x11, 0x94, 0x48, 0x22, 0x47, 0xe9, 0x64, 0xc4, 0xa8, 0xfc, + 0x8c, 0x1a, 0xa0, 0xf0, 0x3f, 0xd8, 0x56, 0x7d, 0x2c, 0xef, 0x22, 0x33, 0x90, 0xc7, 0x87, 0x49, 0x4e, 0xc1, 0xd9, + 0x38, 0xd1, 0xfe, 0x8c, 0xca, 0xa2, 0x36, 0x98, 0xd4, 0x0b, 0xcf, 0xa6, 0xf5, 0x81, 0x28, 0xa5, 0x7a, 0xde, 0x26, + 0xda, 0xb7, 0x8e, 0xa4, 0x3f, 0xad, 0xbf, 0xe4, 0x2c, 0x3a, 0x9d, 0x0d, 0x50, 0x78, 0x92, 0x9b, 0x6a, 0x5f, 0xcc, + 0x62, 0x54, 0x7e, 0x46, 0xc2, 0xf6, 0x8d, 0x13, 0xe8, 0x90, 0xd8, 0xb8, 0x5e, 0x2e, 0x39, 0xf7, 0xf5, 0x82, 0xc3, + 0xaf, 0xbe, 0x9f, 0x5d, 0x80, 0x7c, 0x69, 0xd0, 0x93, 0xa9, 0x6f, 0xd5, 0x2d, 0xb3, 0xcf, 0x25, 0x12, 0x3b, 0xc8, + 0xac, 0x99, 0xa7, 0x10, 0x18, 0x7d, 0x6e, 0xe8, 0x9c, 0x63, 0x7b, 0xdb, 0x3b, 0xbb, 0x09, 0xcd, 0x26, 0x78, 0xf4, + 0x6e, 0x59, 0x18, 0x01, 0xec, 0x9a, 0xb7, 0xa8, 0x83, 0x4f, 0x9a, 0x65, 0xe6, 0x95, 0x6e, 0x7e, 0xaa, 0xff, 0xe6, + 0x08, 0x21, 0xbc, 0xcf, 0xe6, 0xef, 0x15, 0xe8, 0xd9, 0xba, 0xe7, 0x9b, 0xce, 0x4a, 0x6f, 0x36, 0xd4, 0xea, 0x9f, + 0x09, 0xd6, 0x29, 0xb0, 0x7c, 0xaf, 0x31, 0xa4, 0xb2, 0x31, 0x2a, 0x3f, 0x23, 0x30, 0xc6, 0xa5, 0x94, 0xc0, 0x35, + 0xa2, 0x66, 0x37, 0x74, 0x4e, 0xbc, 0xa6, 0xfc, 0x82, 0xca, 0xb0, 0xe0, 0x90, 0xd0, 0x15, 0x33, 0xa7, 0xd8, 0x4a, + 0xf1, 0x04, 0x98, 0xf7, 0x41, 0xec, 0xda, 0x0e, 0x7f, 0xcd, 0x50, 0x2f, 0x17, 0x91, 0xf6, 0x8d, 0x76, 0x4d, 0xd6, + 0x4d, 0x43, 0xef, 0xb0, 0x54, 0xcc, 0xaa, 0x4d, 0xdf, 0xe4, 0x96, 0x04, 0xe3, 0x9e, 0xd1, 0xb5, 0x1b, 0x4c, 0x6a, + 0x88, 0xb8, 0xc1, 0x2c, 0x1f, 0x7f, 0x46, 0x65, 0x51, 0x04, 0x9d, 0x5e, 0xea, 0x5d, 0x01, 0x8c, 0x35, 0x73, 0xfa, + 0x87, 0x74, 0x2e, 0xfb, 0x0b, 0x41, 0x5a, 0xb3, 0x67, 0x1d, 0x52, 0x92, 0xdb, 0xd2, 0x33, 0xe9, 0x10, 0x56, 0x13, + 0x6d, 0xd6, 0x47, 0x8c, 0x9a, 0xd7, 0x61, 0x7a, 0x37, 0xa1, 0x0c, 0x8e, 0x59, 0xf8, 0x14, 0x89, 0xeb, 0x13, 0x3c, + 0xee, 0xce, 0xa9, 0x27, 0x35, 0xb7, 0x61, 0xc9, 0xed, 0xe1, 0x1c, 0xe5, 0x3c, 0x7a, 0x47, 0xb1, 0x59, 0x9c, 0xd2, + 0xdf, 0x3f, 0x55, 0xf2, 0x73, 0x79, 0x18, 0x14, 0xce, 0xbf, 0x73, 0xc7, 0x37, 0xea, 0x53, 0xf7, 0xcd, 0x5b, 0x5f, + 0xfd, 0xaa, 0x14, 0xdf, 0x3d, 0x6f, 0x86, 0x78, 0x44, 0xdb, 0x81, 0xca, 0xaf, 0xf3, 0x3e, 0xb9, 0x68, 0xc4, 0x2c, + 0x38, 0x24, 0x34, 0x5f, 0xc2, 0xa3, 0x40, 0x72, 0x16, 0x1d, 0xc3, 0x0c, 0xbc, 0xe2, 0x25, 0x8b, 0x28, 0x3c, 0x49, + 0x41, 0xff, 0x0d, 0x95, 0x71, 0x39, 0xa8, 0x01, 0xde, 0x08, 0x0c, 0xb3, 0x9c, 0xd8, 0xb4, 0xe4, 0x90, 0x64, 0x56, + 0xc1, 0x61, 0x7b, 0xcb, 0x84, 0x70, 0xd5, 0x32, 0xb6, 0x74, 0x48, 0x6c, 0x5c, 0x42, 0xd0, 0xb8, 0x57 +}; + +static uint8_t T7[256][4] = { + 0xa7, 0x50, 0x51, 0xf4, 0x65, 0x53, 0x7e, 0x41, 0xa4, 0xc3, 0x1a, 0x17, 0x5e, 0x96, 0x3a, 0x27, 0x6b, 0xcb, 0x3b, + 0xab, 0x45, 0xf1, 0x1f, 0x9d, 0x58, 0xab, 0xac, 0xfa, 0x03, 0x93, 0x4b, 0xe3, 0xfa, 0x55, 0x20, 0x30, 0x6d, 0xf6, + 0xad, 0x76, 0x76, 0x91, 0x88, 0xcc, 0x4c, 0x25, 0xf5, 0x02, 0xd7, 0xfc, 0x4f, 0xe5, 0xcb, 0xd7, 0xc5, 0x2a, 0x44, + 0x80, 0x26, 0x35, 0xa3, 0x8f, 0xb5, 0x62, 0x5a, 0x49, 0xde, 0xb1, 0x1b, 0x67, 0x25, 0xba, 0x0e, 0x98, 0x45, 0xea, + 0xc0, 0xe1, 0x5d, 0xfe, 0x75, 0x02, 0xc3, 0x2f, 0xf0, 0x12, 0x81, 0x4c, 0x97, 0xa3, 0x8d, 0x46, 0xf9, 0xc6, 0x6b, + 0xd3, 0x5f, 0xe7, 0x03, 0x8f, 0x9c, 0x95, 0x15, 0x92, 0x7a, 0xeb, 0xbf, 0x6d, 0x59, 0xda, 0x95, 0x52, 0x83, 0x2d, + 0xd4, 0xbe, 0x21, 0xd3, 0x58, 0x74, 0x69, 0x29, 0x49, 0xe0, 0xc8, 0x44, 0x8e, 0xc9, 0x89, 0x6a, 0x75, 0xc2, 0x79, + 0x78, 0xf4, 0x8e, 0x3e, 0x6b, 0x99, 0x58, 0x71, 0xdd, 0x27, 0xb9, 0x4f, 0xb6, 0xbe, 0xe1, 0xad, 0x17, 0xf0, 0x88, + 0xac, 0x66, 0xc9, 0x20, 0x3a, 0xb4, 0x7d, 0xce, 0x4a, 0x18, 0x63, 0xdf, 0x31, 0x82, 0xe5, 0x1a, 0x33, 0x60, 0x97, + 0x51, 0x7f, 0x45, 0x62, 0x53, 0x77, 0xe0, 0xb1, 0x64, 0xae, 0x84, 0xbb, 0x6b, 0xa0, 0x1c, 0xfe, 0x81, 0x2b, 0x94, + 0xf9, 0x08, 0x68, 0x58, 0x70, 0x48, 0xfd, 0x19, 0x8f, 0x45, 0x6c, 0x87, 0x94, 0xde, 0xf8, 0xb7, 0x52, 0x7b, 0xd3, + 0x23, 0xab, 0x73, 0x02, 0xe2, 0x72, 0x4b, 0x8f, 0x57, 0xe3, 0x1f, 0xab, 0x2a, 0x66, 0x55, 0x28, 0x07, 0xb2, 0xeb, + 0xc2, 0x03, 0x2f, 0xb5, 0x7b, 0x9a, 0x86, 0xc5, 0x08, 0xa5, 0xd3, 0x37, 0x87, 0xf2, 0x30, 0x28, 0xa5, 0xb2, 0x23, + 0xbf, 0x6a, 0xba, 0x02, 0x03, 0x82, 0x5c, 0xed, 0x16, 0x1c, 0x2b, 0x8a, 0xcf, 0xb4, 0x92, 0xa7, 0x79, 0xf2, 0xf0, + 0xf3, 0x07, 0xe2, 0xa1, 0x4e, 0x69, 0xf4, 0xcd, 0x65, 0xda, 0xbe, 0xd5, 0x06, 0x05, 0x62, 0x1f, 0xd1, 0x34, 0xfe, + 0x8a, 0xc4, 0xa6, 0x53, 0x9d, 0x34, 0x2e, 0x55, 0xa0, 0xa2, 0xf3, 0xe1, 0x32, 0x05, 0x8a, 0xeb, 0x75, 0xa4, 0xf6, + 0xec, 0x39, 0x0b, 0x83, 0xef, 0xaa, 0x40, 0x60, 0x9f, 0x06, 0x5e, 0x71, 0x10, 0x51, 0xbd, 0x6e, 0x8a, 0xf9, 0x3e, + 0x21, 0x06, 0x3d, 0x96, 0xdd, 0x05, 0xae, 0xdd, 0x3e, 0xbd, 0x46, 0x4d, 0xe6, 0x8d, 0xb5, 0x91, 0x54, 0x5d, 0x05, + 0x71, 0xc4, 0xd4, 0x6f, 0x04, 0x06, 0x15, 0xff, 0x60, 0x50, 0xfb, 0x24, 0x19, 0x98, 0xe9, 0x97, 0xd6, 0xbd, 0x43, + 0xcc, 0x89, 0x40, 0x9e, 0x77, 0x67, 0xd9, 0x42, 0xbd, 0xb0, 0xe8, 0x8b, 0x88, 0x07, 0x89, 0x5b, 0x38, 0xe7, 0x19, + 0xee, 0xdb, 0x79, 0xc8, 0x0a, 0x47, 0xa1, 0x7c, 0x0f, 0xe9, 0x7c, 0x42, 0x1e, 0xc9, 0xf8, 0x84, 0x00, 0x00, 0x00, + 0x00, 0x86, 0x83, 0x09, 0x80, 0xed, 0x48, 0x32, 0x2b, 0x70, 0xac, 0x1e, 0x11, 0x72, 0x4e, 0x6c, 0x5a, 0xff, 0xfb, + 0xfd, 0x0e, 0x38, 0x56, 0x0f, 0x85, 0xd5, 0x1e, 0x3d, 0xae, 0x39, 0x27, 0x36, 0x2d, 0xd9, 0x64, 0x0a, 0x0f, 0xa6, + 0x21, 0x68, 0x5c, 0x54, 0xd1, 0x9b, 0x5b, 0x2e, 0x3a, 0x24, 0x36, 0x67, 0xb1, 0x0c, 0x0a, 0xe7, 0x0f, 0x93, 0x57, + 0x96, 0xd2, 0xb4, 0xee, 0x91, 0x9e, 0x1b, 0x9b, 0xc5, 0x4f, 0x80, 0xc0, 0x20, 0xa2, 0x61, 0xdc, 0x4b, 0x69, 0x5a, + 0x77, 0x1a, 0x16, 0x1c, 0x12, 0xba, 0x0a, 0xe2, 0x93, 0x2a, 0xe5, 0xc0, 0xa0, 0xe0, 0x43, 0x3c, 0x22, 0x17, 0x1d, + 0x12, 0x1b, 0x0d, 0x0b, 0x0e, 0x09, 0xc7, 0xad, 0xf2, 0x8b, 0xa8, 0xb9, 0x2d, 0xb6, 0xa9, 0xc8, 0x14, 0x1e, 0x19, + 0x85, 0x57, 0xf1, 0x07, 0x4c, 0xaf, 0x75, 0xdd, 0xbb, 0xee, 0x99, 0x60, 0xfd, 0xa3, 0x7f, 0x26, 0x9f, 0xf7, 0x01, + 0xf5, 0xbc, 0x5c, 0x72, 0x3b, 0xc5, 0x44, 0x66, 0x7e, 0x34, 0x5b, 0xfb, 0x29, 0x76, 0x8b, 0x43, 0xc6, 0xdc, 0xcb, + 0x23, 0xfc, 0x68, 0xb6, 0xed, 0xf1, 0x63, 0xb8, 0xe4, 0xdc, 0xca, 0xd7, 0x31, 0x85, 0x10, 0x42, 0x63, 0x22, 0x40, + 0x13, 0x97, 0x11, 0x20, 0x84, 0xc6, 0x24, 0x7d, 0x85, 0x4a, 0x3d, 0xf8, 0xd2, 0xbb, 0x32, 0x11, 0xae, 0xf9, 0xa1, + 0x6d, 0xc7, 0x29, 0x2f, 0x4b, 0x1d, 0x9e, 0x30, 0xf3, 0xdc, 0xb2, 0x52, 0xec, 0x0d, 0x86, 0xe3, 0xd0, 0x77, 0xc1, + 0x16, 0x6c, 0x2b, 0xb3, 0xb9, 0x99, 0xa9, 0x70, 0x48, 0xfa, 0x11, 0x94, 0x64, 0x22, 0x47, 0xe9, 0x8c, 0xc4, 0xa8, + 0xfc, 0x3f, 0x1a, 0xa0, 0xf0, 0x2c, 0xd8, 0x56, 0x7d, 0x90, 0xef, 0x22, 0x33, 0x4e, 0xc7, 0x87, 0x49, 0xd1, 0xc1, + 0xd9, 0x38, 0xa2, 0xfe, 0x8c, 0xca, 0x0b, 0x36, 0x98, 0xd4, 0x81, 0xcf, 0xa6, 0xf5, 0xde, 0x28, 0xa5, 0x7a, 0x8e, + 0x26, 0xda, 0xb7, 0xbf, 0xa4, 0x3f, 0xad, 0x9d, 0xe4, 0x2c, 0x3a, 0x92, 0x0d, 0x50, 0x78, 0xcc, 0x9b, 0x6a, 0x5f, + 0x46, 0x62, 0x54, 0x7e, 0x13, 0xc2, 0xf6, 0x8d, 0xb8, 0xe8, 0x90, 0xd8, 0xf7, 0x5e, 0x2e, 0x39, 0xaf, 0xf5, 0x82, + 0xc3, 0x80, 0xbe, 0x9f, 0x5d, 0x93, 0x7c, 0x69, 0xd0, 0x2d, 0xa9, 0x6f, 0xd5, 0x12, 0xb3, 0xcf, 0x25, 0x99, 0x3b, + 0xc8, 0xac, 0x7d, 0xa7, 0x10, 0x18, 0x63, 0x6e, 0xe8, 0x9c, 0xbb, 0x7b, 0xdb, 0x3b, 0x78, 0x09, 0xcd, 0x26, 0x18, + 0xf4, 0x6e, 0x59, 0xb7, 0x01, 0xec, 0x9a, 0x9a, 0xa8, 0x83, 0x4f, 0x6e, 0x65, 0xe6, 0x95, 0xe6, 0x7e, 0xaa, 0xff, + 0xcf, 0x08, 0x21, 0xbc, 0xe8, 0xe6, 0xef, 0x15, 0x9b, 0xd9, 0xba, 0xe7, 0x36, 0xce, 0x4a, 0x6f, 0x09, 0xd4, 0xea, + 0x9f, 0x7c, 0xd6, 0x29, 0xb0, 0xb2, 0xaf, 0x31, 0xa4, 0x23, 0x31, 0x2a, 0x3f, 0x94, 0x30, 0xc6, 0xa5, 0x66, 0xc0, + 0x35, 0xa2, 0xbc, 0x37, 0x74, 0x4e, 0xca, 0xa6, 0xfc, 0x82, 0xd0, 0xb0, 0xe0, 0x90, 0xd8, 0x15, 0x33, 0xa7, 0x98, + 0x4a, 0xf1, 0x04, 0xda, 0xf7, 0x41, 0xec, 0x50, 0x0e, 0x7f, 0xcd, 0xf6, 0x2f, 0x17, 0x91, 0xd6, 0x8d, 0x76, 0x4d, + 0xb0, 0x4d, 0x43, 0xef, 0x4d, 0x54, 0xcc, 0xaa, 0x04, 0xdf, 0xe4, 0x96, 0xb5, 0xe3, 0x9e, 0xd1, 0x88, 0x1b, 0x4c, + 0x6a, 0x1f, 0xb8, 0xc1, 0x2c, 0x51, 0x7f, 0x46, 0x65, 0xea, 0x04, 0x9d, 0x5e, 0x35, 0x5d, 0x01, 0x8c, 0x74, 0x73, + 0xfa, 0x87, 0x41, 0x2e, 0xfb, 0x0b, 0x1d, 0x5a, 0xb3, 0x67, 0xd2, 0x52, 0x92, 0xdb, 0x56, 0x33, 0xe9, 0x10, 0x47, + 0x13, 0x6d, 0xd6, 0x61, 0x8c, 0x9a, 0xd7, 0x0c, 0x7a, 0x37, 0xa1, 0x14, 0x8e, 0x59, 0xf8, 0x3c, 0x89, 0xeb, 0x13, + 0x27, 0xee, 0xce, 0xa9, 0xc9, 0x35, 0xb7, 0x61, 0xe5, 0xed, 0xe1, 0x1c, 0xb1, 0x3c, 0x7a, 0x47, 0xdf, 0x59, 0x9c, + 0xd2, 0x73, 0x3f, 0x55, 0xf2, 0xce, 0x79, 0x18, 0x14, 0x37, 0xbf, 0x73, 0xc7, 0xcd, 0xea, 0x53, 0xf7, 0xaa, 0x5b, + 0x5f, 0xfd, 0x6f, 0x14, 0xdf, 0x3d, 0xdb, 0x86, 0x78, 0x44, 0xf3, 0x81, 0xca, 0xaf, 0xc4, 0x3e, 0xb9, 0x68, 0x34, + 0x2c, 0x38, 0x24, 0x40, 0x5f, 0xc2, 0xa3, 0xc3, 0x72, 0x16, 0x1d, 0x25, 0x0c, 0xbc, 0xe2, 0x49, 0x8b, 0x28, 0x3c, + 0x95, 0x41, 0xff, 0x0d, 0x01, 0x71, 0x39, 0xa8, 0xb3, 0xde, 0x08, 0x0c, 0xe4, 0x9c, 0xd8, 0xb4, 0xc1, 0x90, 0x64, + 0x56, 0x84, 0x61, 0x7b, 0xcb, 0xb6, 0x70, 0xd5, 0x32, 0x5c, 0x74, 0x48, 0x6c, 0x57, 0x42, 0xd0, 0xb8 +}; + +static uint8_t T8[256][4] = { + 0xf4, 0xa7, 0x50, 0x51, 0x41, 0x65, 0x53, 0x7e, 0x17, 0xa4, 0xc3, 0x1a, 0x27, 0x5e, 0x96, 0x3a, 0xab, 0x6b, 0xcb, + 0x3b, 0x9d, 0x45, 0xf1, 0x1f, 0xfa, 0x58, 0xab, 0xac, 0xe3, 0x03, 0x93, 0x4b, 0x30, 0xfa, 0x55, 0x20, 0x76, 0x6d, + 0xf6, 0xad, 0xcc, 0x76, 0x91, 0x88, 0x02, 0x4c, 0x25, 0xf5, 0xe5, 0xd7, 0xfc, 0x4f, 0x2a, 0xcb, 0xd7, 0xc5, 0x35, + 0x44, 0x80, 0x26, 0x62, 0xa3, 0x8f, 0xb5, 0xb1, 0x5a, 0x49, 0xde, 0xba, 0x1b, 0x67, 0x25, 0xea, 0x0e, 0x98, 0x45, + 0xfe, 0xc0, 0xe1, 0x5d, 0x2f, 0x75, 0x02, 0xc3, 0x4c, 0xf0, 0x12, 0x81, 0x46, 0x97, 0xa3, 0x8d, 0xd3, 0xf9, 0xc6, + 0x6b, 0x8f, 0x5f, 0xe7, 0x03, 0x92, 0x9c, 0x95, 0x15, 0x6d, 0x7a, 0xeb, 0xbf, 0x52, 0x59, 0xda, 0x95, 0xbe, 0x83, + 0x2d, 0xd4, 0x74, 0x21, 0xd3, 0x58, 0xe0, 0x69, 0x29, 0x49, 0xc9, 0xc8, 0x44, 0x8e, 0xc2, 0x89, 0x6a, 0x75, 0x8e, + 0x79, 0x78, 0xf4, 0x58, 0x3e, 0x6b, 0x99, 0xb9, 0x71, 0xdd, 0x27, 0xe1, 0x4f, 0xb6, 0xbe, 0x88, 0xad, 0x17, 0xf0, + 0x20, 0xac, 0x66, 0xc9, 0xce, 0x3a, 0xb4, 0x7d, 0xdf, 0x4a, 0x18, 0x63, 0x1a, 0x31, 0x82, 0xe5, 0x51, 0x33, 0x60, + 0x97, 0x53, 0x7f, 0x45, 0x62, 0x64, 0x77, 0xe0, 0xb1, 0x6b, 0xae, 0x84, 0xbb, 0x81, 0xa0, 0x1c, 0xfe, 0x08, 0x2b, + 0x94, 0xf9, 0x48, 0x68, 0x58, 0x70, 0x45, 0xfd, 0x19, 0x8f, 0xde, 0x6c, 0x87, 0x94, 0x7b, 0xf8, 0xb7, 0x52, 0x73, + 0xd3, 0x23, 0xab, 0x4b, 0x02, 0xe2, 0x72, 0x1f, 0x8f, 0x57, 0xe3, 0x55, 0xab, 0x2a, 0x66, 0xeb, 0x28, 0x07, 0xb2, + 0xb5, 0xc2, 0x03, 0x2f, 0xc5, 0x7b, 0x9a, 0x86, 0x37, 0x08, 0xa5, 0xd3, 0x28, 0x87, 0xf2, 0x30, 0xbf, 0xa5, 0xb2, + 0x23, 0x03, 0x6a, 0xba, 0x02, 0x16, 0x82, 0x5c, 0xed, 0xcf, 0x1c, 0x2b, 0x8a, 0x79, 0xb4, 0x92, 0xa7, 0x07, 0xf2, + 0xf0, 0xf3, 0x69, 0xe2, 0xa1, 0x4e, 0xda, 0xf4, 0xcd, 0x65, 0x05, 0xbe, 0xd5, 0x06, 0x34, 0x62, 0x1f, 0xd1, 0xa6, + 0xfe, 0x8a, 0xc4, 0x2e, 0x53, 0x9d, 0x34, 0xf3, 0x55, 0xa0, 0xa2, 0x8a, 0xe1, 0x32, 0x05, 0xf6, 0xeb, 0x75, 0xa4, + 0x83, 0xec, 0x39, 0x0b, 0x60, 0xef, 0xaa, 0x40, 0x71, 0x9f, 0x06, 0x5e, 0x6e, 0x10, 0x51, 0xbd, 0x21, 0x8a, 0xf9, + 0x3e, 0xdd, 0x06, 0x3d, 0x96, 0x3e, 0x05, 0xae, 0xdd, 0xe6, 0xbd, 0x46, 0x4d, 0x54, 0x8d, 0xb5, 0x91, 0xc4, 0x5d, + 0x05, 0x71, 0x06, 0xd4, 0x6f, 0x04, 0x50, 0x15, 0xff, 0x60, 0x98, 0xfb, 0x24, 0x19, 0xbd, 0xe9, 0x97, 0xd6, 0x40, + 0x43, 0xcc, 0x89, 0xd9, 0x9e, 0x77, 0x67, 0xe8, 0x42, 0xbd, 0xb0, 0x89, 0x8b, 0x88, 0x07, 0x19, 0x5b, 0x38, 0xe7, + 0xc8, 0xee, 0xdb, 0x79, 0x7c, 0x0a, 0x47, 0xa1, 0x42, 0x0f, 0xe9, 0x7c, 0x84, 0x1e, 0xc9, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x86, 0x83, 0x09, 0x2b, 0xed, 0x48, 0x32, 0x11, 0x70, 0xac, 0x1e, 0x5a, 0x72, 0x4e, 0x6c, 0x0e, 0xff, + 0xfb, 0xfd, 0x85, 0x38, 0x56, 0x0f, 0xae, 0xd5, 0x1e, 0x3d, 0x2d, 0x39, 0x27, 0x36, 0x0f, 0xd9, 0x64, 0x0a, 0x5c, + 0xa6, 0x21, 0x68, 0x5b, 0x54, 0xd1, 0x9b, 0x36, 0x2e, 0x3a, 0x24, 0x0a, 0x67, 0xb1, 0x0c, 0x57, 0xe7, 0x0f, 0x93, + 0xee, 0x96, 0xd2, 0xb4, 0x9b, 0x91, 0x9e, 0x1b, 0xc0, 0xc5, 0x4f, 0x80, 0xdc, 0x20, 0xa2, 0x61, 0x77, 0x4b, 0x69, + 0x5a, 0x12, 0x1a, 0x16, 0x1c, 0x93, 0xba, 0x0a, 0xe2, 0xa0, 0x2a, 0xe5, 0xc0, 0x22, 0xe0, 0x43, 0x3c, 0x1b, 0x17, + 0x1d, 0x12, 0x09, 0x0d, 0x0b, 0x0e, 0x8b, 0xc7, 0xad, 0xf2, 0xb6, 0xa8, 0xb9, 0x2d, 0x1e, 0xa9, 0xc8, 0x14, 0xf1, + 0x19, 0x85, 0x57, 0x75, 0x07, 0x4c, 0xaf, 0x99, 0xdd, 0xbb, 0xee, 0x7f, 0x60, 0xfd, 0xa3, 0x01, 0x26, 0x9f, 0xf7, + 0x72, 0xf5, 0xbc, 0x5c, 0x66, 0x3b, 0xc5, 0x44, 0xfb, 0x7e, 0x34, 0x5b, 0x43, 0x29, 0x76, 0x8b, 0x23, 0xc6, 0xdc, + 0xcb, 0xed, 0xfc, 0x68, 0xb6, 0xe4, 0xf1, 0x63, 0xb8, 0x31, 0xdc, 0xca, 0xd7, 0x63, 0x85, 0x10, 0x42, 0x97, 0x22, + 0x40, 0x13, 0xc6, 0x11, 0x20, 0x84, 0x4a, 0x24, 0x7d, 0x85, 0xbb, 0x3d, 0xf8, 0xd2, 0xf9, 0x32, 0x11, 0xae, 0x29, + 0xa1, 0x6d, 0xc7, 0x9e, 0x2f, 0x4b, 0x1d, 0xb2, 0x30, 0xf3, 0xdc, 0x86, 0x52, 0xec, 0x0d, 0xc1, 0xe3, 0xd0, 0x77, + 0xb3, 0x16, 0x6c, 0x2b, 0x70, 0xb9, 0x99, 0xa9, 0x94, 0x48, 0xfa, 0x11, 0xe9, 0x64, 0x22, 0x47, 0xfc, 0x8c, 0xc4, + 0xa8, 0xf0, 0x3f, 0x1a, 0xa0, 0x7d, 0x2c, 0xd8, 0x56, 0x33, 0x90, 0xef, 0x22, 0x49, 0x4e, 0xc7, 0x87, 0x38, 0xd1, + 0xc1, 0xd9, 0xca, 0xa2, 0xfe, 0x8c, 0xd4, 0x0b, 0x36, 0x98, 0xf5, 0x81, 0xcf, 0xa6, 0x7a, 0xde, 0x28, 0xa5, 0xb7, + 0x8e, 0x26, 0xda, 0xad, 0xbf, 0xa4, 0x3f, 0x3a, 0x9d, 0xe4, 0x2c, 0x78, 0x92, 0x0d, 0x50, 0x5f, 0xcc, 0x9b, 0x6a, + 0x7e, 0x46, 0x62, 0x54, 0x8d, 0x13, 0xc2, 0xf6, 0xd8, 0xb8, 0xe8, 0x90, 0x39, 0xf7, 0x5e, 0x2e, 0xc3, 0xaf, 0xf5, + 0x82, 0x5d, 0x80, 0xbe, 0x9f, 0xd0, 0x93, 0x7c, 0x69, 0xd5, 0x2d, 0xa9, 0x6f, 0x25, 0x12, 0xb3, 0xcf, 0xac, 0x99, + 0x3b, 0xc8, 0x18, 0x7d, 0xa7, 0x10, 0x9c, 0x63, 0x6e, 0xe8, 0x3b, 0xbb, 0x7b, 0xdb, 0x26, 0x78, 0x09, 0xcd, 0x59, + 0x18, 0xf4, 0x6e, 0x9a, 0xb7, 0x01, 0xec, 0x4f, 0x9a, 0xa8, 0x83, 0x95, 0x6e, 0x65, 0xe6, 0xff, 0xe6, 0x7e, 0xaa, + 0xbc, 0xcf, 0x08, 0x21, 0x15, 0xe8, 0xe6, 0xef, 0xe7, 0x9b, 0xd9, 0xba, 0x6f, 0x36, 0xce, 0x4a, 0x9f, 0x09, 0xd4, + 0xea, 0xb0, 0x7c, 0xd6, 0x29, 0xa4, 0xb2, 0xaf, 0x31, 0x3f, 0x23, 0x31, 0x2a, 0xa5, 0x94, 0x30, 0xc6, 0xa2, 0x66, + 0xc0, 0x35, 0x4e, 0xbc, 0x37, 0x74, 0x82, 0xca, 0xa6, 0xfc, 0x90, 0xd0, 0xb0, 0xe0, 0xa7, 0xd8, 0x15, 0x33, 0x04, + 0x98, 0x4a, 0xf1, 0xec, 0xda, 0xf7, 0x41, 0xcd, 0x50, 0x0e, 0x7f, 0x91, 0xf6, 0x2f, 0x17, 0x4d, 0xd6, 0x8d, 0x76, + 0xef, 0xb0, 0x4d, 0x43, 0xaa, 0x4d, 0x54, 0xcc, 0x96, 0x04, 0xdf, 0xe4, 0xd1, 0xb5, 0xe3, 0x9e, 0x6a, 0x88, 0x1b, + 0x4c, 0x2c, 0x1f, 0xb8, 0xc1, 0x65, 0x51, 0x7f, 0x46, 0x5e, 0xea, 0x04, 0x9d, 0x8c, 0x35, 0x5d, 0x01, 0x87, 0x74, + 0x73, 0xfa, 0x0b, 0x41, 0x2e, 0xfb, 0x67, 0x1d, 0x5a, 0xb3, 0xdb, 0xd2, 0x52, 0x92, 0x10, 0x56, 0x33, 0xe9, 0xd6, + 0x47, 0x13, 0x6d, 0xd7, 0x61, 0x8c, 0x9a, 0xa1, 0x0c, 0x7a, 0x37, 0xf8, 0x14, 0x8e, 0x59, 0x13, 0x3c, 0x89, 0xeb, + 0xa9, 0x27, 0xee, 0xce, 0x61, 0xc9, 0x35, 0xb7, 0x1c, 0xe5, 0xed, 0xe1, 0x47, 0xb1, 0x3c, 0x7a, 0xd2, 0xdf, 0x59, + 0x9c, 0xf2, 0x73, 0x3f, 0x55, 0x14, 0xce, 0x79, 0x18, 0xc7, 0x37, 0xbf, 0x73, 0xf7, 0xcd, 0xea, 0x53, 0xfd, 0xaa, + 0x5b, 0x5f, 0x3d, 0x6f, 0x14, 0xdf, 0x44, 0xdb, 0x86, 0x78, 0xaf, 0xf3, 0x81, 0xca, 0x68, 0xc4, 0x3e, 0xb9, 0x24, + 0x34, 0x2c, 0x38, 0xa3, 0x40, 0x5f, 0xc2, 0x1d, 0xc3, 0x72, 0x16, 0xe2, 0x25, 0x0c, 0xbc, 0x3c, 0x49, 0x8b, 0x28, + 0x0d, 0x95, 0x41, 0xff, 0xa8, 0x01, 0x71, 0x39, 0x0c, 0xb3, 0xde, 0x08, 0xb4, 0xe4, 0x9c, 0xd8, 0x56, 0xc1, 0x90, + 0x64, 0xcb, 0x84, 0x61, 0x7b, 0x32, 0xb6, 0x70, 0xd5, 0x6c, 0x5c, 0x74, 0x48, 0xb8, 0x57, 0x42, 0xd0 +}; + +static uint8_t S5[256] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, + 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, + 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, + 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, + 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, + 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, + 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, + 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, + 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, + 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, + 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, + 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, + 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, + 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d +}; + +static uint8_t U1[256][4] = { + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x09, 0x0d, 0x0b, 0x1c, 0x12, 0x1a, 0x16, 0x12, 0x1b, 0x17, 0x1d, 0x38, 0x24, 0x34, + 0x2c, 0x36, 0x2d, 0x39, 0x27, 0x24, 0x36, 0x2e, 0x3a, 0x2a, 0x3f, 0x23, 0x31, 0x70, 0x48, 0x68, 0x58, 0x7e, 0x41, + 0x65, 0x53, 0x6c, 0x5a, 0x72, 0x4e, 0x62, 0x53, 0x7f, 0x45, 0x48, 0x6c, 0x5c, 0x74, 0x46, 0x65, 0x51, 0x7f, 0x54, + 0x7e, 0x46, 0x62, 0x5a, 0x77, 0x4b, 0x69, 0xe0, 0x90, 0xd0, 0xb0, 0xee, 0x99, 0xdd, 0xbb, 0xfc, 0x82, 0xca, 0xa6, + 0xf2, 0x8b, 0xc7, 0xad, 0xd8, 0xb4, 0xe4, 0x9c, 0xd6, 0xbd, 0xe9, 0x97, 0xc4, 0xa6, 0xfe, 0x8a, 0xca, 0xaf, 0xf3, + 0x81, 0x90, 0xd8, 0xb8, 0xe8, 0x9e, 0xd1, 0xb5, 0xe3, 0x8c, 0xca, 0xa2, 0xfe, 0x82, 0xc3, 0xaf, 0xf5, 0xa8, 0xfc, + 0x8c, 0xc4, 0xa6, 0xf5, 0x81, 0xcf, 0xb4, 0xee, 0x96, 0xd2, 0xba, 0xe7, 0x9b, 0xd9, 0xdb, 0x3b, 0xbb, 0x7b, 0xd5, + 0x32, 0xb6, 0x70, 0xc7, 0x29, 0xa1, 0x6d, 0xc9, 0x20, 0xac, 0x66, 0xe3, 0x1f, 0x8f, 0x57, 0xed, 0x16, 0x82, 0x5c, + 0xff, 0x0d, 0x95, 0x41, 0xf1, 0x04, 0x98, 0x4a, 0xab, 0x73, 0xd3, 0x23, 0xa5, 0x7a, 0xde, 0x28, 0xb7, 0x61, 0xc9, + 0x35, 0xb9, 0x68, 0xc4, 0x3e, 0x93, 0x57, 0xe7, 0x0f, 0x9d, 0x5e, 0xea, 0x04, 0x8f, 0x45, 0xfd, 0x19, 0x81, 0x4c, + 0xf0, 0x12, 0x3b, 0xab, 0x6b, 0xcb, 0x35, 0xa2, 0x66, 0xc0, 0x27, 0xb9, 0x71, 0xdd, 0x29, 0xb0, 0x7c, 0xd6, 0x03, + 0x8f, 0x5f, 0xe7, 0x0d, 0x86, 0x52, 0xec, 0x1f, 0x9d, 0x45, 0xf1, 0x11, 0x94, 0x48, 0xfa, 0x4b, 0xe3, 0x03, 0x93, + 0x45, 0xea, 0x0e, 0x98, 0x57, 0xf1, 0x19, 0x85, 0x59, 0xf8, 0x14, 0x8e, 0x73, 0xc7, 0x37, 0xbf, 0x7d, 0xce, 0x3a, + 0xb4, 0x6f, 0xd5, 0x2d, 0xa9, 0x61, 0xdc, 0x20, 0xa2, 0xad, 0x76, 0x6d, 0xf6, 0xa3, 0x7f, 0x60, 0xfd, 0xb1, 0x64, + 0x77, 0xe0, 0xbf, 0x6d, 0x7a, 0xeb, 0x95, 0x52, 0x59, 0xda, 0x9b, 0x5b, 0x54, 0xd1, 0x89, 0x40, 0x43, 0xcc, 0x87, + 0x49, 0x4e, 0xc7, 0xdd, 0x3e, 0x05, 0xae, 0xd3, 0x37, 0x08, 0xa5, 0xc1, 0x2c, 0x1f, 0xb8, 0xcf, 0x25, 0x12, 0xb3, + 0xe5, 0x1a, 0x31, 0x82, 0xeb, 0x13, 0x3c, 0x89, 0xf9, 0x08, 0x2b, 0x94, 0xf7, 0x01, 0x26, 0x9f, 0x4d, 0xe6, 0xbd, + 0x46, 0x43, 0xef, 0xb0, 0x4d, 0x51, 0xf4, 0xa7, 0x50, 0x5f, 0xfd, 0xaa, 0x5b, 0x75, 0xc2, 0x89, 0x6a, 0x7b, 0xcb, + 0x84, 0x61, 0x69, 0xd0, 0x93, 0x7c, 0x67, 0xd9, 0x9e, 0x77, 0x3d, 0xae, 0xd5, 0x1e, 0x33, 0xa7, 0xd8, 0x15, 0x21, + 0xbc, 0xcf, 0x08, 0x2f, 0xb5, 0xc2, 0x03, 0x05, 0x8a, 0xe1, 0x32, 0x0b, 0x83, 0xec, 0x39, 0x19, 0x98, 0xfb, 0x24, + 0x17, 0x91, 0xf6, 0x2f, 0x76, 0x4d, 0xd6, 0x8d, 0x78, 0x44, 0xdb, 0x86, 0x6a, 0x5f, 0xcc, 0x9b, 0x64, 0x56, 0xc1, + 0x90, 0x4e, 0x69, 0xe2, 0xa1, 0x40, 0x60, 0xef, 0xaa, 0x52, 0x7b, 0xf8, 0xb7, 0x5c, 0x72, 0xf5, 0xbc, 0x06, 0x05, + 0xbe, 0xd5, 0x08, 0x0c, 0xb3, 0xde, 0x1a, 0x17, 0xa4, 0xc3, 0x14, 0x1e, 0xa9, 0xc8, 0x3e, 0x21, 0x8a, 0xf9, 0x30, + 0x28, 0x87, 0xf2, 0x22, 0x33, 0x90, 0xef, 0x2c, 0x3a, 0x9d, 0xe4, 0x96, 0xdd, 0x06, 0x3d, 0x98, 0xd4, 0x0b, 0x36, + 0x8a, 0xcf, 0x1c, 0x2b, 0x84, 0xc6, 0x11, 0x20, 0xae, 0xf9, 0x32, 0x11, 0xa0, 0xf0, 0x3f, 0x1a, 0xb2, 0xeb, 0x28, + 0x07, 0xbc, 0xe2, 0x25, 0x0c, 0xe6, 0x95, 0x6e, 0x65, 0xe8, 0x9c, 0x63, 0x6e, 0xfa, 0x87, 0x74, 0x73, 0xf4, 0x8e, + 0x79, 0x78, 0xde, 0xb1, 0x5a, 0x49, 0xd0, 0xb8, 0x57, 0x42, 0xc2, 0xa3, 0x40, 0x5f, 0xcc, 0xaa, 0x4d, 0x54, 0x41, + 0xec, 0xda, 0xf7, 0x4f, 0xe5, 0xd7, 0xfc, 0x5d, 0xfe, 0xc0, 0xe1, 0x53, 0xf7, 0xcd, 0xea, 0x79, 0xc8, 0xee, 0xdb, + 0x77, 0xc1, 0xe3, 0xd0, 0x65, 0xda, 0xf4, 0xcd, 0x6b, 0xd3, 0xf9, 0xc6, 0x31, 0xa4, 0xb2, 0xaf, 0x3f, 0xad, 0xbf, + 0xa4, 0x2d, 0xb6, 0xa8, 0xb9, 0x23, 0xbf, 0xa5, 0xb2, 0x09, 0x80, 0x86, 0x83, 0x07, 0x89, 0x8b, 0x88, 0x15, 0x92, + 0x9c, 0x95, 0x1b, 0x9b, 0x91, 0x9e, 0xa1, 0x7c, 0x0a, 0x47, 0xaf, 0x75, 0x07, 0x4c, 0xbd, 0x6e, 0x10, 0x51, 0xb3, + 0x67, 0x1d, 0x5a, 0x99, 0x58, 0x3e, 0x6b, 0x97, 0x51, 0x33, 0x60, 0x85, 0x4a, 0x24, 0x7d, 0x8b, 0x43, 0x29, 0x76, + 0xd1, 0x34, 0x62, 0x1f, 0xdf, 0x3d, 0x6f, 0x14, 0xcd, 0x26, 0x78, 0x09, 0xc3, 0x2f, 0x75, 0x02, 0xe9, 0x10, 0x56, + 0x33, 0xe7, 0x19, 0x5b, 0x38, 0xf5, 0x02, 0x4c, 0x25, 0xfb, 0x0b, 0x41, 0x2e, 0x9a, 0xd7, 0x61, 0x8c, 0x94, 0xde, + 0x6c, 0x87, 0x86, 0xc5, 0x7b, 0x9a, 0x88, 0xcc, 0x76, 0x91, 0xa2, 0xf3, 0x55, 0xa0, 0xac, 0xfa, 0x58, 0xab, 0xbe, + 0xe1, 0x4f, 0xb6, 0xb0, 0xe8, 0x42, 0xbd, 0xea, 0x9f, 0x09, 0xd4, 0xe4, 0x96, 0x04, 0xdf, 0xf6, 0x8d, 0x13, 0xc2, + 0xf8, 0x84, 0x1e, 0xc9, 0xd2, 0xbb, 0x3d, 0xf8, 0xdc, 0xb2, 0x30, 0xf3, 0xce, 0xa9, 0x27, 0xee, 0xc0, 0xa0, 0x2a, + 0xe5, 0x7a, 0x47, 0xb1, 0x3c, 0x74, 0x4e, 0xbc, 0x37, 0x66, 0x55, 0xab, 0x2a, 0x68, 0x5c, 0xa6, 0x21, 0x42, 0x63, + 0x85, 0x10, 0x4c, 0x6a, 0x88, 0x1b, 0x5e, 0x71, 0x9f, 0x06, 0x50, 0x78, 0x92, 0x0d, 0x0a, 0x0f, 0xd9, 0x64, 0x04, + 0x06, 0xd4, 0x6f, 0x16, 0x1d, 0xc3, 0x72, 0x18, 0x14, 0xce, 0x79, 0x32, 0x2b, 0xed, 0x48, 0x3c, 0x22, 0xe0, 0x43, + 0x2e, 0x39, 0xf7, 0x5e, 0x20, 0x30, 0xfa, 0x55, 0xec, 0x9a, 0xb7, 0x01, 0xe2, 0x93, 0xba, 0x0a, 0xf0, 0x88, 0xad, + 0x17, 0xfe, 0x81, 0xa0, 0x1c, 0xd4, 0xbe, 0x83, 0x2d, 0xda, 0xb7, 0x8e, 0x26, 0xc8, 0xac, 0x99, 0x3b, 0xc6, 0xa5, + 0x94, 0x30, 0x9c, 0xd2, 0xdf, 0x59, 0x92, 0xdb, 0xd2, 0x52, 0x80, 0xc0, 0xc5, 0x4f, 0x8e, 0xc9, 0xc8, 0x44, 0xa4, + 0xf6, 0xeb, 0x75, 0xaa, 0xff, 0xe6, 0x7e, 0xb8, 0xe4, 0xf1, 0x63, 0xb6, 0xed, 0xfc, 0x68, 0x0c, 0x0a, 0x67, 0xb1, + 0x02, 0x03, 0x6a, 0xba, 0x10, 0x18, 0x7d, 0xa7, 0x1e, 0x11, 0x70, 0xac, 0x34, 0x2e, 0x53, 0x9d, 0x3a, 0x27, 0x5e, + 0x96, 0x28, 0x3c, 0x49, 0x8b, 0x26, 0x35, 0x44, 0x80, 0x7c, 0x42, 0x0f, 0xe9, 0x72, 0x4b, 0x02, 0xe2, 0x60, 0x50, + 0x15, 0xff, 0x6e, 0x59, 0x18, 0xf4, 0x44, 0x66, 0x3b, 0xc5, 0x4a, 0x6f, 0x36, 0xce, 0x58, 0x74, 0x21, 0xd3, 0x56, + 0x7d, 0x2c, 0xd8, 0x37, 0xa1, 0x0c, 0x7a, 0x39, 0xa8, 0x01, 0x71, 0x2b, 0xb3, 0x16, 0x6c, 0x25, 0xba, 0x1b, 0x67, + 0x0f, 0x85, 0x38, 0x56, 0x01, 0x8c, 0x35, 0x5d, 0x13, 0x97, 0x22, 0x40, 0x1d, 0x9e, 0x2f, 0x4b, 0x47, 0xe9, 0x64, + 0x22, 0x49, 0xe0, 0x69, 0x29, 0x5b, 0xfb, 0x7e, 0x34, 0x55, 0xf2, 0x73, 0x3f, 0x7f, 0xcd, 0x50, 0x0e, 0x71, 0xc4, + 0x5d, 0x05, 0x63, 0xdf, 0x4a, 0x18, 0x6d, 0xd6, 0x47, 0x13, 0xd7, 0x31, 0xdc, 0xca, 0xd9, 0x38, 0xd1, 0xc1, 0xcb, + 0x23, 0xc6, 0xdc, 0xc5, 0x2a, 0xcb, 0xd7, 0xef, 0x15, 0xe8, 0xe6, 0xe1, 0x1c, 0xe5, 0xed, 0xf3, 0x07, 0xf2, 0xf0, + 0xfd, 0x0e, 0xff, 0xfb, 0xa7, 0x79, 0xb4, 0x92, 0xa9, 0x70, 0xb9, 0x99, 0xbb, 0x6b, 0xae, 0x84, 0xb5, 0x62, 0xa3, + 0x8f, 0x9f, 0x5d, 0x80, 0xbe, 0x91, 0x54, 0x8d, 0xb5, 0x83, 0x4f, 0x9a, 0xa8, 0x8d, 0x46, 0x97, 0xa3 +}; + +static uint8_t U2[256][4] = { + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0e, 0x09, 0x0d, 0x16, 0x1c, 0x12, 0x1a, 0x1d, 0x12, 0x1b, 0x17, 0x2c, 0x38, 0x24, + 0x34, 0x27, 0x36, 0x2d, 0x39, 0x3a, 0x24, 0x36, 0x2e, 0x31, 0x2a, 0x3f, 0x23, 0x58, 0x70, 0x48, 0x68, 0x53, 0x7e, + 0x41, 0x65, 0x4e, 0x6c, 0x5a, 0x72, 0x45, 0x62, 0x53, 0x7f, 0x74, 0x48, 0x6c, 0x5c, 0x7f, 0x46, 0x65, 0x51, 0x62, + 0x54, 0x7e, 0x46, 0x69, 0x5a, 0x77, 0x4b, 0xb0, 0xe0, 0x90, 0xd0, 0xbb, 0xee, 0x99, 0xdd, 0xa6, 0xfc, 0x82, 0xca, + 0xad, 0xf2, 0x8b, 0xc7, 0x9c, 0xd8, 0xb4, 0xe4, 0x97, 0xd6, 0xbd, 0xe9, 0x8a, 0xc4, 0xa6, 0xfe, 0x81, 0xca, 0xaf, + 0xf3, 0xe8, 0x90, 0xd8, 0xb8, 0xe3, 0x9e, 0xd1, 0xb5, 0xfe, 0x8c, 0xca, 0xa2, 0xf5, 0x82, 0xc3, 0xaf, 0xc4, 0xa8, + 0xfc, 0x8c, 0xcf, 0xa6, 0xf5, 0x81, 0xd2, 0xb4, 0xee, 0x96, 0xd9, 0xba, 0xe7, 0x9b, 0x7b, 0xdb, 0x3b, 0xbb, 0x70, + 0xd5, 0x32, 0xb6, 0x6d, 0xc7, 0x29, 0xa1, 0x66, 0xc9, 0x20, 0xac, 0x57, 0xe3, 0x1f, 0x8f, 0x5c, 0xed, 0x16, 0x82, + 0x41, 0xff, 0x0d, 0x95, 0x4a, 0xf1, 0x04, 0x98, 0x23, 0xab, 0x73, 0xd3, 0x28, 0xa5, 0x7a, 0xde, 0x35, 0xb7, 0x61, + 0xc9, 0x3e, 0xb9, 0x68, 0xc4, 0x0f, 0x93, 0x57, 0xe7, 0x04, 0x9d, 0x5e, 0xea, 0x19, 0x8f, 0x45, 0xfd, 0x12, 0x81, + 0x4c, 0xf0, 0xcb, 0x3b, 0xab, 0x6b, 0xc0, 0x35, 0xa2, 0x66, 0xdd, 0x27, 0xb9, 0x71, 0xd6, 0x29, 0xb0, 0x7c, 0xe7, + 0x03, 0x8f, 0x5f, 0xec, 0x0d, 0x86, 0x52, 0xf1, 0x1f, 0x9d, 0x45, 0xfa, 0x11, 0x94, 0x48, 0x93, 0x4b, 0xe3, 0x03, + 0x98, 0x45, 0xea, 0x0e, 0x85, 0x57, 0xf1, 0x19, 0x8e, 0x59, 0xf8, 0x14, 0xbf, 0x73, 0xc7, 0x37, 0xb4, 0x7d, 0xce, + 0x3a, 0xa9, 0x6f, 0xd5, 0x2d, 0xa2, 0x61, 0xdc, 0x20, 0xf6, 0xad, 0x76, 0x6d, 0xfd, 0xa3, 0x7f, 0x60, 0xe0, 0xb1, + 0x64, 0x77, 0xeb, 0xbf, 0x6d, 0x7a, 0xda, 0x95, 0x52, 0x59, 0xd1, 0x9b, 0x5b, 0x54, 0xcc, 0x89, 0x40, 0x43, 0xc7, + 0x87, 0x49, 0x4e, 0xae, 0xdd, 0x3e, 0x05, 0xa5, 0xd3, 0x37, 0x08, 0xb8, 0xc1, 0x2c, 0x1f, 0xb3, 0xcf, 0x25, 0x12, + 0x82, 0xe5, 0x1a, 0x31, 0x89, 0xeb, 0x13, 0x3c, 0x94, 0xf9, 0x08, 0x2b, 0x9f, 0xf7, 0x01, 0x26, 0x46, 0x4d, 0xe6, + 0xbd, 0x4d, 0x43, 0xef, 0xb0, 0x50, 0x51, 0xf4, 0xa7, 0x5b, 0x5f, 0xfd, 0xaa, 0x6a, 0x75, 0xc2, 0x89, 0x61, 0x7b, + 0xcb, 0x84, 0x7c, 0x69, 0xd0, 0x93, 0x77, 0x67, 0xd9, 0x9e, 0x1e, 0x3d, 0xae, 0xd5, 0x15, 0x33, 0xa7, 0xd8, 0x08, + 0x21, 0xbc, 0xcf, 0x03, 0x2f, 0xb5, 0xc2, 0x32, 0x05, 0x8a, 0xe1, 0x39, 0x0b, 0x83, 0xec, 0x24, 0x19, 0x98, 0xfb, + 0x2f, 0x17, 0x91, 0xf6, 0x8d, 0x76, 0x4d, 0xd6, 0x86, 0x78, 0x44, 0xdb, 0x9b, 0x6a, 0x5f, 0xcc, 0x90, 0x64, 0x56, + 0xc1, 0xa1, 0x4e, 0x69, 0xe2, 0xaa, 0x40, 0x60, 0xef, 0xb7, 0x52, 0x7b, 0xf8, 0xbc, 0x5c, 0x72, 0xf5, 0xd5, 0x06, + 0x05, 0xbe, 0xde, 0x08, 0x0c, 0xb3, 0xc3, 0x1a, 0x17, 0xa4, 0xc8, 0x14, 0x1e, 0xa9, 0xf9, 0x3e, 0x21, 0x8a, 0xf2, + 0x30, 0x28, 0x87, 0xef, 0x22, 0x33, 0x90, 0xe4, 0x2c, 0x3a, 0x9d, 0x3d, 0x96, 0xdd, 0x06, 0x36, 0x98, 0xd4, 0x0b, + 0x2b, 0x8a, 0xcf, 0x1c, 0x20, 0x84, 0xc6, 0x11, 0x11, 0xae, 0xf9, 0x32, 0x1a, 0xa0, 0xf0, 0x3f, 0x07, 0xb2, 0xeb, + 0x28, 0x0c, 0xbc, 0xe2, 0x25, 0x65, 0xe6, 0x95, 0x6e, 0x6e, 0xe8, 0x9c, 0x63, 0x73, 0xfa, 0x87, 0x74, 0x78, 0xf4, + 0x8e, 0x79, 0x49, 0xde, 0xb1, 0x5a, 0x42, 0xd0, 0xb8, 0x57, 0x5f, 0xc2, 0xa3, 0x40, 0x54, 0xcc, 0xaa, 0x4d, 0xf7, + 0x41, 0xec, 0xda, 0xfc, 0x4f, 0xe5, 0xd7, 0xe1, 0x5d, 0xfe, 0xc0, 0xea, 0x53, 0xf7, 0xcd, 0xdb, 0x79, 0xc8, 0xee, + 0xd0, 0x77, 0xc1, 0xe3, 0xcd, 0x65, 0xda, 0xf4, 0xc6, 0x6b, 0xd3, 0xf9, 0xaf, 0x31, 0xa4, 0xb2, 0xa4, 0x3f, 0xad, + 0xbf, 0xb9, 0x2d, 0xb6, 0xa8, 0xb2, 0x23, 0xbf, 0xa5, 0x83, 0x09, 0x80, 0x86, 0x88, 0x07, 0x89, 0x8b, 0x95, 0x15, + 0x92, 0x9c, 0x9e, 0x1b, 0x9b, 0x91, 0x47, 0xa1, 0x7c, 0x0a, 0x4c, 0xaf, 0x75, 0x07, 0x51, 0xbd, 0x6e, 0x10, 0x5a, + 0xb3, 0x67, 0x1d, 0x6b, 0x99, 0x58, 0x3e, 0x60, 0x97, 0x51, 0x33, 0x7d, 0x85, 0x4a, 0x24, 0x76, 0x8b, 0x43, 0x29, + 0x1f, 0xd1, 0x34, 0x62, 0x14, 0xdf, 0x3d, 0x6f, 0x09, 0xcd, 0x26, 0x78, 0x02, 0xc3, 0x2f, 0x75, 0x33, 0xe9, 0x10, + 0x56, 0x38, 0xe7, 0x19, 0x5b, 0x25, 0xf5, 0x02, 0x4c, 0x2e, 0xfb, 0x0b, 0x41, 0x8c, 0x9a, 0xd7, 0x61, 0x87, 0x94, + 0xde, 0x6c, 0x9a, 0x86, 0xc5, 0x7b, 0x91, 0x88, 0xcc, 0x76, 0xa0, 0xa2, 0xf3, 0x55, 0xab, 0xac, 0xfa, 0x58, 0xb6, + 0xbe, 0xe1, 0x4f, 0xbd, 0xb0, 0xe8, 0x42, 0xd4, 0xea, 0x9f, 0x09, 0xdf, 0xe4, 0x96, 0x04, 0xc2, 0xf6, 0x8d, 0x13, + 0xc9, 0xf8, 0x84, 0x1e, 0xf8, 0xd2, 0xbb, 0x3d, 0xf3, 0xdc, 0xb2, 0x30, 0xee, 0xce, 0xa9, 0x27, 0xe5, 0xc0, 0xa0, + 0x2a, 0x3c, 0x7a, 0x47, 0xb1, 0x37, 0x74, 0x4e, 0xbc, 0x2a, 0x66, 0x55, 0xab, 0x21, 0x68, 0x5c, 0xa6, 0x10, 0x42, + 0x63, 0x85, 0x1b, 0x4c, 0x6a, 0x88, 0x06, 0x5e, 0x71, 0x9f, 0x0d, 0x50, 0x78, 0x92, 0x64, 0x0a, 0x0f, 0xd9, 0x6f, + 0x04, 0x06, 0xd4, 0x72, 0x16, 0x1d, 0xc3, 0x79, 0x18, 0x14, 0xce, 0x48, 0x32, 0x2b, 0xed, 0x43, 0x3c, 0x22, 0xe0, + 0x5e, 0x2e, 0x39, 0xf7, 0x55, 0x20, 0x30, 0xfa, 0x01, 0xec, 0x9a, 0xb7, 0x0a, 0xe2, 0x93, 0xba, 0x17, 0xf0, 0x88, + 0xad, 0x1c, 0xfe, 0x81, 0xa0, 0x2d, 0xd4, 0xbe, 0x83, 0x26, 0xda, 0xb7, 0x8e, 0x3b, 0xc8, 0xac, 0x99, 0x30, 0xc6, + 0xa5, 0x94, 0x59, 0x9c, 0xd2, 0xdf, 0x52, 0x92, 0xdb, 0xd2, 0x4f, 0x80, 0xc0, 0xc5, 0x44, 0x8e, 0xc9, 0xc8, 0x75, + 0xa4, 0xf6, 0xeb, 0x7e, 0xaa, 0xff, 0xe6, 0x63, 0xb8, 0xe4, 0xf1, 0x68, 0xb6, 0xed, 0xfc, 0xb1, 0x0c, 0x0a, 0x67, + 0xba, 0x02, 0x03, 0x6a, 0xa7, 0x10, 0x18, 0x7d, 0xac, 0x1e, 0x11, 0x70, 0x9d, 0x34, 0x2e, 0x53, 0x96, 0x3a, 0x27, + 0x5e, 0x8b, 0x28, 0x3c, 0x49, 0x80, 0x26, 0x35, 0x44, 0xe9, 0x7c, 0x42, 0x0f, 0xe2, 0x72, 0x4b, 0x02, 0xff, 0x60, + 0x50, 0x15, 0xf4, 0x6e, 0x59, 0x18, 0xc5, 0x44, 0x66, 0x3b, 0xce, 0x4a, 0x6f, 0x36, 0xd3, 0x58, 0x74, 0x21, 0xd8, + 0x56, 0x7d, 0x2c, 0x7a, 0x37, 0xa1, 0x0c, 0x71, 0x39, 0xa8, 0x01, 0x6c, 0x2b, 0xb3, 0x16, 0x67, 0x25, 0xba, 0x1b, + 0x56, 0x0f, 0x85, 0x38, 0x5d, 0x01, 0x8c, 0x35, 0x40, 0x13, 0x97, 0x22, 0x4b, 0x1d, 0x9e, 0x2f, 0x22, 0x47, 0xe9, + 0x64, 0x29, 0x49, 0xe0, 0x69, 0x34, 0x5b, 0xfb, 0x7e, 0x3f, 0x55, 0xf2, 0x73, 0x0e, 0x7f, 0xcd, 0x50, 0x05, 0x71, + 0xc4, 0x5d, 0x18, 0x63, 0xdf, 0x4a, 0x13, 0x6d, 0xd6, 0x47, 0xca, 0xd7, 0x31, 0xdc, 0xc1, 0xd9, 0x38, 0xd1, 0xdc, + 0xcb, 0x23, 0xc6, 0xd7, 0xc5, 0x2a, 0xcb, 0xe6, 0xef, 0x15, 0xe8, 0xed, 0xe1, 0x1c, 0xe5, 0xf0, 0xf3, 0x07, 0xf2, + 0xfb, 0xfd, 0x0e, 0xff, 0x92, 0xa7, 0x79, 0xb4, 0x99, 0xa9, 0x70, 0xb9, 0x84, 0xbb, 0x6b, 0xae, 0x8f, 0xb5, 0x62, + 0xa3, 0xbe, 0x9f, 0x5d, 0x80, 0xb5, 0x91, 0x54, 0x8d, 0xa8, 0x83, 0x4f, 0x9a, 0xa3, 0x8d, 0x46, 0x97 +}; + +static uint8_t U3[256][4] = { + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0b, 0x0e, 0x09, 0x1a, 0x16, 0x1c, 0x12, 0x17, 0x1d, 0x12, 0x1b, 0x34, 0x2c, 0x38, + 0x24, 0x39, 0x27, 0x36, 0x2d, 0x2e, 0x3a, 0x24, 0x36, 0x23, 0x31, 0x2a, 0x3f, 0x68, 0x58, 0x70, 0x48, 0x65, 0x53, + 0x7e, 0x41, 0x72, 0x4e, 0x6c, 0x5a, 0x7f, 0x45, 0x62, 0x53, 0x5c, 0x74, 0x48, 0x6c, 0x51, 0x7f, 0x46, 0x65, 0x46, + 0x62, 0x54, 0x7e, 0x4b, 0x69, 0x5a, 0x77, 0xd0, 0xb0, 0xe0, 0x90, 0xdd, 0xbb, 0xee, 0x99, 0xca, 0xa6, 0xfc, 0x82, + 0xc7, 0xad, 0xf2, 0x8b, 0xe4, 0x9c, 0xd8, 0xb4, 0xe9, 0x97, 0xd6, 0xbd, 0xfe, 0x8a, 0xc4, 0xa6, 0xf3, 0x81, 0xca, + 0xaf, 0xb8, 0xe8, 0x90, 0xd8, 0xb5, 0xe3, 0x9e, 0xd1, 0xa2, 0xfe, 0x8c, 0xca, 0xaf, 0xf5, 0x82, 0xc3, 0x8c, 0xc4, + 0xa8, 0xfc, 0x81, 0xcf, 0xa6, 0xf5, 0x96, 0xd2, 0xb4, 0xee, 0x9b, 0xd9, 0xba, 0xe7, 0xbb, 0x7b, 0xdb, 0x3b, 0xb6, + 0x70, 0xd5, 0x32, 0xa1, 0x6d, 0xc7, 0x29, 0xac, 0x66, 0xc9, 0x20, 0x8f, 0x57, 0xe3, 0x1f, 0x82, 0x5c, 0xed, 0x16, + 0x95, 0x41, 0xff, 0x0d, 0x98, 0x4a, 0xf1, 0x04, 0xd3, 0x23, 0xab, 0x73, 0xde, 0x28, 0xa5, 0x7a, 0xc9, 0x35, 0xb7, + 0x61, 0xc4, 0x3e, 0xb9, 0x68, 0xe7, 0x0f, 0x93, 0x57, 0xea, 0x04, 0x9d, 0x5e, 0xfd, 0x19, 0x8f, 0x45, 0xf0, 0x12, + 0x81, 0x4c, 0x6b, 0xcb, 0x3b, 0xab, 0x66, 0xc0, 0x35, 0xa2, 0x71, 0xdd, 0x27, 0xb9, 0x7c, 0xd6, 0x29, 0xb0, 0x5f, + 0xe7, 0x03, 0x8f, 0x52, 0xec, 0x0d, 0x86, 0x45, 0xf1, 0x1f, 0x9d, 0x48, 0xfa, 0x11, 0x94, 0x03, 0x93, 0x4b, 0xe3, + 0x0e, 0x98, 0x45, 0xea, 0x19, 0x85, 0x57, 0xf1, 0x14, 0x8e, 0x59, 0xf8, 0x37, 0xbf, 0x73, 0xc7, 0x3a, 0xb4, 0x7d, + 0xce, 0x2d, 0xa9, 0x6f, 0xd5, 0x20, 0xa2, 0x61, 0xdc, 0x6d, 0xf6, 0xad, 0x76, 0x60, 0xfd, 0xa3, 0x7f, 0x77, 0xe0, + 0xb1, 0x64, 0x7a, 0xeb, 0xbf, 0x6d, 0x59, 0xda, 0x95, 0x52, 0x54, 0xd1, 0x9b, 0x5b, 0x43, 0xcc, 0x89, 0x40, 0x4e, + 0xc7, 0x87, 0x49, 0x05, 0xae, 0xdd, 0x3e, 0x08, 0xa5, 0xd3, 0x37, 0x1f, 0xb8, 0xc1, 0x2c, 0x12, 0xb3, 0xcf, 0x25, + 0x31, 0x82, 0xe5, 0x1a, 0x3c, 0x89, 0xeb, 0x13, 0x2b, 0x94, 0xf9, 0x08, 0x26, 0x9f, 0xf7, 0x01, 0xbd, 0x46, 0x4d, + 0xe6, 0xb0, 0x4d, 0x43, 0xef, 0xa7, 0x50, 0x51, 0xf4, 0xaa, 0x5b, 0x5f, 0xfd, 0x89, 0x6a, 0x75, 0xc2, 0x84, 0x61, + 0x7b, 0xcb, 0x93, 0x7c, 0x69, 0xd0, 0x9e, 0x77, 0x67, 0xd9, 0xd5, 0x1e, 0x3d, 0xae, 0xd8, 0x15, 0x33, 0xa7, 0xcf, + 0x08, 0x21, 0xbc, 0xc2, 0x03, 0x2f, 0xb5, 0xe1, 0x32, 0x05, 0x8a, 0xec, 0x39, 0x0b, 0x83, 0xfb, 0x24, 0x19, 0x98, + 0xf6, 0x2f, 0x17, 0x91, 0xd6, 0x8d, 0x76, 0x4d, 0xdb, 0x86, 0x78, 0x44, 0xcc, 0x9b, 0x6a, 0x5f, 0xc1, 0x90, 0x64, + 0x56, 0xe2, 0xa1, 0x4e, 0x69, 0xef, 0xaa, 0x40, 0x60, 0xf8, 0xb7, 0x52, 0x7b, 0xf5, 0xbc, 0x5c, 0x72, 0xbe, 0xd5, + 0x06, 0x05, 0xb3, 0xde, 0x08, 0x0c, 0xa4, 0xc3, 0x1a, 0x17, 0xa9, 0xc8, 0x14, 0x1e, 0x8a, 0xf9, 0x3e, 0x21, 0x87, + 0xf2, 0x30, 0x28, 0x90, 0xef, 0x22, 0x33, 0x9d, 0xe4, 0x2c, 0x3a, 0x06, 0x3d, 0x96, 0xdd, 0x0b, 0x36, 0x98, 0xd4, + 0x1c, 0x2b, 0x8a, 0xcf, 0x11, 0x20, 0x84, 0xc6, 0x32, 0x11, 0xae, 0xf9, 0x3f, 0x1a, 0xa0, 0xf0, 0x28, 0x07, 0xb2, + 0xeb, 0x25, 0x0c, 0xbc, 0xe2, 0x6e, 0x65, 0xe6, 0x95, 0x63, 0x6e, 0xe8, 0x9c, 0x74, 0x73, 0xfa, 0x87, 0x79, 0x78, + 0xf4, 0x8e, 0x5a, 0x49, 0xde, 0xb1, 0x57, 0x42, 0xd0, 0xb8, 0x40, 0x5f, 0xc2, 0xa3, 0x4d, 0x54, 0xcc, 0xaa, 0xda, + 0xf7, 0x41, 0xec, 0xd7, 0xfc, 0x4f, 0xe5, 0xc0, 0xe1, 0x5d, 0xfe, 0xcd, 0xea, 0x53, 0xf7, 0xee, 0xdb, 0x79, 0xc8, + 0xe3, 0xd0, 0x77, 0xc1, 0xf4, 0xcd, 0x65, 0xda, 0xf9, 0xc6, 0x6b, 0xd3, 0xb2, 0xaf, 0x31, 0xa4, 0xbf, 0xa4, 0x3f, + 0xad, 0xa8, 0xb9, 0x2d, 0xb6, 0xa5, 0xb2, 0x23, 0xbf, 0x86, 0x83, 0x09, 0x80, 0x8b, 0x88, 0x07, 0x89, 0x9c, 0x95, + 0x15, 0x92, 0x91, 0x9e, 0x1b, 0x9b, 0x0a, 0x47, 0xa1, 0x7c, 0x07, 0x4c, 0xaf, 0x75, 0x10, 0x51, 0xbd, 0x6e, 0x1d, + 0x5a, 0xb3, 0x67, 0x3e, 0x6b, 0x99, 0x58, 0x33, 0x60, 0x97, 0x51, 0x24, 0x7d, 0x85, 0x4a, 0x29, 0x76, 0x8b, 0x43, + 0x62, 0x1f, 0xd1, 0x34, 0x6f, 0x14, 0xdf, 0x3d, 0x78, 0x09, 0xcd, 0x26, 0x75, 0x02, 0xc3, 0x2f, 0x56, 0x33, 0xe9, + 0x10, 0x5b, 0x38, 0xe7, 0x19, 0x4c, 0x25, 0xf5, 0x02, 0x41, 0x2e, 0xfb, 0x0b, 0x61, 0x8c, 0x9a, 0xd7, 0x6c, 0x87, + 0x94, 0xde, 0x7b, 0x9a, 0x86, 0xc5, 0x76, 0x91, 0x88, 0xcc, 0x55, 0xa0, 0xa2, 0xf3, 0x58, 0xab, 0xac, 0xfa, 0x4f, + 0xb6, 0xbe, 0xe1, 0x42, 0xbd, 0xb0, 0xe8, 0x09, 0xd4, 0xea, 0x9f, 0x04, 0xdf, 0xe4, 0x96, 0x13, 0xc2, 0xf6, 0x8d, + 0x1e, 0xc9, 0xf8, 0x84, 0x3d, 0xf8, 0xd2, 0xbb, 0x30, 0xf3, 0xdc, 0xb2, 0x27, 0xee, 0xce, 0xa9, 0x2a, 0xe5, 0xc0, + 0xa0, 0xb1, 0x3c, 0x7a, 0x47, 0xbc, 0x37, 0x74, 0x4e, 0xab, 0x2a, 0x66, 0x55, 0xa6, 0x21, 0x68, 0x5c, 0x85, 0x10, + 0x42, 0x63, 0x88, 0x1b, 0x4c, 0x6a, 0x9f, 0x06, 0x5e, 0x71, 0x92, 0x0d, 0x50, 0x78, 0xd9, 0x64, 0x0a, 0x0f, 0xd4, + 0x6f, 0x04, 0x06, 0xc3, 0x72, 0x16, 0x1d, 0xce, 0x79, 0x18, 0x14, 0xed, 0x48, 0x32, 0x2b, 0xe0, 0x43, 0x3c, 0x22, + 0xf7, 0x5e, 0x2e, 0x39, 0xfa, 0x55, 0x20, 0x30, 0xb7, 0x01, 0xec, 0x9a, 0xba, 0x0a, 0xe2, 0x93, 0xad, 0x17, 0xf0, + 0x88, 0xa0, 0x1c, 0xfe, 0x81, 0x83, 0x2d, 0xd4, 0xbe, 0x8e, 0x26, 0xda, 0xb7, 0x99, 0x3b, 0xc8, 0xac, 0x94, 0x30, + 0xc6, 0xa5, 0xdf, 0x59, 0x9c, 0xd2, 0xd2, 0x52, 0x92, 0xdb, 0xc5, 0x4f, 0x80, 0xc0, 0xc8, 0x44, 0x8e, 0xc9, 0xeb, + 0x75, 0xa4, 0xf6, 0xe6, 0x7e, 0xaa, 0xff, 0xf1, 0x63, 0xb8, 0xe4, 0xfc, 0x68, 0xb6, 0xed, 0x67, 0xb1, 0x0c, 0x0a, + 0x6a, 0xba, 0x02, 0x03, 0x7d, 0xa7, 0x10, 0x18, 0x70, 0xac, 0x1e, 0x11, 0x53, 0x9d, 0x34, 0x2e, 0x5e, 0x96, 0x3a, + 0x27, 0x49, 0x8b, 0x28, 0x3c, 0x44, 0x80, 0x26, 0x35, 0x0f, 0xe9, 0x7c, 0x42, 0x02, 0xe2, 0x72, 0x4b, 0x15, 0xff, + 0x60, 0x50, 0x18, 0xf4, 0x6e, 0x59, 0x3b, 0xc5, 0x44, 0x66, 0x36, 0xce, 0x4a, 0x6f, 0x21, 0xd3, 0x58, 0x74, 0x2c, + 0xd8, 0x56, 0x7d, 0x0c, 0x7a, 0x37, 0xa1, 0x01, 0x71, 0x39, 0xa8, 0x16, 0x6c, 0x2b, 0xb3, 0x1b, 0x67, 0x25, 0xba, + 0x38, 0x56, 0x0f, 0x85, 0x35, 0x5d, 0x01, 0x8c, 0x22, 0x40, 0x13, 0x97, 0x2f, 0x4b, 0x1d, 0x9e, 0x64, 0x22, 0x47, + 0xe9, 0x69, 0x29, 0x49, 0xe0, 0x7e, 0x34, 0x5b, 0xfb, 0x73, 0x3f, 0x55, 0xf2, 0x50, 0x0e, 0x7f, 0xcd, 0x5d, 0x05, + 0x71, 0xc4, 0x4a, 0x18, 0x63, 0xdf, 0x47, 0x13, 0x6d, 0xd6, 0xdc, 0xca, 0xd7, 0x31, 0xd1, 0xc1, 0xd9, 0x38, 0xc6, + 0xdc, 0xcb, 0x23, 0xcb, 0xd7, 0xc5, 0x2a, 0xe8, 0xe6, 0xef, 0x15, 0xe5, 0xed, 0xe1, 0x1c, 0xf2, 0xf0, 0xf3, 0x07, + 0xff, 0xfb, 0xfd, 0x0e, 0xb4, 0x92, 0xa7, 0x79, 0xb9, 0x99, 0xa9, 0x70, 0xae, 0x84, 0xbb, 0x6b, 0xa3, 0x8f, 0xb5, + 0x62, 0x80, 0xbe, 0x9f, 0x5d, 0x8d, 0xb5, 0x91, 0x54, 0x9a, 0xa8, 0x83, 0x4f, 0x97, 0xa3, 0x8d, 0x46 +}; + +static uint8_t U4[256][4] = { + 0x00, 0x00, 0x00, 0x00, 0x09, 0x0d, 0x0b, 0x0e, 0x12, 0x1a, 0x16, 0x1c, 0x1b, 0x17, 0x1d, 0x12, 0x24, 0x34, 0x2c, + 0x38, 0x2d, 0x39, 0x27, 0x36, 0x36, 0x2e, 0x3a, 0x24, 0x3f, 0x23, 0x31, 0x2a, 0x48, 0x68, 0x58, 0x70, 0x41, 0x65, + 0x53, 0x7e, 0x5a, 0x72, 0x4e, 0x6c, 0x53, 0x7f, 0x45, 0x62, 0x6c, 0x5c, 0x74, 0x48, 0x65, 0x51, 0x7f, 0x46, 0x7e, + 0x46, 0x62, 0x54, 0x77, 0x4b, 0x69, 0x5a, 0x90, 0xd0, 0xb0, 0xe0, 0x99, 0xdd, 0xbb, 0xee, 0x82, 0xca, 0xa6, 0xfc, + 0x8b, 0xc7, 0xad, 0xf2, 0xb4, 0xe4, 0x9c, 0xd8, 0xbd, 0xe9, 0x97, 0xd6, 0xa6, 0xfe, 0x8a, 0xc4, 0xaf, 0xf3, 0x81, + 0xca, 0xd8, 0xb8, 0xe8, 0x90, 0xd1, 0xb5, 0xe3, 0x9e, 0xca, 0xa2, 0xfe, 0x8c, 0xc3, 0xaf, 0xf5, 0x82, 0xfc, 0x8c, + 0xc4, 0xa8, 0xf5, 0x81, 0xcf, 0xa6, 0xee, 0x96, 0xd2, 0xb4, 0xe7, 0x9b, 0xd9, 0xba, 0x3b, 0xbb, 0x7b, 0xdb, 0x32, + 0xb6, 0x70, 0xd5, 0x29, 0xa1, 0x6d, 0xc7, 0x20, 0xac, 0x66, 0xc9, 0x1f, 0x8f, 0x57, 0xe3, 0x16, 0x82, 0x5c, 0xed, + 0x0d, 0x95, 0x41, 0xff, 0x04, 0x98, 0x4a, 0xf1, 0x73, 0xd3, 0x23, 0xab, 0x7a, 0xde, 0x28, 0xa5, 0x61, 0xc9, 0x35, + 0xb7, 0x68, 0xc4, 0x3e, 0xb9, 0x57, 0xe7, 0x0f, 0x93, 0x5e, 0xea, 0x04, 0x9d, 0x45, 0xfd, 0x19, 0x8f, 0x4c, 0xf0, + 0x12, 0x81, 0xab, 0x6b, 0xcb, 0x3b, 0xa2, 0x66, 0xc0, 0x35, 0xb9, 0x71, 0xdd, 0x27, 0xb0, 0x7c, 0xd6, 0x29, 0x8f, + 0x5f, 0xe7, 0x03, 0x86, 0x52, 0xec, 0x0d, 0x9d, 0x45, 0xf1, 0x1f, 0x94, 0x48, 0xfa, 0x11, 0xe3, 0x03, 0x93, 0x4b, + 0xea, 0x0e, 0x98, 0x45, 0xf1, 0x19, 0x85, 0x57, 0xf8, 0x14, 0x8e, 0x59, 0xc7, 0x37, 0xbf, 0x73, 0xce, 0x3a, 0xb4, + 0x7d, 0xd5, 0x2d, 0xa9, 0x6f, 0xdc, 0x20, 0xa2, 0x61, 0x76, 0x6d, 0xf6, 0xad, 0x7f, 0x60, 0xfd, 0xa3, 0x64, 0x77, + 0xe0, 0xb1, 0x6d, 0x7a, 0xeb, 0xbf, 0x52, 0x59, 0xda, 0x95, 0x5b, 0x54, 0xd1, 0x9b, 0x40, 0x43, 0xcc, 0x89, 0x49, + 0x4e, 0xc7, 0x87, 0x3e, 0x05, 0xae, 0xdd, 0x37, 0x08, 0xa5, 0xd3, 0x2c, 0x1f, 0xb8, 0xc1, 0x25, 0x12, 0xb3, 0xcf, + 0x1a, 0x31, 0x82, 0xe5, 0x13, 0x3c, 0x89, 0xeb, 0x08, 0x2b, 0x94, 0xf9, 0x01, 0x26, 0x9f, 0xf7, 0xe6, 0xbd, 0x46, + 0x4d, 0xef, 0xb0, 0x4d, 0x43, 0xf4, 0xa7, 0x50, 0x51, 0xfd, 0xaa, 0x5b, 0x5f, 0xc2, 0x89, 0x6a, 0x75, 0xcb, 0x84, + 0x61, 0x7b, 0xd0, 0x93, 0x7c, 0x69, 0xd9, 0x9e, 0x77, 0x67, 0xae, 0xd5, 0x1e, 0x3d, 0xa7, 0xd8, 0x15, 0x33, 0xbc, + 0xcf, 0x08, 0x21, 0xb5, 0xc2, 0x03, 0x2f, 0x8a, 0xe1, 0x32, 0x05, 0x83, 0xec, 0x39, 0x0b, 0x98, 0xfb, 0x24, 0x19, + 0x91, 0xf6, 0x2f, 0x17, 0x4d, 0xd6, 0x8d, 0x76, 0x44, 0xdb, 0x86, 0x78, 0x5f, 0xcc, 0x9b, 0x6a, 0x56, 0xc1, 0x90, + 0x64, 0x69, 0xe2, 0xa1, 0x4e, 0x60, 0xef, 0xaa, 0x40, 0x7b, 0xf8, 0xb7, 0x52, 0x72, 0xf5, 0xbc, 0x5c, 0x05, 0xbe, + 0xd5, 0x06, 0x0c, 0xb3, 0xde, 0x08, 0x17, 0xa4, 0xc3, 0x1a, 0x1e, 0xa9, 0xc8, 0x14, 0x21, 0x8a, 0xf9, 0x3e, 0x28, + 0x87, 0xf2, 0x30, 0x33, 0x90, 0xef, 0x22, 0x3a, 0x9d, 0xe4, 0x2c, 0xdd, 0x06, 0x3d, 0x96, 0xd4, 0x0b, 0x36, 0x98, + 0xcf, 0x1c, 0x2b, 0x8a, 0xc6, 0x11, 0x20, 0x84, 0xf9, 0x32, 0x11, 0xae, 0xf0, 0x3f, 0x1a, 0xa0, 0xeb, 0x28, 0x07, + 0xb2, 0xe2, 0x25, 0x0c, 0xbc, 0x95, 0x6e, 0x65, 0xe6, 0x9c, 0x63, 0x6e, 0xe8, 0x87, 0x74, 0x73, 0xfa, 0x8e, 0x79, + 0x78, 0xf4, 0xb1, 0x5a, 0x49, 0xde, 0xb8, 0x57, 0x42, 0xd0, 0xa3, 0x40, 0x5f, 0xc2, 0xaa, 0x4d, 0x54, 0xcc, 0xec, + 0xda, 0xf7, 0x41, 0xe5, 0xd7, 0xfc, 0x4f, 0xfe, 0xc0, 0xe1, 0x5d, 0xf7, 0xcd, 0xea, 0x53, 0xc8, 0xee, 0xdb, 0x79, + 0xc1, 0xe3, 0xd0, 0x77, 0xda, 0xf4, 0xcd, 0x65, 0xd3, 0xf9, 0xc6, 0x6b, 0xa4, 0xb2, 0xaf, 0x31, 0xad, 0xbf, 0xa4, + 0x3f, 0xb6, 0xa8, 0xb9, 0x2d, 0xbf, 0xa5, 0xb2, 0x23, 0x80, 0x86, 0x83, 0x09, 0x89, 0x8b, 0x88, 0x07, 0x92, 0x9c, + 0x95, 0x15, 0x9b, 0x91, 0x9e, 0x1b, 0x7c, 0x0a, 0x47, 0xa1, 0x75, 0x07, 0x4c, 0xaf, 0x6e, 0x10, 0x51, 0xbd, 0x67, + 0x1d, 0x5a, 0xb3, 0x58, 0x3e, 0x6b, 0x99, 0x51, 0x33, 0x60, 0x97, 0x4a, 0x24, 0x7d, 0x85, 0x43, 0x29, 0x76, 0x8b, + 0x34, 0x62, 0x1f, 0xd1, 0x3d, 0x6f, 0x14, 0xdf, 0x26, 0x78, 0x09, 0xcd, 0x2f, 0x75, 0x02, 0xc3, 0x10, 0x56, 0x33, + 0xe9, 0x19, 0x5b, 0x38, 0xe7, 0x02, 0x4c, 0x25, 0xf5, 0x0b, 0x41, 0x2e, 0xfb, 0xd7, 0x61, 0x8c, 0x9a, 0xde, 0x6c, + 0x87, 0x94, 0xc5, 0x7b, 0x9a, 0x86, 0xcc, 0x76, 0x91, 0x88, 0xf3, 0x55, 0xa0, 0xa2, 0xfa, 0x58, 0xab, 0xac, 0xe1, + 0x4f, 0xb6, 0xbe, 0xe8, 0x42, 0xbd, 0xb0, 0x9f, 0x09, 0xd4, 0xea, 0x96, 0x04, 0xdf, 0xe4, 0x8d, 0x13, 0xc2, 0xf6, + 0x84, 0x1e, 0xc9, 0xf8, 0xbb, 0x3d, 0xf8, 0xd2, 0xb2, 0x30, 0xf3, 0xdc, 0xa9, 0x27, 0xee, 0xce, 0xa0, 0x2a, 0xe5, + 0xc0, 0x47, 0xb1, 0x3c, 0x7a, 0x4e, 0xbc, 0x37, 0x74, 0x55, 0xab, 0x2a, 0x66, 0x5c, 0xa6, 0x21, 0x68, 0x63, 0x85, + 0x10, 0x42, 0x6a, 0x88, 0x1b, 0x4c, 0x71, 0x9f, 0x06, 0x5e, 0x78, 0x92, 0x0d, 0x50, 0x0f, 0xd9, 0x64, 0x0a, 0x06, + 0xd4, 0x6f, 0x04, 0x1d, 0xc3, 0x72, 0x16, 0x14, 0xce, 0x79, 0x18, 0x2b, 0xed, 0x48, 0x32, 0x22, 0xe0, 0x43, 0x3c, + 0x39, 0xf7, 0x5e, 0x2e, 0x30, 0xfa, 0x55, 0x20, 0x9a, 0xb7, 0x01, 0xec, 0x93, 0xba, 0x0a, 0xe2, 0x88, 0xad, 0x17, + 0xf0, 0x81, 0xa0, 0x1c, 0xfe, 0xbe, 0x83, 0x2d, 0xd4, 0xb7, 0x8e, 0x26, 0xda, 0xac, 0x99, 0x3b, 0xc8, 0xa5, 0x94, + 0x30, 0xc6, 0xd2, 0xdf, 0x59, 0x9c, 0xdb, 0xd2, 0x52, 0x92, 0xc0, 0xc5, 0x4f, 0x80, 0xc9, 0xc8, 0x44, 0x8e, 0xf6, + 0xeb, 0x75, 0xa4, 0xff, 0xe6, 0x7e, 0xaa, 0xe4, 0xf1, 0x63, 0xb8, 0xed, 0xfc, 0x68, 0xb6, 0x0a, 0x67, 0xb1, 0x0c, + 0x03, 0x6a, 0xba, 0x02, 0x18, 0x7d, 0xa7, 0x10, 0x11, 0x70, 0xac, 0x1e, 0x2e, 0x53, 0x9d, 0x34, 0x27, 0x5e, 0x96, + 0x3a, 0x3c, 0x49, 0x8b, 0x28, 0x35, 0x44, 0x80, 0x26, 0x42, 0x0f, 0xe9, 0x7c, 0x4b, 0x02, 0xe2, 0x72, 0x50, 0x15, + 0xff, 0x60, 0x59, 0x18, 0xf4, 0x6e, 0x66, 0x3b, 0xc5, 0x44, 0x6f, 0x36, 0xce, 0x4a, 0x74, 0x21, 0xd3, 0x58, 0x7d, + 0x2c, 0xd8, 0x56, 0xa1, 0x0c, 0x7a, 0x37, 0xa8, 0x01, 0x71, 0x39, 0xb3, 0x16, 0x6c, 0x2b, 0xba, 0x1b, 0x67, 0x25, + 0x85, 0x38, 0x56, 0x0f, 0x8c, 0x35, 0x5d, 0x01, 0x97, 0x22, 0x40, 0x13, 0x9e, 0x2f, 0x4b, 0x1d, 0xe9, 0x64, 0x22, + 0x47, 0xe0, 0x69, 0x29, 0x49, 0xfb, 0x7e, 0x34, 0x5b, 0xf2, 0x73, 0x3f, 0x55, 0xcd, 0x50, 0x0e, 0x7f, 0xc4, 0x5d, + 0x05, 0x71, 0xdf, 0x4a, 0x18, 0x63, 0xd6, 0x47, 0x13, 0x6d, 0x31, 0xdc, 0xca, 0xd7, 0x38, 0xd1, 0xc1, 0xd9, 0x23, + 0xc6, 0xdc, 0xcb, 0x2a, 0xcb, 0xd7, 0xc5, 0x15, 0xe8, 0xe6, 0xef, 0x1c, 0xe5, 0xed, 0xe1, 0x07, 0xf2, 0xf0, 0xf3, + 0x0e, 0xff, 0xfb, 0xfd, 0x79, 0xb4, 0x92, 0xa7, 0x70, 0xb9, 0x99, 0xa9, 0x6b, 0xae, 0x84, 0xbb, 0x62, 0xa3, 0x8f, + 0xb5, 0x5d, 0x80, 0xbe, 0x9f, 0x54, 0x8d, 0xb5, 0x91, 0x4f, 0x9a, 0xa8, 0x83, 0x46, 0x97, 0xa3, 0x8d +}; + +static uint32_t rcon[30] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// API +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Rijndael::Rijndael() +{ + m_state = Invalid; +} + +Rijndael::~Rijndael() +{ + // nothing here +} + +int Rijndael::init(Mode mode, Direction dir, const uint8_t *key, KeyLength keyLen, uint8_t *initVector) +{ + // Not initialized yet + m_state = Invalid; + + // Check the mode + if ((mode != CBC) && (mode != ECB) && (mode != CFB1)) + return RIJNDAEL_UNSUPPORTED_MODE; + m_mode = mode; + + // And the direction + if ((dir != Encrypt) && (dir != Decrypt)) + return RIJNDAEL_UNSUPPORTED_DIRECTION; + m_direction = dir; + + // Allow to set an init vector + if (initVector) + { + // specified init vector + for (int i = 0; i < MAX_IV_SIZE; i++) + { + m_initVector[i] = initVector[i]; + } + } + else + { + // zero init vector + for (int i = 0; i < MAX_IV_SIZE; i++) + { + m_initVector[i] = 0; + } + } + + uint32_t uKeyLenInBytes; + + // And check the key length + switch (keyLen) + { + case Key16Bytes: + uKeyLenInBytes = 16; + m_uRounds = 10; + break; + case Key24Bytes: + uKeyLenInBytes = 24; + m_uRounds = 12; + break; + case Key32Bytes: + uKeyLenInBytes = 32; + m_uRounds = 14; + break; + default: + return RIJNDAEL_UNSUPPORTED_KEY_LENGTH; + break; + } + // The number of rounds is calculated as + // m_uRounds = (m_uKeyLenInBits / 32) + 6; + + if (!key) + return RIJNDAEL_BAD_KEY; + + uint8_t keyMatrix[_MAX_KEY_COLUMNS][4]; + + for (uint32_t i = 0; i < uKeyLenInBytes; i++) + keyMatrix[i >> 2][i & 3] = key[i]; + + keySched(keyMatrix); + + if (m_direction == Decrypt) + keyEncToDec(); + + m_state = Valid; + + return RIJNDAEL_SUCCESS; +} + +int Rijndael::blockEncrypt(const uint8_t *input, int inputLen, uint8_t *outBuffer) +{ + int i, k, numBlocks; + uint8_t block[16], iv[4][4]; + + if (m_state != Valid) + return RIJNDAEL_NOT_INITIALIZED; + if (m_direction != Encrypt) + return RIJNDAEL_BAD_DIRECTION; + + if (input == 0 || inputLen <= 0) + return 0; + + numBlocks = inputLen / 128; + + switch (m_mode) + { + case ECB: + for (i = numBlocks; i > 0; i--) + { + encrypt(input, outBuffer); + input += 16; + outBuffer += 16; + } + break; + case CBC: + ((uint32_t *)block)[0] = ((uint32_t *)m_initVector)[0] ^ ((uint32_t *)input)[0]; + ((uint32_t *)block)[1] = ((uint32_t *)m_initVector)[1] ^ ((uint32_t *)input)[1]; + ((uint32_t *)block)[2] = ((uint32_t *)m_initVector)[2] ^ ((uint32_t *)input)[2]; + ((uint32_t *)block)[3] = ((uint32_t *)m_initVector)[3] ^ ((uint32_t *)input)[3]; + encrypt(block, outBuffer); + input += 16; + for (i = numBlocks - 1; i > 0; i--) + { + ((uint32_t *)block)[0] = ((uint32_t *)outBuffer)[0] ^ ((uint32_t *)input)[0]; + ((uint32_t *)block)[1] = ((uint32_t *)outBuffer)[1] ^ ((uint32_t *)input)[1]; + ((uint32_t *)block)[2] = ((uint32_t *)outBuffer)[2] ^ ((uint32_t *)input)[2]; + ((uint32_t *)block)[3] = ((uint32_t *)outBuffer)[3] ^ ((uint32_t *)input)[3]; + outBuffer += 16; + encrypt(block, outBuffer); + input += 16; + } + break; + case CFB1: +#if STRICT_ALIGN + memcpy(iv, m_initVector, 16); +#else /* !STRICT_ALIGN */ + *((uint32_t *)iv[0]) = *((uint32_t *)(m_initVector)); + *((uint32_t *)iv[1]) = *((uint32_t *)(m_initVector + 4)); + *((uint32_t *)iv[2]) = *((uint32_t *)(m_initVector + 8)); + *((uint32_t *)iv[3]) = *((uint32_t *)(m_initVector + 12)); +#endif /* ?STRICT_ALIGN */ + for (i = numBlocks; i > 0; i--) + { + for (k = 0; k < 128; k++) + { + *((uint32_t *)block) = *((uint32_t *)iv[0]); + *((uint32_t *)(block + 4)) = *((uint32_t *)iv[1]); + *((uint32_t *)(block + 8)) = *((uint32_t *)iv[2]); + *((uint32_t *)(block + 12)) = *((uint32_t *)iv[3]); + encrypt(block, block); + outBuffer[k / 8] ^= (block[0] & 0x80) >> (k & 7); + iv[0][0] = (iv[0][0] << 1) | (iv[0][1] >> 7); + iv[0][1] = (iv[0][1] << 1) | (iv[0][2] >> 7); + iv[0][2] = (iv[0][2] << 1) | (iv[0][3] >> 7); + iv[0][3] = (iv[0][3] << 1) | (iv[1][0] >> 7); + iv[1][0] = (iv[1][0] << 1) | (iv[1][1] >> 7); + iv[1][1] = (iv[1][1] << 1) | (iv[1][2] >> 7); + iv[1][2] = (iv[1][2] << 1) | (iv[1][3] >> 7); + iv[1][3] = (iv[1][3] << 1) | (iv[2][0] >> 7); + iv[2][0] = (iv[2][0] << 1) | (iv[2][1] >> 7); + iv[2][1] = (iv[2][1] << 1) | (iv[2][2] >> 7); + iv[2][2] = (iv[2][2] << 1) | (iv[2][3] >> 7); + iv[2][3] = (iv[2][3] << 1) | (iv[3][0] >> 7); + iv[3][0] = (iv[3][0] << 1) | (iv[3][1] >> 7); + iv[3][1] = (iv[3][1] << 1) | (iv[3][2] >> 7); + iv[3][2] = (iv[3][2] << 1) | (iv[3][3] >> 7); + iv[3][3] = (iv[3][3] << 1) | ((outBuffer[k / 8] >> (7 - (k & 7))) & 1); + } + } + break; + default: + return -1; + break; + } + + return 128 * numBlocks; +} + +int Rijndael::padEncrypt(const uint8_t *input, int inputOctets, uint8_t *outBuffer) +{ + int i, numBlocks, padLen; + uint8_t block[16], *iv; + + if (m_state != Valid) + return RIJNDAEL_NOT_INITIALIZED; + if (m_direction != Encrypt) + return RIJNDAEL_NOT_INITIALIZED; + + if (input == 0 || inputOctets <= 0) + return 0; + + numBlocks = inputOctets / 16; + + switch (m_mode) + { + case ECB: + for (i = numBlocks; i > 0; i--) + { + encrypt(input, outBuffer); + input += 16; + outBuffer += 16; + } + padLen = 16 - (inputOctets - 16 * numBlocks); + // assert(padLen > 0 && padLen <= 16); + memcpy(block, input, 16 - padLen); + memset(block + 16 - padLen, padLen, padLen); + encrypt(block, outBuffer); + break; + case CBC: + iv = m_initVector; + for (i = numBlocks; i > 0; i--) + { + ((uint32_t *)block)[0] = ((uint32_t *)input)[0] ^ ((uint32_t *)iv)[0]; + ((uint32_t *)block)[1] = ((uint32_t *)input)[1] ^ ((uint32_t *)iv)[1]; + ((uint32_t *)block)[2] = ((uint32_t *)input)[2] ^ ((uint32_t *)iv)[2]; + ((uint32_t *)block)[3] = ((uint32_t *)input)[3] ^ ((uint32_t *)iv)[3]; + encrypt(block, outBuffer); + iv = outBuffer; + input += 16; + outBuffer += 16; + } + padLen = 16 - (inputOctets - 16 * numBlocks); + // assert(padLen > 0 && padLen <= 16); // DO SOMETHING HERE ? + for (i = 0; i < 16 - padLen; i++) + { + block[i] = input[i] ^ iv[i]; + } + for (i = 16 - padLen; i < 16; i++) + { + block[i] = (uint8_t)padLen ^ iv[i]; + } + encrypt(block, outBuffer); + break; + default: + return -1; + break; + } + + return 16 * (numBlocks + 1); +} + +int Rijndael::blockDecrypt(const uint8_t *input, int inputLen, uint8_t *outBuffer) +{ + int i, k, numBlocks; + uint8_t block[16], iv[4][4]; + + if (m_state != Valid) + return RIJNDAEL_NOT_INITIALIZED; + if ((m_mode != CFB1) && (m_direction == Encrypt)) + return RIJNDAEL_BAD_DIRECTION; + + if (input == 0 || inputLen <= 0) + return 0; + + numBlocks = inputLen / 128; + + switch (m_mode) + { + case ECB: + for (i = numBlocks; i > 0; i--) + { + decrypt(input, outBuffer); + input += 16; + outBuffer += 16; + } + break; + case CBC: +#if STRICT_ALIGN + memcpy(iv, m_initVector, 16); +#else + *((uint32_t *)iv[0]) = *((uint32_t *)(m_initVector)); + *((uint32_t *)iv[1]) = *((uint32_t *)(m_initVector + 4)); + *((uint32_t *)iv[2]) = *((uint32_t *)(m_initVector + 8)); + *((uint32_t *)iv[3]) = *((uint32_t *)(m_initVector + 12)); +#endif + for (i = numBlocks; i > 0; i--) + { + decrypt(input, block); + ((uint32_t *)block)[0] ^= *((uint32_t *)iv[0]); + ((uint32_t *)block)[1] ^= *((uint32_t *)iv[1]); + ((uint32_t *)block)[2] ^= *((uint32_t *)iv[2]); + ((uint32_t *)block)[3] ^= *((uint32_t *)iv[3]); +#if STRICT_ALIGN + memcpy(iv, input, 16); + memcpy(outBuf, block, 16); +#else + *((uint32_t *)iv[0]) = ((uint32_t *)input)[0]; + ((uint32_t *)outBuffer)[0] = ((uint32_t *)block)[0]; + *((uint32_t *)iv[1]) = ((uint32_t *)input)[1]; + ((uint32_t *)outBuffer)[1] = ((uint32_t *)block)[1]; + *((uint32_t *)iv[2]) = ((uint32_t *)input)[2]; + ((uint32_t *)outBuffer)[2] = ((uint32_t *)block)[2]; + *((uint32_t *)iv[3]) = ((uint32_t *)input)[3]; + ((uint32_t *)outBuffer)[3] = ((uint32_t *)block)[3]; +#endif + input += 16; + outBuffer += 16; + } + break; + case CFB1: +#if STRICT_ALIGN + memcpy(iv, m_initVector, 16); +#else + *((uint32_t *)iv[0]) = *((uint32_t *)(m_initVector)); + *((uint32_t *)iv[1]) = *((uint32_t *)(m_initVector + 4)); + *((uint32_t *)iv[2]) = *((uint32_t *)(m_initVector + 8)); + *((uint32_t *)iv[3]) = *((uint32_t *)(m_initVector + 12)); +#endif + for (i = numBlocks; i > 0; i--) + { + for (k = 0; k < 128; k++) + { + *((uint32_t *)block) = *((uint32_t *)iv[0]); + *((uint32_t *)(block + 4)) = *((uint32_t *)iv[1]); + *((uint32_t *)(block + 8)) = *((uint32_t *)iv[2]); + *((uint32_t *)(block + 12)) = *((uint32_t *)iv[3]); + encrypt(block, block); + iv[0][0] = (iv[0][0] << 1) | (iv[0][1] >> 7); + iv[0][1] = (iv[0][1] << 1) | (iv[0][2] >> 7); + iv[0][2] = (iv[0][2] << 1) | (iv[0][3] >> 7); + iv[0][3] = (iv[0][3] << 1) | (iv[1][0] >> 7); + iv[1][0] = (iv[1][0] << 1) | (iv[1][1] >> 7); + iv[1][1] = (iv[1][1] << 1) | (iv[1][2] >> 7); + iv[1][2] = (iv[1][2] << 1) | (iv[1][3] >> 7); + iv[1][3] = (iv[1][3] << 1) | (iv[2][0] >> 7); + iv[2][0] = (iv[2][0] << 1) | (iv[2][1] >> 7); + iv[2][1] = (iv[2][1] << 1) | (iv[2][2] >> 7); + iv[2][2] = (iv[2][2] << 1) | (iv[2][3] >> 7); + iv[2][3] = (iv[2][3] << 1) | (iv[3][0] >> 7); + iv[3][0] = (iv[3][0] << 1) | (iv[3][1] >> 7); + iv[3][1] = (iv[3][1] << 1) | (iv[3][2] >> 7); + iv[3][2] = (iv[3][2] << 1) | (iv[3][3] >> 7); + iv[3][3] = (iv[3][3] << 1) | ((input[k / 8] >> (7 - (k & 7))) & 1); + outBuffer[k / 8] ^= (block[0] & 0x80) >> (k & 7); + } + } + break; + default: + return -1; + break; + } + + return 128 * numBlocks; +} + +int Rijndael::padDecrypt(const uint8_t *input, int inputOctets, uint8_t *outBuffer) +{ + int i, numBlocks, padLen; + uint8_t block[16]; + uint32_t iv[4]; + + if (m_state != Valid) + return RIJNDAEL_NOT_INITIALIZED; + if (m_direction != Decrypt) + return RIJNDAEL_BAD_DIRECTION; + + if (input == 0 || inputOctets <= 0) + return 0; + + if ((inputOctets % 16) != 0) + return RIJNDAEL_CORRUPTED_DATA; + + numBlocks = inputOctets / 16; + + switch (m_mode) + { + case ECB: + for (i = numBlocks - 1; i > 0; i--) + { + decrypt(input, outBuffer); + input += 16; + outBuffer += 16; + } + + decrypt(input, block); + padLen = block[15]; + if (padLen >= 16) + return RIJNDAEL_CORRUPTED_DATA; + for (i = 16 - padLen; i < 16; i++) + { + if (block[i] != padLen) + return RIJNDAEL_CORRUPTED_DATA; + } + memcpy(outBuffer, block, 16 - padLen); + break; + case CBC: + memcpy(iv, m_initVector, 16); + /* all blocks but last */ + for (i = numBlocks - 1; i > 0; i--) + { + decrypt(input, block); + ((uint32_t *)block)[0] ^= iv[0]; + ((uint32_t *)block)[1] ^= iv[1]; + ((uint32_t *)block)[2] ^= iv[2]; + ((uint32_t *)block)[3] ^= iv[3]; + memcpy(iv, input, 16); + memcpy(outBuffer, block, 16); + input += 16; + outBuffer += 16; + } + /* last block */ + decrypt(input, block); + ((uint32_t *)block)[0] ^= iv[0]; + ((uint32_t *)block)[1] ^= iv[1]; + ((uint32_t *)block)[2] ^= iv[2]; + ((uint32_t *)block)[3] ^= iv[3]; + padLen = block[15]; + if (padLen <= 0 || padLen > 16) + return RIJNDAEL_CORRUPTED_DATA; + for (i = 16 - padLen; i < 16; i++) + { + if (block[i] != padLen) + return RIJNDAEL_CORRUPTED_DATA; + } + memcpy(outBuffer, block, 16 - padLen); + break; + + default: + return -1; + break; + } + + return 16 * numBlocks - padLen; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ALGORITHM +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Rijndael::keySched(uint8_t key[_MAX_KEY_COLUMNS][4]) +{ + unsigned j, rconpointer = 0; + + // Calculate the necessary round keys + // The number of calculations depends on keyBits and blockBits + unsigned uKeyColumns = m_uRounds - 6; + + uint8_t tempKey[_MAX_KEY_COLUMNS][4]; + + // Copy the input key to the temporary key matrix + + for (j = 0; j < uKeyColumns; j++) + { + *((uint32_t *)(tempKey[j])) = *((uint32_t *)(key[j])); + } + + unsigned r = 0; + unsigned t = 0; + + // copy values into round key array + for (j = 0; (j < uKeyColumns) && (r <= m_uRounds);) + { + for (; (j < uKeyColumns) && (t < 4); j++, t++) + { + *((uint32_t *)m_expandedKey[r][t]) = *((uint32_t *)tempKey[j]); + } + + if (t == 4) + { + r++; + t = 0; + } + } + + while (r <= m_uRounds) + { + tempKey[0][0] ^= S[tempKey[uKeyColumns - 1][1]]; + tempKey[0][1] ^= S[tempKey[uKeyColumns - 1][2]]; + tempKey[0][2] ^= S[tempKey[uKeyColumns - 1][3]]; + tempKey[0][3] ^= S[tempKey[uKeyColumns - 1][0]]; + tempKey[0][0] ^= rcon[rconpointer++]; + + if (uKeyColumns != 8) + { + for (j = 1; j < uKeyColumns; j++) + { + *((uint32_t *)tempKey[j]) ^= *((uint32_t *)tempKey[j - 1]); + } + } + else + { + for (j = 1; j < uKeyColumns / 2; j++) + { + *((uint32_t *)tempKey[j]) ^= *((uint32_t *)tempKey[j - 1]); + } + tempKey[uKeyColumns / 2][0] ^= S[tempKey[uKeyColumns / 2 - 1][0]]; + tempKey[uKeyColumns / 2][1] ^= S[tempKey[uKeyColumns / 2 - 1][1]]; + tempKey[uKeyColumns / 2][2] ^= S[tempKey[uKeyColumns / 2 - 1][2]]; + tempKey[uKeyColumns / 2][3] ^= S[tempKey[uKeyColumns / 2 - 1][3]]; + for (j = uKeyColumns / 2 + 1; j < uKeyColumns; j++) + { + *((uint32_t *)tempKey[j]) ^= *((uint32_t *)tempKey[j - 1]); + } + } + for (j = 0; (j < uKeyColumns) && (r <= m_uRounds);) + { + for (; (j < uKeyColumns) && (t < 4); j++, t++) + { + *((uint32_t *)m_expandedKey[r][t]) = *((uint32_t *)tempKey[j]); + } + if (t == 4) + { + r++; + t = 0; + } + } + } +} + +void Rijndael::keyEncToDec() +{ + unsigned r; + uint8_t *w; + + for (r = 1; r < m_uRounds; r++) + { + w = m_expandedKey[r][0]; + *((uint32_t *)w) = + *((uint32_t *)U1[w[0]]) ^ *((uint32_t *)U2[w[1]]) ^ *((uint32_t *)U3[w[2]]) ^ *((uint32_t *)U4[w[3]]); + w = m_expandedKey[r][1]; + *((uint32_t *)w) = + *((uint32_t *)U1[w[0]]) ^ *((uint32_t *)U2[w[1]]) ^ *((uint32_t *)U3[w[2]]) ^ *((uint32_t *)U4[w[3]]); + w = m_expandedKey[r][2]; + *((uint32_t *)w) = + *((uint32_t *)U1[w[0]]) ^ *((uint32_t *)U2[w[1]]) ^ *((uint32_t *)U3[w[2]]) ^ *((uint32_t *)U4[w[3]]); + w = m_expandedKey[r][3]; + *((uint32_t *)w) = + *((uint32_t *)U1[w[0]]) ^ *((uint32_t *)U2[w[1]]) ^ *((uint32_t *)U3[w[2]]) ^ *((uint32_t *)U4[w[3]]); + } +} + +void Rijndael::encrypt(const uint8_t a[16], uint8_t b[16]) +{ + unsigned r; + uint8_t temp[4][4]; + + *((uint32_t *)temp[0]) = *((uint32_t *)(a)) ^ *((uint32_t *)m_expandedKey[0][0]); + *((uint32_t *)temp[1]) = *((uint32_t *)(a + 4)) ^ *((uint32_t *)m_expandedKey[0][1]); + *((uint32_t *)temp[2]) = *((uint32_t *)(a + 8)) ^ *((uint32_t *)m_expandedKey[0][2]); + *((uint32_t *)temp[3]) = *((uint32_t *)(a + 12)) ^ *((uint32_t *)m_expandedKey[0][3]); + *((uint32_t *)(b)) = *((uint32_t *)T1[temp[0][0]]) ^ *((uint32_t *)T2[temp[1][1]]) ^ *((uint32_t *)T3[temp[2][2]]) ^ + *((uint32_t *)T4[temp[3][3]]); + *((uint32_t *)(b + 4)) = *((uint32_t *)T1[temp[1][0]]) ^ *((uint32_t *)T2[temp[2][1]]) ^ + *((uint32_t *)T3[temp[3][2]]) ^ *((uint32_t *)T4[temp[0][3]]); + *((uint32_t *)(b + 8)) = *((uint32_t *)T1[temp[2][0]]) ^ *((uint32_t *)T2[temp[3][1]]) ^ + *((uint32_t *)T3[temp[0][2]]) ^ *((uint32_t *)T4[temp[1][3]]); + *((uint32_t *)(b + 12)) = *((uint32_t *)T1[temp[3][0]]) ^ *((uint32_t *)T2[temp[0][1]]) ^ + *((uint32_t *)T3[temp[1][2]]) ^ *((uint32_t *)T4[temp[2][3]]); + for (r = 1; r < m_uRounds - 1; r++) + { + *((uint32_t *)temp[0]) = *((uint32_t *)(b)) ^ *((uint32_t *)m_expandedKey[r][0]); + *((uint32_t *)temp[1]) = *((uint32_t *)(b + 4)) ^ *((uint32_t *)m_expandedKey[r][1]); + *((uint32_t *)temp[2]) = *((uint32_t *)(b + 8)) ^ *((uint32_t *)m_expandedKey[r][2]); + *((uint32_t *)temp[3]) = *((uint32_t *)(b + 12)) ^ *((uint32_t *)m_expandedKey[r][3]); + + *((uint32_t *)(b)) = *((uint32_t *)T1[temp[0][0]]) ^ *((uint32_t *)T2[temp[1][1]]) ^ + *((uint32_t *)T3[temp[2][2]]) ^ *((uint32_t *)T4[temp[3][3]]); + *((uint32_t *)(b + 4)) = *((uint32_t *)T1[temp[1][0]]) ^ *((uint32_t *)T2[temp[2][1]]) ^ + *((uint32_t *)T3[temp[3][2]]) ^ *((uint32_t *)T4[temp[0][3]]); + *((uint32_t *)(b + 8)) = *((uint32_t *)T1[temp[2][0]]) ^ *((uint32_t *)T2[temp[3][1]]) ^ + *((uint32_t *)T3[temp[0][2]]) ^ *((uint32_t *)T4[temp[1][3]]); + *((uint32_t *)(b + 12)) = *((uint32_t *)T1[temp[3][0]]) ^ *((uint32_t *)T2[temp[0][1]]) ^ + *((uint32_t *)T3[temp[1][2]]) ^ *((uint32_t *)T4[temp[2][3]]); + } + *((uint32_t *)temp[0]) = *((uint32_t *)(b)) ^ *((uint32_t *)m_expandedKey[m_uRounds - 1][0]); + *((uint32_t *)temp[1]) = *((uint32_t *)(b + 4)) ^ *((uint32_t *)m_expandedKey[m_uRounds - 1][1]); + *((uint32_t *)temp[2]) = *((uint32_t *)(b + 8)) ^ *((uint32_t *)m_expandedKey[m_uRounds - 1][2]); + *((uint32_t *)temp[3]) = *((uint32_t *)(b + 12)) ^ *((uint32_t *)m_expandedKey[m_uRounds - 1][3]); + b[0] = T1[temp[0][0]][1]; + b[1] = T1[temp[1][1]][1]; + b[2] = T1[temp[2][2]][1]; + b[3] = T1[temp[3][3]][1]; + b[4] = T1[temp[1][0]][1]; + b[5] = T1[temp[2][1]][1]; + b[6] = T1[temp[3][2]][1]; + b[7] = T1[temp[0][3]][1]; + b[8] = T1[temp[2][0]][1]; + b[9] = T1[temp[3][1]][1]; + b[10] = T1[temp[0][2]][1]; + b[11] = T1[temp[1][3]][1]; + b[12] = T1[temp[3][0]][1]; + b[13] = T1[temp[0][1]][1]; + b[14] = T1[temp[1][2]][1]; + b[15] = T1[temp[2][3]][1]; + *((uint32_t *)(b)) ^= *((uint32_t *)m_expandedKey[m_uRounds][0]); + *((uint32_t *)(b + 4)) ^= *((uint32_t *)m_expandedKey[m_uRounds][1]); + *((uint32_t *)(b + 8)) ^= *((uint32_t *)m_expandedKey[m_uRounds][2]); + *((uint32_t *)(b + 12)) ^= *((uint32_t *)m_expandedKey[m_uRounds][3]); +} + +void Rijndael::decrypt(const uint8_t a[16], uint8_t b[16]) +{ + int r; + uint8_t temp[4][4]; + + *((uint32_t *)temp[0]) = *((uint32_t *)(a)) ^ *((uint32_t *)m_expandedKey[m_uRounds][0]); + *((uint32_t *)temp[1]) = *((uint32_t *)(a + 4)) ^ *((uint32_t *)m_expandedKey[m_uRounds][1]); + *((uint32_t *)temp[2]) = *((uint32_t *)(a + 8)) ^ *((uint32_t *)m_expandedKey[m_uRounds][2]); + *((uint32_t *)temp[3]) = *((uint32_t *)(a + 12)) ^ *((uint32_t *)m_expandedKey[m_uRounds][3]); + + *((uint32_t *)(b)) = *((uint32_t *)T5[temp[0][0]]) ^ *((uint32_t *)T6[temp[3][1]]) ^ *((uint32_t *)T7[temp[2][2]]) ^ + *((uint32_t *)T8[temp[1][3]]); + *((uint32_t *)(b + 4)) = *((uint32_t *)T5[temp[1][0]]) ^ *((uint32_t *)T6[temp[0][1]]) ^ + *((uint32_t *)T7[temp[3][2]]) ^ *((uint32_t *)T8[temp[2][3]]); + *((uint32_t *)(b + 8)) = *((uint32_t *)T5[temp[2][0]]) ^ *((uint32_t *)T6[temp[1][1]]) ^ + *((uint32_t *)T7[temp[0][2]]) ^ *((uint32_t *)T8[temp[3][3]]); + *((uint32_t *)(b + 12)) = *((uint32_t *)T5[temp[3][0]]) ^ *((uint32_t *)T6[temp[2][1]]) ^ + *((uint32_t *)T7[temp[1][2]]) ^ *((uint32_t *)T8[temp[0][3]]); + for (r = m_uRounds - 1; r > 1; r--) + { + *((uint32_t *)temp[0]) = *((uint32_t *)(b)) ^ *((uint32_t *)m_expandedKey[r][0]); + *((uint32_t *)temp[1]) = *((uint32_t *)(b + 4)) ^ *((uint32_t *)m_expandedKey[r][1]); + *((uint32_t *)temp[2]) = *((uint32_t *)(b + 8)) ^ *((uint32_t *)m_expandedKey[r][2]); + *((uint32_t *)temp[3]) = *((uint32_t *)(b + 12)) ^ *((uint32_t *)m_expandedKey[r][3]); + *((uint32_t *)(b)) = *((uint32_t *)T5[temp[0][0]]) ^ *((uint32_t *)T6[temp[3][1]]) ^ + *((uint32_t *)T7[temp[2][2]]) ^ *((uint32_t *)T8[temp[1][3]]); + *((uint32_t *)(b + 4)) = *((uint32_t *)T5[temp[1][0]]) ^ *((uint32_t *)T6[temp[0][1]]) ^ + *((uint32_t *)T7[temp[3][2]]) ^ *((uint32_t *)T8[temp[2][3]]); + *((uint32_t *)(b + 8)) = *((uint32_t *)T5[temp[2][0]]) ^ *((uint32_t *)T6[temp[1][1]]) ^ + *((uint32_t *)T7[temp[0][2]]) ^ *((uint32_t *)T8[temp[3][3]]); + *((uint32_t *)(b + 12)) = *((uint32_t *)T5[temp[3][0]]) ^ *((uint32_t *)T6[temp[2][1]]) ^ + *((uint32_t *)T7[temp[1][2]]) ^ *((uint32_t *)T8[temp[0][3]]); + } + + *((uint32_t *)temp[0]) = *((uint32_t *)(b)) ^ *((uint32_t *)m_expandedKey[1][0]); + *((uint32_t *)temp[1]) = *((uint32_t *)(b + 4)) ^ *((uint32_t *)m_expandedKey[1][1]); + *((uint32_t *)temp[2]) = *((uint32_t *)(b + 8)) ^ *((uint32_t *)m_expandedKey[1][2]); + *((uint32_t *)temp[3]) = *((uint32_t *)(b + 12)) ^ *((uint32_t *)m_expandedKey[1][3]); + b[0] = S5[temp[0][0]]; + b[1] = S5[temp[3][1]]; + b[2] = S5[temp[2][2]]; + b[3] = S5[temp[1][3]]; + b[4] = S5[temp[1][0]]; + b[5] = S5[temp[0][1]]; + b[6] = S5[temp[3][2]]; + b[7] = S5[temp[2][3]]; + b[8] = S5[temp[2][0]]; + b[9] = S5[temp[1][1]]; + b[10] = S5[temp[0][2]]; + b[11] = S5[temp[3][3]]; + b[12] = S5[temp[3][0]]; + b[13] = S5[temp[2][1]]; + b[14] = S5[temp[1][2]]; + b[15] = S5[temp[0][3]]; + *((uint32_t *)(b)) ^= *((uint32_t *)m_expandedKey[0][0]); + *((uint32_t *)(b + 4)) ^= *((uint32_t *)m_expandedKey[0][1]); + *((uint32_t *)(b + 8)) ^= *((uint32_t *)m_expandedKey[0][2]); + *((uint32_t *)(b + 12)) ^= *((uint32_t *)m_expandedKey[0][3]); +} diff --git a/src/blfwk/src/serial.c b/src/blfwk/src/serial.c new file mode 100644 index 0000000..1834712 --- /dev/null +++ b/src/blfwk/src/serial.c @@ -0,0 +1,401 @@ +/* + * This file is part of the Bus Pirate project (http://code.google.com/p/the-bus-pirate/). + * + * Written and maintained by the Bus Pirate project and http://dangerousprototypes.com + * + * To the extent possible under law, the project has + * waived all copyright and related or neighboring rights to Bus Pirate. This + * work is published from United States. + * + * For details see: http://creativecommons.org/publicdomain/zero/1.0/. + * + * This program 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. + */ +/* + * OS independent serial interface + * + * Heavily based on Pirate-Loader: + * http://the-bus-pirate.googlecode.com/svn/trunk/bootloader-v4/pirate-loader/source/pirate-loader.c + * + */ + +#include +#include +#include +#include +#include + +#include "blfwk/serial.h" + +#ifdef LINUX +#include +#endif + +int serial_setup(int fd, speed_t speed) +{ +#if defined(WIN32) + COMMTIMEOUTS timeouts; + DCB dcb = { 0 }; + HANDLE hCom = (HANDLE)fd; + + dcb.DCBlength = sizeof(dcb); + + dcb.BaudRate = speed; + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.StopBits = ONESTOPBIT; + + if (!SetCommState(hCom, &dcb)) + { + return -1; + } + + // These timeouts mean: + // read: return immediately with whatever data is available, if any + // write: timeouts not used + // reference: http://www.robbayer.com/files/serial-win.pdf + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + + if (!SetCommTimeouts(hCom, &timeouts)) + { + return -1; + } + +#elif defined(LINUX) || defined(MACOSX) + struct termios tty; + + memset(&tty, 0x00, sizeof(tty)); + cfmakeraw(&tty); + + tty.c_cflag &= ~(PARENB | CSTOPB | CSIZE); + tty.c_cflag |= (CS8 | CLOCAL | CREAD | HUPCL); + tty.c_oflag = 0; + tty.c_lflag = 0; + +#if defined(LINUX) + // Non standard baud rates depend on the specific system. + // Only supports standard baud rates. + switch (speed) + { + case 9600: + speed = B9600; + break; + case 19200: + speed = B19200; + break; + case 38400: + speed = B38400; + break; + case 57600: + speed = B57600; + break; + case 115200: + speed = B115200; + break; + case 230400: + speed = B230400; + break; + case 460800: + speed = B460800; + break; + case 500000: + speed = B500000; + break; + case 576000: + speed = B576000; + break; + case 921600: + speed = B921600; + break; + case 1000000: + speed = B1000000; + break; + case 1152000: + speed = B1152000; + break; + case 1500000: + speed = B1500000; + break; + case 2000000: + speed = B2000000; + break; + case 2500000: + speed = B2500000; + break; + case 3000000: + speed = B3000000; + break; + case 3500000: + speed = B3500000; + break; + case 4000000: + speed = B4000000; + break; + default: + printf("Warning: unsupported standard baud rate(%d), set to default(57600)\n", speed); + speed = B57600; + break; + } + cfsetospeed(&tty, speed); + cfsetispeed(&tty, speed); +#elif defined(MACOSX) + // Set a dummy speed here, the real speed will be set by IOSSIOSPEED + cfsetospeed(&tty, B57600); + cfsetispeed(&tty, B57600); +#endif // LINUX + + // Completely non-blocking read + // VMIN = 0 and VTIME = 0 + // Completely non-blocking read + // reference: http://www.unixwiz.net/techtips/termios-vmin-vtime.html + tty.c_cc[VTIME] = 0; + tty.c_cc[VMIN] = 0; + + if (tcsetattr(fd, TCSAFLUSH, &tty) < 0) + { + return -1; + } + +#if defined(MACOSX) + if (ioctl(fd, IOSSIOSPEED, &speed) == -1) + { + return -1; + } +#endif // MACOSX +#endif // WIN32 + +#if defined(MAXOSX) + // Set the receive latency to 1us which is used by serial driver to determine how often to dequeue characters + // received by the hardware. + unsigned long us = 1UL; + if (ioctl(fd, IOSSDATALAT, &us) < 0) + { + return -1; + } +#endif // MAXOSX + + return 0; +} + +int serial_set_read_timeout(int fd, uint32_t timeoutMs) +{ +#if defined(WIN32) + COMMTIMEOUTS timeouts; + HANDLE hCom = (HANDLE)fd; + + // These timeouts mean: + // read: return if: + // 1. Inter-character timeout exceeds ReadIntervalTimeout + // 2. Total timeout exceeds (ReadIntervalTimeout*ReadTotalTimeoutMultiplier*number of characters) + + // ReadTotalTimeoutConstant + // In practice it seems that no matter how many characters you ask for, if no characters appear on the interface + // then + // only ReadTotalTimeoutConstant applies. + // write: timeouts not used + // reference: http://www.robbayer.com/files/serial-win.pdf + if (timeoutMs != 0) + { + timeouts.ReadIntervalTimeout = 1000; + timeouts.ReadTotalTimeoutMultiplier = 10; + timeouts.ReadTotalTimeoutConstant = timeoutMs; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + } + else + { + // Need a seperate case for timeoutMs == 0 + // setting all these values to 0 results in no timeout + // so set them to a minimum value, this will return immediately + // if there is no data available + timeouts.ReadIntervalTimeout = 1; + timeouts.ReadTotalTimeoutMultiplier = 1; + timeouts.ReadTotalTimeoutConstant = 1; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + } + + if (!SetCommTimeouts(hCom, &timeouts)) + { + return -1; + } + +#elif defined(LINUX) || defined(MAXOSX) + struct termios tty; + + memset(&tty, 0x00, sizeof(tty)); + tcgetattr(fd, &tty); + + // Completely non-blocking read + // VMIN = 0 and VTIME > 0 + // Pure timed read + // reference: http://www.unixwiz.net/techtips/termios-vmin-vtime.html + if (timeoutMs && (timeoutMs < 100)) + { + // since the lowest resolution this handles is .1 seconds we will set it to that for any non zero + // timeout value less than 100ms + tty.c_cc[VTIME] = 1; + } + else + { + tty.c_cc[VTIME] = (timeoutMs / 100); // in 0.1 sec intervals + } + + tty.c_cc[VMIN] = 0; + + if (tcsetattr(fd, TCSAFLUSH, &tty) < 0) + { + return -1; + } + +#endif // WIN32 + + return 0; +} + +int serial_write(int fd, char *buf, int size) +{ +#ifdef WIN32 + HANDLE hCom = (HANDLE)fd; + unsigned long bwritten = 0; + + if (!WriteFile(hCom, buf, size, &bwritten, NULL)) + { + return 0; + } + else + { + return bwritten; + } +#else + return write(fd, buf, size); +#endif +} + +int serial_read(int fd, char *buf, int size) +{ +#ifdef WIN32 + HANDLE hCom = (HANDLE)fd; + unsigned long bread = 0; + + if (!ReadFile(hCom, buf, size, &bread, NULL)) + { + return 0; + } + else + { + return bread; + } +#else + int len = 0; + int ret = 0; + int timeout = 0; + + while (len < size) + { + ret = read(fd, buf + len, size - len); + if (ret == -1) + { + return -1; + } + + if (ret == 0) + { + timeout++; + +#if defined(MACOSX) + // There is a huge gap every 32 bytes on some MACOS. So use an extremely large value to workaround it. + if (timeout >= 1000 * 1000) + { + break; + } +#else + if (timeout >= 10) + { + break; + } +#endif + + continue; + } + else + { + // Reset the timeout, once data byte(s) is(are) received. + timeout = 0; + } + + len += ret; + } + + return len; +#endif +} + +int serial_open(char *port) +{ + int fd; +#if defined(WIN32) + static char full_path[32] = { 0 }; + + HANDLE hCom = NULL; + + if (port[0] != '\\') + { + _snprintf(full_path, sizeof(full_path) - 1, "\\\\.\\%s", port); + port = full_path; + } + +#pragma warning(suppress : 6053) + hCom = CreateFileA(port, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (!hCom || hCom == INVALID_HANDLE_VALUE) + { + fd = -1; + } + else + { + fd = (int)hCom; + } +#elif defined(LINUX) + fd = open(port, O_RDWR | O_NOCTTY); + if (fd == -1) + { + fprintf(stderr, "Could not open serial port.\n"); + return -1; + } +#elif defined(MACOSX) + // There is an issue in some MACOS about the blocking operations for serial devices. So use non-blocking to avoid + // it. + fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd == -1) + { + fprintf(stderr, "Could not open serial port.\n"); + return -1; + } + + // O_NONBLOCK is required for the OPEN operation. Clear the O_NONBLOCK flag, so subsequent operation will block. + if (fcntl(fd, F_SETFL, 0) == -1) + { + fprintf(stderr, "Failed to set non-blocking mode to the serial port.\n"); + return -1; + } +#endif + return fd; +} + +int serial_close(int fd) +{ +#ifdef WIN32 + HANDLE hCom = (HANDLE)fd; + + CloseHandle(hCom); +#else + close(fd); +#endif + return 0; +} diff --git a/src/blfwk/src/spi.c b/src/blfwk/src/spi.c new file mode 100644 index 0000000..bbf81e4 --- /dev/null +++ b/src/blfwk/src/spi.c @@ -0,0 +1,150 @@ +/* + * Copyright 2020 - 2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include "spi.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +#define DEFAULT_BITS_PER_WORD (8) + +/******************************************************************************* + * Variables + ******************************************************************************/ + +static struct spi_ioc_transfer spi_data; + +/******************************************************************************* + * Codes + ******************************************************************************/ + +// See spi.h for documentation of this method. +int spi_setup(int fd, uint32_t speed, uint32_t mode, uint32_t bits_per_word) +{ + int ret = -1; + + if (fd < 0) + { + return -1; + } + + // Set phase, polarity chipselect active state and bit order + ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); + if (ret < 0) + { + return ret; + } + + // Set bits per word + spi_data.bits_per_word = (bits_per_word != 8) && (bits_per_word != 9) ? DEFAULT_BITS_PER_WORD : bits_per_word; + ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, (unsigned long)&spi_data.bits_per_word); + if (ret < 0) + { + return ret; + } + + // Set Max speed and current speed + spi_data.speed_hz = speed; // current speed + return ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, (unsigned long)&speed); +} + +// See spi.h for documentation of this method. +int spi_set_timeout(int fd, uint32_t milliseconds) +{ + /* + * Linux SPI-DEV doesn't support to set timeout from user space. + * The timeout is maintained within the device driver. + * Different SPI drivers have different default timeout values. + */ + + return 0; +} + +// See spi.h for documentation of this method. +int spi_write(int fd, char *buf, int size) +{ + if (fd < 0) + { + return -1; + } + + /* + * Do not convert a pointer type to __u64 directly. It will lead an issue for 32bit archtectures + */ + spi_data.tx_buf = (intptr_t)buf; + spi_data.rx_buf = (intptr_t)NULL; + spi_data.len = size; + spi_data.cs_change = 0; + + return ioctl(fd, SPI_IOC_MESSAGE(1), &spi_data); +} + +// See spi.h for documentation of this method. +int spi_read(int fd, char *buf, int size) +{ + if (fd < 0) + { + return -1; + } + + /* + * Do not convert a pointer type to __u64 directly. It will lead an issue for 32bit archtectures + */ + spi_data.tx_buf = (intptr_t)NULL; + spi_data.rx_buf = (intptr_t)buf; + spi_data.len = size; + spi_data.cs_change = 0; + + return ioctl(fd, SPI_IOC_MESSAGE(1), &spi_data); +} + +// See spi.h for documentation of this method. +int spi_open(char *port) +{ + int fd = -1; + + if (port == NULL) + { + return -1; + } + + fd = open(port, O_RDWR); + if (fd < 0) + { + fprintf(stderr, "Failed to open SPI port(%s).\n", port); + } + + return fd; +} + +// See spi.h for documentation of this method. +int spi_close(int fd) +{ + int ret; + if (fd < 0) + { + return -1; + } + + ret = close(fd); + if (ret < 0) + { + fprintf(stderr, "Failed to close SPI port.\n"); + } + else + { + fd = -1; + } + + return ret; +} + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/src/utils.cpp b/src/blfwk/src/utils.cpp new file mode 100644 index 0000000..b055a59 --- /dev/null +++ b/src/blfwk/src/utils.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include "blfwk/format_string.h" +#ifdef LINUX +#include +#endif + +namespace utils +{ +std::vector &split(const std::string &s, char delim, std::vector &elems) +{ + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) + { + elems.push_back(item); + } + return elems; +} + +std::vector string_split(const std::string &s, char delim) +{ + std::vector elems; + split(s, delim, elems); + return elems; +} + +std::string string_hex(const std::string &s) +{ + std::string hex; + std::string::const_iterator it = s.begin(); + for (; it != s.end(); ++it) + { + if (isxdigit(*it)) + { + hex.append(1, *it); + } + } + + return hex; +} + +bool stringtoi(const std::string &s, int32_t &number) +{ + if (s.empty()) + return false; + + char *p; + int64_t temp; + temp = strtoll(s.c_str(), &p, 0); + if ((temp > INT32_MAX) || (temp < INT32_MIN)) + { + return false; + } + number = static_cast(temp); + return (p != NULL) && (*p == 0); +} + +bool stringtoui(const std::string &s, uint32_t &number) +{ + if (s.empty()) + return false; + if (s[0] == '-') + return false; + + char *p; + uint64_t temp; + temp = strtoull(s.c_str(), &p, 0); + if (temp > UINT32_MAX) + { + return false; + } + number = static_cast(temp); + return (p != NULL) && (*p == 0); +} + +// GB, MB, KB, bytes +std::string scale_bytes(uint64_t sizeInBytes) +{ + double originalSize = (double)sizeInBytes; + double scaledSize = 0; + + scaledSize = originalSize / (1024 * 1024 * 1024); + if (scaledSize >= 1.0) // GB + { + if (sizeInBytes % (1024 * 1024 * 1024)) + return format_string("%.3f GB", scaledSize); + else + return format_string("%.f GB", scaledSize); + } + + scaledSize = originalSize / (1024 * 1024); + if (scaledSize >= 1.0) // MB + { + if (sizeInBytes % (1024 * 1024)) + return format_string("%.3f MB", scaledSize); + else + return format_string("%.f MB", scaledSize); + } + + scaledSize = originalSize / 1024; + if (scaledSize >= 1.0) // KB + { + if (sizeInBytes % 1024) + return format_string("%.3f KB", scaledSize); + else + return format_string("%.f KB", scaledSize); + } + else // bytes + { + return format_string("%d bytes", sizeInBytes); + } + +} // ScaleBytes() + +} // namespace utils + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/blfwk/stdafx.h b/src/blfwk/stdafx.h new file mode 100644 index 0000000..9a5dc7f --- /dev/null +++ b/src/blfwk/stdafx.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef stdafx_h_ +#define stdafx_h_ + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +// Default to external release. +#ifndef SGTL_INTERNAL +#define SGTL_INTERNAL 0 +#endif + +#include +#include +#include + +#if defined(WIN32) +//#include + +// define this macro for use in VC++ +#if !defined(__LITTLE_ENDIAN__) +#define __LITTLE_ENDIAN__ 1 +#endif // !defined(__LITTLE_ENDIAN__) +#endif // defined(WIN32) + +#if defined(LINUX) +// For Linux systems only, types.h only defines the signed +// integer types. This is not professional code. +// Update: They are defined in the header files in the more recent version of redhat enterprise gcc. +//#include "/usr/include/sys/types.h" +//#include +// typedef unsigned long uint32_t; +// typedef unsigned short uint16_t; +// typedef unsigned char uint8_t; + +//#define TCHAR char +//#define _tmain main + +// give a default endian in case one is not defined on Linux (it should be, though) +#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) +#define __LITTLE_ENDIAN__ 1 +#endif // !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) + +#endif // defined(Linux) + +// gcc on Mac OS X +#if defined(__GNUC__) && (defined(__APPLE_CPP__) || defined(__APPLE_CC__) || defined(__MACOS_CLASSIC__)) +#include + +#if defined(TARGET_RT_LITTLE_ENDIAN) && TARGET_RT_LITTLE_ENDIAN +#if !defined(__LITTLE_ENDIAN__) +#define __LITTLE_ENDIAN__ +#endif +#elif defined(TARGET_RT_BIG_ENDIAN) && TARGET_RT_BIG_ENDIAN +#if !defined(__BIG_ENDIAN__) +#define __BIG_ENDIAN__ +#endif +#endif +#endif + +#if !defined(TRUE) +#define TRUE 1 +#endif // !defined(TRUE) + +#if !defined(FALSE) +#define FALSE 0 +#endif // !defined(FALSE) + +#endif // stdafx_h_ diff --git a/src/blfwk/utils.h b/src/blfwk/utils.h new file mode 100644 index 0000000..2c9e282 --- /dev/null +++ b/src/blfwk/utils.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013-2014 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _utils_h_ +#define _utils_h_ + +#include +#include + +namespace utils +{ +//! @brief Split a string into tokens by delimiter. +std::vector string_split(const std::string &s, char delim); + +//! @brief Remove all except hex digits from a string. +std::string string_hex(const std::string &s); + +//! @brief Check if a string is signed a number. If so, return number. +bool stringtoi(const std::string &s, int32_t &number); + +//! @brief Check if a string is an unsigned number. If so, return number. +bool stringtoui(const std::string &s, uint32_t &number); + +//! @brief Format bytes into GB, MB, KB, or bytes. +std::string scale_bytes(uint64_t sizeInBytes); + +} // namespace utils + +#endif // _utils_h_ + +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/bootloader/bl_peripheral.h b/src/bootloader/bl_peripheral.h new file mode 100644 index 0000000..e11f3a3 --- /dev/null +++ b/src/bootloader/bl_peripheral.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _peripheral_h +#define _peripheral_h + +#include +#include "bootloader_common.h" + +//! @addtogroup peripheral +//! @{ + +//////////////////////////////////////////////////////////////////////////////// +// Declarations +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Peripheral type bit mask definitions. +//! +//! These bit mask constants serve multiple purposes. They are each a unique value that identifies +//! a peripheral type. They are also the mask for the bits used in the bootloader configuration +//! flash region to list available peripherals and control which peripherals are enabled. +enum _peripheral_types +{ + kPeripheralType_UART = (1 << 0), + kPeripheralType_I2CSlave = (1 << 1), + kPeripheralType_SPISlave = (1 << 2), + kPeripheralType_CAN = (1 << 3), + kPeripheralType_USB_HID = (1 << 4), + kPeripheralType_USB_CDC = (1 << 5), + kPeripheralType_USB_DFU = (1 << 6), + kPeripheralType_USB_MSC = (1 << 7) +}; + +//! @brief Pinmux types. +typedef enum _pinmux_types +{ + kPinmuxType_Default = 0, + kPinmuxType_PollForActivity = 1, + kPinmuxType_Peripheral = 2, + kPinmuxType_RestoreForActivity = 3 +} pinmux_type_t; + +// Forward declaration. +typedef struct PeripheralDescriptor peripheral_descriptor_t; + +typedef void (*serial_byte_receive_func_t)(uint8_t); + +//! @brief Peripheral control interface. +typedef struct _peripheral_control_interface +{ + bool (*pollForActivity)(const peripheral_descriptor_t *self); + status_t (*init)(const peripheral_descriptor_t *self, serial_byte_receive_func_t function); + void (*shutdown)(const peripheral_descriptor_t *self); + void (*pump)(const peripheral_descriptor_t *self); +} peripheral_control_interface_t; + +//! @brief Peripheral abstract byte interface. +typedef struct _peripheral_byte_inteface +{ + status_t (*init)(const peripheral_descriptor_t *self); +#ifdef BOOTLOADER_HOST + status_t (*read)(const peripheral_descriptor_t *self, uint8_t *buffer, uint32_t requestedBytes); +#endif // #ifdef BOOTLOADER_HOST + status_t (*write)(const peripheral_descriptor_t *self, const uint8_t *buffer, uint32_t byteCount); +} peripheral_byte_inteface_t; + +//! @brief Packet types. +typedef enum _packet_type +{ + kPacketType_Command, //!< Send or expect a command packet + kPacketType_Data //!< Send or expect a data packet +} packet_type_t; + +//! @brief Peripheral Packet Interface. +typedef struct _peripheral_packet_interface +{ + status_t (*init)(const peripheral_descriptor_t *self); + status_t (*readPacket)(const peripheral_descriptor_t *self, + uint8_t **packet, + uint32_t *packetLength, + packet_type_t packetType); + status_t (*writePacket)(const peripheral_descriptor_t *self, + const uint8_t *packet, + uint32_t byteCount, + packet_type_t packetType); + void (*abortDataPhase)(const peripheral_descriptor_t *self); + status_t (*finalize)(const peripheral_descriptor_t *self); + uint32_t (*getMaxPacketSize)(const peripheral_descriptor_t *self); + void (*byteReceivedCallback)(uint8_t byte); +} peripheral_packet_interface_t; + +//! @brief Peripheral descriptor. +//! +//! Instances of this struct describe a particular instance of a peripheral that is +//! available for bootloading. +struct PeripheralDescriptor +{ + //! @brief Bit mask identifying the peripheral type. + //! + //! See #_peripheral_types for a list of valid bits. + uint32_t typeMask; + + //! @brief The instance number of the peripheral. + uint32_t instance; + + //! @brief Configure pinmux setting for the peripheral. + void (*pinmuxConfig)(uint32_t instance, pinmux_type_t pinmux); + + //! @brief Control interface for the peripheral. + const peripheral_control_interface_t *controlInterface; + + //! @brief Byte-level interface for the peripheral. + //! + //! May be NULL since not all periperhals support this interface. + const peripheral_byte_inteface_t *byteInterface; + + //! @brief Packet level interface for the peripheral. + const peripheral_packet_interface_t *packetInterface; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Externs +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Array of all peripherals available in this device. +extern const peripheral_descriptor_t g_peripherals[]; + +//! @} + +#endif // _peripheral_h +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/crc/crc16.h b/src/crc/crc16.h new file mode 100644 index 0000000..84b9518 --- /dev/null +++ b/src/crc/crc16.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _CRC16_H_ +#define _CRC16_H_ + +#include + +//! @addtogroup crc16 +//! @{ + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// + +//! @brief State information for the CRC16 algorithm. +typedef struct Crc16Data +{ + uint16_t currentCrc; //!< Current CRC value. +} crc16_data_t; + +//////////////////////////////////////////////////////////////////////////////// +// API +//////////////////////////////////////////////////////////////////////////////// + +#if __cplusplus +extern "C" { +#endif + +//! @name CRC16 +//@{ + +//! @brief Initializes the parameters of the crc function, must be called first. +//! +//! @param crc16Config Instantiation of the data structure of type crc16_data_t. +void crc16_init(crc16_data_t *crc16Config); + +//! @brief A "running" crc calculator that updates the crc value after each call. +//! +//! @param crc16Config Instantiation of the data structure of type crc16_data_t. +//! @param src Pointer to the source buffer of data. +//! @param lengthInBytes The length, given in bytes (not words or long-words). +void crc16_update(crc16_data_t *crc16Config, const uint8_t *src, uint32_t lengthInBytes); + +//! @brief Calculates the final crc value, padding with zeros if necessary, must be called last. +//! +//! @param crc16Config Instantiation of the data structure of type crc16_data_t. +//! @param hash Pointer to the value returned for the final calculated crc value. +void crc16_finalize(crc16_data_t *crc16Config, uint16_t *hash); + +//@} + +#if __cplusplus +} +#endif + +//! @} + +#endif +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/crc/crc32.h b/src/crc/crc32.h new file mode 100644 index 0000000..69a92f3 --- /dev/null +++ b/src/crc/crc32.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _CRC_H_ +#define _CRC_H_ + +#include + +//! @addtogroup crc32 +//! @{ + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// + +//! @brief State information for the CRC32 algorithm. +typedef struct Crc32Data +{ + uint32_t currentCrc; //!< Current CRC value. + uint32_t byteCountCrc; //!< Number of bytes processed. +} crc32_data_t; + +//////////////////////////////////////////////////////////////////////////////// +// API +//////////////////////////////////////////////////////////////////////////////// + +#if __cplusplus +extern "C" { +#endif + +//! @name CRC32 +//@{ + +//! @brief Initializes the parameters of the crc function, must be called first +//! +//! @param crc32Config Instantiation of the data structure of type crc32_data_t +//! @retval kStatus_Success +void crc32_init(crc32_data_t *crc32Config); + +//! @brief A "running" crc calculator that updates the crc value after each call +//! +//! @param crc32Config Instantiation of the data structure of type crc32_data_t +//! @param src Pointer to the source buffer of data +//! @param lengthInBytes The length, given in bytes (not words or long-words) +//! @retval kStatus_Success +void crc32_update(crc32_data_t *crc32Config, const uint8_t *src, uint32_t lengthInBytes); + +//! @brief Calculates the final crc value, padding with zeros if necessary, must be called last +//! +//! @param crc32Config Instantiation of the data structure of type crc32_data_t +//! @param hash Pointer to the value returned for the final calculated crc value +//! @retval kStatus_Success +void crc32_finalize(crc32_data_t *crc32Config, uint32_t *hash); + +//@} + +#if __cplusplus +} +#endif + +//! @} + +#endif +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/crc/src/crc16.c b/src/crc/src/crc16.c new file mode 100644 index 0000000..f64a2ce --- /dev/null +++ b/src/crc/src/crc16.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "crc/crc16.h" +#include + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// +void crc16_init(crc16_data_t *crc16Config) +{ + assert(crc16Config); + + // initialize running crc and byte count + crc16Config->currentCrc = 0; +} + +void crc16_update(crc16_data_t *crc16Config, const uint8_t *src, uint32_t lengthInBytes) +{ + assert(crc16Config); + assert(src); + + uint32_t crc = crc16Config->currentCrc; + + uint32_t j; + for (j = 0; j < lengthInBytes; ++j) + { + uint32_t i; + uint32_t byte = src[j]; + crc ^= byte << 8; + for (i = 0; i < 8; ++i) + { + uint32_t temp = crc << 1; + if (crc & 0x8000) + { + temp ^= 0x1021; + } + crc = temp; + } + } + + crc16Config->currentCrc = crc; +} + +void crc16_finalize(crc16_data_t *crc16Config, uint16_t *hash) +{ + assert(crc16Config); + assert(hash); + + *hash = crc16Config->currentCrc; +} +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/crc/src/crc32.c b/src/crc/src/crc32.c new file mode 100644 index 0000000..23d4b2c --- /dev/null +++ b/src/crc/src/crc32.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "crc/crc32.h" +#include + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//! Table of CRC-32's of all single byte values. The values in +//! this table are those used in the Ethernet CRC algorithm. +static const uint32_t s_crc32Table[] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, + 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, + 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, + 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, + 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, + 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, + 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, + 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, + 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, + 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, + 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, + 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, + 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +// initialize the members of the allocated crc32_data_t struct +void crc32_init(crc32_data_t *crc32Config) +{ + // initialize running crc and byte count + crc32Config->currentCrc = 0xFFFFFFFF; + crc32Config->byteCountCrc = 0; +} + +// "running" crc32 calculation +void crc32_update(crc32_data_t *crc32Config, const uint8_t *src, uint32_t lengthInBytes) +{ + assert(src); + uint32_t crc = crc32Config->currentCrc; + crc32Config->byteCountCrc += lengthInBytes; + + while (lengthInBytes--) + { + uint8_t c = *src++ & 0xff; + crc = (crc << 8) ^ s_crc32Table[(crc >> 24) ^ c]; + } + + crc32Config->currentCrc = crc; +} + +// finalize the crc32 calculation for non-word-aligned counts +void crc32_finalize(crc32_data_t *crc32Config, uint32_t *hash) +{ + uint32_t crc = crc32Config->currentCrc; + uint32_t byteCount = crc32Config->byteCountCrc; + + // pad with zeroes + if (byteCount % 4) + { + uint32_t i; + for (i = byteCount % 4; i < 4; i++) + { + crc = (crc << 8) ^ s_crc32Table[(crc >> 24) ^ 0]; + } + } + + crc32Config->currentCrc = crc; + + *hash = crc32Config->currentCrc; +} +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/include/bootloader_common.h b/src/include/bootloader_common.h new file mode 100644 index 0000000..996fd23 --- /dev/null +++ b/src/include/bootloader_common.h @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __BOOTLOADER_COMMON_H__ +#define __BOOTLOADER_COMMON_H__ + +#include +#include +#include +#if !defined(WIN32) +#include +#endif +#include "blfwk/bootloader_config.h" + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// + +/******************************************************************************* +* Definitions +******************************************************************************/ + +/*! @brief Construct a status code value from a group and code number. */ +#define MAKE_STATUS(group, code) ((((group)*100) + (code))) + +/*! @brief Construct the version number for drivers. */ +#define MAKE_VERSION(major, minor, bugfix) (((major) << 16) | ((minor) << 8) | (bugfix)) + +/* Debug console type definition. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_NONE 0U /*!< No debug console. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_UART 1U /*!< Debug console base on UART. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_LPUART 2U /*!< Debug console base on LPUART. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_LPSCI 3U /*!< Debug console base on LPSCI. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_USBCDC 4U /*!< Debug console base on USBCDC. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_FLEXCOMM 5U /*!< Debug console base on USBCDC. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_IUART 6U /*!< Debug console base on i.MX UART. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_VUSART 7U /*!< Debug console base on LPC_USART. */ + +/*! @brief Status group numbers. */ +enum _status_groups +{ + kStatusGroup_Generic = 0, /*!< Group number for generic status codes. */ + kStatusGroup_FLASH = 1, /*!< Group number for FLASH status codes. */ + kStatusGroup_LPSPI = 4, /*!< Group number for LPSPI status codes. */ + kStatusGroup_FLEXIO_SPI = 5, /*!< Group number for FLEXIO SPI status codes. */ + kStatusGroup_DSPI = 6, /*!< Group number for DSPI status codes. */ + kStatusGroup_FLEXIO_UART = 7, /*!< Group number for FLEXIO UART status codes. */ + kStatusGroup_FLEXIO_I2C = 8, /*!< Group number for FLEXIO I2C status codes. */ + kStatusGroup_LPI2C = 9, /*!< Group number for LPI2C status codes. */ + kStatusGroup_UART = 10, /*!< Group number for UART status codes. */ + kStatusGroup_I2C = 11, /*!< Group number for UART status codes. */ + kStatusGroup_LPSCI = 12, /*!< Group number for LPSCI status codes. */ + kStatusGroup_LPUART = 13, /*!< Group number for LPUART status codes. */ + kStatusGroup_SPI = 14, /*!< Group number for SPI status code.*/ + kStatusGroup_XRDC = 15, /*!< Group number for XRDC status code.*/ + kStatusGroup_SEMA42 = 16, /*!< Group number for SEMA42 status code.*/ + kStatusGroup_SDHC = 17, /*!< Group number for SDHC status code */ + kStatusGroup_SDMMC = 18, /*!< Group number for SDMMC status code */ + kStatusGroup_SAI = 19, /*!< Group number for SAI status code */ + kStatusGroup_MCG = 20, /*!< Group number for MCG status codes. */ + kStatusGroup_SCG = 21, /*!< Group number for SCG status codes. */ + kStatusGroup_SDSPI = 22, /*!< Group number for SDSPI status codes. */ + kStatusGroup_FLEXIO_I2S = 23, /*!< Group number for FLEXIO I2S status codes */ + kStatusGroup_FLEXIO_MCULCD = 24, /*!< Group number for FLEXIO LCD status codes */ + kStatusGroup_FLASHIAP = 25, /*!< Group number for FLASHIAP status codes */ + kStatusGroup_FLEXCOMM_I2C = 26, /*!< Group number for FLEXCOMM I2C status codes */ + kStatusGroup_I2S = 27, /*!< Group number for I2S status codes */ + kStatusGroup_IUART = 28, /*!< Group number for IUART status codes */ + kStatusGroup_CSI = 29, /*!< Group number for CSI status codes */ + kStatusGroup_SDRAMC = 35, /*!< Group number for SDRAMC status codes. */ + kStatusGroup_POWER = 39, /*!< Group number for POWER status codes. */ + kStatusGroup_ENET = 40, /*!< Group number for ENET status codes. */ + kStatusGroup_PHY = 41, /*!< Group number for PHY status codes. */ + kStatusGroup_TRGMUX = 42, /*!< Group number for TRGMUX status codes. */ + kStatusGroup_SMARTCARD = 43, /*!< Group number for SMARTCARD status codes. */ + kStatusGroup_LMEM = 44, /*!< Group number for LMEM status codes. */ + kStatusGroup_QSPI = 45, /*!< Group number for QSPI status codes. */ + kStatusGroup_DMA = 50, /*!< Group number for DMA status codes. */ + kStatusGroup_EDMA = 51, /*!< Group number for EDMA status codes. */ + kStatusGroup_DMAMGR = 52, /*!< Group number for DMAMGR status codes. */ + kStatusGroup_FLEXCAN = 53, /*!< Group number for FlexCAN status codes. */ + kStatusGroup_LTC = 54, /*!< Group number for LTC status codes. */ + kStatusGroup_FLEXIO_CAMERA = 55, /*!< Group number for FLEXIO CAMERA status codes. */ + kStatusGroup_LPC_SPI = 56, /*!< Group number for LPC_SPI status codes. */ + kStatusGroup_LPC_USART = 57, /*!< Group number for LPC_USART status codes. */ + kStatusGroup_DMIC = 58, /*!< Group number for DMIC status codes. */ + kStatusGroup_SDIF = 59, /*!< Group number for SDIF status codes.*/ + kStatusGroup_SPIFI = 60, /*!< Group number for SPIFI status codes. */ + kStatusGroup_OTP = 61, /*!< Group number for OTP status codes. */ + kStatusGroup_MCAN = 62, /*!< Group number for MCAN status codes. */ + kStatusGroup_CAAM = 63, /*!< Group number for CAAM status codes. */ + kStatusGroup_ECSPI = 64, /*!< Group number for ECSPI status codes. */ + kStatusGroup_USDHC = 65, /*!< Group number for USDHC status codes.*/ + kStatusGroup_LPC_I2C = 66, /*!< Group number for LPC_I2C status codes.*/ + kStatusGroup_ESAI = 69, /*!< Group number for ESAI status codes. */ + kStatusGroup_FLEXSPI = 70, /*!< Group number for FLEXSPI status codes. */ + kStatusGroup_MMDC = 71, /*!< Group number for MMDC status codes. */ + kStatusGroup_MICFIL = 72, /*!< Group number for MIC status codes. */ + kStatusGroup_SDMA = 73, /*!< Group number for SDMA status codes. */ + kStatusGroup_NOTIFIER = 98, /*!< Group number for NOTIFIER status codes. */ + kStatusGroup_DebugConsole = 99, /*!< Group number for debug console status codes. */ + kStatusGroup_ApplicationRangeStart = 100, /*!< Starting number for application groups. */ +}; + +enum _bl_status_groups +{ + kStatusGroup_Bootloader = kStatusGroup_ApplicationRangeStart, //!< Bootloader status group number (100). + kStatusGroup_SBLoader, //!< SB loader status group number (101). + kStatusGroup_MemoryInterface, //!< Memory interface status group number (102). + kStatusGroup_PropertyStore, //!< Property store status group number (103). + kStatusGroup_AppCrcCheck, //!< Application crc check status group number (104). + kStatusGroup_Packetizer, //!< Packetizer status group number (105). + kStatusGroup_ReliableUpdate, //!< Reliable Update status groupt number (106). + kStatusGroup_Authentication, //!< Authentication feature (107). + kStatusGroup_RomApi //!< ROM API status group number (108). +}; + +/*! @brief Generic status return codes. */ +enum _generic_status +{ + kStatus_Success = MAKE_STATUS(kStatusGroup_Generic, 0), + kStatus_Fail = MAKE_STATUS(kStatusGroup_Generic, 1), + kStatus_ReadOnly = MAKE_STATUS(kStatusGroup_Generic, 2), + kStatus_OutOfRange = MAKE_STATUS(kStatusGroup_Generic, 3), + kStatus_InvalidArgument = MAKE_STATUS(kStatusGroup_Generic, 4), + kStatus_Timeout = MAKE_STATUS(kStatusGroup_Generic, 5), + kStatus_NoTransferInProgress = MAKE_STATUS(kStatusGroup_Generic, 6), +}; + +//! @brief Bootloader status codes. +//! @ingroup bl_core +enum _bootloader_status +{ + kStatus_UnknownCommand = MAKE_STATUS(kStatusGroup_Bootloader, 0), + kStatus_SecurityViolation = MAKE_STATUS(kStatusGroup_Bootloader, 1), + kStatus_AbortDataPhase = MAKE_STATUS(kStatusGroup_Bootloader, 2), + kStatus_Ping = MAKE_STATUS(kStatusGroup_Bootloader, 3), + kStatus_NoResponse = MAKE_STATUS(kStatusGroup_Bootloader, 4), + kStatus_NoResponseExpected = MAKE_STATUS(kStatusGroup_Bootloader, 5), + kStatus_CommandUnsupported = MAKE_STATUS(kStatusGroup_Bootloader, 6), +}; + +// !@brief SB loader status codes. +enum _sbloader_status +{ + kStatusRomLdrSectionOverrun = MAKE_STATUS(kStatusGroup_SBLoader, 0), + kStatusRomLdrSignature = MAKE_STATUS(kStatusGroup_SBLoader, 1), + kStatusRomLdrSectionLength = MAKE_STATUS(kStatusGroup_SBLoader, 2), + kStatusRomLdrUnencryptedOnly = MAKE_STATUS(kStatusGroup_SBLoader, 3), + kStatusRomLdrEOFReached = MAKE_STATUS(kStatusGroup_SBLoader, 4), + kStatusRomLdrChecksum = MAKE_STATUS(kStatusGroup_SBLoader, 5), + kStatusRomLdrCrc32Error = MAKE_STATUS(kStatusGroup_SBLoader, 6), + kStatusRomLdrUnknownCommand = MAKE_STATUS(kStatusGroup_SBLoader, 7), + kStatusRomLdrIdNotFound = MAKE_STATUS(kStatusGroup_SBLoader, 8), + kStatusRomLdrDataUnderrun = MAKE_STATUS(kStatusGroup_SBLoader, 9), + kStatusRomLdrJumpReturned = MAKE_STATUS(kStatusGroup_SBLoader, 10), + kStatusRomLdrCallFailed = MAKE_STATUS(kStatusGroup_SBLoader, 11), + kStatusRomLdrKeyNotFound = MAKE_STATUS(kStatusGroup_SBLoader, 12), + kStatusRomLdrSecureOnly = MAKE_STATUS(kStatusGroup_SBLoader, 13), + kStatusRomLdrResetReturned = MAKE_STATUS(kStatusGroup_SBLoader, 14), + kStatusRomLdrRollbackBlocked = MAKE_STATUS(kStatusGroup_SBLoader, 15), + kStatusRomLdrInvalidSectionMacCount = MAKE_STATUS(kStatusGroup_SBLoader, 16), + kStatusRomLdrUnexpectedCommand = MAKE_STATUS(kStatusGroup_SBLoader, 17), +}; + +/*! @brief Type used for all status and error return values. */ +typedef int32_t status_t; + +#ifndef NULL +#define NULL 0 +#endif + +// The following macros are to be used when trying to save code size for specific peripheral configurations +// that will only be using one peripheral instance. most of the peripheral driver code can use multiple instances but by +// just using one +// we can save space +#define USE_ONLY_UART(instance) (defined(BL_FEATURE_UART_OPTIMIZE) && (BL_UART_USED_INSTANCE == instance)) +#define USE_ONLY_SPI(instance) (defined(BL_FEATURE_SPI_OPTIMIZE) && (BL_SPI_USED_INSTANCE == instance)) +#define USE_ONLY_I2C(instance) (defined(BL_FEATURE_I2C_OPTIMIZE) && (BL_I2C_USED_INSTANCE == instance)) + +//! @name Min/max macros +//@{ +#if !defined(MIN) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#if !defined(MAX) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif +//@} + +//! @brief Computes the number of elements in an array. +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +//! @name Byte swap macros +//@{ +#define BSWAP_16(x) (uint16_t)((((x)&0xFF00) >> 0x8) | (((x)&0xFF) << 0x8)) +#define BSWAP_32(val) \ + (uint32_t)((BSWAP_16((uint32_t)(val) & (uint32_t)0xFFFF) << 0x10) | (BSWAP_16((uint32_t)((val) >> 0x10)))) +//@} + +//! @name Alignment macros +//@{ +#ifndef ALIGN_DOWN +#define ALIGN_DOWN(x, a) ((x) & -(a)) +#endif +#ifndef ALIGN_UP +#define ALIGN_UP(x, a) (-(-(x) & -(a))) +#endif +//@} + +//! @brief Build a 32-bit code from four character values. +//! +//! The resulting value is built with a byte order such that the string +//! being readable in expected order when viewed in a hex editor, if the value +//! is treated as a 32-bit little endian value. +#define FOUR_CHAR_CODE(a, b, c, d) (((d) << 24) | ((c) << 16) | ((b) << 8) | ((a))) + +#if (defined(DEBUG) || defined(_DEBUG)) && !defined(DEBUG_PRINT_DISABLE) +static inline void debug_printf(const char *format, ...); + +//! @brief Debug print utility. +//! +//! This print function will only output text when the @a DEBUG macro is defined. +static inline void debug_printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + vprintf(format, args); +// Temporarily disable MISRA rule 14.2 +#if defined(__ICCARM__) +#pragma diag_suppress = Pm049 +#endif + va_end(args); +#if defined(__ICCARM__) +#pragma diag_default = Pm049 +#endif +} +#else // (DEBUG || _DEBUG) && !DEBUG_PRINT_DISABLE +// Empty macro to cause debug_printf() calls to disappear. +#define debug_printf(x, ...) \ + do \ + { \ + } while (false) +#endif // (DEBUG || _DEBUG) && !DEBUG_PRINT_DISABLE + +//! @brief Structure of version property. +//! +//! @ingroup bl_core +typedef union StandardVersion +{ + struct + { + uint8_t bugfix; //!< bugfix version [7:0] + uint8_t minor; //!< minor version [15:8] + uint8_t major; //!< major version [23:16] + char name; //!< name [31:24] + }; + uint32_t version; //!< combined version numbers + +#if defined(__cplusplus) + StandardVersion() + : version(0) + { + } + StandardVersion(uint32_t version) + : version(version) + { + } +#endif +} standard_version_t; + +#endif // __BOOTLOADER_COMMON_H__ +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/include/bootloader_hid_report_ids.h b/src/include/bootloader_hid_report_ids.h new file mode 100644 index 0000000..271de88 --- /dev/null +++ b/src/include/bootloader_hid_report_ids.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(__BOOTLOADER_HID_REPORT_IDS_H__) +#define __BOOTLOADER_HID_REPORT_IDS_H__ + +#include "packet/command_packet.h" +#ifndef BOOTLOADER_HOST +#include "usb_descriptor.h" +#endif +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Report IDs to use for the bootloader. +enum _hid_report_ids +{ + kBootloaderReportID_CommandOut = 1, + kBootloaderReportID_DataOut = 2, + kBootloaderReportID_CommandIn = 3, + kBootloaderReportID_DataIn = 4 +}; + +//! @brief Structure of a bootloader HID header. +typedef struct _bl_hid_header +{ + uint8_t reportID; //!< The report ID. + uint8_t _padding; //!< Pad byte necessary so that the length is 2-byte aligned and the data is 4-byte aligned. Set + //! to zero. + uint8_t packetLengthLsb; //!< Low byte of the packet length in bytes. + uint8_t packetLengthMsb; //!< High byte of the packet length in bytes. +} bl_hid_header_t; + +//! @brief Structure of a bootloader HID report. +typedef struct _bl_hid_report +{ + bl_hid_header_t header; //!< Header of the report. +#ifdef BOOTLOADER_HOST + uint8_t packet[kMaxHostPacketSize]; //!< Used by blhost. Always set to the max size. +#else + uint8_t packet[kMinUsbHidPacketBufferSize]; //!< The packet data that is transferred in the report. +#if BL_CONFIG_HS_USB_HID && (((BL_MIN_PACKET_SIZE + BL_PACKET_SIZE_HEADER_SIZE) % BL_CONFIG_REPORT_SIZE_MULTIPLER)) + uint8_t padding[BL_HS_REPORT_SIZE * BL_CONFIG_REPORT_SIZE_MULTIPLER - BL_FS_REPORT_SIZE]; +#endif +#endif +} bl_hid_report_t; + +#endif // __BOOTLOADER_HID_REPORT_IDS_H__ +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/memory/memory.h b/src/memory/memory.h new file mode 100644 index 0000000..42e1ccf --- /dev/null +++ b/src/memory/memory.h @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * Copyright 2016-2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _memory_h +#define _memory_h + +#include +#include "bootloader_common.h" + +//! @addtogroup memif +//! @{ + +//////////////////////////////////////////////////////////////////////////////// +// Declarations +//////////////////////////////////////////////////////////////////////////////// +//! @brief Bit mask for device ID. +#define DEVICE_ID_MASK 0xff +//! @brief Bit position of device ID. +#define DEVICE_ID_SHIFT 0 +//! @brief Bit mask for group ID. +#define GROUP_ID_MASK 0xf00 +//! @brief Bit position of group ID. +#define GROUP_ID_SHIFT 8 + +/*! @brief Construct a memory ID from a given group ID and device ID. */ +#define MAKE_MEMORYID(group, device) \ + ((((group) << GROUP_ID_SHIFT) & GROUP_ID_MASK) | (((device) << DEVICE_ID_SHIFT) & DEVICE_ID_MASK)) +/*! @brief Get group ID from a given memory ID. */ +#define GROUPID(memoryId) (((memoryId)&GROUP_ID_MASK) >> GROUP_ID_SHIFT) + +/*! @brief Get device ID from a given memory ID. */ +#define DEVICEID(memoryId) (((memoryId)&DEVICE_ID_MASK) >> DEVICE_ID_SHIFT) + +/*! @brief Memory group definition. */ +enum _bl_memory_groups +{ + kGroup_Internal = 0, //!< Kinetis internal 4G memory region. + kGroup_External = 1, //!< Kinetis external memory region. +}; + +/*! @brief Memory device ID definition. */ +enum _bl_memory_id +{ + /* Memory ID bitfiled definition. + | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + | Reserved | INT/EXT | Type | Sub-Type | + | | 0: INT | INT: | | + | | 1: EXT | 0: NorFlash0 | 0: Internal Flash(FTFX) | + | | | | 1: QSPI | + | | | | 4: IFR | + | | | | 8: SEMC | + | | | | 9: FlexSPI | + | | | | A: SPIFI | + | | | | others: Unused | + | | | | | + | | | 1: ExecuteOnlyRegion | 0: Internal Flash(FTFX) | + | | | | others: Unused | + | | | | | + | | | others: Unused | | + | | | | | + | | | EXT: | | + | | | 0: NandFlash | 0: SEMC | + | | | | 1: FlexSPI | + | | | | others: Unused | + | | | | | + | | | 1: NorFlash/EEPROM | 0: LPSPI | + | | | | 1: LPI2C | + | | | | others: Unused | + | | | | | + | | | 2: SD/SDHC/SDXC/MMC/eMMC | 0: uSDHC SD | + | | | | 1: uSDHC MMC | + | | | | others: Unused | + | | | others: Unused | | + + INT : Internal 4G memory, including internal memory modules, and XIP external memory modules. + EXT : Non-XIP external memory modules. + */ + kMemoryInternal = MAKE_MEMORYID(kGroup_Internal, 0), // Internal memory (include all on chip memory) + kMemoryQuadSpi0 = MAKE_MEMORYID(kGroup_Internal, 1), // Qsuad SPI memory 0 + kMemoryIFR0 = MAKE_MEMORYID(kGroup_Internal, 4), // Nonvolatile information register 0. Only used by SB loader. + kMemorySemcNor = MAKE_MEMORYID(kGroup_Internal, 8), // SEMC Nor memory + kMemoryFlexSpiNor = MAKE_MEMORYID(kGroup_Internal, 9), // Flex SPI Nor memory + kMemorySpifiNor = MAKE_MEMORYID(kGroup_Internal, 0xA), // SPIFI Nor memory + kMemoryFlashExecuteOnly = MAKE_MEMORYID(kGroup_Internal, 0x10), // Execute-only region on internal Flash + + kMemorySemcNand = MAKE_MEMORYID(kGroup_External, 0), // SEMC NAND memory + kMemorySpiNand = MAKE_MEMORYID(kGroup_External, 1), // SPI NAND memory + kMemorySpiNorEeprom = MAKE_MEMORYID(kGroup_External, 0x10), // SPI NOR/EEPROM memory + kMemoryI2cNorEeprom = MAKE_MEMORYID(kGroup_External, 0x11), // I2C NOR/EEPROM memory + kMemorySDCard = MAKE_MEMORYID(kGroup_External, 0x20), // eSD, SD, SDHC, SDXC memory Card + kMemoryMMCCard = MAKE_MEMORYID(kGroup_External, 0x21), // MMC, eMMC memory Card + // +}; + +//! @brief Memory interface status codes. +enum _memory_interface_status +{ + kStatusMemoryRangeInvalid = MAKE_STATUS(kStatusGroup_MemoryInterface, 0), + kStatusMemoryReadFailed = MAKE_STATUS(kStatusGroup_MemoryInterface, 1), + kStatusMemoryWriteFailed = MAKE_STATUS(kStatusGroup_MemoryInterface, 2), + kStatusMemoryCumulativeWrite = MAKE_STATUS(kStatusGroup_MemoryInterface, 3), + kStatusMemoryAppOverlapWithExecuteOnlyRegion = MAKE_STATUS(kStatusGroup_MemoryInterface, 4), + kStatusMemoryNotConfigured = MAKE_STATUS(kStatusGroup_MemoryInterface, 5), + kStatusMemoryAlignmentError = MAKE_STATUS(kStatusGroup_MemoryInterface, 6), + kStatusMemoryVerifyFailed = MAKE_STATUS(kStatusGroup_MemoryInterface, 7), + kStatusMemoryWriteProtected = MAKE_STATUS(kStatusGroup_MemoryInterface, 8), + kStatusMemoryAddressError = MAKE_STATUS(kStatusGroup_MemoryInterface, 9), + kStatusMemoryBlankCheckFailed = MAKE_STATUS(kStatusGroup_MemoryInterface, 10), + kStatusMemoryBlankPageReadDisallowed = MAKE_STATUS(kStatusGroup_MemoryInterface, 11), + kStatusMemoryProtectedPageReadDisallowed = MAKE_STATUS(kStatusGroup_MemoryInterface, 12), + kStatusMemoryFfrSpecRegionWriteBroken = MAKE_STATUS(kStatusGroup_MemoryInterface, 13), + kStatusMemoryUnsupportedCommand = MAKE_STATUS(kStatusGroup_MemoryInterface, 14), + +}; + +/* +* @brief Flashiap status codes. +*/ +enum _flashiap_status +{ + kStatus_FLASHIAP_Success = kStatus_Success, /*!< Api is executed successfully */ + kStatus_FLASHIAP_InvalidCommand = MAKE_STATUS(kStatusGroup_FLASHIAP, 1U), /*!< Invalid command */ + kStatus_FLASHIAP_SrcAddrError = + MAKE_STATUS(kStatusGroup_FLASHIAP, 2U), /*!< Source address is not on word boundary */ + kStatus_FLASHIAP_DstAddrError = + MAKE_STATUS(kStatusGroup_FLASHIAP, 3U), /*!< Destination address is not on a correct boundary */ + kStatus_FLASHIAP_SrcAddrNotMapped = + MAKE_STATUS(kStatusGroup_FLASHIAP, 4U), /*!< Source address is not mapped in the memory map */ + kStatus_FLASHIAP_DstAddrNotMapped = + MAKE_STATUS(kStatusGroup_FLASHIAP, 5U), /*!< Destination address is not mapped in the memory map */ + kStatus_FLASHIAP_CountError = + MAKE_STATUS(kStatusGroup_FLASHIAP, 6U), /*!< Byte count is not multiple of 4 or is not a permitted value */ + kStatus_FLASHIAP_InvalidSector = + MAKE_STATUS(kStatusGroup_FLASHIAP, + 7), /*!< Sector number is invalid or end sector number is greater than start sector number */ + kStatus_FLASHIAP_SectorNotblank = MAKE_STATUS(kStatusGroup_FLASHIAP, 8U), /*!< One or more sectors are not blank */ + kStatus_FLASHIAP_NotPrepared = + MAKE_STATUS(kStatusGroup_FLASHIAP, 9U), /*!< Command to prepare sector for write operation was not executed */ + kStatus_FLASHIAP_CompareError = + MAKE_STATUS(kStatusGroup_FLASHIAP, 10U), /*!< Destination and source memory contents do not match */ + kStatus_FLASHIAP_Busy = + MAKE_STATUS(kStatusGroup_FLASHIAP, 11U), /*!< Flash programming hardware interface is busy */ + kStatus_FLASHIAP_ParamError = + MAKE_STATUS(kStatusGroup_FLASHIAP, 12U), /*!< Insufficient number of parameters or invalid parameter */ + kStatus_FLASHIAP_AddrError = MAKE_STATUS(kStatusGroup_FLASHIAP, 13U), /*!< Address is not on word boundary */ + kStatus_FLASHIAP_AddrNotMapped = + MAKE_STATUS(kStatusGroup_FLASHIAP, 14U), /*!< Address is not mapped in the memory map */ + kStatus_FLASHIAP_NoPower = MAKE_STATUS(kStatusGroup_FLASHIAP, 24U), /*!< Flash memory block is powered down */ + kStatus_FLASHIAP_NoClock = + MAKE_STATUS(kStatusGroup_FLASHIAP, 27U), /*!< Flash memory block or controller is not clocked */ +}; + +/*! +* @brief Flashiap status codes. +*/ +enum _flashiap_wrapper_status +{ + kStatus_FLASHIAP_InvalidArgument = MAKE_STATUS(kStatusGroup_FLASHIAP, 50U), /*!< Invalid argument*/ + kStatus_FLASHIAP_UnknownProperty = MAKE_STATUS(kStatusGroup_FLASHIAP, 51U), /*!< Unknown property.*/ + kStatus_FLASHIAP_AlignmentError = + MAKE_STATUS(kStatusGroup_FLASHIAP, 52U), /*!< Parameter is not aligned with the specified baseline*/ + kStatus_FLASHIAP_AddressError = MAKE_STATUS(kStatusGroup_FLASHIAP, 53U), /*!< Address is out of range */ + kStatus_FLASHIAP_EraseKeyError = MAKE_STATUS(kStatusGroup_FLASHIAP, 54U), /*!< API erase key is invalid.*/ + kStatus_FLASHIAP_MemoryNotblank = + MAKE_STATUS(kStatusGroup_FLASHIAP, 55U), /*!< memory to be verified are not blank.*/ +}; + +// !@brief Memory property enum codes +enum +{ + /* Memory property bitfield definition. + * Bit[0]: 0 -- NotExecutable( None-XIP) + * 1 -- Executable(XIP) + * Bit[1] : Reserved.(Reserved for Int/Ext. 0 Internal, 1 External) + * Bit[3:2] : Reserved. + * Bit[7:4] : 0000 -- FLASH. + * 0001 -- RAM. + * 0010 -- Device. + * 0100 -- Reserved. + * 1000 -- Reserved. + * Bit[15-8] : Reserved. + * Bit[16] : Reserved.(Reserved for Bufferable. 0 Not bufferable, 1 Bufferable) + * Bit[17] : Reserved.(Reserved for Cacheable. 0 Not cacheable, 1 Cacheable) + * Bit[18] : Reserved.(Reserved for Shareable. 0 Not shareable, 1 Shareable) + * Bit[31-19] : Reserved. + */ + kMemoryNotExecutable = 0, //!< The memory doesn't support executing in place. + kMemoryIsExecutable = 1, //!< The memory supports executing in place. + kMemoryType_FLASH = 0x00, //!< The memory is FLASH device + kMemoryType_RAM = 0x10, //!< The memory is RAM device + kMemoryType_Device = 0x20, //!< The memory is device register +}; + +//! @brief Interface to memory operations. +//! +//! This is the main abstract interface to all memory operations. +typedef struct _memory_interface +{ + status_t (*init)(void); + status_t (*read)(uint32_t address, uint32_t length, uint8_t *buffer, uint32_t memoryId); + status_t (*write)(uint32_t address, uint32_t length, const uint8_t *buffer, uint32_t memoryId); + status_t (*fill)(uint32_t address, uint32_t length, uint32_t pattern); + status_t (*flush)(void); + status_t (*finalize)(void); + status_t (*erase)(uint32_t address, uint32_t length, uint32_t memoryId); +} memory_interface_t; + +//! @brief Interface to memory operations for one region of memory. +typedef struct _memory_region_interface +{ + status_t (*init)(void); + status_t (*read)(uint32_t address, uint32_t length, uint8_t *buffer); + status_t (*write)(uint32_t address, uint32_t length, const uint8_t *buffer); + status_t (*fill)(uint32_t address, uint32_t length, uint32_t pattern); + status_t (*flush)(void); + status_t (*erase)(uint32_t address, uint32_t length); +} memory_region_interface_t; + +//! @brief Structure of a memory map entry. +typedef struct _memory_map_entry +{ + uint32_t startAddress; + uint32_t endAddress; + uint32_t memoryProperty; + const memory_region_interface_t *memoryInterface; +} memory_map_entry_t; + +#if BL_FEATURE_EXPAND_MEMORY + +typedef struct _external_memory_region_interface +{ + status_t (*init)(void); + status_t (*read)(uint32_t address, uint32_t length, uint8_t *buffer); + status_t (*write)(uint32_t address, uint32_t length, const uint8_t *buffer); + status_t (*erase)(uint32_t address, uint32_t length); + status_t (*config)(uint32_t *buffer); + status_t (*flush)(void); + status_t (*finalize)(void); +} external_memory_region_interface_t; + +typedef struct _external_memory_map_entry +{ + uint32_t memoryId; + uint32_t status; + uint32_t basicUnitCount; + uint32_t basicUnitSize; + const external_memory_region_interface_t *memoryInterface; +} external_memory_map_entry_t; +#endif // BL_FEATURE_EXPAND_MEMORY + +//! @brief Memory Map index constants +enum _memorymap_constants +{ +#if !BL_FEATURE_HAS_NO_INTERNAL_FLASH + kIndexFlashArray = 0, + kIndexSRAM = 1, +#if defined(KV58F22_SERIES) + kIndexDTCM = 2, + kIndexOCRAM = 3, +#endif // KV58F22_SERIES +#if defined(K28F15_SERIES) + kIndexOCRAM = 4, +#endif +#else + kIndexSRAM = 0, +#if CPU_IS_ARM_CORTEX_M7 + kIndexDTCM = 1, + kIndexOCRAM = 2, +#endif // CPU_IS_ARM_CORTEX_M7 +#endif // #if !BL_FEATURE_HAS_NO_INTERNAL_FLASH +#if BL_FEATURE_FLEXSPI_NOR_MODULE + kIndexFlexSpiNor = 3, + kIndexFlexSpiNorAlias = 4, +#endif // #if BL_FEATURE_FLEXSPI_NOR_MODULE +#if BL_FEATURE_QSPI_MODULE + kIndexQspiMemory = 2, + kIndexQspiAliasArea = 3, +#endif // BL_FEATURE_QSPI_MODULE + kSRAMSeparatrix = (uint32_t)0x20000000 //!< This value is the start address of SRAM_U +}; + +#if BL_FEATURE_EXPAND_MEMORY +enum _external_memorymap_constants +{ + kIndexStart = 0, + // Never add index for external memory map. + // Because external memories don't have fixed indexes, + // and are changed by bootloader configuration. + // Please call find_external_map_index() to get the correct index. +}; +#endif // BL_FEATURE_EXPAND_MEMORY + +//! @brief flash memory erase all options. +typedef enum _flash_erase_all_option +{ + kFlashEraseAllOption_Blocks = 0, + kFlashEraseAllOption_ExecuteOnlySegments = 1 +} flash_erase_all_option_t; + +//////////////////////////////////////////////////////////////////////////////// +// Externs +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Memory map for the system. +extern memory_map_entry_t g_memoryMap[]; + +//! @brief External memory map for the system. +#if BL_FEATURE_EXPAND_MEMORY +extern external_memory_map_entry_t g_externalMemoryMap[]; +#endif // BL_FEATURE_EXPAND_MEMORY + +//! @name Memory interfaces +//@{ + +//! @brief Abstract memory interface. +//! +//! This interface utilizes the memory map to perform different memory operations +//! depending on the region of memory being accessed. +extern const memory_interface_t g_memoryInterface; + +//! @brief Main Flash memory interface. +extern const memory_region_interface_t g_flashMemoryInterface; + +#if BL_HAS_SECONDARY_INTERNAL_FLASH +//! @brief Secondary Flash memory interface. +extern const memory_region_interface_t g_secondaryFlashMemoryInterface; +#endif + +//! @brief Memory interface for memory with Normal type. +//! +//! Use of multiword loads and stores is allowed with this memory type. +extern const memory_region_interface_t g_normalMemoryInterface; + +#if defined(KV58F22_SERIES) +//! @brief Memory interface for memory with Normal type. +//! +//! Use of multiword loads and stores is allowed with this memory type. +extern const memory_region_interface_t g_normalDTCMInterface; +#endif // KV58F22_SERIES + +#if defined(KV58F22_SERIES) || defined(K28F15_SERIES) +//! @brief Memory interface for memory with Normal type. +//! +//! Use of multiword loads and stores is allowed with this memory type. +extern const memory_region_interface_t g_normalOCRAMInterface; +#endif // defined(KV58F22_SERIES) || defined(K28F15_SERIES) + +//! @brief Memory interface for memory with Device or Strongly-ordered type. +//! +//! This memory type does not support multiword loads and stores. +extern const memory_region_interface_t g_deviceMemoryInterface; + +#if defined BL_FEATURE_QSPI_MODULE +extern const memory_region_interface_t g_qspiMemoryInterface; +#if BL_FEATURE_QSPI_ALIAS_AREA +extern const memory_region_interface_t g_qspiAliasAreaInterface; +#endif // BL_FEATURE_QSPI_ALIAS_AREA +#endif // BL_FEATURE_QSPI_MODULE + +#if BL_FEATURE_FLEXSPI_NOR_MODULE +extern const memory_region_interface_t g_flexspiMemoryInterface; +#if BL_FEATURE_FLEXSPI_ALIAS_AREA +extern const memory_region_interface_t g_flexspiAliasAreaInterface; +#endif // #if BL_FEATURE_FLEXSPI_ALIAS_AREA +#endif // #if BL_FEATURE_FLEXSPI_NOR_MODULE + +#if BL_FEATURE_EXPAND_MEMORY +#if BL_FEATURE_SPINAND_MODULE +extern const external_memory_region_interface_t g_spiNandMemoryInterface; +#endif // BL_FEATURE_SPINAND_MODULE +#if BL_FEATURE_SPI_NOR_EEPROM_MODULE +extern const external_memory_region_interface_t g_spiNorEepromMemoryInterface; +#endif // BL_FEATURE_SPI_NOR_EEPROM_MODULE +#if BL_FEATURE_SD_MODULE +extern const external_memory_region_interface_t g_sdMemoryInterface; +#endif +#if BL_FEATURE_MMC_MODULE +extern const external_memory_region_interface_t g_mmcMemoryInterface; +#endif +#endif // BL_FEATURE_EXPAND_MEMORY + +//@} + +//////////////////////////////////////////////////////////////////////////////// +// Prototypes +//////////////////////////////////////////////////////////////////////////////// + +#if defined(__cplusplus) +extern "C" { +#endif // __cplusplus + +//! @name Generic memory interface implementation +//@{ + +//! @brief Initialize memory interface. +status_t mem_init(void); + +//! @brief Read memory. +status_t mem_read(uint32_t address, uint32_t length, uint8_t *buffer, uint32_t memoryId); + +//! @brief Write memory. +status_t mem_write(uint32_t address, uint32_t length, const uint8_t *buffer, uint32_t memoryId); + +//! @brief Fill memory with a word pattern. +status_t mem_fill(uint32_t address, uint32_t length, uint32_t pattern); + +//! @brief Erase memory. +status_t mem_erase(uint32_t address, uint32_t length, uint32_t memoryId); + +//! @brief Flush memory. +status_t mem_flush(void); + +//! @brief Reset state machine of memory interface. +status_t mem_finalize(void); + +//! @brief Find a map entry that matches address and length. +status_t find_map_entry(uint32_t address, uint32_t length, const memory_map_entry_t **map); + +#if BL_FEATURE_EXPAND_MEMORY +//! @brief Find an external map entry that matches address and length. +status_t find_external_map_entry(uint32_t address, + uint32_t length, + uint32_t memory_id, + const external_memory_map_entry_t **map); + +//! @brief Find an external map index that matches the given memory id. +status_t find_external_map_index(uint32_t memoryId, uint32_t *index); +#endif // BL_FEATURE_EXPAND_MEMORY + +//!@brief Check is the specified memory region is erased. +bool mem_is_erased(uint32_t address, uint32_t length); + +//@} + +//! @name Memory utilities +//@{ + +//! @brief Determine if all or part of block is in a reserved region. +bool mem_is_block_reserved(uint32_t address, uint32_t length); + +//@} + +//! @name Flash erase operations +//@{ + +//! @brief Erase Flash memory. +status_t flash_mem_erase(uint32_t address, uint32_t length); + +#if BL_FEATURE_FAC_ERASE +//! @brief Erase all Flash memory or all Flash execute-only segments. +//! +//! It is only valid for non-flash resident bootloader when option is erasing execute-only segments. +status_t flash_mem_erase_all(flash_erase_all_option_t eraseOption); +#else +//! @brief Erase all Flash memory. +//! +//! If building for flash resident bootloader, we have to decompose the the flash erase all +//! operation into two region erases. This allows the user to still do an erase all, but not +//! wipe out the bootloader itself. +status_t flash_mem_erase_all(void); +#endif + +//! @brief Erase all Flash memory (unsecure). +status_t flash_mem_erase_all_unsecure(void); + +//@} + +//! @name QSPI erase operation +//@{ + +//! @brief Erase all QSPI memory +status_t qspi_mem_erase_all(void); + +//! @brief Configure QSPI memory +status_t configure_qspi(const uint32_t address); +//@} + +//! @name FlexSPI NOR erase operation +//@{ + +//! @brief Config FlexSPI NOR memory +status_t flexspi_nor_mem_config(uint32_t *config); + +//! @brief Erase all FlexSPI NOR memory +status_t flexspi_nor_mem_erase_all(void); + +//! @brief Get Property from flexspi0 NOR Flash driver +status_t flexspi_nor_get_property(uint32_t whichProperty, uint32_t *value); + +//! @brief Get the status of flexspi configuration +bool is_flexspi_nor_configured(void); + +//@} + +//! @name SPI NAND erase operation +//@{ + +//! @brief Erase all SPI NAND memory +status_t spinand_mem_erase_all(void); + +//! @brief Get Property from spi NAND flash driver. +status_t spinand_get_property(uint32_t whichProperty, uint32_t *value); + +//@} + +//! @name SPI NOR/EEPROM +//@{ + +//! @brief Get Property from spi NOR/EEPROM flash driver. +status_t spi_nor_eeprom_get_property(uint32_t whichProperty, uint32_t *value); + +//@} + +//! @name SD CARD +//@{ + +//! @brief Get Property from SD card driver. +status_t sd_get_property(uint32_t whichProperty, uint32_t *value); + +//@} + +//! @name MMC CARD +//@{ + +//! @brief Get Property from MMC card driver. +status_t mmc_get_property(uint32_t whichProperty, uint32_t *value); + +//@} + +#if defined(__cplusplus) +} +#endif // __cplusplus + +#if defined(BOOTLOADER_HOST) + +//////////////////////////////////////////////////////////////////////////////// +// Simulator host prototypes +//////////////////////////////////////////////////////////////////////////////// + +#if __cplusplus +extern "C" { +#endif + +//! @brief Erase all flash. +void host_flash_erase_all(void); + +//! @brief Erase all flash (unsecure). +void host_flash_erase_all_unsecure(void); + +//! @brief Erase a region of flash. +void host_flash_erase_region(uint32_t address, uint32_t count); + +#if __cplusplus +} +#endif + +#endif // BOOTLOADER_HOST + +//! @} + +#endif // _memory_h +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/middleware/libusbsio/bin/Win32/libusbsio.lib b/src/middleware/libusbsio/bin/Win32/libusbsio.lib new file mode 100644 index 0000000..94dc013 Binary files /dev/null and b/src/middleware/libusbsio/bin/Win32/libusbsio.lib differ diff --git a/src/middleware/libusbsio/bin/linux/amd64/libusbsio.a b/src/middleware/libusbsio/bin/linux/amd64/libusbsio.a new file mode 100644 index 0000000..5b37763 Binary files /dev/null and b/src/middleware/libusbsio/bin/linux/amd64/libusbsio.a differ diff --git a/src/middleware/libusbsio/bin/linux/amd64/libusbsio.so b/src/middleware/libusbsio/bin/linux/amd64/libusbsio.so new file mode 100644 index 0000000..6c60fe1 Binary files /dev/null and b/src/middleware/libusbsio/bin/linux/amd64/libusbsio.so differ diff --git a/src/middleware/libusbsio/bin/linux/i386/libusbsio.a b/src/middleware/libusbsio/bin/linux/i386/libusbsio.a new file mode 100644 index 0000000..03ec3a7 Binary files /dev/null and b/src/middleware/libusbsio/bin/linux/i386/libusbsio.a differ diff --git a/src/middleware/libusbsio/bin/linux/i386/libusbsio.so b/src/middleware/libusbsio/bin/linux/i386/libusbsio.so new file mode 100644 index 0000000..96716a4 Binary files /dev/null and b/src/middleware/libusbsio/bin/linux/i386/libusbsio.so differ diff --git a/src/middleware/libusbsio/bin/osx/libusbsio.a b/src/middleware/libusbsio/bin/osx/libusbsio.a new file mode 100644 index 0000000..624e012 Binary files /dev/null and b/src/middleware/libusbsio/bin/osx/libusbsio.a differ diff --git a/src/middleware/libusbsio/bin/osx/libusbsio.dylib b/src/middleware/libusbsio/bin/osx/libusbsio.dylib new file mode 100644 index 0000000..7ba1bca Binary files /dev/null and b/src/middleware/libusbsio/bin/osx/libusbsio.dylib differ diff --git a/src/middleware/libusbsio/inc/lpcusbsio.h b/src/middleware/libusbsio/inc/lpcusbsio.h new file mode 100644 index 0000000..b998568 --- /dev/null +++ b/src/middleware/libusbsio/inc/lpcusbsio.h @@ -0,0 +1,763 @@ +/* + * @brief LPC USB serial I/O interface definition + * + * @note + * Copyright(C) NXP Semiconductors, 2014 + * All rights reserved. + * + * @par + * Software that is described herein is for illustrative purposes only + * which provides customers with programming information regarding the + * LPC products. This software is supplied "AS IS" without any warranties of + * any kind, and NXP Semiconductors and its licensor disclaim any and + * all warranties, express or implied, including all implied warranties of + * merchantability, fitness for a particular purpose and non-infringement of + * intellectual property rights. NXP Semiconductors assumes no responsibility + * or liability for the use of the software, conveys no license or rights under any + * patent, copyright, mask work right, or any other intellectual property rights in + * or to any products. NXP Semiconductors reserves the right to make changes + * in the software without notification. NXP Semiconductors also makes no + * representation or warranty that such application will be suitable for the + * specified use without further testing or modification. + * + * @par + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, under NXP Semiconductors' and its + * licensor's relevant copyrights in the software, without fee, provided that it + * is used in conjunction with NXP Semiconductors microcontrollers. This + * copyright, permission, and disclaimer notice must appear in all copies of + * this code. + */ +#ifndef __LPCUSBSIO_H +#define __LPCUSBSIO_H + +#include +#include "lpcusbsio_protocol.h" + +#if defined(LPCUSBSIO_EXPORTS) +#define LPCUSBSIO_API __declspec(dllexport) +#elif defined(LPCUSBSIO_IMPORTS) +#define LPCUSBSIO_API __declspec(dllimport) +#else +#define LPCUSBSIO_API +#endif + +#if defined(_MSC_VER) +#pragma comment(lib,"setupapi.lib") +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup LPCUSBSIO_API LPC USB serial I/O (LPCUSBSIO) API interface + * API description
+ * The LPCUSBSIO APIs can be divided into two broad sets. The first set consists of + * five control APIs and the second set consists of two data transferring APIs. On error + * most APIs return an LPCUSBSIO_ERR_T code. Application code can call LPCUSBSIO_Error() routine + * to get user presentable uni-code string corresponding to the last error. + *
+ * The current version of LPCUSBSIO allows communicating with I2C, SPI slave devices and GPIO. + * + * @{ + */ + +/** NXP USB-IF vendor ID. */ +#define LPCUSBSIO_VID 0x1FC9 +/** USB-IF product ID for LPCUSBSIO devices. */ +#define LPCUSBSIO_PID 0x0090 +#define MCULINKSIO_PID 0x0143 + +/** Read time-out value in milliseconds used by the library. If a response is not received + * + */ +#define LPCUSBSIO_READ_TMO 500 + +/** I2C_IO_OPTIONS Options to I2C_DeviceWrite & I2C_DeviceRead routines +* @{ +*/ +/** Generate start condition before transmitting */ +#define I2C_TRANSFER_OPTIONS_START_BIT 0x0001 + +/** Generate stop condition at the end of transfer */ +#define I2C_TRANSFER_OPTIONS_STOP_BIT 0x0002 + +/** Continue transmitting data in bulk without caring about Ack or nAck from device if this bit is +* not set. If this bit is set then stop transmitting the data in the buffer when the device nAcks +*/ +#define I2C_TRANSFER_OPTIONS_BREAK_ON_NACK 0x0004 + +/** lpcusbsio-I2C generates an ACKs for every Byte read. Some I2C slaves require the I2C +master to generate a nACK for the last data Byte read. Setting this bit enables working with such +I2C slaves */ +#define I2C_TRANSFER_OPTIONS_NACK_LAST_BYTE 0x0008 + +/* Setting this bit would mean that the address field should be ignored. +* The address is either a part of the data or this is a special I2C +* frame that doesn't require an address. For example when transferring a +* frame greater than the USB_HID packet this option can be used. +*/ +#define I2C_TRANSFER_OPTIONS_NO_ADDRESS 0x00000040 + +/** @} */ + +/** I2C_FAST_TRANSFER_OPTIONS I2C master faster transfer options +* @{ +*/ + +/** Ignore NACK during data transfer. By default transfer is aborted. */ +#define I2C_FAST_XFER_OPTION_IGNORE_NACK 0x01 +/** ACK last Byte received. By default we NACK last Byte we receive per I2C specification. */ +#define I2C_FAST_XFER_OPTION_LAST_RX_ACK 0x02 + +/** +* @} +*/ + +/****************************************************************************** +* Type defines +******************************************************************************/ +/** @brief Handle type */ +typedef void *LPC_HANDLE; + +/** @brief Error types returned by LPCUSBSIO APIs */ +typedef enum LPCUSBSIO_ERR_t { + /** All API return positive number for success */ + LPCUSBSIO_OK = 0, + /** HID library error. */ + LPCUSBSIO_ERR_HID_LIB = -1, + /** Handle passed to the function is invalid. */ + LPCUSBSIO_ERR_BAD_HANDLE = -2, + /** Thread Synchronization error. */ + LPCUSBSIO_ERR_SYNCHRONIZATION = -3, + /** Memory Allocation error. */ + LPCUSBSIO_ERR_MEM_ALLOC = -4, + /** Mutex Creation error. */ + LPCUSBSIO_ERR_MUTEX_CREATE = -5, + + /* Errors from hardware I2C interface*/ + /** Fatal error occurred */ + LPCUSBSIO_ERR_FATAL = -0x11, + /** Transfer aborted due to NACK */ + LPCUSBSIO_ERR_I2C_NAK = -0x12, + /** Transfer aborted due to bus error */ + LPCUSBSIO_ERR_I2C_BUS = -0x13, + /** NAK received after SLA+W or SLA+R */ + LPCUSBSIO_ERR_I2C_SLAVE_NAK = -0x14, + /** I2C bus arbitration lost to other master */ + LPCUSBSIO_ERR_I2C_ARBLOST = -0x15, + + /* Errors from firmware's HID-SIO bridge module */ + /** Transaction timed out */ + LPCUSBSIO_ERR_TIMEOUT = -0x20, + /** Invalid HID_SIO Request or Request not supported in this version. */ + LPCUSBSIO_ERR_INVALID_CMD = -0x21, + /** Invalid parameters are provided for the given Request. */ + LPCUSBSIO_ERR_INVALID_PARAM = -0x22, + /** Partial transfer completed. */ + LPCUSBSIO_ERR_PARTIAL_DATA = -0x23, +} LPCUSBSIO_ERR_T; + +/** @brief I2C clock rates */ +typedef enum I2C_ClockRate_t { + I2C_CLOCK_STANDARD_MODE = 100000, /*!< 100kb/sec */ + I2C_CLOCK_FAST_MODE = 400000, /*!< 400kb/sec */ + I2C_CLOCK_FAST_MODE_PLUS = 1000000, /*!< 1000kb/sec */ +} I2C_CLOCKRATE_T; + +/** @brief I2C Port configuration information */ +typedef struct PortConfig_t { + I2C_CLOCKRATE_T ClockRate; /*!< I2C Clock speed */ + uint32_t Options; /*!< Configuration options */ +} I2C_PORTCONFIG_T; + +/** @brief I2C Fast transfer parameter structure */ +typedef struct I2CFastXferParam_t { + uint16_t txSz; /*!< Number of bytes in transmit array, + if 0 only receive transfer will be carried on */ + uint16_t rxSz; /*!< Number of bytes to received, + if 0 only transmission we be carried on */ + uint16_t options; /*!< Fast transfer options */ + uint16_t slaveAddr; /*!< 7-bit I2C Slave address */ + const uint8_t *txBuff; /*!< Pointer to array of bytes to be transmitted */ + uint8_t *rxBuff; /*!< Pointer memory where bytes received from I2C be stored */ +} I2C_FAST_XFER_T; + +/** Macro to generate SPI device number from port and pin */ +#define LPCUSBSIO_GEN_SPI_DEVICE_NUM(port, pin) ((((uint8_t)(port) & 0x07) << 5) | ((pin) & 0x1F)) + +/** @brief SPI transfer parameter structure */ +typedef struct SPIXferParam_t { + uint16_t length; /*!< Number of bytes to transmit and receive */ + uint8_t options; /*!< Transfer options */ + uint8_t device; /*!< SPI slave device, use @ref LPCUSBSIO_GEN_SPI_DEVICE_NUM macro + to derive device number from a GPIO port and pin number */ + const uint8_t *txBuff; /*!< Pointer to array of bytes to be transmitted */ + uint8_t *rxBuff; /*!< Pointer memory where bytes received from SPI be stored */ +} SPI_XFER_T; + +/* SPI config option aliases */ +#define SPI_CONFIG_OPTION_DATA_SIZE_8 HID_SPI_CONFIG_OPTION_DATA_SIZE_8 +#define SPI_CONFIG_OPTION_DATA_SIZE_16 HID_SPI_CONFIG_OPTION_DATA_SIZE_16 +#define SPI_CONFIG_OPTION_POL_0 HID_SPI_CONFIG_OPTION_POL_0 +#define SPI_CONFIG_OPTION_POL_1 HID_SPI_CONFIG_OPTION_POL_1 +#define SPI_CONFIG_OPTION_PHA_0 HID_SPI_CONFIG_OPTION_PHA_0 +#define SPI_CONFIG_OPTION_PHA_1 HID_SPI_CONFIG_OPTION_PHA_1 +#define SPI_CONFIG_OPTION_PRE_DELAY(x) HID_SPI_CONFIG_OPTION_PRE_DELAY(x) +#define SPI_CONFIG_OPTION_POST_DELAY(x) HID_SPI_CONFIG_OPTION_POST_DELAY(x) + +/****************************************************************************** +* LPCUSBSIO functions +******************************************************************************/ + +/** @brief Get number LPCUSBSIO ports available on the LPC controller. + * + * This function gets the number of LPCUSBSIO ports that are available on the LPC controller. + * The number of ports available in each of these chips is different. + * + * @param vid : Vendor ID. + * @param pid : Product ID. + * + * @returns + * The number of ports available on the LPC controller. + * + */ +LPCUSBSIO_API int LPCUSBSIO_GetNumPorts(uint32_t vid, uint32_t pid); + +/** @brief Opens the indexed Serial IO port. +* +* This function opens the indexed port and provides a handle to it. Valid values for +* the index of port can be from 0 to the value obtained using LPCUSBSIO_GetNumPorts +* – 1). +* +* @param index : Index of the port to be opened. +* +* @returns +* This function returns a handle to LPCUSBSIO port object on +* success or NULL on failure. +*/ +LPCUSBSIO_API LPC_HANDLE LPCUSBSIO_Open(uint32_t index); + + +/** @brief Closes a LPC Serial IO port. +* +* Closes a Serial IO port and frees all resources that were used by it. +* +* @param hUsbSio : Handle of the LPSUSBSIO port. +* +* @returns +* This function returns LPCUSBSIO_OK on success and negative error code on failure. +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t LPCUSBSIO_Close(LPC_HANDLE hUsbSio); + +/** @brief Get version string of the LPCUSBSIO library. +* +* @param hUsbSio : A device handle returned from LPCUSBSIO_Open(). +* +* @returns +* This function returns a string containing the version of the library. +* If the device handle passed is not NULL then the firmware version of +* the connected device is appended to the string. +*/ +LPCUSBSIO_API const char *LPCUSBSIO_GetVersion(LPC_HANDLE hUsbSio); + +/** @brief Get a string describing the last error which occurred. +* +* @param hUsbSio : A device handle returned from LPCUSBSIO_Open(). +* +* @returns +* This function returns a string containing the last error +* which occurred or NULL if none has occurred. +* +*/ +LPCUSBSIO_API const wchar_t *LPCUSBSIO_Error(LPC_HANDLE hUsbSio); + +/** @brief Returns the number of I2C ports supported by Serial IO device. +* +* @param hUsbSio : A device handle returned from LPCUSBSIO_Open(). +* +* @returns +* This function returns the number of I2C ports on success and negative error code on failure. +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API uint32_t LPCUSBSIO_GetNumI2CPorts(LPC_HANDLE hUsbSio); + +/** @brief Returns the number of SPI ports supported by Serial IO device. +* +* @param hUsbSio : A device handle returned from LPCUSBSIO_Open(). +* +* @returns +* This function returns the number of SPI ports on success and negative error code on failure. +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API uint32_t LPCUSBSIO_GetNumSPIPorts(LPC_HANDLE hUsbSio); + +/** @brief Returns the number of GPIO ports supported by Serial IO device. +* +* @param hUsbSio : A device handle returned from LPCUSBSIO_Open(). +* +* @returns +* This function returns the number of GPIO ports on success and negative error code on failure. +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API uint32_t LPCUSBSIO_GetNumGPIOPorts(LPC_HANDLE hUsbSio); + +/** @brief Returns the max number of bytes supported for I2C/SPI transfers by the Serial IO device. +* +* @param hUsbSio : A device handle returned from LPCUSBSIO_Open(). +* +* @returns +* This function returns the max data transfer size on success and negative error code on failure. +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API uint32_t LPCUSBSIO_GetMaxDataSize(LPC_HANDLE hUsbSio); + +/** @brief Returns the last error seen by the Library. +* +* @returns +* This function returns the last error seen by the library. +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t LPCUSBSIO_GetLastError(void); + +/****************************************************************************** +* I2C functions +******************************************************************************/ + +/** @brief Initialize a I2C port. + * + * This function initializes the I2C port and the communication parameters associated + * with it. + * + * @param hUsbSio : Handle of the LPSUSBSIO port. + * @param config : Pointer to I2C_PORTCONFIG_T structure. Members of + * I2C_PORTCONFIG_T structure contains the values for I2C + * master clock and Options + * @param portNum : I2C port number. + * + * @returns + * This function returns a handle to I2C port object on success or NULL on failure. + * Use LPCUSBSIO_Error() function to get last error. + */ +LPCUSBSIO_API LPC_HANDLE I2C_Open(LPC_HANDLE hUsbSio, I2C_PORTCONFIG_T *config, uint8_t portNum); + +/** @brief Closes a I2C port. + * + * Deinitializes I2C port and frees all resources that were used by it. + * + * @param hI2C : Handle of the I2C port. + * + * @returns + * This function returns LPCUSBSIO_OK on success and negative error code on failure. + * Check @ref LPCUSBSIO_ERR_T for more details on error code. + * + */ +LPCUSBSIO_API int32_t I2C_Close(LPC_HANDLE hI2C); + +/** @brief Reset I2C Controller. + * + * @param hI2C : A device handle returned from I2C_Open(). + * + * @returns + * This function returns LPCUSBSIO_OK on success and negative error code on failure. + * Check @ref LPCUSBSIO_ERR_T for more details on error code. + * + */ +LPCUSBSIO_API int32_t I2C_Reset(LPC_HANDLE hI2C); + + +/** @brief Read from an addressed I2C slave. + * + * This function reads the specified number of bytes from an addressed I2C slave. + * The @a options parameter effects the transfers. Some example transfers are shown below : + * - When I2C_TRANSFER_OPTIONS_START_BIT, I2C_TRANSFER_OPTIONS_STOP_BIT and + * I2C_TRANSFER_OPTIONS_NACK_LAST_BYTE are set. + * + * S Addr Rd [A] [rxBuff0] A [rxBuff1] A ...[rxBuffN] NA P + * + * - If I2C_TRANSFER_OPTIONS_NO_ADDRESS is also set. + * + * S [rxBuff0] A [rxBuff1] A ...[rxBuffN] NA P + * + * - if I2C_TRANSFER_OPTIONS_NACK_LAST_BYTE is not set + * + * S Addr Rd [A] [rxBuff0] A [rxBuff1] A ...[rxBuffN] A P + * + * - If I2C_TRANSFER_OPTIONS_STOP_BIT is not set. + * + * S Addr Rd [A] [rxBuff0] A [rxBuff1] A ...[rxBuffN] NA + * + * @param hI2C : Handle of the I2C port. + * @param deviceAddress : Address of the I2C slave. This is a 7bit value and + * it should not contain the data direction bit, i.e. the decimal + * value passed should be always less than 128 + * @param buffer : Pointer to the buffer where the read data is to be stored + * @param sizeToTransfer: Number of bytes to be read + * @param options: This parameter specifies data transfer options. Check HID_I2C_TRANSFER_OPTIONS_ macros. + * @returns + * This function returns number of bytes read on success and negative error code on failure. + * Check @ref LPCUSBSIO_ERR_T for more details on error code. + */ +LPCUSBSIO_API int32_t I2C_DeviceRead(LPC_HANDLE hI2C, + uint8_t deviceAddress, + uint8_t *buffer, + uint16_t sizeToTransfer, + uint8_t options); + +/** @brief Writes to the addressed I2C slave. + * + * This function writes the specified number of bytes to an addressed I2C slave. + * The @a options parameter effects the transfers. Some example transfers are shown below : + * - When I2C_TRANSFER_OPTIONS_START_BIT, I2C_TRANSFER_OPTIONS_STOP_BIT and + * I2C_TRANSFER_OPTIONS_BREAK_ON_NACK are set. + * + * S Addr Wr[A] txBuff0[A] txBuff1[A] ... txBuffN[A] P + * + * - If I2C_TRANSFER_OPTIONS_NO_ADDRESS is also set. + * + * S txBuff0[A ] ... txBuffN[A] P + * + * - if I2C_TRANSFER_OPTIONS_BREAK_ON_NACK is not set + * + * S Addr Wr[A] txBuff0[A or NA] ... txBuffN[A or NA] P + * + * - If I2C_TRANSFER_OPTIONS_STOP_BIT is not set. + * + * S Addr Wr[A] txBuff0[A] txBuff1[A] ... txBuffN[A] + * + * @param hI2C : Handle of the I2C port. + * @param deviceAddress : Address of the I2C slave. This is a 7bit value and + * it should not contain the data direction bit, i.e. the decimal + * value passed should be always less than 128 + * @param buffer : Pointer to the buffer where the data to be written is stored + * @param sizeToTransfer: Number of bytes to be written + * @param options : This parameter specifies data transfer options. Check HID_I2C_TRANSFER_OPTIONS_ macros. + * @returns + * This function returns number of bytes written on success and negative error code on failure. + * Check @ref LPCUSBSIO_ERR_T for more details on error code. + */ +LPCUSBSIO_API int32_t I2C_DeviceWrite(LPC_HANDLE hI2C, + uint8_t deviceAddress, + uint8_t *buffer, + uint16_t sizeToTransfer, + uint8_t options); + +/**@brief Transmit and Receive data in I2C master mode + * + * The parameter @a xfer should have its member @a slaveAddr initialized + * to the 7 - Bit slave address to which the master will do the xfer, Bit0 + * to bit6 should have the address and Bit8 is ignored.During the transfer + * no code(like event handler) must change the content of the memory + * pointed to by @a xfer.The member of @a xfer, @a txBuff and @a txSz be + * initialized to the memory from which the I2C must pick the data to be + * transferred to slave and the number of bytes to send respectively, similarly + * @a rxBuff and @a rxSz must have pointer to memory where data received + * from slave be stored and the number of data to get from slave respectively. + * + * Following types of transfers are possible : + * - Write-only transfer : When @a rxSz member of @a xfer is set to 0. + * + * S Addr Wr[A] txBuff0[A] txBuff1[A] ... txBuffN[A] P + * + * - If I2C_FAST_XFER_OPTION_IGNORE_NACK is set in @a options member + * + * S Addr Wr[A] txBuff0[A or NA] ... txBuffN[A or NA] P + * + * - Read-only transfer : When @a txSz member of @a xfer is set to 0. + * + * S Addr Rd[A][rxBuff0] A[rxBuff1] A ...[rxBuffN] NA P + * + * - If I2C_FAST_XFER_OPTION_LAST_RX_ACK is set in @a options member + * + * S Addr Rd[A][rxBuff0] A[rxBuff1] A ...[rxBuffN] A P + * + * - Read-Write transfer : When @a rxSz and @ txSz members of @a xfer are non - zero. + * + * S Addr Wr[A] txBuff0[A] txBuff1[A] ... txBuffN[A]
+ * S Addr Rd[A][rxBuff0] A[rxBuff1] A ...[rxBuffN] NA P
+ * + * @param hI2C : Handle of the I2C port. + * @param xfer : Pointer to a I2C_FAST_XFER_T structure. + * @returns + * This function returns number of bytes read or written on success and negative error code on failure. + * Check @ref LPCUSBSIO_ERR_T for more details on error code. + */ +LPCUSBSIO_API int32_t I2C_FastXfer(LPC_HANDLE hI2C, I2C_FAST_XFER_T *xfer); + +/****************************************************************************** +* SPI functions +******************************************************************************/ + +/** @brief Initialize a SPI port. +* +* This function initializes the SPI port and the communication parameters associated +* with it. +* +* @param hUsbSio : Handle of the LPSUSBSIO port. +* @param config : Pointer to HID_SPI_PORTCONFIG_T structure. Members of +* HID_SPI_PORTCONFIG_T structure contains the values for SPI +* data size, clock polarity and phase +* @param portNum : SPI port number. +* +* @returns +* This function returns a handle to SPI port object on success or NULL on failure. +* Use LPCUSBSIO_Error() function to get last error. +*/ + +LPCUSBSIO_API LPC_HANDLE SPI_Open(LPC_HANDLE hUsbSio, HID_SPI_PORTCONFIG_T *config, uint8_t portNum); + +/** @brief Closes a SPI port. +* +* Deinitializes SPI port and frees all resources that were used by it. +* +* @param hSPI : Handle of the SPI port. +* +* @returns +* This function returns LPCUSBSIO_OK on success and negative error code on failure. +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t SPI_Close(LPC_HANDLE hSPI); + +/**@brief Transmit and Receive data in SPI master mode +* +* During the transfer no code(like event handler) must change the content of the memory +* pointed to by @a xfer.The member of @a xfer, @a txBuff and @a length be +* initialized to the memory from which the SPI must pick the data to be +* transferred to slave and the number of bytes to send respectively, similarly +* @a rxBuff and @a length must have pointer to memory where data received +* from slave be stored and the number of data to get from slave respectively. +* Since SPI is full duplex transmission transmit length and receive length are the same +* and represented by the member of @a xfer, @a length. +* +* @param hSPI : Handle of the SPI port. +* @param xfer : Pointer to a SPI_XFER_T structure. +* @returns +* This function returns number of bytes read on success and negative error code on failure. +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +*/ +LPCUSBSIO_API int32_t SPI_Transfer(LPC_HANDLE hSPI, SPI_XFER_T *xfer); + +/** @brief Reset SPI Controller. +* +* @param hSPI : A device handle returned from SPI_Open(). +* +* @returns +* This function returns LPCUSBSIO_OK on success and negative error code on failure. +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t SPI_Reset(LPC_HANDLE hSPI); + +/****************************************************************************** +* GPIO functions +******************************************************************************/ + +/** @brief Read a GPIO port. +* +* Reads the pin status of the GPIO port mentioned by @a port. Each port has 32 pins associated with it. +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param status : Pointer to GPIO port status, which is updated by the function. +* +* @returns +* This function returns negative error code on failure and returns the number of bytes +* read on success Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_ReadPort(LPC_HANDLE hUsbSio, uint8_t port, uint32_t* status); + +/** @brief Write to a GPIO port. +* +* Write the pin status of the GPIO port mentioned by @a port. Each port has 32 pins associated with it. +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param status : Pointer GPIO port status to be written. After writing into the GPIO port +* this value is updated with the read back from that port. +* +* @returns +* This function returns negative error code on failure and returns the number of bytes +* written on success. Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_WritePort(LPC_HANDLE hUsbSio, uint8_t port, uint32_t* status); + +/** @brief Set GPIO port bits. +* +* Sets the selected pins of the GPIO port mentioned by @a port. Each port has 32 pins associated with it. +* The pins selected are indicated by the corresponding high bits of @a pins +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param pins : Indicates which pins need to be set high by setting the corresponding bit in this variable. +* +* @returns +* This function returns negative error code on failure and returns the number of bytes updated +* on success. Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_SetPort(LPC_HANDLE hUsbSio, uint8_t port, uint32_t pins); + +/** @brief Clear GPIO port bits. +* +* Clears the selected pins of the GPIO port mentioned by @a port. Each port has 32 pins associated with it. +* The pins selected are indicated by the corresponding high bits of @a pins +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param pins : Indicates which pins need to be cleared by setting the corresponding bit in this variable. +* +* @returns +* This function returns negative error code on failure and returns the number of bytes +* updated on success. Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_ClearPort(LPC_HANDLE hUsbSio, uint8_t port, uint32_t pins); + +/** @brief Read GPIO port direction bits. +* +* Reads the direction status for all pins of the GPIO port mentioned by @a port. Each port has 32 pins associated with it. +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param pPins : Pointer to GPIO port direction status, which is updated by the function. +* +* @returns +* This function returns negative error code on failure and returns the number of bytes +* read on success. Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_GetPortDir(LPC_HANDLE hUsbSio, uint8_t port, uint32_t* pPins); + +/** @brief Sets GPIO port pins direction to output. +* +* Sets the direction of selected pins as output for the GPIO port mentioned by @a port. Each port has 32 pins associated with it. +* The pins selected are indicated by the corresponding high bits of @a pins +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param pins : Indicates which pins are output pins by setting the corresponding bit in this variable. +* +* @returns +* This function returns negative error code on failure and returns the number of bytes +* updated on success. Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_SetPortOutDir(LPC_HANDLE hUsbSio, uint8_t port, uint32_t pins); + +/** @brief Sets GPIO port pins direction to input. +* +* Sets the direction of selected pins as input for the GPIO port mentioned by @a port. Each port has 32 pins associated with it. +* The pins selected are indicated by the corresponding high bits of @a pins +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param pins : Indicates which pins are input pins by setting the corresponding bit in this variable. +* +* @returns +* This function returns negative error code on failure and returns the number of bytes +* updated on success. Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_SetPortInDir(LPC_HANDLE hUsbSio, uint8_t port, uint32_t pins); + +/** @brief Sets a specific GPIO port pin value to high. +* +* Sets a specific pin indicated by @a pin to high value for the GPIO port mentioned by @a port. +* Each port has 32 pins associated with it. +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param pin : The pin number which needs to be set (0 - 31). +* +* @returns +* This function returns negative error code on failure and returns the number of bytes +* updated on success. Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_SetPin(LPC_HANDLE hUsbSio, uint8_t port, uint8_t pin); + +/** @brief Reads the state of a specific GPIO port pin. +* +* Read the state of a specific pin indicated by @a pin for the GPIO port mentioned by @a port. +* Each port has 32 pins associated with it. +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param pin : The pin number which needs to be read (0 - 31). +* +* @returns +* This function returns negative error code on failure and returns the pin state (0 or 1) +* upon on success. Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_GetPin(LPC_HANDLE hUsbSio, uint8_t port, uint8_t pin); + +/** @brief Clears a specific GPIO port pin. +* +* Clears a specific pin indicated by @a pin for the GPIO port mentioned by @a port. +* Each port has 32 pins associated with it. +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param pin : The pin number which needs to be cleared (0 - 31). +* +* @returns +* This function returns negative error code on failure and returns the number of bytes +* updated on success. Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_ClearPin(LPC_HANDLE hUsbSio, uint8_t port, uint8_t pin); + +/** @brief Toggles the state of a specific GPIO port pin. +* +* Toggles the state of a specific pin indicated by @a pin for the GPIO port mentioned by @a port. +* Each port has 32 pins associated with it. +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param pin : The pin number which needs to be toggled (0 - 31). +* +* @returns +* This function returns negative error code on failure and returns zero on success +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_TogglePin(LPC_HANDLE hUsbSio, uint8_t port, uint8_t pin); + +/** @brief Configures the IO mode for a specific GPIO port pin. +* +* Configures the IO mode of a specific pin indicated by @a pin for the GPIO port mentioned by @a port +* to the value mentioned by @a mode. +* Each port has 32 pins associated with it. +* +* @param hUsbSio: Handle to LPCUSBSIO port. +* @param port : GPIO port number. +* @param pin : The pin number which needs to be configured (0 - 31). +* @param mode : The 32 bit IO mode value that needs to updated. +* +* @returns +* This function returns negative error code on failure and returns zero on success +* Check @ref LPCUSBSIO_ERR_T for more details on error code. +* +*/ +LPCUSBSIO_API int32_t GPIO_ConfigIOPin(LPC_HANDLE hUsbSio, uint8_t port, uint8_t pin, uint32_t mode); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /*__LPCUSBSIO_H*/ diff --git a/src/middleware/libusbsio/inc/lpcusbsio_protocol.h b/src/middleware/libusbsio/inc/lpcusbsio_protocol.h new file mode 100644 index 0000000..c2484c5 --- /dev/null +++ b/src/middleware/libusbsio/inc/lpcusbsio_protocol.h @@ -0,0 +1,420 @@ +/* + * @brief Protocol definitions for LPCUSBSIO's interface + * + * @note + * Copyright(C) NXP Semiconductors, 2014 + * All rights reserved. + * + * @par + * Software that is described herein is for illustrative purposes only + * which provides customers with programming information regarding the + * LPC products. This software is supplied "AS IS" without any warranties of + * any kind, and NXP Semiconductors and its licensor disclaim any and + * all warranties, express or implied, including all implied warranties of + * merchantability, fitness for a particular purpose and non-infringement of + * intellectual property rights. NXP Semiconductors assumes no responsibility + * or liability for the use of the software, conveys no license or rights under any + * patent, copyright, mask work right, or any other intellectual property rights in + * or to any products. NXP Semiconductors reserves the right to make changes + * in the software without notification. NXP Semiconductors also makes no + * representation or warranty that such application will be suitable for the + * specified use without further testing or modification. + * + * @par + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, under NXP Semiconductors' and its + * licensor's relevant copyrights in the software, without fee, provided that it + * is used in conjunction with NXP Semiconductors microcontrollers. This + * copyright, permission, and disclaimer notice must appear in all copies of + * this code. + */ + +#include + +#ifndef __LPCUSBSIO_PROTOCOL_H_ +#define __LPCUSBSIO_PROTOCOL_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#pragma pack(1) + +#define HID_USAGE_PAGE_SERIAL_IO 0xEA + +/** @defgroup LPCUSBSIO_PROTOCOL LPCUSBSIO protocol definitions for SIO interfaces + * @ingroup LPCUSBSIO + * This package defines the LPCUSBSIO packet structure for SIO interface. + * Both the firmware and PC (host) libraries use this definitions to construct + * the SIO payloads. + * @{ + */ + +#define HID_SIO_PACKET_SZ 64 /*!< Packet size of each SIO command packet */ +#define HID_SIO_PACKET_HEADER_SZ 8 /*!< Size of the header in SIO command packet */ +#define HID_SIO_MAX_XFER_PARAM_SIZE 8 /*!< Maximum value among the sizes of HID_SPI_XFER_PARAMS_T, HID_I2C_XFER_PARAMS_T and HID_I2C_RW_PARAMS_T*/ +#define HID_SIO_PACKET_DATA_SZ (HID_SIO_PACKET_SZ - HID_SIO_PACKET_HEADER_SZ) /*!< Size of data in each SIO command packet */ +#define HID_SIO_CALC_PACKETS_COUNT(x) ((x)?(((x)+HID_SIO_PACKET_DATA_SZ-1) / HID_SIO_PACKET_DATA_SZ):1) /*!< Total packet count needed to transfer data size (x) */ +#define HID_SIO_CALC_TRANSFER_LEN(x) ((x) + (HID_SIO_CALC_PACKETS_COUNT(x) * HID_SIO_PACKET_HEADER_SZ)) /*!< Transfer size calculation for a data size (x) */ + + +/********************************************************************** + HID_I2C Requests + **********************************************************************/ +/** Request to abort and flush all pending requests + * Request Packet layout: + * No parameters + * Response Packet layout: + * No response data + * sesID field in header contains the I2C port number. + */ +#define HID_I2C_REQ_RESET 0x00 + +/** Request to initialize the I2C port. + * Request Packet layout: + * byte[0 - 3] : I2C bus speed + * byte[4 - 7] : Configuration options + * Response Packet layout: + * No response data + * sesID field in header contains the I2C port number. + */ +#define HID_I2C_REQ_INIT_PORT 0x01 + +/** Request to de-initialize the I2C port. + * Request Packet layout: + * No parameters + * Response Packet layout: + * No response data + * sesID field in header contains the I2C port number. + */ +#define HID_I2C_REQ_DEINIT_PORT 0x02 + +/** Request to write data to the I2C port. + * Request Packet layout: + * byte[0 - 1] : Length of the transfer. + * byte[2] : options. + * byte[3] : I2C slave address + * byte[4 - ..] : Write data. + * Response Packet layout: + * No response data + * sesID field in header contains the I2C port number. + */ +#define HID_I2C_REQ_DEVICE_WRITE 0x03 + +/** Request to read data from the I2C port. + * Request Packet layout: + * byte[0 - 1] : Length of the transfer. + * byte[2] : options. + * byte[3] : I2C slave address + * byte[4 - ..] : reserved. + * Response Packet layout: + * byte[0 - ..] : read data. + * sesID field in header contains the I2C port number. + */ +#define HID_I2C_REQ_DEVICE_READ 0x04 + +/** Request to write and then read data from the I2C port. + * Request Packet layout: + * byte[0 - 1] : Transmit Length. + * byte[2 - 3] : Receive Length. + * byte[4 - 5] : Transfer options + * byte[6 - 7] : I2C Slave address. + * byte[8 - ..] : Transmit data if any. + * Response Packet layout: + * byte[0 - ..] : Read Data. + * sesID field in header contains the I2C port number. + */ +#define HID_I2C_REQ_DEVICE_XFER 0x05 + +/** Last I2C specific request */ +#define HID_I2C_REQ_MAX 0x0F + +/********************************************************************** + HID_SPI Requests +**********************************************************************/ +/** Request to reset and abort the SPI controller. + * Request Packet layout: + * No parameters + * Response Packet layout: + * No response data + * sesID field in header contains the SPI port number. + */ +#define HID_SPI_REQ_RESET 0x10 /*!< Request to abort and flush all pending requests */ +/** Request to initialize SPI port. + * Request Packet layout: + * byte[0 - 3] : SPI clock speed + * byte[4 - 7] : Configuration options + * Response Packet layout: + * No response data + * sesID field in header contains the SPI port number. + */ +#define HID_SPI_REQ_INIT_PORT 0x11 /*!< Request to initialize the SPI port */ +/** Request to deinitializ SPI port. + * Request Packet layout: + * No parameters + * Response Packet layout: + * No response data + * sesID field in header contains the SPI port number. + */ +#define HID_SPI_REQ_DEINIT_PORT 0x12 /*!< Request to de-initialize the SPI port */ +/** Request to write read data from the SPI port. + * Request Packet layout: + * byte[0 - 1] : Length of the transfer. + * byte[2] : options. + * byte[3] : SPI device address + * byte[4 - ..] : Write data. + * Response Packet layout: + * byte[0 - ..] : Read data. + * sesID field in header contains the SPI port number. + */ +#define HID_SPI_REQ_DEVICE_XFER 0x13 /*!< Request to write and then read data from the SPI port */ + +/** Last SPI specific request */ +#define HID_SPI_REQ_MAX 0x1F + +/********************************************************************** + HID_GPIO Requests +**********************************************************************/ + +/** Request to write GPIO port state. + * Request Packet layout: + * byte[0 - 3] : Bits/PINs to be set + * byte[4 - 7] : Bits/PINs to be cleared + * Response Packet layout: + * byte[0 - 3] : GPIO port PIN status + * sesID field in header contains the GPIO port number. + */ +#define HID_GPIO_REQ_PORT_VALUE 0x20 + +/** Request to set GPIO port pin direction state. + * Request Packet layout: + * byte[0 - 3] : Bits/PINs to be set as outputs + * byte[4 - 7] : Bits/PINs to be set as inputs + * Response Packet layout: + * byte[0 - 3] : GPIO port DIR status + * sesID field in header contains the GPIO port number. + */ +#define HID_GPIO_REQ_PORT_DIR 0x21 + +/** Request to set GPIO pin state. + * Request Packet layout: + * byte[0] : GPIO pin number + * byte[1 - 3] : Reserved + * Response Packet layout: + * No response data + * sesID field in header contains the GPIO port number. + */ +#define HID_GPIO_REQ_TOGGLE_PIN 0x23 + +/** Request to set IOCON state. + * Request Packet layout: + * byte[0 - 3] : PIN mode + * byte[4] : PIN number + * Response Packet layout: + * No response data + * sesID field in header contains the port number. + */ +#define HID_GPIO_REQ_IOCONFIG 0x24 + +/** Last GPIO specific request */ +#define HID_GPIO_REQ_MAX 0x2F + +/********************************************************************** + HID_SIO Requests +**********************************************************************/ + +/** Request to get LPCUSBSIO information. + * Request Packet layout: + * No parameters + * Response Packet layout: + * byte[0] : Number of I2C ports available on this instance + * byte[1] : Number of SPI ports available on this instance + * byte[2] : Number of GPIO ports available on this instance + * byte[3] : Reserved + * byte[4 - 5] : Firmware Minor version number + * byte[6 - 7] : Firmware Major version number + * byte[8 -...] : Firmware version string + */ +#define HID_SIO_REQ_DEV_INFO 0xF0 + +/** Last SIO specific request */ +#define HID_SIO_REQ_MAX 0xFF + +/** + * @brief HID to SIO bridge Request structure. + * Defines the structure of HID to SIO Request packet. This is same as + * HID OUT report. + */ +typedef struct __HIDSIO_OUT_REPORT { + uint16_t transfer_len; /*!< Total transfer length of the SIO request including the header */ + uint16_t packet_num; /*!< Number of the packet for the SIO transfer, starts from 0. */ + uint8_t packet_len; /*!< Length of the current HID_SIO packet including the header, this is <= 64 */ + uint8_t transId; /*!< HID_SIO transaction identifier. Rolls over after 255. */ + uint8_t sesId; /*!< HID_SIO session identifier. */ + uint8_t req; /*!< HID_SIO Request */ + uint8_t data[]; /*!< Data corresponding to the Request */ + +} HID_SIO_OUT_REPORT_T; + +/** HID_SIO responses. The response code below 0x10 should match with I2CM_STATUS codes in the firmware. */ +#define HID_SIO_RES_OK 0x00 /*!< Requested Request was executed successfully. */ +#define HID_SIO_RES_ERROR 0x01 /*!< Unknown error condition. */ +#define HID_SIO_RES_NAK 0x02 /*!< No device responded for the transmitted data. */ +#define HID_SIO_RES_BUS_ERROR 0x03 /*!< I2C bus error */ +#define HID_SIO_RES_SLAVE_NAK 0x04 /*!< NAK received after SLA+R */ +#define HID_SIO_RES_ARBLOST 0x05 /*!< Arbitration lost */ + +#define HID_SIO_RES_TIMEOUT 0x10 /*!< Transaction timed out. */ +#define HID_SIO_RES_INVALID_CMD 0x11 /*!< Invalid HID_SIO Request or Request not supported in this version. */ +#define HID_SIO_RES_INVALID_PARAM 0x12 /*!< Invalid parameters are provided for the given Request. */ +#define HID_SIO_RES_PARTIAL_DATA 0x13 /*!< Partial transfer completed. */ + +/** + * @brief HID to SIO bridge response structure. + * Defines the structure of HID to SIO Response packet. This is same as + * HID IN report. + */ +typedef struct __HIDSIO_IN_REPORT { + uint16_t transfer_len; /*!< Total transfer length of the SIO response including the header */ + uint16_t packet_num; /*!< Number of the packet for the SIO transfer, starts from 0. */ + uint8_t packet_len; /*!< Length of the current HID_SIO packet including the header, this is <= 64 */ + uint8_t transId; /*!< HID_SIO transaction identifier. */ + uint8_t sesId; /*!< HID_SIO session identifier. */ + uint8_t resp; /*!< HID_SIO response */ + uint8_t data[]; /*!< Data corresponding to the response */ + +} HID_SIO_IN_REPORT_T; + +/** + * @brief Port configuration information + */ +typedef struct __HIDI2C_PortConfig_t { + uint32_t busSpeed; /*!< I2C bus speed */ + uint32_t Options; /*!< Configuration options */ +} HID_I2C_PORTCONFIG_T; + +/** SPI_PORT_CFG_OPTIONS Port Configuration options + * @{ + */ +/** SPI Data Size is 8 Bits */ +#define HID_SPI_CONFIG_OPTION_DATA_SIZE_8 (0x0007) +/** SPI Data Size is 16 Bits */ +#define HID_SPI_CONFIG_OPTION_DATA_SIZE_16 (0x000F) +/** SPI Clock Default Polarity is Low */ +#define HID_SPI_CONFIG_OPTION_POL_0 ((uint32_t) 0 << 6) +/** SPI Clock Default Polarity is High */ +#define HID_SPI_CONFIG_OPTION_POL_1 ((uint32_t) 1 << 6) +/** SPI Data is captured on the first clock transition of the frame */ +#define HID_SPI_CONFIG_OPTION_PHA_0 ((uint32_t) 0 << 7) +/** SPI Data is captured on the second clock transition of the frame */ +#define HID_SPI_CONFIG_OPTION_PHA_1 ((uint32_t) 1 << 7) +/** SPI Pre Delay in micro seconds max of 255 */ +#define HID_SPI_CONFIG_OPTION_PRE_DELAY(x) ((uint32_t) (x) << 8) +/** SPI Post Delay in micro seconds max of 255 */ +#define HID_SPI_CONFIG_OPTION_POST_DELAY(x) ((uint32_t) (x) << 16) + + +/** @} */ + +/** +* @brief Port configuration information +*/ +typedef struct __HIDSPI_PortConfig_t { + uint32_t busSpeed; /*!< SPI bus speed */ + uint32_t Options; /*!< Configuration options */ +} SPI_PORTCONFIG_T, HID_SPI_PORTCONFIG_T; + +/** I2C_IO_OPTIONS Options to I2C_DeviceWrite & I2C_DeviceRead routines + * @{ + */ +/** Generate start condition before transmitting */ +#define HID_I2C_TRANSFER_OPTIONS_START_BIT 0x0001 + +/** Generate stop condition at the end of transfer */ +#define HID_I2C_TRANSFER_OPTIONS_STOP_BIT 0x0002 + +/** Continue transmitting data in bulk without caring about Ack or nAck from device if this bit is + not set. If this bit is set then stop transmitting the data in the buffer when the device nAcks*/ +#define HID_I2C_TRANSFER_OPTIONS_BREAK_ON_NACK 0x0004 + +/** lpcusbsio-I2C generates an ACKs for every byte read. Some I2C slaves require the I2C + master to generate a nACK for the last data byte read. Setting this bit enables working with such + I2C slaves */ +#define HID_I2C_TRANSFER_OPTIONS_NACK_LAST_BYTE 0x0008 + +/* Setting this bit would mean that the address field should be ignored. + The address is either a part of the data or this is a special I2C + frame that doesn't require an address. For example when transferring a + frame greater than the USB_HID packet this option can be used. */ +#define HID_I2C_TRANSFER_OPTIONS_NO_ADDRESS 0x00000040 + +/** @} */ + +/** + * @brief HID to I2C bridge read and write transfer parameters structure. + * Defines the structure of HID to I2C read-write transfer parameters. + */ +typedef struct __HIDI2C_RW_PARAMS { + uint16_t length; /*!< Length of the transfer.*/ + uint8_t options; /*!< check @ref I2C_IO_OPTIONS. */ + uint8_t slaveAddr; /*!< I2C slave device address. */ + uint8_t data[]; /*!< Data corresponding to the response */ + +} HID_I2C_RW_PARAMS_T; + +/** I2C_FAST_XFER_OPTIONS I2C master faster transfer options + * @{ + */ + +/** Ignore NACK during data transfer. By default transfer is aborted. */ +#define I2C_FAST_XFER_OPTION_IGNORE_NACK 0x01 +/** ACK last byte received. By default we NACK last byte we receive per I2C spec. */ +#define I2C_FAST_XFER_OPTION_LAST_RX_ACK 0x02 + +/** + * @} + */ + +/** + * @brief HID to I2C bridge fast transfer parameters structure. + * Defines the parameters structure for HID_I2C_REQ_DEVICE_XFER command. + */ +typedef struct __HIDI2C_XFER_PARAMS { + uint16_t txLength; /*!< Length of the Tx transfer.*/ + uint16_t rxLength; /*!< Length of the Rx transfer. */ + uint16_t options; /*!< check @ref I2C_FAST_XFER_OPTIONS. */ + uint16_t slaveAddr; /*!< I2C slave device address. */ + uint8_t data[]; /*!< Data corresponding to the response */ + +} HID_I2C_XFER_PARAMS_T; + +/** Macro to convert SPI device to the GPIO port number */ +#define HID_SPI_DEVICE_TO_PORT(n) (((n) & 0xE0) >> 5) +/** Macro to convert SPI device to the GPIO pin number */ +#define HID_SPI_DEVICE_TO_PIN(n) ((n) & 0x1F) + +/** +* @brief HID to SPI bridge transfer parameters structure. +* Defines the parameters structure for HID_SPI_REQ_DEVICE_XFER command. +*/ +typedef struct __HIDSPI_XFER_PARAMS { + uint16_t length; /*!< Length of the SPI transfer.*/ + uint8_t options; /*!< SPI transfer options. Currently unused */ + uint8_t device; /*!< SPI slave device number i.e. SSELn. + Out of 8 bits the first 3 bits represent the port number and the last 5 bits represent the pin number */ + uint8_t data[]; /*!< Data corresponding to the response */ + +} HID_SPI_XFER_PARAMS_T; + +/** + * @} + */ + +#pragma pack() + +#ifdef __cplusplus +} +#endif + +#endif /* __LPCUSBSIO_PROTOCOL_H_ */ diff --git a/src/packet/command_packet.h b/src/packet/command_packet.h new file mode 100644 index 0000000..76865c1 --- /dev/null +++ b/src/packet/command_packet.h @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * Copyright 2016-2020 NXP. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#if !defined(__COMMAND_PACKET_H__) +#define __COMMAND_PACKET_H__ + +#include "bootloader_common.h" + +//! @addtogroup packet +//! @{ + +//////////////////////////////////////////////////////////////////////////////// +// Declarations +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Command/Data Packet constants. +enum _command_packet_constants +{ + kMinPacketBufferSize = 32, +#if BL_FEATURE_EXPAND_PACKET_SIZE + kDefaultFramingPacketBufferSize = 1024, +#if BL_CONFIG_HS_USB_HID + // Make sure that kDefaultUsbHidPacketBufferSize < 1018 + kDefaultUsbHidPacketBufferSize = 1016, +#else + kDefaultUsbHidPacketBufferSize = 56, +#endif // BL_CONFIG_HS_USB_HID +#if defined(BL_EXPANDED_FRAMING_PACKET_SIZE) + kMinFramingPacketBufferSize = BL_EXPANDED_FRAMING_PACKET_SIZE, +#else + kMinFramingPacketBufferSize = kDefaultFramingPacketBufferSize, +#endif // defined(BL_EXPANDED_FRAMING_PACKET_SIZE) +#if defined(BL_EXPANDED_USB_HID_PACKET_SIZE) + kMinUsbHidPacketBufferSize = BL_EXPANDED_USB_HID_PACKET_SIZE, +#else + kMinUsbHidPacketBufferSize = kDefaultUsbHidPacketBufferSize, +#endif // defined(BL_EXPANDED_USB_HID_PACKET_SIZE) +#else // !BL_FEATURE_EXPAND_PACKET_SIZE + kMinFramingPacketBufferSize = kMinPacketBufferSize, + kMinUsbHidPacketBufferSize = kMinPacketBufferSize, +#endif // BL_FEATURE_EXPAND_PACKET_SIZE + kMaxHostPacketSize = 8192, + kDefaultMaxPacketSize = kMinPacketBufferSize, + kMaxPropertyReturnValues = + (kMinPacketBufferSize / sizeof(uint32_t)) - 2, //!< Max number of words a property can return + //! One word is header, one parameter reserved for status + + kMaxProgramOnceValues = + (kMinPacketBufferSize / sizeof(uint32_t)) - 3, //!< Max number of words a program once command can write + //! One word is header, two parameters reserved for index and byteCount + kMaxTrustProvisioningReturnValues = + (kMinPacketBufferSize / sizeof(uint32_t)) - 2u, //!< Max number of words a program once command can write. One + //!< word is header, tone parameter reserved for status + kCommandTagCount = 12 //!< Number of non-response command tags +}; + +//! @brief Commands codes. +enum _command_tags +{ + kCommandTag_GenericResponse = 0xa0, + kCommandTag_FlashEraseAll = 0x01, + kCommandTag_FlashEraseRegion = 0x02, + kCommandTag_ReadMemory = 0x03, + kCommandTag_ReadMemoryResponse = 0xa3, + kCommandTag_WriteMemory = 0x04, + kCommandTag_FillMemory = 0x05, + kCommandTag_FlashSecurityDisable = 0x06, + kCommandTag_GetProperty = 0x07, + kCommandTag_GetPropertyResponse = 0xa7, + kCommandTag_ReceiveSbFile = 0x08, + kCommandTag_Execute = 0x09, + kCommandTag_Call = 0x0a, + kCommandTag_Reset = 0x0b, + kCommandTag_SetProperty = 0x0c, + kCommandTag_FlashEraseAllUnsecure = 0x0d, + kCommandTag_FlashProgramOnce = 0x0e, + kCommandTag_FlashReadOnce = 0x0f, + kCommandTag_FlashReadOnceResponse = 0xaf, + kCommandTag_FlashReadResource = 0x10, + kCommandTag_FlashReadResourceResponse = 0xb0, + kCommandTag_ConfigureMemory = 0x11, + kCommandTag_ReliableUpdate = 0x12, + kCommandTag_GenerateKeyBlob = 0x13, + kCommandTag_GenerateKeyBlobResponse = 0xb3, + kCommandTag_FuseProgram = 0x14, + kCommandTag_KeyProvisioning = 0x15, + kCommandTag_KeyProvisioningResponse = 0xb5, + kCommandTag_TrustProvisioning = 0x16U, + kCommandTag_TrustProvisioningResponse = 0xb6U, + kCommandTag_FuseRead = 0x17U, + + kCommandTag_ConfigureI2c = 0xc1, //! Reserved command tag for Bus Pal + kCommandTag_ConfigureSpi = 0xc2, //! Reserved command tag for Bus Pal + kCommandTag_ConfigureCan = 0xc3, //! Reserved command tag for Bus Pal + + kFirstCommandTag = kCommandTag_FlashEraseAll, + + //! Maximum linearly incrementing command tag value, excluding the response commands and bus pal commands. + kLastCommandTag = kCommandTag_FuseRead, + + kResponseCommandHighNibbleMask = + 0xa0 //!< Mask for the high nibble of a command tag that identifies it as a response command. +}; + +//! @brief Command packet flags. +enum _command_packet_flags +{ + kCommandFlag_None = 0, + kCommandFlag_HasDataPhase = 1 +}; + +enum _command_key_provisioning_operation +{ + kKeyProvisioning_Operation_Enroll = 0, + kKeyProvisioning_Operation_SetUserKey = 1, + kKeyProvisioning_Operation_SetIntrinsicKey = 2, + kKeyProvisioning_Operation_WriteNonVolatile = 3, + kKeyProvisioning_Operation_ReadNonVolatile = 4, + kKeyProvisioning_Operation_WriteKeyStore = 5, + kKeyProvisioning_Operation_ReadKeyStore = 6, +}; + +//! @brief Make a trust provisioning operation code. +#define TP_OPT_MAKE(cat, index) ((((cat)&0xFF) << 24) | (index)) +//! @brief Get the category of a specific trust provisioning operation. +#define TP_OPT_GET_CAT(opt) (((opt) >> 24) & 0xFF) +//! @brief Get the index of a specific trust provisioning operation. +#define TP_OPT_GET_INDEX(opt) ((opt) & (~(0xFF << 24))) + +//! @brief Trust provisioning operation categories. +enum TrustProvisioning_Operation_Category +{ + kTrustProvisioning_Operation_Category_OEM = 0x0u, /*!< OEM trusted facility commands. */ + kTrustProvisioning_Operation_Category_NXP = 0x2u, /*!< NXP factory commands. */ + kTrustProvisioning_Operation_Category_DEV = 0x3u, /*!< OEM/CM factory commands. */ + kTrustProvisioning_Operation_Category_FLD = 0x4u, /*!< In-field commands. */ +}; + +//! @brief Trust provisioning operations +enum TrustProvisioning_Operation +{ + /*!< OEM trusted facility commands. */ + kTrustProvisioning_Operation_Oem_GenMasterShare = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_OEM, 0u), + kTrustProvisioning_Operation_Oem_SetMasterShare = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_OEM, 1u), + kTrustProvisioning_Operation_Oem_GetCustCertDicePuk = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_OEM, 2u), + kTrustProvisioning_Operation_Hsm_GenKey = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_OEM, 3u), + kTrustProvisioning_Operation_Hsm_StoreKey = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_OEM, 4u), + kTrustProvisioning_Operation_Hsm_EncryptBlock = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_OEM, 5u), + kTrustProvisioning_Operation_Hsm_EncryptSign = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_OEM, 6u), + /*!< NXP factory commands. */ + kTrustProvisioning_Operation_Nxp_RtsGetId = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_NXP, 0u), + kTrustProvisioning_Operation_Nxp_RtsInsertCertificate = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_NXP, 1u), + kTrustProvisioning_Operation_Nxp_SsfInsertCertificate = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_NXP, 2u), + /*!< OEM/CM factory commands. */ + kTrustProvisioning_Operation_Dev_AuthChallengeNxp = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_DEV, 0u), + kTrustProvisioning_Operation_Dev_AuthChallengeOem = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_DEV, 1u), + kTrustProvisioning_Operation_Dev_SetWrapData = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_DEV, 2u), + /*!< In-field commands. */ + kTrustProvisioning_Operation_Dev_GetUuid = TP_OPT_MAKE(kTrustProvisioning_Operation_Category_FLD, 0u), +}; + +enum TrustProvisioning_KeyType +{ + /* HSM GEN KEY - key type definition. */ + kKeyType_HsmGenKey_MfwIsK = 0xC3A5u, + kKeyType_HsmGenKey_MfwEncK = 0xA5C3u, + kKeyType_HsmGenKey_GenSignK = 0x5A3Cu, + kKeyType_HsmGenKey_GenCustMkSK = 0x3C5Au, + + /* HSM STORE KEY - key type definition. */ + kKeyType_HsmStoreKey_CKDFK = 1u, + kKeyType_HsmStoreKey_HKDFK = 2u, + kKeyType_HsmStoreKey_HMACK = 3u, + kKeyType_HsmStoreKey_CMACK = 4u, + kKeyType_HsmStoreKey_AESK = 5u, + kKeyType_HsmStoreKey_KUOK = 6u, +}; + +//! @brief Trust provisioning parameters +typedef union TrustProvisioningParms +{ + struct + { + uint32_t oemShareInputAddr; + uint32_t oemShareInputSize; + uint32_t oemEncShareOutputAddr; + uint32_t oemEncShareOutputSize; + uint32_t oemEncMasterShareOutputAddr; + uint32_t oemEncMasterShareOutputSize; + uint32_t oemCustCertPukOutputAddr; + uint32_t oemCustCertPukOutputSize; + } oemGenMasterShare; + struct + { + uint32_t oemShareInputAddr; + uint32_t oemShareInputSize; + uint32_t oemEncMasterShareInputAddr; + uint32_t oemEncMasterShareInputSize; + } oemSetMasterShare; + struct + { + uint32_t oemRkthInputAddr; + uint32_t oemRkthInputSize; + uint32_t oemCustCertDicePukOutputAddr; + uint32_t oemCustCertDicePukOutputSize; + } oemGetCustCertDicePuk; + struct + { + uint32_t keyType; + uint32_t keyProp; + uint32_t keyBlobOutputAddr; + uint32_t keyBlobOutputSize; + uint32_t ecdsaPukOutputAddr; + uint32_t ecdsaPukOutputSize; + } hsmGenKey; + struct + { + uint32_t keyType; + uint32_t keyProp; + uint32_t keyInputAddr; + uint32_t keyInputSize; + uint32_t keyBlobOutputAddr; + uint32_t keyBlobOutputSize; + } hsmStoreKey; + struct + { + uint32_t mfgCustMkSk0BlobInputAddr; + uint32_t mfgCustMkSk0BlobInputSize; + uint32_t kekId; + uint32_t sb3HeaderInputAddr; + uint32_t sb3HeaderInputSize; + uint32_t blockNum; + uint32_t blockDataAddr; + uint32_t blockDataSize; + } hsmEncBlk; + struct + { + uint32_t keyBlobInputAddr; + uint32_t keyBlobInputSize; + uint32_t blockDataInputAddr; + uint32_t blockDataInputSize; + uint32_t signatureOutputAddr; + uint32_t signatureOutputSize; + } hsmEncSign; +} trust_provisioning_parms_t; + +//! @brief Trust provisioning parameters +typedef union TrustProvisioningReturn +{ + struct + { + uint32_t oemEncShareOutputSize; + uint32_t oemEncMasterShareOutputSize; + uint32_t oemCustCertPukOutputSize; + } oemGenMasterShare; + // No return value for oemSetMasterShare + // struct + //{ + // + //} oemSetMasterShare; + struct + { + uint32_t oemCustCertDicePukOutputSize; + } oemGetCustCertDicePuk; + struct + { + uint32_t keyBlobOutputSize; + uint32_t ecdsaPukOutputSize; + } hsmGenKey; + struct + { + uint32_t keyBlobOutputHeader; + uint32_t keyBlobOutputSize; + } hsmStoreKey; + // No return value for hsmEncBlk + // struct + //{ + // + //} hsmEncBlk; + struct + { + uint32_t signatureOutputSize; + } hsmEncSign; +} trust_provisioning_return_t; + +//! @brief Command packet format. +typedef struct CommandPacket +{ + uint8_t commandTag; //!< A command tag. + uint8_t flags; //!< Combination of packet flags. + uint8_t reserved; //!< Reserved, helpful for alignment, set to zero. + uint8_t parameterCount; //!< Number of parameters that follow in buffer. +} command_packet_t; + +//! @name Command Packet formats +//@{ + +//! @brief FlashEraseAll packet format. +typedef struct FlashEraseAllPacket +{ + command_packet_t commandPacket; //!< header + uint32_t memoryId; //!< Paremeter 0: Flash memory identifiers. +} flash_erase_all_packet_t; + +//! @brief FlashEraseRegion packet format. +typedef struct FlashEraseRegionPacket +{ + command_packet_t commandPacket; //!< header + uint32_t startAddress; //!< Paremeter 0: start address. + uint32_t byteCount; //!< Parameter 1: number of bytes. + uint32_t memoryId; //!< Parameter 2: ID of the Memory Device to erase. +} flash_erase_region_packet_t; + +//! @brief GetProperty packet format. +typedef struct GetPropertyPacket +{ + command_packet_t commandPacket; //!< header + uint32_t propertyTag; //!< Parameter 0: requested property tag. + uint32_t memoryId; //!< Parameter 1: requested property for certain external memory +} get_property_packet_t; + +//! @brief SetProperty packet format. +typedef struct SetPropertyPacket +{ + command_packet_t commandPacket; //!< header + uint32_t propertyTag; //!< Paremeter 0: property tag. + uint32_t propertyValue; //!< Parameter 1: value to set. +} set_property_packet_t; + +//! @brief ReceiveSbFile packet format. +typedef struct ReceiveSbFilePacket +{ + command_packet_t commandPacket; //!< header + uint32_t byteCount; //!< Parameter 0: Number of bytes to receive. +} receive_sb_file_packet_t; + +//! @brief WriteMemory packet format. +typedef struct WriteMemoryPacket +{ + command_packet_t commandPacket; //!< header + uint32_t startAddress; //!< Paremeter 0: Start address of memory to write to. + uint32_t byteCount; //!< Parameter 1: Number of bytes to write. + uint32_t memoryId; //!< Parameter 2: ID of the Memory Device to write to. +} write_memory_packet_t; + +enum _generate_key_blob_packet_data_phase +{ + kGenKeyBlob_Phase_SendKey = 0, + kGenKeyBlob_Phase_ReceiveKeyBlob = 1, +}; + +//! @brief GenerateKeyBlob packet format. +typedef struct GenerateKeyBlobPacket +{ + command_packet_t commandPacket; //!< header + uint32_t keySel; //!< Paremeter 0: The key used to generate the blob, must be specified by the two phases. + uint32_t keyLength; //!< Parameter 1: Key length in byte. + uint32_t operationPhase; //!< Parameter 2: Operation phase, refer to _generate_key_blob_packet_data_phase +} generate_key_blob_packet_t; + +//! @brief ReadMemory packet format. +typedef struct ReadMemoryPacket +{ + command_packet_t commandPacket; //!< header + uint32_t startAddress; //!< Paremeter 0: Start address of memory to read from. + uint32_t byteCount; //!< Parameter 1: Number of bytes to read. + uint32_t memoryId; //!< Parameter 2: ID of the Memory Device to read from. +} read_memory_packet_t; + +//! @brief FillMemory packet format. +typedef struct FillMemoryPacket +{ + command_packet_t commandPacket; //!< header + uint32_t startAddress; //!< Paremeter 0: start address. + uint32_t byteCount; //!< Parameter 1: number of bytes. + uint32_t patternWord; //!< Parameter 1: pattern word. +} fill_memory_packet_t; + +//! @brief Execute/Call command function pointer definition. +typedef status_t (*call_function_t)(uint32_t); + +//! @brief Execute/Call packet format. +typedef struct ExecuteCallPacket +{ + command_packet_t commandPacket; //!< header + uint32_t callAddress; //!< Paremeter 0: function address. + uint32_t argumentWord; //!< Parameter 1: argument. + uint32_t stackpointer; //!< Parameter 2: stack pointer +} execute_call_packet_t; + +//! @brief FlashSecurityDisable packet format. +typedef struct FlashSecurityDisablePacket +{ + command_packet_t commandPacket; //!< header + uint32_t keyLow; //!< Paremeter 0: key bytes 0-3. + uint32_t keyHigh; //!< Parameter 1: key bytes 4-7. +} flash_security_disable_packet_t; + +//! @brief FlashProgramOnce packet format +typedef struct ProgramOncePacket +{ + command_packet_t commandPacket; //!< header + uint32_t index; //!< Parameter 0: index of pragram once field + uint32_t byteCount; //!< Parameter 1: number of bytes + uint32_t data[kMaxProgramOnceValues]; //!< Parameter 2: data to be programmed +} flash_program_once_packet_t; + +//! @brief FlashReadOnce packet format +typedef struct ReadOncePacket +{ + command_packet_t commandPacket; //!< header + uint32_t index; //!< Parameter 0: index of pragram once field to be read + uint32_t byteCount; //!< Parameter 1: number of bytes +} flash_read_once_packet_t; + +//! @brief FlashReadResource packet format +typedef struct FlashReadResourcePacket +{ + command_packet_t commandPacket; //!< header + uint32_t startAddress; //!< Parameter 0: start address + uint32_t byteCount; //!< Parameter 1: number of bytes + uint32_t option; //!< Parameter 2: option for flash read resource command +} flash_read_resource_packet_t; + +//! @brief ConfigureMemory packet format +typedef struct ConfigureMemoryPacket +{ + command_packet_t commandPacket; //!< header + uint32_t flashMemId; //!< Parameter 0: ID of the Memory Device to configure. + uint32_t configBlockAddress; //!< Parameter 1: address of config block to use +} configure_memory_packet_t; + +//! @brief ReliableUpdate packet format +typedef struct ReliableUpdatePacket +{ + command_packet_t commandPacket; //!< header + uint32_t address; //!< Parameter 0: For software implementation , this is backup app start address; + //!< Parameter 0: For hardware implementation , this is swap indicator address; +} reliable_update_packet_t; + +//! @brief KeyProvisioning packet format +typedef struct KeyProvisioningPacket +{ + command_packet_t commandPacket; //!< header + uint32_t operation; //!< Operation defined at _command_key_provisioning_operation + uint32_t type; //!< Key type. + uint32_t index; //!< Key index register. + uint32_t size; //!< Key size. +} key_provisioning_packet_t; + +//! @brief TrustProvisioning packet format +typedef struct TrustProvisioningPacket +{ + command_packet_t commandPacket; //!< header + uint32_t operation; //!< Key operation, refer to the constant enumeration of trust provisioning operations. + uint32_t parms[]; +} trust_provisioning_packet_t; +//@} + +//! @name Response bus-pal command formats +//@{ + +//! @brief ConfigureI2c packet format +typedef struct ConfigureI2cPacket +{ + command_packet_t commandPacket; //!< header + uint32_t address; //!< Parameter 0: address + uint32_t speed; //!< Parameter 1: speed +} configure_i2c_packet_t; + +//! @brief ConfigureSpi packet format +typedef struct ConfigureSpiPacket +{ + command_packet_t commandPacket; //!< header + uint32_t speedKhz; //!< Parameter 0: spped Khz + uint32_t polarity; //!< Parameter 1: polarity + uint32_t phase; //!< Parameter 2: phase + uint32_t direction; //!< Parameter 3: directionpolarity +} configure_spi_packet_t; + +//! @brief ConfigureCan packet format +typedef struct ConfigureCanPacket +{ + command_packet_t commandPacket; //!< header + uint32_t speed; //!< Parameter 0: spped index + uint32_t txid; //!< Parameter 1: txid + uint32_t rxid; //!< Parameter 2: rxid +} configure_can_packet_t; + +//@} + +//! @name Response Packet formats +//@{ + +//! @brief Generic response packet format. +typedef struct GenericResponsePacket +{ + command_packet_t commandPacket; //!< header + uint32_t status; //!< parameter 0 + uint32_t commandTag; //!< parameter 1 +} generic_response_packet_t; + +//! @brief Get Property response packet format. +typedef struct GetPropertyResponsePacket +{ + command_packet_t commandPacket; //!< header + uint32_t status; //!< parameter 0 + uint32_t propertyValue[kMaxPropertyReturnValues]; //!< up to 6 other parameters +} get_property_response_packet_t; + +//! @brief Generate Key Blob response packet format. +typedef struct GenerateKeyBlobResponsePacket +{ + command_packet_t commandPacket; //!< header + uint32_t status; //!< parameter 0 + uint32_t dataByteCount; //!< parameter 1 +} generate_key_blob_response_packet_t; + +//! @brief Read Memory response packet format. +typedef struct ReadMemoryResponsePacket +{ + command_packet_t commandPacket; //!< header + uint32_t status; //!< parameter 0 + uint32_t dataByteCount; //!< parameter 1 +} read_memory_response_packet_t; + +//! @brief Flash Read Once response packet format. +typedef struct FlashReadOnceResponsePacket +{ + command_packet_t commandPacket; //!< header + uint32_t status; //!< parameter 0 + uint32_t byteCount; //!< parameter 1 + uint32_t data[kMaxProgramOnceValues]; //!< parameter 2 +} flash_read_once_response_packet_t; + +//! @brief Flash Read Resource response packet format. +typedef struct FlashReadResourceResponsePacket +{ + command_packet_t commandPacket; //!< header + uint32_t status; //!< parameter 0 + uint32_t dataByteCount; //!< parameter 1 +} flash_read_resource_response_packet_t; + +//! @brief Key Provisioning response packet format. +typedef struct KeyProvisioningResponsePacket +{ + command_packet_t commandPacket; //!< header + uint32_t status; //!< parameter 0 + uint32_t keyByteCount; //!< parameter 1 +} key_provisioning_response_packet_t; + +//! @brief Trust Provisioning response packet format. +typedef struct TrustProvisioningResponsePacket +{ + command_packet_t commandPacket; //!< header + uint32_t status; //!< parameter 0 + uint32_t returnValue[kMaxTrustProvisioningReturnValues]; //!< return values +} trust_provisioning_response_packet_t; +//@} + +//! @} + +#endif // __COMMAND_PACKET_H__ +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/packet/serial_packet.h b/src/packet/serial_packet.h new file mode 100644 index 0000000..2bb846a --- /dev/null +++ b/src/packet/serial_packet.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _packet_h +#define _packet_h + +#include "bootloader_common.h" +#include "bootloader/bl_peripheral.h" + +//! @addtogroup packet +//! @{ + +//////////////////////////////////////////////////////////////////////////////// +// Declarations +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Version constants for serial framing protocol. +//! @note Recalculate crc16 in k_PingResponse if these values change. +enum _serial_protocol_version_constants +{ + kSerialProtocol_Version_Name = 'P', + kSerialProtocol_Version_Major = 1, + kSerialProtocol_Version_Minor = 2, + kSerialProtocol_Version_Bugfix = 0 +}; + +//! @brief Serial framing packet constants. +enum _framing_packet_constants +{ + kFramingPacketStartByte = 0x5a, + kFramingPacketType_Ack = 0xa1, + kFramingPacketType_Nak = 0xa2, + kFramingPacketType_AckAbort = 0xa3, + kFramingPacketType_Command = 0xa4, + kFramingPacketType_Data = 0xa5, + kFramingPacketType_Ping = 0xa6, + kFramingPacketType_PingResponse = 0xa7 +}; + +//! @brief Timeout and other constants. +enum _timing_constants +{ + kHostMaxStartByteReadCount = 2, + kDefaultByteReadTimeoutMs = 10, //!< Default value for receiving 1 byte timeout + kCallbackBufferSize = 64 //!< Size for callback buffer, Must be power of 2 for easy wrap +}; + +//! @brief Incoming data buffer allocation size. +enum _serial_packet_constants +{ +#ifdef BOOTLOADER_HOST + kIncomingPacketBufferSize = kMaxHostPacketSize, + kOutgoingPacketBufferSize = kMaxHostPacketSize +#else + kIncomingPacketBufferSize = kMinFramingPacketBufferSize, + kOutgoingPacketBufferSize = kMinFramingPacketBufferSize +#endif +}; + +//! @brief Packet state machine modes. +enum _serial_packet_mode +{ + kSerialModeCmd, + kSerialModeAck, + kSerialModeIdle +}; + +//! @brief Serial framing header. +typedef struct FramingHeader +{ + uint8_t startByte; //!< #kFramingPacketStartByte + uint8_t packetType; //!< Framing packet type +} framing_header_t; + +//! @brief Serial framing sync packet. +typedef struct FramingSyncPacket +{ + framing_header_t header; //!< Framing packet header +} framing_sync_packet_t; + +//! @brief Serial framing data packet. +#pragma pack(1) +typedef struct FramingDataPacket +{ + framing_header_t header; //!< Framing packet header + uint16_t length; //!< Number of data bytes that follow + uint16_t crc16; //!< CRC-16 of data packet header and data +} framing_data_packet_t; +#pragma pack() + +//! @brief Framing packet with data area. +typedef struct SerialFramingPacket +{ + framing_data_packet_t dataPacket; //!< Packet header. + uint8_t data[kOutgoingPacketBufferSize]; //!< Payload. +} serial_framing_packet_t; + +//! @brief Format of global context data. +typedef struct SerialData +{ + uint8_t data[kIncomingPacketBufferSize]; //!< Buffer for incomming packet data payload, must be uint32_t aligned. + uint8_t callbackBuffer[kCallbackBufferSize]; //!< Buffer for incoming data from the byte callback + serial_framing_packet_t framingPacket; //!< Buffer for outgoing packet. + volatile uint32_t writeOffset; //!< The offset into the buffer that the ISR will queue data into + uint32_t readOffset; //!< The offset into the buffer that the app has read out + bool isAckNeeded; //!< True if need to send ACK to previously received packet + bool isBackToBackWrite; //!< True if executing back-to-back write + bool isAckAbortNeeded; //!< True if next ACK should be ACK Abort +} serial_data_t; + +//! @brief Serial ping response format. +//! +//! This is the format of the response to a Ping packet. +typedef struct PingResponse +{ + standard_version_t version; //!< Serial framing protocol version + uint16_t options; //!< Serial framing protocol options bitfield + uint16_t crc16; //!< CRC-16 of other fields +} ping_response_t; + +//////////////////////////////////////////////////////////////////////////////// +// Externs +//////////////////////////////////////////////////////////////////////////////// + +extern const peripheral_packet_interface_t g_framingPacketInterface; + +//////////////////////////////////////////////////////////////////////////////// +// Prototypes +//////////////////////////////////////////////////////////////////////////////// + +#if defined(__cplusplus) +extern "C" { +#endif // __cplusplus + +//! @brief Initialize component. +status_t serial_packet_init(const peripheral_descriptor_t *self); + +//! @brief Read packet using serial framing. +//! +//! On return, caller must call flow control method to send AckContinue or AckWait followed by Continue. +status_t serial_packet_read(const peripheral_descriptor_t *self, + uint8_t **packet, + uint32_t *packetLength, + packet_type_t packetType); + +//! @brief Write packet using serial framing. +status_t serial_packet_write(const peripheral_descriptor_t *self, + const uint8_t *packet, + uint32_t byteCount, + packet_type_t packetType); + +//! @brief Abort data phase. +//! +//! Respond to next host data packet with AckAbort instead of Ack +//! (i.e. receiver data phase abort). +void serial_packet_abort(const peripheral_descriptor_t *self); + +//! @brief Finalize. +status_t serial_packet_finalize(const peripheral_descriptor_t *self); + +//! @brief Get max packet size. +uint32_t serial_packet_get_max_packet_size(const peripheral_descriptor_t *self); + +//! @brief Send a sync packet of the specified type. +status_t serial_packet_send_sync(uint8_t framingPacketType); + +//! @brief Send a ping message back in response to a ping. +status_t serial_send_ping_response(const peripheral_descriptor_t *peripheral); + +//! @brief Queues a byte received by the active peripheral +void serial_packet_queue_byte(uint8_t byte); + +#if defined(BOOTLOADER_HOST) +void host_delay(uint32_t milliseconds); +#endif // BOOTLOADER_HOST + +#if defined(__cplusplus) +} +#endif // __cplusplus + +//! @} + +#endif // _packet_h +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/property/property.h b/src/property/property.h new file mode 100644 index 0000000..bc426db --- /dev/null +++ b/src/property/property.h @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2013-2015, Freescale Semiconductor, Inc. + * Copyright 2019-2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _property_h +#define _property_h + +#include + +#include "bootloader_common.h" +#include "packet/command_packet.h" +#if !defined(BOOTLOADER_HOST) +#include "fsl_device_registers.h" +#include "utilities/vector_table_info.h" +#endif + +//! @addtogroup property +//! @{ + +//////////////////////////////////////////////////////////////////////////////// +// Declarations +//////////////////////////////////////////////////////////////////////////////// + +//! @name Command Availability +//@{ + +//! Sets a bit in the available commands property value to indicate a command with +//! the given tag is available. +#define HAS_CMD(tag) (1 << ((tag)-kFirstCommandTag)) + +//! Checks whether a command with the specified tag is present in the provided command +//! availability mask. +#define IS_CMD_AVAILABLE(mask, tag) (((mask)&HAS_CMD(tag)) != 0) + +enum _available_commands +{ + kAvailableCommands = ( +#if !BL_FEATURE_MIN_PROFILE + HAS_CMD(kCommandTag_FlashEraseAll) | HAS_CMD(kCommandTag_FlashEraseRegion) | HAS_CMD(kCommandTag_WriteMemory) | + HAS_CMD(kCommandTag_FuseProgram) | HAS_CMD(kCommandTag_FuseRead) +#if BL_FEATURE_FLASH_SECURITY + | HAS_CMD(kCommandTag_FlashSecurityDisable) +#endif // BL_FEATURE_ERASEALL_UNSECURE + | HAS_CMD(kCommandTag_GetProperty) | HAS_CMD(kCommandTag_Execute) | HAS_CMD(kCommandTag_Reset) | + HAS_CMD(kCommandTag_SetProperty) | HAS_CMD(kCommandTag_ReadMemory) | HAS_CMD(kCommandTag_FillMemory) | + HAS_CMD(kCommandTag_ReceiveSbFile) | HAS_CMD(kCommandTag_Call) +#if BL_FEATURE_ERASEALL_UNSECURE + | HAS_CMD(kCommandTag_FlashEraseAllUnsecure) +#endif // BL_FEATURE_ERASEALL_UNSECURE + | HAS_CMD(kCommandTag_FlashReadOnce) | HAS_CMD(kCommandTag_FlashProgramOnce) +#if !BL_FEATURE_HAS_NO_READ_SOURCE + | HAS_CMD(kCommandTag_FlashReadResource) +#endif // !BL_FEATURE_HAS_NO_READ_SOURCE +#if BL_FEATURE_QSPI_MODULE || BL_FEATURE_EXPAND_MEMORY + | HAS_CMD(kCommandTag_ConfigureMemory) +#endif // BL_FEATURE_QSPI_MODULE || BL_FEATURE_EXPAND_MEMORY +#if BL_FEATURE_RELIABLE_UPDATE + | HAS_CMD(kCommandTag_ReliableUpdate) +#endif // BL_FEATURE_RELIABLE_UPDATE + +#else // BL_FEATURE_MIN_PROFILE + HAS_CMD(kCommandTag_FlashEraseAll) | HAS_CMD(kCommandTag_FlashEraseRegion) | HAS_CMD(kCommandTag_WriteMemory) +#if BL_FEATURE_FLASH_SECURITY + | HAS_CMD(kCommandTag_FlashSecurityDisable) +#endif // BL_FEATURE_FLASH_SECURITY + | HAS_CMD(kCommandTag_GetProperty) | HAS_CMD(kCommandTag_Execute) | HAS_CMD(kCommandTag_Reset) | + HAS_CMD(kCommandTag_SetProperty) +#if BL_FEATURE_READ_MEMORY + | HAS_CMD(kCommandTag_ReadMemory) +#endif // BL_FEATURE_READ_MEMORY +#if BL_FEATURE_FILL_MEMORY + | HAS_CMD(kCommandTag_FillMemory) +#endif // BL_FEATURE_FILL_MEMORY +#if BL_FEATURE_ERASEALL_UNSECURE + | HAS_CMD(kCommandTag_FlashEraseAllUnsecure) +#endif // BL_FEATURE_ERASEALL_UNSECURE +#endif // BL_FEATURE_MIN_PROFILE + ) +}; + +//@} + +//! @brief Property store status codes. +enum _property_errors +{ + kStatus_UnknownProperty = MAKE_STATUS(kStatusGroup_PropertyStore, 0), + kStatus_ReadOnlyProperty = MAKE_STATUS(kStatusGroup_PropertyStore, 1), //!< Property is read-only. + kStatus_InvalidPropertyValue = MAKE_STATUS(kStatusGroup_PropertyStore, 2) //!< Property value is out of range. +}; + +//! @brief Property tags. +//! @note Do not change any tag values. Add tags at the end. +enum _property_tag +{ + kPropertyTag_ListProperties = 0x00, + kPropertyTag_BootloaderVersion = 0x01, + kPropertyTag_AvailablePeripherals = 0x02, + kPropertyTag_FlashStartAddress = 0x03, + kPropertyTag_FlashSizeInBytes = 0x04, + kPropertyTag_FlashSectorSize = 0x05, + kPropertyTag_FlashBlockCount = 0x06, + kPropertyTag_AvailableCommands = 0x07, + kPropertyTag_CheckStatus = 0x08, + kPropertyTag_Reserved9 = 0x09, + kPropertyTag_VerifyWrites = 0x0a, + kPropertyTag_MaxPacketSize = 0x0b, + kPropertyTag_ReservedRegions = 0x0c, + kPropertyTag_Reserved13 = 0x0d, + kPropertyTag_RAMStartAddress = 0x0e, + kPropertyTag_RAMSizeInBytes = 0x0f, + kPropertyTag_SystemDeviceId = 0x10, + kPropertyTag_SecurityState = 0x11, + kPropertyTag_UniqueDeviceId = 0x12, + kPropertyTag_FacSupport = 0x13, + kPropertyTag_FlashAccessSegmentSize = 0x14, + kPropertyTag_FlashAccessSegmentCount = 0x15, + kPropertyTag_FlashReadMargin = 0x16, + kPropertyTag_QspiInitStatus = 0x17, + kPropertyTag_TargetVersion = 0x18, + kPropertyTag_ExternalMemoryAttributes = 0x19, + kPropertyTag_ReliableUpdateStatus = 0x1a, + kPropertyTag_FlashPageSize = 0x1b, + kPropertyTag_IrqNotifierPin = 0x1c, + kPropertyTag_FfrKeystoreUpdateOpt = 0x1d, + kPropertyTag_ByteWriteTimeoutMs = 0x1e, + kPropertyTag_InvalidProperty = 0xFF, +}; + +//! @brief Property constants. +enum _property_constants +{ + kProperty_ReservedRegionsCount = 2, + kProperty_FlashReservedRegionIndex = 0, + kProperty_RamReservedRegionIndex = 1, + + kProperty_FlashVersionIdSizeInBytes = 8, +}; + +//! @brief Bit positions for clock flags in configuration data. +enum _clock_flags +{ + kClockFlag_HighSpeed = (1 << 0) +}; + +//! @brief Bit positions for boot flags in configuration data +enum _boot_flags +{ + kBootFlag_DirectBoot = (1 << 0) +}; + +//!@brief Security State definitions +enum _security_state +{ + kSecurityState_Legacy_Unsecure = 0, + kSecurityState_Legacy_Secure = 1, + kSecurityState_SKBOOT_Unsecure = 0x5aa55aa5u, + kSecurityState_SKBOOT_Secure = 0xc33cc33cu, +}; + +//!@brief CheckStatus ID definitions +enum __checkstatus_id +{ + kCheckStatusId_CrcStatus = 0, + kCheckSattusId_LastError = 1, +}; + +//! @brief Flash constants. +enum _flash_constants +{ +#if !defined(BOOTLOADER_HOST) + //! @brief The bootloader configuration data location . + //! + //! A User Application should populate a BootloaderConfigurationData + //! struct at 0x3c0 from the beginning of the application image which must + //! be the User Application vector table for the flash-resident bootloader + //! collaboration. + kBootloaderConfigAreaAddress = (uint32_t)(APP_VECTOR_TABLE) + 0x3c0, +#endif // BOOTLOADER_HOST + +#if BL_HAS_SECONDARY_INTERNAL_FLASH + kFLASHCount = 2, +#else + kFLASHCount = 1, +#endif +}; + +//! @brief Format of bootloader configuration data on Flash. +typedef struct BootloaderConfigurationData +{ + uint32_t tag; //!< [00:03] Tag value used to validate the bootloader configuration data. Must be set to 'kcfg'. + uint32_t crcStartAddress; //!< [04:07] + uint32_t crcByteCount; //!< [08:0b] + uint32_t crcExpectedValue; //!< [0c:0f] + uint8_t enabledPeripherals; //!< [10:10] + uint8_t i2cSlaveAddress; //!< [11:11] + uint16_t peripheralDetectionTimeoutMs; //!< [12:13] Timeout in milliseconds for peripheral detection before jumping + //! to application code + uint16_t usbVid; //!< [14:15] + uint16_t usbPid; //!< [16:17] + uint32_t usbStringsPointer; //!< [18:1b] + uint8_t clockFlags; //!< [1c:1c] High Speed and other clock options + uint8_t clockDivider; //!< [1d:1d] One's complement of clock divider, zero divider is divide by 1 + uint8_t bootFlags; //!< [1e:1e] One's complemnt of direct boot flag, 0xFE represents direct boot + uint8_t pad0; //!< [1f:1f] Reserved, set to 0xFF + uint32_t mmcauConfigPointer; //!< [20:23] Holds a pointer value to the MMCAU configuration + uint32_t keyBlobPointer; //!< [24:27] Holds a pointer value to the key blob array used to configure OTFAD + uint8_t qspiPort; //!< [28:28] qspi port: 0xFF-PORTE, 0xFE-PORTC + uint8_t canConfig1; //!< [29:29] ClkSel[1], PropSeg[3], SpeedIndex[4] + uint16_t canConfig2; //!< [2a:2b] Pdiv[8], Pseg1[3], Pseg2[3], rjw[2] + uint16_t canTxId; //!< [2c:2d] txId + uint16_t canRxId; //!< [2e:2f] rxId + uint32_t qspi_config_block_pointer; //!< [30:33] QSPI config block pointer. +} bootloader_configuration_data_t; + +//! @brief Structure of a reserved regions entry. +typedef struct ReservedRegion +{ + uint32_t startAddress; + uint32_t endAddress; +} reserved_region_t; + +//! @brief Structure of a unique device id. +typedef struct UniqueDeviceId +{ + uint32_t uidl; +#if defined(SIM_UIDM_UID) + uint32_t uidm; +#else + uint32_t uidml; + uint32_t uidmh; +#endif // defined(SIM_UIDM) +#if defined(BOOTLOADER_HOST) | defined(SIM_UIDH) | defined(SIM_UIDH_UID) + uint32_t uidh; +#endif +} unique_device_id_t; + +//! @brief External Memory Properties tag +enum _external_memory_property_tags +{ + kExternalMemoryPropertyTag_InitStatus = 0, //!< Init status tag + kExternalMemoryPropertyTag_StartAddress = 1, //!< Start address tag + kExternalMemoryPropertyTag_MemorySizeInKbytes = 2, //!< Memory size tag + kExternalMemoryPropertyTag_PageSize = 3, //!< Pag size tag + kExternalMemoryPropertyTag_SectorSize = 4, //!< Sector size tag + kExternalMemoryPropertyTag_BlockSize = 5, //!< Block size tag + + kExternalMemoryPropertyTag_Start = kExternalMemoryPropertyTag_StartAddress, + kExternalMemoryPropertyTag_End = kExternalMemoryPropertyTag_BlockSize, +}; + +//! @brief Exernal Memory attribute store +typedef struct +{ + uint32_t availableAttributesFlag; //!< Available Atrributes, bit map + uint32_t startAddress; //!< start Address of external memory + uint32_t flashSizeInKB; //!< flash size of external memory + uint32_t pageSize; //!< page size of external memory + uint32_t sectorSize; //!< sector size of external memory + uint32_t blockSize; //!< block size of external memory +} external_memory_property_store_t; + +//! @brief nIRQ notifier pin property store +typedef union _irq_notifier_pin_property_store +{ + struct + { + uint32_t pin : 8; + uint32_t port : 8; + uint32_t rsv0 : 15; + uint32_t enable : 1; + } B; + uint32_t U; +} irq_notifier_pin_property_store_t; + +enum _ffr_keystore_update_opt +{ + kFfrKeystoreUpdateOpt_KeyProvisioning = 0x0u, + kFfrKeystoreUpdateOpt_WriteMemory = 0x1u, + kFfrKeystoreUpdateOpt_Invalid = 0xFFFFFFFFu, +}; + +enum _ram_constants +{ +#if defined(KV58F22_SERIES) + kRAMCount = 3, +#elif defined(K28F15_SERIES) + kRAMCount = 2, +#else + kRAMCount = 1, +#endif + + kPropertyIndex_SRAM = 0, +#if defined(K28F15_SERIES) + kPropertyIndex_OCRAM = 1, +#elif defined(KV58F22_SERIES) + kPropertyIndex_DTCM = 1, + kPropertyIndex_OCRAM = 2, +#endif +}; + +//! @brief Structure of property store. +typedef struct PropertyStore +{ + standard_version_t bootloaderVersion; //!< Current bootloader version. + standard_version_t serialProtocolVersion; //!< Serial protocol version number. + standard_version_t targetVersion; //!< Target version number. + uint32_t availablePeripherals; //!< The set of peripherals supported available on this chip. See enum + //!_peripheral_types in bl_peripheral.h. + uint32_t flashStartAddress[kFLASHCount]; //!< Start address of program flash. + uint32_t flashSizeInBytes[kFLASHCount]; //!< Size in bytes of program flash. + uint32_t flashSectorSize[kFLASHCount]; //!< The size in bytes of one sector of program flash. This is the minimum + //! erase size. + uint32_t flashBlockSize[kFLASHCount]; //!< The size in bytes of one block of program flash. + uint32_t flashBlockCount[kFLASHCount]; //!< Number of blocks in the flash array. + uint32_t ramStartAddress[kRAMCount]; //!< Start address of RAM + uint32_t ramSizeInBytes[kRAMCount]; //!< Size in bytes of RAM + uint32_t crcCheckStatus; //!< Status code from the last CRC check operation. + uint32_t verifyWrites; //!< Boolean controlling whether the bootloader will verify writes to flash. Non-zero enables + //! verificaton. Writable by host. + uint32_t availableCommands; //!< Bit mask of the available commands. + unique_device_id_t UniqueDeviceId; //!< Unique identification for the device. + uint32_t flashFacSupport[kFLASHCount]; //!< Boolean indicating whether the FAC feature is supported + uint32_t flashAccessSegmentSize[kFLASHCount]; //!< The size in bytes of one segment of flash + uint32_t flashAccessSegmentCount[kFLASHCount]; //!< The count of flash access segment within flash module + uint32_t flashReadMargin; //!< The margin level setting for flash erase and program Verify CMDs + uint32_t qspiInitStatus; //!< Result of QSPI+OTFAD init during bootloader startup + reserved_region_t reservedRegions[kProperty_ReservedRegionsCount]; //!< Flash and Ram reserved regions. + bootloader_configuration_data_t + configurationData; //!< Configuration data from flash address 0x3c0-0x3ff in sector 0 (64 bytes max) + external_memory_property_store_t externalMemoryPropertyStore; //!< Property store for external memory + uint32_t reliableUpdateStatus; //!< Status of reliable update +} property_store_t; + +enum _property_store_tags +{ + //! @brief Tag value used to validate the bootloader configuration data. + kPropertyStoreTag = FOUR_CHAR_CODE('k', 'c', 'f', 'g') +}; + +//! @brief External Memory properties interface +typedef struct ExternalMemoryPropertyInterface +{ + uint32_t memoryId; + status_t (*get)(uint32_t tag, uint32_t *value); +} external_memory_property_interface_t; + +//! @brief Interface to property operations. +typedef struct PropertyInterface +{ + status_t (*load_user_config)(void); //!< Load the user configuration data + status_t (*init)(void); //!< Initialize + status_t (*get)(uint8_t tag, uint32_t id, const void **value, uint32_t *valueSize); //!< Get property + status_t (*set_uint32)(uint8_t tag, uint32_t value); //!< Set uint32_t property + property_store_t *store; //!< The property store +} property_interface_t; + +//////////////////////////////////////////////////////////////////////////////// +// Externs +//////////////////////////////////////////////////////////////////////////////// + +//! @brief Property interface. +extern const property_interface_t g_propertyInterface; + +//////////////////////////////////////////////////////////////////////////////// +// Prototypes +//////////////////////////////////////////////////////////////////////////////// + +#if __cplusplus +extern "C" +{ +#endif + + //! @name Property Store + //@{ + + //! @brief Early initialization function to get user configuration data + status_t bootloader_property_load_user_config(void); + + //! @brief Initialize the property store. + status_t bootloader_property_init(void); + + //! @brief Get a property. + //! + //! Example calling sequence for uint32_t property: + //! @code + //! void * value; + //! uint32_t valueSize; + //! status_t status = bootloader_property_get(sometag, &value, &valueSize); + //! uint32_t result = *(uint32_t *)value; + //! @endcode + //! + //! @param tag Tag of the requested property + //! @param memoryId Id for specified external memory, for example: 1 represent QuadSPI 0 + //! @param value Pointer to where to write a pointer to the result, may be NULL + //! @param valueSize Size in bytes of the property value, may be NULL + //! + //! @retval kStatus_Success + //! @retval kStatus_UnknownProperty + status_t bootloader_property_get(uint8_t tag, uint32_t memoryId, const void **value, uint32_t *valueSize); + + //! @brief Set a property. + //! + //! Only uint32_t properties can be set with this function. + //! + //! @param tag Tag of the property to set + //! @param value New property value + //! + //! @retval kStatus_Success + //! @retval kStatus_UnknownProperty + //! @retval kStatus_ReadOnlyProperty + status_t bootloader_property_set_uint32(uint8_t tag, uint32_t value); + + //@} + +#if __cplusplus +} +#endif + +//! @} + +#endif // _property_h +//////////////////////////////////////////////////////////////////////////////// +// EOF +//////////////////////////////////////////////////////////////////////////////// diff --git a/tools/blhost.xcodeproj/project.pbxproj b/tools/blhost.xcodeproj/project.pbxproj new file mode 100644 index 0000000..50a263c --- /dev/null +++ b/tools/blhost.xcodeproj/project.pbxproj @@ -0,0 +1,672 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXAggregateTarget section */ + 02936E57172CD33100D07977 /* All blhost tools */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 02936E58172CD33100D07977 /* Build configuration list for PBXAggregateTarget "All blhost tools" */; + buildPhases = ( + ); + dependencies = ( + 02936E5E172CD35E00D07977 /* PBXTargetDependency */, + ); + name = "All blhost tools"; + productName = "All validation tools"; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 020F8999175E4DB5009F0B96 /* crc32.c in Sources */ = {isa = PBXBuildFile; fileRef = 020F8984175E4DB5009F0B96 /* crc32.c */; }; + 02528C3F16FE32760026B4C2 /* blhost.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02528C2816FE31980026B4C2 /* blhost.cpp */; }; + 02528C4316FE32880026B4C2 /* options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02528C1916FE308F0026B4C2 /* options.cpp */; }; + 02558D3117C3AC2600D3721D /* jsoncpp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02558D3017C3AC2600D3721D /* jsoncpp.cpp */; }; + 02558D3517CF9C1E00D3721D /* utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02558D3417CF9C1E00D3721D /* utils.cpp */; }; + 02558D3C17D7B05100D3721D /* format_string.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02558D3B17D7B05100D3721D /* format_string.cpp */; }; + 0289203F170A1279007D3438 /* serial.c in Sources */ = {isa = PBXBuildFile; fileRef = 0289203D170A1279007D3438 /* serial.c */; }; + 02936E54172CD27A00D07977 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02936E52172CD27A00D07977 /* Logging.cpp */; }; + 02C2957118996B4E00C08DCF /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02C2956D18996B3500C08DCF /* IOKit.framework */; }; + 02C2957218996B5300C08DCF /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02C2956F18996B3D00C08DCF /* CoreFoundation.framework */; }; + 04957C8E1AA8CDF10083BEDA /* Blob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C711AA8CDF10083BEDA /* Blob.cpp */; }; + 04957C8F1AA8CDF10083BEDA /* Bootloader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C721AA8CDF10083BEDA /* Bootloader.cpp */; }; + 04957C901AA8CDF10083BEDA /* BusPal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C731AA8CDF10083BEDA /* BusPal.cpp */; }; + 04957C911AA8CDF10083BEDA /* BusPalPeripheral.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C741AA8CDF10083BEDA /* BusPalPeripheral.cpp */; }; + 04957C921AA8CDF10083BEDA /* Command.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C751AA8CDF10083BEDA /* Command.cpp */; }; + 04957C931AA8CDF10083BEDA /* DataSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C761AA8CDF10083BEDA /* DataSource.cpp */; }; + 04957C941AA8CDF10083BEDA /* DataSourceImager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C771AA8CDF10083BEDA /* DataSourceImager.cpp */; }; + 04957C951AA8CDF10083BEDA /* DataTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C781AA8CDF10083BEDA /* DataTarget.cpp */; }; + 04957C961AA8CDF10083BEDA /* ELFSourceFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C791AA8CDF10083BEDA /* ELFSourceFile.cpp */; }; + 04957C971AA8CDF10083BEDA /* ExcludesListMatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C7A1AA8CDF10083BEDA /* ExcludesListMatcher.cpp */; }; + 04957C981AA8CDF10083BEDA /* GHSSecInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C7B1AA8CDF10083BEDA /* GHSSecInfo.cpp */; }; + 04957C991AA8CDF10083BEDA /* GlobMatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C7C1AA8CDF10083BEDA /* GlobMatcher.cpp */; }; + 04957C9A1AA8CDF10083BEDA /* SBSourceFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C7D1AA8CDF10083BEDA /* SBSourceFile.cpp */; }; + 04957C9B1AA8CDF10083BEDA /* SearchPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C7E1AA8CDF10083BEDA /* SearchPath.cpp */; }; + 04957C9C1AA8CDF10083BEDA /* SerialPacketizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C7F1AA8CDF10083BEDA /* SerialPacketizer.cpp */; }; + 04957CA11AA8CDF10083BEDA /* SourceFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C841AA8CDF10083BEDA /* SourceFile.cpp */; }; + 04957CA21AA8CDF10083BEDA /* SRecordSourceFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C851AA8CDF10083BEDA /* SRecordSourceFile.cpp */; }; + 04957CA31AA8CDF10083BEDA /* StELFFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C861AA8CDF10083BEDA /* StELFFile.cpp */; }; + 04957CA41AA8CDF10083BEDA /* StExecutableImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C871AA8CDF10083BEDA /* StExecutableImage.cpp */; }; + 04957CA51AA8CDF10083BEDA /* StSRecordFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C881AA8CDF10083BEDA /* StSRecordFile.cpp */; }; + 04957CA61AA8CDF10083BEDA /* UartPeripheral.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C891AA8CDF10083BEDA /* UartPeripheral.cpp */; }; + 04957CA71AA8CDF10083BEDA /* Updater.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C8A1AA8CDF10083BEDA /* Updater.cpp */; }; + 04957CA81AA8CDF10083BEDA /* UsbHidPacketizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C8B1AA8CDF10083BEDA /* UsbHidPacketizer.cpp */; }; + 04957CA91AA8CDF10083BEDA /* UsbHidPeripheral.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C8C1AA8CDF10083BEDA /* UsbHidPeripheral.cpp */; }; + 04957CAA1AA8CDF10083BEDA /* Value.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 04957C8D1AA8CDF10083BEDA /* Value.cpp */; }; + 3B8BB8461BD17B73000EFB8B /* AESCounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B8BB83E1BD17B73000EFB8B /* AESCounter.cpp */; }; + 3B8BB8471BD17B73000EFB8B /* AESKey.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B8BB83F1BD17B73000EFB8B /* AESKey.cpp */; }; + 3B8BB8481BD17B73000EFB8B /* HexValues.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B8BB8401BD17B73000EFB8B /* HexValues.cpp */; }; + 3B8BB8491BD17B73000EFB8B /* IntelHexSourceFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B8BB8411BD17B73000EFB8B /* IntelHexSourceFile.cpp */; }; + 3B8BB84A1BD17B73000EFB8B /* Random.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B8BB8421BD17B73000EFB8B /* Random.cpp */; }; + 3B8BB84B1BD17B73000EFB8B /* rijndael.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B8BB8431BD17B73000EFB8B /* rijndael.cpp */; }; + 3B8BB84C1BD17B73000EFB8B /* RijndaelCTR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B8BB8441BD17B73000EFB8B /* RijndaelCTR.cpp */; }; + 3B8BB84D1BD17B73000EFB8B /* StIntelHexFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3B8BB8451BD17B73000EFB8B /* StIntelHexFile.cpp */; }; + BF49F39A17F34E8B00A0B079 /* crc16.c in Sources */ = {isa = PBXBuildFile; fileRef = BF49F39917F34E8B00A0B079 /* crc16.c */; }; + BFB431BF000005C8007086B9 /* hid-mac.c in Sources */ = {isa = PBXBuildFile; fileRef = BFB431BE000005C8007086B9 /* hid-mac.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 02936E5D172CD35E00D07977 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 02B3B3AC16DE7116009769BD /* Project object */; + proxyType = 1; + remoteGlobalIDString = 02528C3516FE31DC0026B4C2; + remoteInfo = blhost; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 02528C3416FE31DC0026B4C2 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0208C5EB1713633900E031AD /* host_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = host_types.h; sourceTree = ""; }; + 020F8982175E4DB5009F0B96 /* crc32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crc32.h; sourceTree = ""; }; + 020F8984175E4DB5009F0B96 /* crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = crc32.c; sourceTree = ""; }; + 02528C1616FE308F0026B4C2 /* options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = options.h; sourceTree = ""; }; + 02528C1916FE308F0026B4C2 /* options.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = options.cpp; sourceTree = ""; }; + 02528C2816FE31980026B4C2 /* blhost.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = blhost.cpp; sourceTree = ""; }; + 02528C3116FE31980026B4C2 /* targetver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = targetver.h; sourceTree = ""; }; + 02528C3616FE31DC0026B4C2 /* blhost */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = blhost; sourceTree = BUILT_PRODUCTS_DIR; }; + 02558D2E17C3AC1900D3721D /* json.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json.h; sourceTree = ""; }; + 02558D3017C3AC2600D3721D /* jsoncpp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsoncpp.cpp; sourceTree = ""; }; + 02558D3317CF9C1800D3721D /* utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = ""; }; + 02558D3417CF9C1E00D3721D /* utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = utils.cpp; sourceTree = ""; }; + 02558D3A17D7B04200D3721D /* format_string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = format_string.h; sourceTree = ""; }; + 02558D3B17D7B05100D3721D /* format_string.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = format_string.cpp; sourceTree = ""; }; + 0289203D170A1279007D3438 /* serial.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = serial.c; sourceTree = ""; }; + 02892055170A1957007D3438 /* bootloader_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bootloader_common.h; sourceTree = ""; }; + 02936E52172CD27A00D07977 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Logging.cpp; sourceTree = ""; }; + 02936E56172CD28200D07977 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = ""; }; + 02936F5A174FA84E00D07977 /* serial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = serial.h; sourceTree = ""; }; + 029F2B04179DC5A800277DA7 /* fsl_platform_status.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fsl_platform_status.h; sourceTree = ""; }; + 029F2B05179DC5A800277DA7 /* fsl_platform_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fsl_platform_types.h; sourceTree = ""; }; + 02C2956D18996B3500C08DCF /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 02C2956F18996B3D00C08DCF /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 04957C711AA8CDF10083BEDA /* Blob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Blob.cpp; sourceTree = ""; }; + 04957C721AA8CDF10083BEDA /* Bootloader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Bootloader.cpp; sourceTree = ""; }; + 04957C731AA8CDF10083BEDA /* BusPal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BusPal.cpp; sourceTree = ""; }; + 04957C741AA8CDF10083BEDA /* BusPalPeripheral.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BusPalPeripheral.cpp; sourceTree = ""; }; + 04957C751AA8CDF10083BEDA /* Command.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Command.cpp; sourceTree = ""; }; + 04957C761AA8CDF10083BEDA /* DataSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataSource.cpp; sourceTree = ""; }; + 04957C771AA8CDF10083BEDA /* DataSourceImager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataSourceImager.cpp; sourceTree = ""; }; + 04957C781AA8CDF10083BEDA /* DataTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataTarget.cpp; sourceTree = ""; }; + 04957C791AA8CDF10083BEDA /* ELFSourceFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ELFSourceFile.cpp; sourceTree = ""; }; + 04957C7A1AA8CDF10083BEDA /* ExcludesListMatcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExcludesListMatcher.cpp; sourceTree = ""; }; + 04957C7B1AA8CDF10083BEDA /* GHSSecInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GHSSecInfo.cpp; sourceTree = ""; }; + 04957C7C1AA8CDF10083BEDA /* GlobMatcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GlobMatcher.cpp; sourceTree = ""; }; + 04957C7D1AA8CDF10083BEDA /* SBSourceFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SBSourceFile.cpp; sourceTree = ""; }; + 04957C7E1AA8CDF10083BEDA /* SearchPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SearchPath.cpp; sourceTree = ""; }; + 04957C7F1AA8CDF10083BEDA /* SerialPacketizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerialPacketizer.cpp; sourceTree = ""; }; + 04957C841AA8CDF10083BEDA /* SourceFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SourceFile.cpp; sourceTree = ""; }; + 04957C851AA8CDF10083BEDA /* SRecordSourceFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SRecordSourceFile.cpp; sourceTree = ""; }; + 04957C861AA8CDF10083BEDA /* StELFFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StELFFile.cpp; sourceTree = ""; }; + 04957C871AA8CDF10083BEDA /* StExecutableImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StExecutableImage.cpp; sourceTree = ""; }; + 04957C881AA8CDF10083BEDA /* StSRecordFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StSRecordFile.cpp; sourceTree = ""; }; + 04957C891AA8CDF10083BEDA /* UartPeripheral.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UartPeripheral.cpp; sourceTree = ""; }; + 04957C8A1AA8CDF10083BEDA /* Updater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Updater.cpp; sourceTree = ""; }; + 04957C8B1AA8CDF10083BEDA /* UsbHidPacketizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UsbHidPacketizer.cpp; sourceTree = ""; }; + 04957C8C1AA8CDF10083BEDA /* UsbHidPeripheral.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UsbHidPeripheral.cpp; sourceTree = ""; }; + 04957C8D1AA8CDF10083BEDA /* Value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Value.cpp; sourceTree = ""; }; + 04957CB41AA8F5B60083BEDA /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = ""; }; + 04957CB51AA8F6670083BEDA /* BlfwkErrors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlfwkErrors.h; sourceTree = ""; }; + 04957CB61AA8F6670083BEDA /* Blob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Blob.h; sourceTree = ""; }; + 04957CB71AA8F6670083BEDA /* Bootloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Bootloader.h; sourceTree = ""; }; + 04957CB81AA8F6670083BEDA /* BusPal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BusPal.h; sourceTree = ""; }; + 04957CB91AA8F6670083BEDA /* BusPalPeripheral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BusPalPeripheral.h; sourceTree = ""; }; + 04957CBA1AA8F6670083BEDA /* Command.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Command.h; sourceTree = ""; }; + 04957CBB1AA8F6670083BEDA /* DataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSource.h; sourceTree = ""; }; + 04957CBC1AA8F6670083BEDA /* DataSourceImager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSourceImager.h; sourceTree = ""; }; + 04957CBD1AA8F6670083BEDA /* DataTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataTarget.h; sourceTree = ""; }; + 04957CBE1AA8F6670083BEDA /* ELF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELF.h; sourceTree = ""; }; + 04957CBF1AA8F6670083BEDA /* ELFSourceFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELFSourceFile.h; sourceTree = ""; }; + 04957CC01AA8F6670083BEDA /* EndianUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EndianUtilities.h; sourceTree = ""; }; + 04957CC11AA8F6670083BEDA /* ExcludesListMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExcludesListMatcher.h; sourceTree = ""; }; + 04957CC21AA8F6670083BEDA /* GHSSecInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GHSSecInfo.h; sourceTree = ""; }; + 04957CC31AA8F6670083BEDA /* GlobMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GlobMatcher.h; sourceTree = ""; }; + 04957CC41AA8F6670083BEDA /* Packetizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Packetizer.h; sourceTree = ""; }; + 04957CC51AA8F6670083BEDA /* Peripheral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Peripheral.h; sourceTree = ""; }; + 04957CC61AA8F6670083BEDA /* SBSourceFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBSourceFile.h; sourceTree = ""; }; + 04957CC71AA8F6670083BEDA /* SearchPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchPath.h; sourceTree = ""; }; + 04957CC81AA8F6670083BEDA /* SerialPacketizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SerialPacketizer.h; sourceTree = ""; }; + 04957CC91AA8F6670083BEDA /* SimPacketizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimPacketizer.h; sourceTree = ""; }; + 04957CCA1AA8F6670083BEDA /* SimPeripheral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimPeripheral.h; sourceTree = ""; }; + 04957CCB1AA8F6670083BEDA /* Simulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Simulator.h; sourceTree = ""; }; + 04957CCC1AA8F6670083BEDA /* SimulatorMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimulatorMemory.h; sourceTree = ""; }; + 04957CCD1AA8F6670083BEDA /* SourceFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SourceFile.h; sourceTree = ""; }; + 04957CCE1AA8F6670083BEDA /* StELFFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StELFFile.h; sourceTree = ""; }; + 04957CCF1AA8F6670083BEDA /* StExecutableImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StExecutableImage.h; sourceTree = ""; }; + 04957CD01AA8F6670083BEDA /* StringMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringMatcher.h; sourceTree = ""; }; + 04957CD11AA8F6670083BEDA /* StSRecordFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StSRecordFile.h; sourceTree = ""; }; + 04957CD21AA8F6670083BEDA /* UartPeripheral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UartPeripheral.h; sourceTree = ""; }; + 04957CD31AA8F6670083BEDA /* Updater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Updater.h; sourceTree = ""; }; + 04957CD41AA8F6670083BEDA /* UsbHidPacketizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UsbHidPacketizer.h; sourceTree = ""; }; + 04957CD51AA8F6670083BEDA /* UsbHidPeripheral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UsbHidPeripheral.h; sourceTree = ""; }; + 04957CD61AA8F6670083BEDA /* Value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Value.h; sourceTree = ""; }; + 3B8BB83E1BD17B73000EFB8B /* AESCounter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AESCounter.cpp; sourceTree = ""; }; + 3B8BB83F1BD17B73000EFB8B /* AESKey.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AESKey.cpp; sourceTree = ""; }; + 3B8BB8401BD17B73000EFB8B /* HexValues.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HexValues.cpp; sourceTree = ""; }; + 3B8BB8411BD17B73000EFB8B /* IntelHexSourceFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntelHexSourceFile.cpp; sourceTree = ""; }; + 3B8BB8421BD17B73000EFB8B /* Random.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Random.cpp; sourceTree = ""; }; + 3B8BB8431BD17B73000EFB8B /* rijndael.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rijndael.cpp; sourceTree = ""; }; + 3B8BB8441BD17B73000EFB8B /* RijndaelCTR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RijndaelCTR.cpp; sourceTree = ""; }; + 3B8BB8451BD17B73000EFB8B /* StIntelHexFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StIntelHexFile.cpp; sourceTree = ""; }; + 3B8BB84E1BD17B9B000EFB8B /* AESCounter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESCounter.h; sourceTree = ""; }; + 3B8BB84F1BD17B9B000EFB8B /* AESKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AESKey.h; sourceTree = ""; }; + 3B8BB8501BD17B9B000EFB8B /* bootloader_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bootloader_config.h; sourceTree = ""; }; + 3B8BB8511BD17B9B000EFB8B /* HexValues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HexValues.h; sourceTree = ""; }; + 3B8BB8521BD17B9B000EFB8B /* int_size.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = int_size.h; sourceTree = ""; }; + 3B8BB8531BD17B9B000EFB8B /* IntelHexSourceFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntelHexSourceFile.h; sourceTree = ""; }; + 3B8BB8541BD17B9B000EFB8B /* OptionContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OptionContext.h; sourceTree = ""; }; + 3B8BB8551BD17B9B000EFB8B /* Random.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Random.h; sourceTree = ""; }; + 3B8BB8561BD17B9B000EFB8B /* rijndael.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rijndael.h; sourceTree = ""; }; + 3B8BB8571BD17B9B000EFB8B /* RijndaelCTR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RijndaelCTR.h; sourceTree = ""; }; + 3B8BB8581BD17B9B000EFB8B /* smart_ptr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = smart_ptr.h; sourceTree = ""; }; + 3B8BB8591BD17B9B000EFB8B /* SRecordSourceFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRecordSourceFile.h; sourceTree = ""; }; + 3B8BB85A1BD17B9B000EFB8B /* StIntelHexFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StIntelHexFile.h; sourceTree = ""; }; + BF49F39817F34E7900A0B079 /* crc16.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crc16.h; sourceTree = ""; }; + BF49F39917F34E8B00A0B079 /* crc16.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = crc16.c; sourceTree = ""; }; + BFB431BC00000484007086B9 /* hidapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hidapi.h; sourceTree = ""; }; + BFB431BE000005C8007086B9 /* hid-mac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "hid-mac.c"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 02528C3316FE31DC0026B4C2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 02C2957218996B5300C08DCF /* CoreFoundation.framework in Frameworks */, + 02C2957118996B4E00C08DCF /* IOKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 020F8981175E4DB5009F0B96 /* crc */ = { + isa = PBXGroup; + children = ( + BF49F39817F34E7900A0B079 /* crc16.h */, + 020F8982175E4DB5009F0B96 /* crc32.h */, + 020F8983175E4DB5009F0B96 /* src */, + ); + path = crc; + sourceTree = ""; + }; + 020F8983175E4DB5009F0B96 /* src */ = { + isa = PBXGroup; + children = ( + BF49F39917F34E8B00A0B079 /* crc16.c */, + 020F8984175E4DB5009F0B96 /* crc32.c */, + ); + path = src; + sourceTree = ""; + }; + 02528C1416FE308F0026B4C2 /* blfwk */ = { + isa = PBXGroup; + children = ( + 3B8BB84E1BD17B9B000EFB8B /* AESCounter.h */, + 3B8BB84F1BD17B9B000EFB8B /* AESKey.h */, + 3B8BB8501BD17B9B000EFB8B /* bootloader_config.h */, + 3B8BB8511BD17B9B000EFB8B /* HexValues.h */, + 3B8BB8521BD17B9B000EFB8B /* int_size.h */, + 3B8BB8531BD17B9B000EFB8B /* IntelHexSourceFile.h */, + 3B8BB8541BD17B9B000EFB8B /* OptionContext.h */, + 3B8BB8551BD17B9B000EFB8B /* Random.h */, + 3B8BB8561BD17B9B000EFB8B /* rijndael.h */, + 3B8BB8571BD17B9B000EFB8B /* RijndaelCTR.h */, + 3B8BB8581BD17B9B000EFB8B /* smart_ptr.h */, + 3B8BB8591BD17B9B000EFB8B /* SRecordSourceFile.h */, + 3B8BB85A1BD17B9B000EFB8B /* StIntelHexFile.h */, + 02528C1716FE308F0026B4C2 /* src */, + 04957CB51AA8F6670083BEDA /* BlfwkErrors.h */, + 04957CB61AA8F6670083BEDA /* Blob.h */, + 04957CB71AA8F6670083BEDA /* Bootloader.h */, + 04957CB81AA8F6670083BEDA /* BusPal.h */, + 04957CB91AA8F6670083BEDA /* BusPalPeripheral.h */, + 04957CBA1AA8F6670083BEDA /* Command.h */, + 04957CBB1AA8F6670083BEDA /* DataSource.h */, + 04957CBC1AA8F6670083BEDA /* DataSourceImager.h */, + 04957CBD1AA8F6670083BEDA /* DataTarget.h */, + 04957CBE1AA8F6670083BEDA /* ELF.h */, + 04957CBF1AA8F6670083BEDA /* ELFSourceFile.h */, + 04957CC01AA8F6670083BEDA /* EndianUtilities.h */, + 04957CC11AA8F6670083BEDA /* ExcludesListMatcher.h */, + 04957CC21AA8F6670083BEDA /* GHSSecInfo.h */, + 04957CC31AA8F6670083BEDA /* GlobMatcher.h */, + 04957CC41AA8F6670083BEDA /* Packetizer.h */, + 04957CC51AA8F6670083BEDA /* Peripheral.h */, + 04957CC61AA8F6670083BEDA /* SBSourceFile.h */, + 04957CC71AA8F6670083BEDA /* SearchPath.h */, + 04957CC81AA8F6670083BEDA /* SerialPacketizer.h */, + 04957CC91AA8F6670083BEDA /* SimPacketizer.h */, + 04957CCA1AA8F6670083BEDA /* SimPeripheral.h */, + 04957CCB1AA8F6670083BEDA /* Simulator.h */, + 04957CCC1AA8F6670083BEDA /* SimulatorMemory.h */, + 04957CCD1AA8F6670083BEDA /* SourceFile.h */, + 04957CCE1AA8F6670083BEDA /* StELFFile.h */, + 04957CCF1AA8F6670083BEDA /* StExecutableImage.h */, + 04957CD01AA8F6670083BEDA /* StringMatcher.h */, + 04957CD11AA8F6670083BEDA /* StSRecordFile.h */, + 04957CD21AA8F6670083BEDA /* UartPeripheral.h */, + 04957CD31AA8F6670083BEDA /* Updater.h */, + 04957CD41AA8F6670083BEDA /* UsbHidPacketizer.h */, + 04957CD51AA8F6670083BEDA /* UsbHidPeripheral.h */, + 04957CD61AA8F6670083BEDA /* Value.h */, + 04957CB41AA8F5B60083BEDA /* stdafx.h */, + 02558D3A17D7B04200D3721D /* format_string.h */, + BFB431BC00000484007086B9 /* hidapi.h */, + 0208C5EB1713633900E031AD /* host_types.h */, + 02558D2E17C3AC1900D3721D /* json.h */, + 02936E56172CD28200D07977 /* Logging.h */, + 02528C1616FE308F0026B4C2 /* options.h */, + 02936F5A174FA84E00D07977 /* serial.h */, + 02558D3317CF9C1800D3721D /* utils.h */, + ); + path = blfwk; + sourceTree = ""; + }; + 02528C1716FE308F0026B4C2 /* src */ = { + isa = PBXGroup; + children = ( + 3B8BB83E1BD17B73000EFB8B /* AESCounter.cpp */, + 3B8BB83F1BD17B73000EFB8B /* AESKey.cpp */, + 3B8BB8401BD17B73000EFB8B /* HexValues.cpp */, + 3B8BB8411BD17B73000EFB8B /* IntelHexSourceFile.cpp */, + 3B8BB8421BD17B73000EFB8B /* Random.cpp */, + 3B8BB8431BD17B73000EFB8B /* rijndael.cpp */, + 3B8BB8441BD17B73000EFB8B /* RijndaelCTR.cpp */, + 3B8BB8451BD17B73000EFB8B /* StIntelHexFile.cpp */, + 04957C711AA8CDF10083BEDA /* Blob.cpp */, + 04957C721AA8CDF10083BEDA /* Bootloader.cpp */, + 04957C731AA8CDF10083BEDA /* BusPal.cpp */, + 04957C741AA8CDF10083BEDA /* BusPalPeripheral.cpp */, + 04957C751AA8CDF10083BEDA /* Command.cpp */, + 04957C761AA8CDF10083BEDA /* DataSource.cpp */, + 04957C771AA8CDF10083BEDA /* DataSourceImager.cpp */, + 04957C781AA8CDF10083BEDA /* DataTarget.cpp */, + 04957C791AA8CDF10083BEDA /* ELFSourceFile.cpp */, + 04957C7A1AA8CDF10083BEDA /* ExcludesListMatcher.cpp */, + 04957C7B1AA8CDF10083BEDA /* GHSSecInfo.cpp */, + 04957C7C1AA8CDF10083BEDA /* GlobMatcher.cpp */, + 04957C7D1AA8CDF10083BEDA /* SBSourceFile.cpp */, + 04957C7E1AA8CDF10083BEDA /* SearchPath.cpp */, + 04957C7F1AA8CDF10083BEDA /* SerialPacketizer.cpp */, + 04957C841AA8CDF10083BEDA /* SourceFile.cpp */, + 04957C851AA8CDF10083BEDA /* SRecordSourceFile.cpp */, + 04957C861AA8CDF10083BEDA /* StELFFile.cpp */, + 04957C871AA8CDF10083BEDA /* StExecutableImage.cpp */, + 04957C881AA8CDF10083BEDA /* StSRecordFile.cpp */, + 04957C891AA8CDF10083BEDA /* UartPeripheral.cpp */, + 04957C8A1AA8CDF10083BEDA /* Updater.cpp */, + 04957C8B1AA8CDF10083BEDA /* UsbHidPacketizer.cpp */, + 04957C8C1AA8CDF10083BEDA /* UsbHidPeripheral.cpp */, + 04957C8D1AA8CDF10083BEDA /* Value.cpp */, + 02558D3B17D7B05100D3721D /* format_string.cpp */, + BFB431BE000005C8007086B9 /* hid-mac.c */, + 02558D3017C3AC2600D3721D /* jsoncpp.cpp */, + 02936E52172CD27A00D07977 /* Logging.cpp */, + 02528C1916FE308F0026B4C2 /* options.cpp */, + 0289203D170A1279007D3438 /* serial.c */, + 02558D3417CF9C1E00D3721D /* utils.cpp */, + ); + path = src; + sourceTree = ""; + }; + 02528C2516FE31980026B4C2 /* blhost */ = { + isa = PBXGroup; + children = ( + 02528C2716FE31980026B4C2 /* src */, + ); + path = blhost; + sourceTree = ""; + }; + 02528C2716FE31980026B4C2 /* src */ = { + isa = PBXGroup; + children = ( + 02528C2816FE31980026B4C2 /* blhost.cpp */, + 02528C3116FE31980026B4C2 /* targetver.h */, + ); + path = src; + sourceTree = ""; + }; + 02B3B3AB16DE7116009769BD = { + isa = PBXGroup; + children = ( + 02C2956C18996B1E00C08DCF /* Frameworks */, + 02528C2516FE31980026B4C2 /* blhost */, + 02C6C97816FE4AC40023EB1B /* src */, + 02B3B3B516DE7116009769BD /* Products */, + ); + sourceTree = ""; + }; + 02B3B3B516DE7116009769BD /* Products */ = { + isa = PBXGroup; + children = ( + 02528C3616FE31DC0026B4C2 /* blhost */, + ); + name = Products; + sourceTree = ""; + }; + 02C2956C18996B1E00C08DCF /* Frameworks */ = { + isa = PBXGroup; + children = ( + 02C2956F18996B3D00C08DCF /* CoreFoundation.framework */, + 02C2956D18996B3500C08DCF /* IOKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 02C6C97816FE4AC40023EB1B /* src */ = { + isa = PBXGroup; + children = ( + 02528C1416FE308F0026B4C2 /* blfwk */, + 020F8981175E4DB5009F0B96 /* crc */, + 02C6C97C16FE4AF80023EB1B /* include */, + ); + name = src; + path = ../src; + sourceTree = SOURCE_ROOT; + }; + 02C6C97C16FE4AF80023EB1B /* include */ = { + isa = PBXGroup; + children = ( + 029F2B04179DC5A800277DA7 /* fsl_platform_status.h */, + 029F2B05179DC5A800277DA7 /* fsl_platform_types.h */, + 02892055170A1957007D3438 /* bootloader_common.h */, + ); + path = include; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 02528C3516FE31DC0026B4C2 /* blhost */ = { + isa = PBXNativeTarget; + buildConfigurationList = 02528C3C16FE31DC0026B4C2 /* Build configuration list for PBXNativeTarget "blhost" */; + buildPhases = ( + 02528C3216FE31DC0026B4C2 /* Sources */, + 02528C3316FE31DC0026B4C2 /* Frameworks */, + 02528C3416FE31DC0026B4C2 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = blhost; + productName = blhost; + productReference = 02528C3616FE31DC0026B4C2 /* blhost */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 02B3B3AC16DE7116009769BD /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0500; + ORGANIZATIONNAME = "Chris Reed"; + }; + buildConfigurationList = 02B3B3AF16DE7116009769BD /* Build configuration list for PBXProject "blhost" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 02B3B3AB16DE7116009769BD; + productRefGroup = 02B3B3B516DE7116009769BD /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 02528C3516FE31DC0026B4C2 /* blhost */, + 02936E57172CD33100D07977 /* All blhost tools */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 02528C3216FE31DC0026B4C2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 02528C3F16FE32760026B4C2 /* blhost.cpp in Sources */, + 04957CA71AA8CDF10083BEDA /* Updater.cpp in Sources */, + 04957CA21AA8CDF10083BEDA /* SRecordSourceFile.cpp in Sources */, + 02528C4316FE32880026B4C2 /* options.cpp in Sources */, + 04957CA91AA8CDF10083BEDA /* UsbHidPeripheral.cpp in Sources */, + 04957C9B1AA8CDF10083BEDA /* SearchPath.cpp in Sources */, + 04957CA81AA8CDF10083BEDA /* UsbHidPacketizer.cpp in Sources */, + 04957C911AA8CDF10083BEDA /* BusPalPeripheral.cpp in Sources */, + 04957C901AA8CDF10083BEDA /* BusPal.cpp in Sources */, + 04957C921AA8CDF10083BEDA /* Command.cpp in Sources */, + 04957C981AA8CDF10083BEDA /* GHSSecInfo.cpp in Sources */, + 3B8BB84D1BD17B73000EFB8B /* StIntelHexFile.cpp in Sources */, + 3B8BB84C1BD17B73000EFB8B /* RijndaelCTR.cpp in Sources */, + 3B8BB8481BD17B73000EFB8B /* HexValues.cpp in Sources */, + 04957CA31AA8CDF10083BEDA /* StELFFile.cpp in Sources */, + 04957CAA1AA8CDF10083BEDA /* Value.cpp in Sources */, + 0289203F170A1279007D3438 /* serial.c in Sources */, + 02936E54172CD27A00D07977 /* Logging.cpp in Sources */, + 04957C9C1AA8CDF10083BEDA /* SerialPacketizer.cpp in Sources */, + 04957C8E1AA8CDF10083BEDA /* Blob.cpp in Sources */, + 3B8BB8471BD17B73000EFB8B /* AESKey.cpp in Sources */, + 3B8BB8461BD17B73000EFB8B /* AESCounter.cpp in Sources */, + 04957C941AA8CDF10083BEDA /* DataSourceImager.cpp in Sources */, + 04957C9A1AA8CDF10083BEDA /* SBSourceFile.cpp in Sources */, + 020F8999175E4DB5009F0B96 /* crc32.c in Sources */, + BFB431BF000005C8007086B9 /* hid-mac.c in Sources */, + 02558D3117C3AC2600D3721D /* jsoncpp.cpp in Sources */, + 04957C931AA8CDF10083BEDA /* DataSource.cpp in Sources */, + 04957C951AA8CDF10083BEDA /* DataTarget.cpp in Sources */, + 04957CA11AA8CDF10083BEDA /* SourceFile.cpp in Sources */, + 04957CA51AA8CDF10083BEDA /* StSRecordFile.cpp in Sources */, + 04957C971AA8CDF10083BEDA /* ExcludesListMatcher.cpp in Sources */, + 3B8BB84B1BD17B73000EFB8B /* rijndael.cpp in Sources */, + 04957C961AA8CDF10083BEDA /* ELFSourceFile.cpp in Sources */, + 02558D3517CF9C1E00D3721D /* utils.cpp in Sources */, + 04957CA61AA8CDF10083BEDA /* UartPeripheral.cpp in Sources */, + 3B8BB84A1BD17B73000EFB8B /* Random.cpp in Sources */, + 3B8BB8491BD17B73000EFB8B /* IntelHexSourceFile.cpp in Sources */, + 02558D3C17D7B05100D3721D /* format_string.cpp in Sources */, + 04957C8F1AA8CDF10083BEDA /* Bootloader.cpp in Sources */, + 04957C991AA8CDF10083BEDA /* GlobMatcher.cpp in Sources */, + BF49F39A17F34E8B00A0B079 /* crc16.c in Sources */, + 04957CA41AA8CDF10083BEDA /* StExecutableImage.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 02936E5E172CD35E00D07977 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 02528C3516FE31DC0026B4C2 /* blhost */; + targetProxy = 02936E5D172CD35E00D07977 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 02528C3D16FE31DC0026B4C2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + ../src, + ../src/include, + ., + ../src/drivers/common, + ../src/bm_usb, + ); + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 02528C3E16FE31DC0026B4C2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + ../src, + ../src/include, + ., + ../src/drivers/common, + ../src/bm_usb, + ); + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 02936E59172CD33100D07977 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "All blhost tools"; + }; + name = Debug; + }; + 02936E5A172CD33100D07977 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "All blhost tools"; + }; + name = Release; + }; + 02B3B3BB16DE7116009769BD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + MACOSX, + BOOTLOADER_HOST, + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../src, + ../src/include, + ., + ); + MACOSX_DEPLOYMENT_TARGET = 10.7; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 02B3B3BC16DE7116009769BD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + NDEBUG, + BOOTLOADER_HOST, + MACOSX, + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../src, + ../src/include, + ., + ); + MACOSX_DEPLOYMENT_TARGET = 10.7; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 02528C3C16FE31DC0026B4C2 /* Build configuration list for PBXNativeTarget "blhost" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 02528C3D16FE31DC0026B4C2 /* Debug */, + 02528C3E16FE31DC0026B4C2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 02936E58172CD33100D07977 /* Build configuration list for PBXAggregateTarget "All blhost tools" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 02936E59172CD33100D07977 /* Debug */, + 02936E5A172CD33100D07977 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 02B3B3AF16DE7116009769BD /* Build configuration list for PBXProject "blhost" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 02B3B3BB16DE7116009769BD /* Debug */, + 02B3B3BC16DE7116009769BD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 02B3B3AC16DE7116009769BD /* Project object */; +} diff --git a/tools/blhost.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/tools/blhost.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..39f28b0 --- /dev/null +++ b/tools/blhost.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/tools/blhost/arm-gcc/Makefile b/tools/blhost/arm-gcc/Makefile new file mode 100644 index 0000000..a7cb001 --- /dev/null +++ b/tools/blhost/arm-gcc/Makefile @@ -0,0 +1,260 @@ +#----------------------------------------------- +# Make command: +# make build= cpu= all +# : debug or release, release by default. +# : cpu architecure, cortex-a9 by default. +#----------------------------------------------- + +#----------------------------------------------- +# setup variables +# ---------------------------------------------- + +BOOT_ROOT := $(abspath ../../..) +OUTPUT_ROOT := $(abspath ./) + +APP_NAME = blhost + +#----------------------------------------------- +# Toolchain +# ---------------------------------------------- +# GNU Cross Compiler (No linux lib is available) +#PREFIX := arm-none-eabi- +# Linaro Linux-Tageted GNU C Cross Compiler (libudev is not available) +PREFIX := arm-linux-gnueabi- +# Freescale i.MX GNU C Cross Compiler +#PREFIX := arm-poky-linux-gnueabi- +#SYSROOT := /opt/fsl-imx-xwayland/5.4-zeus/sysroots/cortexa9t2hf-neon-poky-linux-gnueabi + +ifneq ($(filter X86%, $(shell uname -m | tr a-z A-Z)),) +# Cross Compiling +CC := $(PREFIX)gcc +CPP := $(PREFIX)cpp +CXX := $(PREFIX)g++ +AS := $(PREFIX)as +# DO NOT use ld. +LD := $(PREFIX)g++ +else +# DO NOT use ld. +LD := g++ +endif + +#----------------------------------------------- +# Architecture +# cortex-a9 by default +#----------------------------------------------- +CPU ?= cortex-a9 + +#----------------------------------------------- +# Debug or Release +# Release by default +#----------------------------------------------- +build ?= release + +include $(BOOT_ROOT)/mk/common.mk + +#----------------------------------------------- +# Include path. Add the include paths like this: +# INCLUDES += ./include/ +#----------------------------------------------- +INCLUDES += $(BOOT_ROOT)/tools/blhost/src \ + $(BOOT_ROOT)/src \ + $(BOOT_ROOT)/src/include \ + $(BOOT_ROOT)/src/blfwk \ + $(BOOT_ROOT)/src/sbloader \ + $(BOOT_ROOT)/src/bootloader \ + $(BOOT_ROOT)/src/crc \ + $(BOOT_ROOT)/src/packet \ + $(BOOT_ROOT)/src/property \ + $(BOOT_ROOT)/src/drivers/common \ + $(BOOT_ROOT)/src/bm_usb + +CXXFLAGS := -D LINUX -D BOOTLOADER_HOST -D __ARM__ -std=c++11 +CFLAGS := -D LINUX -D BOOTLOADER_HOST -D __ARM__ -D _GNU_SOURCE -std=c99 +LIBS := + +SOURCES := $(BOOT_ROOT)/tools/blhost/src/blhost.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Blob.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Bootloader.cpp \ + $(BOOT_ROOT)/src/blfwk/src/BusPal.cpp \ + $(BOOT_ROOT)/src/blfwk/src/BusPalPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Command.cpp \ + $(BOOT_ROOT)/src/blfwk/src/DataSource.cpp \ + $(BOOT_ROOT)/src/blfwk/src/DataSourceImager.cpp \ + $(BOOT_ROOT)/src/blfwk/src/DataTarget.cpp \ + $(BOOT_ROOT)/src/blfwk/src/ELFSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/ExcludesListMatcher.cpp \ + $(BOOT_ROOT)/src/blfwk/src/format_string.cpp \ + $(BOOT_ROOT)/src/blfwk/src/GHSSecInfo.cpp \ + $(BOOT_ROOT)/src/blfwk/src/GlobMatcher.cpp \ + $(BOOT_ROOT)/src/blfwk/src/hid-linux.c \ + $(BOOT_ROOT)/src/blfwk/src/jsoncpp.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Logging.cpp \ + $(BOOT_ROOT)/src/blfwk/src/options.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SBSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SearchPath.cpp \ + $(BOOT_ROOT)/src/blfwk/src/serial.c \ + $(BOOT_ROOT)/src/blfwk/src/SerialPacketizer.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SRecordSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/IntelHexSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StELFFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StExecutableImage.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StSRecordFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StIntelHexFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Updater.cpp \ + $(BOOT_ROOT)/src/blfwk/src/UartPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/UsbHidPacketizer.cpp \ + $(BOOT_ROOT)/src/blfwk/src/UsbHidPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/i2c.c \ + $(BOOT_ROOT)/src/blfwk/src/I2cPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/spi.c \ + $(BOOT_ROOT)/src/blfwk/src/SpiPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/utils.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Value.cpp \ + $(BOOT_ROOT)/src/crc/src/crc16.c \ + $(BOOT_ROOT)/src/crc/src/crc32.c + +# LPC USB Serial I/O is not supported by ARM Linux blhost. +#SOURCES += $(BOOT_ROOT)/src/blfwk/src/LpcUsbSio.cpp \ + $(BOOT_ROOT)/src/blfwk/src/LpcUsbSioPeripheral.cpp +#CFLAGS += -D LPCUSBSIO +#CXXFLAGS += -D LPCUSBSIO +#LIBS += $(BOOT_ROOT)/src/middleware/lpcusbsio/bin/arm_linux/lpcusbsio.a + +INCLUDES := $(foreach includes, $(INCLUDES), -I $(includes)) + +ifeq "$(build)" "debug" +DEBUG_OR_RELEASE := Debug +CFLAGS += -g +CXXFLAGS += -g +LDFLAGS += -g +else +DEBUG_OR_RELEASE := Release +endif + +ifneq ($(filter X86%, $(shell uname -m | tr a-z A-Z)),) +# GNU Cross Compiler +# Linaro Linux-Tageted GNU C Cross Compiler +CFLGAS += -mcpu=$(CPU) +CXXFLGAS += -mcpu=$(CPU) +LDFLGAS += -mcpu=$(CPU) +# Freescale i.MX GNU C Cross Compiler +# CFLGAS += -mfloat-abi=hard -mcpu=$(CPU) --sysroot=$(SYSROOT) +# CXXFLGAS += -mfloat-abi=hard -mcpu=$(CPU) --sysroot=$(SYSROOT) +# LDFLGAS += -mfloat-abi=hard -mcpu=$(CPU) --sysroot=$(SYSROOT) +endif + +TARGET_OUTPUT_ROOT := $(OUTPUT_ROOT)/$(DEBUG_OR_RELEASE) +MAKE_TARGET := $(TARGET_OUTPUT_ROOT)/$(APP_NAME) + +OBJS_ROOT = $(TARGET_OUTPUT_ROOT)/obj + +# Strip sources. +SOURCES := $(strip $(SOURCES)) + +# Convert sources list to absolute paths and root-relative paths. +SOURCES_ABS := $(foreach s,$(SOURCES),$(abspath $(s))) +SOURCES_REL := $(subst $(BOOT_ROOT)/,,$(SOURCES_ABS)) + +# Get a list of unique directories containing the source files. +SOURCE_DIRS_ABS := $(sort $(foreach f,$(SOURCES_ABS),$(dir $(f)))) +SOURCE_DIRS_REL := $(subst $(BOOT_ROOT)/,,$(SOURCE_DIRS_ABS)) + +OBJECTS_DIRS := $(addprefix $(OBJS_ROOT)/,$(SOURCE_DIRS_REL)) + +# Filter source files list into separate source types. +C_SOURCES = $(filter %.c,$(SOURCES_REL)) +CXX_SOURCES = $(filter %.cpp,$(SOURCES_REL)) +ASM_s_SOURCES = $(filter %.s,$(SOURCES_REL)) +ASM_S_SOURCES = $(filter %.S,$(SOURCES_REL)) + +# Convert sources to objects. +OBJECTS_C := $(addprefix $(OBJS_ROOT)/,$(C_SOURCES:.c=.o)) +OBJECTS_CXX := $(addprefix $(OBJS_ROOT)/,$(CXX_SOURCES:.cpp=.o)) +OBJECTS_ASM := $(addprefix $(OBJS_ROOT)/,$(ASM_s_SOURCES:.s=.o)) +OBJECTS_ASM_S := $(addprefix $(OBJS_ROOT)/,$(ASM_S_SOURCES:.S=.o)) + +# Complete list of all object files. +OBJECTS_ALL := $(sort $(OBJECTS_C) $(OBJECTS_CXX) $(OBJECTS_ASM) $(OBJECTS_ASM_S)) + +#------------------------------------------------------------------------------- +# Default target +#------------------------------------------------------------------------------- + +# Note that prerequisite order is important here. The subdirectories must be built first, or you +# may end up with files in the current directory not getting added to libraries. This would happen +# if subdirs modified the library file after local files were compiled but before they were added +# to the library. +.PHONY: all +all: $(MAKE_TARGET) + +## Recipe to create the output object file directories. +$(OBJECTS_DIRS) : + $(at)mkdir -p $@ + +# Object files depend on the directories where they will be created. +# +# The dirs are made order-only prerequisites (by being listed after the '|') so they won't cause +# the objects to be rebuilt, as the modification date on a directory changes whenver its contents +# change. This would cause the objects to always be rebuilt if the dirs were normal prerequisites. +$(OBJECTS_ALL): | $(OBJECTS_DIRS) + +#------------------------------------------------------------------------------- +# Pattern rules for compilation +#------------------------------------------------------------------------------- +# We cd into the source directory before calling the appropriate compiler. This must be done +# on a single command line since make calls individual recipe lines in separate shells, so +# '&&' is used to chain the commands. +# +# Generate make dependencies while compiling using the -MMD option, which excludes system headers. +# If system headers are included, there are path problems on cygwin. The -MP option creates empty +# targets for each header file so that a rebuild will be forced if the file goes missing, but +# no error will occur. + +# Compile C sources. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.c + @$(call printmessage,c,Compiling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(CC) $(CFLAGS) $(SYSTEM_INC) $(INCLUDES) $(DEFINES) -MMD -MF $(basename $@).d -MP -o $@ -c $< + +# Compile C++ sources. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.cpp + @$(call printmessage,cxx,Compiling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(CXX) $(CXXFLAGS) $(SYSTEM_INC) $(INCLUDES) $(DEFINES) -MMD -MF $(basename $@).d -MP -o $@ -c $< + +# For .S assembly files, first run through the C preprocessor then assemble. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.S + @$(call printmessage,asm,Assembling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(CPP) -D__LANGUAGE_ASM__ $(INCLUDES) $(DEFINES) -o $(basename $@).s $< \ + && $(AS) $(ASFLAGS) $(INCLUDES) -MD $(OBJS_ROOT)/$*.d -o $@ $(basename $@).s + +# Assembler sources. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.s + @$(call printmessage,asm,Assembling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(AS) $(ASFLAGS) $(INCLUDES) -MD $(basename $@).d -o $@ $< + +#------------------------------------------------------------------------ +# Build the tagrget +#------------------------------------------------------------------------ + +# Wrap the link objects in start/end group so that ld re-checks each +# file for dependencies. Otherwise linking static libs can be a pain +# since order matters. +$(MAKE_TARGET): $(OBJECTS_ALL) + @$(call printmessage,link,Linking, $(APP_NAME)) + $(at)$(LD) $(LDFLAGS) \ + $(OBJECTS_ALL) $(LIBS) \ + -lc -lstdc++ -lm -ludev \ + -o $@ + @echo "Output binary:" ; echo " $(APP_NAME)" + +#------------------------------------------------------------------------------- +# Clean +#------------------------------------------------------------------------------- +.PHONY: clean cleanall +cleanall: clean +clean: + $(at)rm -rf $(OBJECTS_ALL) $(OBJECTS_DIRS) $(MAKE_TARGET) $(APP_NAME) + +# Include dependency files. +-include $(OBJECTS_ALL:.o=.d) + diff --git a/tools/blhost/blhost.sln b/tools/blhost/blhost.sln new file mode 100644 index 0000000..a04ce94 --- /dev/null +++ b/tools/blhost/blhost.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2013 for Windows Desktop +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "blhost", "src\blhost.vcxproj", "{A498A89A-E965-4B36-B926-850AABF31939}" + ProjectSection(ProjectDependencies) = postProject + {DE64A8C3-695A-491F-B3E3-24D6A3057A40} = {DE64A8C3-695A-491F-B3E3-24D6A3057A40} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "blfwk", "..\common\blfwk\blfwk.vcxproj", "{DE64A8C3-695A-491F-B3E3-24D6A3057A40}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A498A89A-E965-4B36-B926-850AABF31939}.Debug|Win32.ActiveCfg = Debug|Win32 + {A498A89A-E965-4B36-B926-850AABF31939}.Debug|Win32.Build.0 = Debug|Win32 + {A498A89A-E965-4B36-B926-850AABF31939}.Release|Win32.ActiveCfg = Release|Win32 + {A498A89A-E965-4B36-B926-850AABF31939}.Release|Win32.Build.0 = Release|Win32 + {DE64A8C3-695A-491F-B3E3-24D6A3057A40}.Debug|Win32.ActiveCfg = Debug|Win32 + {DE64A8C3-695A-491F-B3E3-24D6A3057A40}.Debug|Win32.Build.0 = Debug|Win32 + {DE64A8C3-695A-491F-B3E3-24D6A3057A40}.Release|Win32.ActiveCfg = Release|Win32 + {DE64A8C3-695A-491F-B3E3-24D6A3057A40}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/blhost/gcc/Makefile b/tools/blhost/gcc/Makefile new file mode 100644 index 0000000..d0d9c93 --- /dev/null +++ b/tools/blhost/gcc/Makefile @@ -0,0 +1,233 @@ +#----------------------------------------------- +# Make command: +# make build= machine= all +# : debug or release, release by default. +# : X86_64 or I386, default based on +# the building enviroment(uname -m). +#----------------------------------------------- + +#----------------------------------------------- +# setup variables +# ---------------------------------------------- + +BOOT_ROOT := $(abspath ../../..) +OUTPUT_ROOT := $(abspath ./) + +APP_NAME = blhost + +#----------------------------------------------- +# Target machine +#----------------------------------------------- +machine ?= $(shell uname -m | tr a-z A-Z) + +#----------------------------------------------- +# Debug or Release +# Release by default +#----------------------------------------------- +build ?= release + +include $(BOOT_ROOT)/mk/common.mk + +#----------------------------------------------- +# Include path. Add the include paths like this: +# INCLUDES += ./include/ +#----------------------------------------------- +INCLUDES += $(BOOT_ROOT)/tools/blhost/src \ + $(BOOT_ROOT)/src \ + $(BOOT_ROOT)/src/include \ + $(BOOT_ROOT)/src/blfwk \ + $(BOOT_ROOT)/src/sbloader \ + $(BOOT_ROOT)/src/bootloader \ + $(BOOT_ROOT)/src/crc \ + $(BOOT_ROOT)/src/packet \ + $(BOOT_ROOT)/src/property \ + $(BOOT_ROOT)/src/drivers/common \ + $(BOOT_ROOT)/src/bm_usb + +CXXFLAGS := -D LINUX -D BOOTLOADER_HOST -D LPCUSBSIO -std=c++11 +CFLAGS := -std=c99 -D LINUX -D BOOTLOADER_HOST -D LPCUSBSIO -D _GNU_SOURCE +LD := g++ +LIBS := + +SOURCES := $(BOOT_ROOT)/tools/blhost/src/blhost.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Blob.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Bootloader.cpp \ + $(BOOT_ROOT)/src/blfwk/src/BusPal.cpp \ + $(BOOT_ROOT)/src/blfwk/src/BusPalPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Command.cpp \ + $(BOOT_ROOT)/src/blfwk/src/DataSource.cpp \ + $(BOOT_ROOT)/src/blfwk/src/DataSourceImager.cpp \ + $(BOOT_ROOT)/src/blfwk/src/DataTarget.cpp \ + $(BOOT_ROOT)/src/blfwk/src/ELFSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/ExcludesListMatcher.cpp \ + $(BOOT_ROOT)/src/blfwk/src/format_string.cpp \ + $(BOOT_ROOT)/src/blfwk/src/GHSSecInfo.cpp \ + $(BOOT_ROOT)/src/blfwk/src/GlobMatcher.cpp \ + $(BOOT_ROOT)/src/blfwk/src/jsoncpp.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Logging.cpp \ + $(BOOT_ROOT)/src/blfwk/src/LpcUsbSio.cpp \ + $(BOOT_ROOT)/src/blfwk/src/LpcUsbSioPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/options.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SBSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SearchPath.cpp \ + $(BOOT_ROOT)/src/blfwk/src/serial.c \ + $(BOOT_ROOT)/src/blfwk/src/SerialPacketizer.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SRecordSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/IntelHexSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StELFFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StExecutableImage.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StSRecordFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StIntelHexFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Updater.cpp \ + $(BOOT_ROOT)/src/blfwk/src/UartPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/UsbHidPacketizer.cpp \ + $(BOOT_ROOT)/src/blfwk/src/UsbHidPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/utils.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Value.cpp \ + $(BOOT_ROOT)/src/crc/src/crc16.c \ + $(BOOT_ROOT)/src/crc/src/crc32.c +ifeq "$(machine)" "X86_64" +LIBS += $(BOOT_ROOT)/src/middleware/libusbsio/bin/linux/amd64/libusbsio.a +else +LIBS += $(BOOT_ROOT)/src/middleware/libusbsio/bin/linux/i386/libusbsio.a +endif + +INCLUDES := $(foreach includes, $(INCLUDES), -I $(includes)) + +BUILD_MACHINE := $(shell uname -m | tr a-z A-Z) +ifneq "$(machine)" "$(BUILD_MACHINE)" +ifeq "$(BUILD_MACHINE)" "X86_64" +CFLAGS += -m32 +CXXFLAGS += -m32 +LDFLAGS += -m32 +else +CFLAGS += -m64 +CXXFLAGS += -m64 +LDFLAGS += -m64 +endif +endif + +ifeq "$(build)" "debug" +DEBUG_OR_RELEASE := Debug +CFLAGS += -g +CXXFLAGS += -g +LDFLAGS += -g +else +DEBUG_OR_RELEASE := Release +endif + +TARGET_OUTPUT_ROOT := $(OUTPUT_ROOT)/$(DEBUG_OR_RELEASE) +MAKE_TARGET := $(TARGET_OUTPUT_ROOT)/$(APP_NAME) + +OBJS_ROOT = $(TARGET_OUTPUT_ROOT)/obj + +# Strip sources. +SOURCES := $(strip $(SOURCES)) + +# Convert sources list to absolute paths and root-relative paths. +SOURCES_ABS := $(foreach s,$(SOURCES),$(abspath $(s))) +SOURCES_REL := $(subst $(BOOT_ROOT)/,,$(SOURCES_ABS)) + +# Get a list of unique directories containing the source files. +SOURCE_DIRS_ABS := $(sort $(foreach f,$(SOURCES_ABS),$(dir $(f)))) +SOURCE_DIRS_REL := $(subst $(BOOT_ROOT)/,,$(SOURCE_DIRS_ABS)) + +OBJECTS_DIRS := $(addprefix $(OBJS_ROOT)/,$(SOURCE_DIRS_REL)) + +# Filter source files list into separate source types. +C_SOURCES = $(filter %.c,$(SOURCES_REL)) +CXX_SOURCES = $(filter %.cpp,$(SOURCES_REL)) +ASM_s_SOURCES = $(filter %.s,$(SOURCES_REL)) +ASM_S_SOURCES = $(filter %.S,$(SOURCES_REL)) + +# Convert sources to objects. +OBJECTS_C := $(addprefix $(OBJS_ROOT)/,$(C_SOURCES:.c=.o)) +OBJECTS_CXX := $(addprefix $(OBJS_ROOT)/,$(CXX_SOURCES:.cpp=.o)) +OBJECTS_ASM := $(addprefix $(OBJS_ROOT)/,$(ASM_s_SOURCES:.s=.o)) +OBJECTS_ASM_S := $(addprefix $(OBJS_ROOT)/,$(ASM_S_SOURCES:.S=.o)) + +# Complete list of all object files. +OBJECTS_ALL := $(sort $(OBJECTS_C) $(OBJECTS_CXX) $(OBJECTS_ASM) $(OBJECTS_ASM_S)) + +#------------------------------------------------------------------------------- +# Default target +#------------------------------------------------------------------------------- + +# Note that prerequisite order is important here. The subdirectories must be built first, or you +# may end up with files in the current directory not getting added to libraries. This would happen +# if subdirs modified the library file after local files were compiled but before they were added +# to the library. +.PHONY: all +all: $(MAKE_TARGET) + +## Recipe to create the output object file directories. +$(OBJECTS_DIRS) : + $(at)mkdir -p $@ + +# Object files depend on the directories where they will be created. +# +# The dirs are made order-only prerequisites (by being listed after the '|') so they won't cause +# the objects to be rebuilt, as the modification date on a directory changes whenver its contents +# change. This would cause the objects to always be rebuilt if the dirs were normal prerequisites. +$(OBJECTS_ALL): | $(OBJECTS_DIRS) + +#------------------------------------------------------------------------------- +# Pattern rules for compilation +#------------------------------------------------------------------------------- +# We cd into the source directory before calling the appropriate compiler. This must be done +# on a single command line since make calls individual recipe lines in separate shells, so +# '&&' is used to chain the commands. +# +# Generate make dependencies while compiling using the -MMD option, which excludes system headers. +# If system headers are included, there are path problems on cygwin. The -MP option creates empty +# targets for each header file so that a rebuild will be forced if the file goes missing, but +# no error will occur. + +# Compile C sources. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.c + @$(call printmessage,c,Compiling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(CC) $(CFLAGS) $(SYSTEM_INC) $(INCLUDES) $(DEFINES) -MMD -MF $(basename $@).d -MP -o $@ -c $< + +# Compile C++ sources. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.cpp + @$(call printmessage,cxx,Compiling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(CXX) $(CXXFLAGS) $(SYSTEM_INC) $(INCLUDES) $(DEFINES) -MMD -MF $(basename $@).d -MP -o $@ -c $< + +# For .S assembly files, first run through the C preprocessor then assemble. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.S + @$(call printmessage,asm,Assembling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(CPP) -D__LANGUAGE_ASM__ $(INCLUDES) $(DEFINES) -o $(basename $@).s $< \ + && $(AS) $(ASFLAGS) $(INCLUDES) -MD $(OBJS_ROOT)/$*.d -o $@ $(basename $@).s + +# Assembler sources. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.s + @$(call printmessage,asm,Assembling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(AS) $(ASFLAGS) $(INCLUDES) -MD $(basename $@).d -o $@ $< + +#------------------------------------------------------------------------ +# Build the tagrget +#------------------------------------------------------------------------ + +# Wrap the link objects in start/end group so that ld re-checks each +# file for dependencies. Otherwise linking static libs can be a pain +# since order matters. +$(MAKE_TARGET): $(OBJECTS_ALL) + @$(call printmessage,link,Linking, $(APP_NAME)) + $(at)$(LD) $(LDFLAGS) \ + $(OBJECTS_ALL) $(LIBS) \ + -lc -lstdc++ -lm -ludev \ + -o $@ + @echo "Output binary:" ; echo " $(APP_NAME)" + +#------------------------------------------------------------------------------- +# Clean +#------------------------------------------------------------------------------- +.PHONY: clean cleanall +cleanall: clean +clean: + $(at)rm -rf $(OBJECTS_ALL) $(OBJECTS_DIRS) $(MAKE_TARGET) $(APP_NAME) + +# Include dependency files. +-include $(OBJECTS_ALL:.o=.d) + diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/Blob.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/Blob.d new file mode 100644 index 0000000..d822928 --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/Blob.d @@ -0,0 +1,6 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/Blob.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/Blob.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/Blob.o b/tools/blhost/gcc/Release/obj/src/blfwk/src/Blob.o new file mode 100644 index 0000000..e40682a Binary files /dev/null and b/tools/blhost/gcc/Release/obj/src/blfwk/src/Blob.o differ diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/Bootloader.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/Bootloader.d new file mode 100644 index 0000000..4c9581b --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/Bootloader.d @@ -0,0 +1,92 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/Bootloader.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/Bootloader.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Bootloader.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Command.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/BusPal.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Packetizer.h \ + /home/ktims/temp/blhost_2.6.7/src/include/bootloader_common.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/bootloader_config.h \ + /home/ktims/temp/blhost_2.6.7/src/bootloader/bl_peripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Progress.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/SourceFile.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataTarget.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StringMatcher.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/OptionContext.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/format_string.h \ + /home/ktims/temp/blhost_2.6.7/src/memory/memory.h \ + /home/ktims/temp/blhost_2.6.7/src/property/property.h \ + /home/ktims/temp/blhost_2.6.7/src/packet/command_packet.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Peripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/LpcUsbSio.h \ + /home/ktims/temp/blhost_2.6.7/src/middleware/libusbsio/inc/lpcusbsio.h \ + /home/ktims/temp/blhost_2.6.7/src/middleware/libusbsio/inc/lpcusbsio_protocol.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/BusPalPeripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/UartPeripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/LpcUsbSioPeripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/SerialPacketizer.h \ + /home/ktims/temp/blhost_2.6.7/src/packet/serial_packet.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/UartPeripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/UsbHidPacketizer.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/UsbHidPeripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/hidapi.h \ + /home/ktims/temp/blhost_2.6.7/src/include/bootloader_hid_report_ids.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/UsbHidPeripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/format_string.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/json.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/utils.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/Bootloader.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Command.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/BusPal.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Packetizer.h: +/home/ktims/temp/blhost_2.6.7/src/include/bootloader_common.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/bootloader_config.h: +/home/ktims/temp/blhost_2.6.7/src/bootloader/bl_peripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Progress.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/SourceFile.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataTarget.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StringMatcher.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/OptionContext.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/format_string.h: +/home/ktims/temp/blhost_2.6.7/src/memory/memory.h: +/home/ktims/temp/blhost_2.6.7/src/property/property.h: +/home/ktims/temp/blhost_2.6.7/src/packet/command_packet.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Peripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/LpcUsbSio.h: +/home/ktims/temp/blhost_2.6.7/src/middleware/libusbsio/inc/lpcusbsio.h: +/home/ktims/temp/blhost_2.6.7/src/middleware/libusbsio/inc/lpcusbsio_protocol.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/BusPalPeripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/UartPeripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/LpcUsbSioPeripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/SerialPacketizer.h: +/home/ktims/temp/blhost_2.6.7/src/packet/serial_packet.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/UartPeripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/UsbHidPacketizer.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/UsbHidPeripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/hidapi.h: +/home/ktims/temp/blhost_2.6.7/src/include/bootloader_hid_report_ids.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/UsbHidPeripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/format_string.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/json.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/utils.h: diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/Bootloader.o b/tools/blhost/gcc/Release/obj/src/blfwk/src/Bootloader.o new file mode 100644 index 0000000..82cda1a Binary files /dev/null and b/tools/blhost/gcc/Release/obj/src/blfwk/src/Bootloader.o differ diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPal.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPal.d new file mode 100644 index 0000000..0263b56 --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPal.d @@ -0,0 +1,10 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPal.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/BusPal.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/BusPal.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/serial.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/BusPal.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/serial.h: diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPal.o b/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPal.o new file mode 100644 index 0000000..8e5d7a1 Binary files /dev/null and b/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPal.o differ diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPalPeripheral.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPalPeripheral.d new file mode 100644 index 0000000..1dd8a01 --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPalPeripheral.d @@ -0,0 +1,28 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPalPeripheral.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/BusPalPeripheral.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/BusPalPeripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/UartPeripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Peripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/BusPal.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/LpcUsbSio.h \ + /home/ktims/temp/blhost_2.6.7/src/middleware/libusbsio/inc/lpcusbsio.h \ + /home/ktims/temp/blhost_2.6.7/src/middleware/libusbsio/inc/lpcusbsio_protocol.h \ + /home/ktims/temp/blhost_2.6.7/src/include/bootloader_common.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/bootloader_config.h \ + /home/ktims/temp/blhost_2.6.7/src/packet/command_packet.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/format_string.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/BusPalPeripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/UartPeripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Peripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/BusPal.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/LpcUsbSio.h: +/home/ktims/temp/blhost_2.6.7/src/middleware/libusbsio/inc/lpcusbsio.h: +/home/ktims/temp/blhost_2.6.7/src/middleware/libusbsio/inc/lpcusbsio_protocol.h: +/home/ktims/temp/blhost_2.6.7/src/include/bootloader_common.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/bootloader_config.h: +/home/ktims/temp/blhost_2.6.7/src/packet/command_packet.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/format_string.h: diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPalPeripheral.o b/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPalPeripheral.o new file mode 100644 index 0000000..b382999 Binary files /dev/null and b/tools/blhost/gcc/Release/obj/src/blfwk/src/BusPalPeripheral.o differ diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/Command.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/Command.d new file mode 100644 index 0000000..363abd0 --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/Command.d @@ -0,0 +1,60 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/Command.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/Command.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Command.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/BusPal.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Packetizer.h \ + /home/ktims/temp/blhost_2.6.7/src/include/bootloader_common.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/bootloader_config.h \ + /home/ktims/temp/blhost_2.6.7/src/bootloader/bl_peripheral.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Progress.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/SourceFile.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataTarget.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StringMatcher.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/OptionContext.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/format_string.h \ + /home/ktims/temp/blhost_2.6.7/src/memory/memory.h \ + /home/ktims/temp/blhost_2.6.7/src/property/property.h \ + /home/ktims/temp/blhost_2.6.7/src/packet/command_packet.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/EndianUtilities.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/json.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/utils.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/Command.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/BusPal.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Packetizer.h: +/home/ktims/temp/blhost_2.6.7/src/include/bootloader_common.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/bootloader_config.h: +/home/ktims/temp/blhost_2.6.7/src/bootloader/bl_peripheral.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Progress.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/host_types.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/SourceFile.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataTarget.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StringMatcher.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/OptionContext.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/format_string.h: +/home/ktims/temp/blhost_2.6.7/src/memory/memory.h: +/home/ktims/temp/blhost_2.6.7/src/property/property.h: +/home/ktims/temp/blhost_2.6.7/src/packet/command_packet.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/EndianUtilities.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/json.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/utils.h: diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/Command.o b/tools/blhost/gcc/Release/obj/src/blfwk/src/Command.o new file mode 100644 index 0000000..eb57972 Binary files /dev/null and b/tools/blhost/gcc/Release/obj/src/blfwk/src/Command.o differ diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSource.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSource.d new file mode 100644 index 0000000..e2a2857 --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSource.d @@ -0,0 +1,22 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSource.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/DataSource.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataTarget.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataTarget.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h: diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSource.o b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSource.o new file mode 100644 index 0000000..92cd527 Binary files /dev/null and b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSource.o differ diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSourceImager.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSourceImager.d new file mode 100644 index 0000000..65f91e0 --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSourceImager.d @@ -0,0 +1,22 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSourceImager.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/DataSourceImager.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataSourceImager.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataSourceImager.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h: diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSourceImager.o b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSourceImager.o new file mode 100644 index 0000000..75ec510 Binary files /dev/null and b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataSourceImager.o differ diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/DataTarget.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataTarget.d new file mode 100644 index 0000000..b8a3950 --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataTarget.d @@ -0,0 +1,24 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/DataTarget.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/DataTarget.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataTarget.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/BlfwkErrors.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataTarget.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/BlfwkErrors.h: diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/DataTarget.o b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataTarget.o new file mode 100644 index 0000000..ee40b37 Binary files /dev/null and b/tools/blhost/gcc/Release/obj/src/blfwk/src/DataTarget.o differ diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/ELFSourceFile.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/ELFSourceFile.d new file mode 100644 index 0000000..9606599 --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/ELFSourceFile.d @@ -0,0 +1,38 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/ELFSourceFile.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/ELFSourceFile.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/ELFSourceFile.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/SourceFile.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/DataTarget.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StringMatcher.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/OptionContext.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StELFFile.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/ELF.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/ELF.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/GHSSecInfo.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/ELFSourceFile.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/SourceFile.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataSource.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Value.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/int_size.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Blob.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StExecutableImage.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/DataTarget.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StringMatcher.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/OptionContext.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StELFFile.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/ELF.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/ELF.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/GHSSecInfo.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h: diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/ELFSourceFile.o b/tools/blhost/gcc/Release/obj/src/blfwk/src/ELFSourceFile.o new file mode 100644 index 0000000..c233bcb Binary files /dev/null and b/tools/blhost/gcc/Release/obj/src/blfwk/src/ELFSourceFile.o differ diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/ExcludesListMatcher.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/ExcludesListMatcher.d new file mode 100644 index 0000000..43fc209 --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/ExcludesListMatcher.d @@ -0,0 +1,8 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/ExcludesListMatcher.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/ExcludesListMatcher.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/ExcludesListMatcher.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/GlobMatcher.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StringMatcher.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/ExcludesListMatcher.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/GlobMatcher.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StringMatcher.h: diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/ExcludesListMatcher.o b/tools/blhost/gcc/Release/obj/src/blfwk/src/ExcludesListMatcher.o new file mode 100644 index 0000000..6b56b48 Binary files /dev/null and b/tools/blhost/gcc/Release/obj/src/blfwk/src/ExcludesListMatcher.o differ diff --git a/tools/blhost/gcc/Release/obj/src/blfwk/src/GHSSecInfo.d b/tools/blhost/gcc/Release/obj/src/blfwk/src/GHSSecInfo.d new file mode 100644 index 0000000..d4e67cd --- /dev/null +++ b/tools/blhost/gcc/Release/obj/src/blfwk/src/GHSSecInfo.d @@ -0,0 +1,16 @@ +/home/ktims/temp/blhost_2.6.7/tools/blhost/gcc/Release/obj/src/blfwk/src/GHSSecInfo.o: \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/src/GHSSecInfo.cpp \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/GHSSecInfo.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/StELFFile.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/ELF.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h \ + /home/ktims/temp/blhost_2.6.7/src/blfwk/EndianUtilities.h +/home/ktims/temp/blhost_2.6.7/src/blfwk/GHSSecInfo.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/StELFFile.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/ELF.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/stdafx.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/smart_ptr.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/Logging.h: +/home/ktims/temp/blhost_2.6.7/src/blfwk/EndianUtilities.h: diff --git a/tools/blhost/mac-gcc/Makefile b/tools/blhost/mac-gcc/Makefile new file mode 100644 index 0000000..f4028b9 --- /dev/null +++ b/tools/blhost/mac-gcc/Makefile @@ -0,0 +1,211 @@ +#----------------------------------------------- +# Make command: +# make build= all +# : debug or release, release by default. +#----------------------------------------------- + +#----------------------------------------------- +# setup variables +# ---------------------------------------------- + +BOOT_ROOT := $(abspath ../../..) +OUTPUT_ROOT := $(abspath ./) + +APP_NAME = blhost + + +#----------------------------------------------- +# Debug or Release +# Release by default +#----------------------------------------------- +build ?= release + +include $(BOOT_ROOT)/mk/common.mk + +#----------------------------------------------- +# Include path. Add the include paths like this: +# INCLUDES += ./include/ +#----------------------------------------------- +INCLUDES += $(BOOT_ROOT)/tools/blhost/src \ + $(BOOT_ROOT)/src \ + $(BOOT_ROOT)/src/include \ + $(BOOT_ROOT)/src/blfwk \ + $(BOOT_ROOT)/src/sbloader \ + $(BOOT_ROOT)/src/bootloader \ + $(BOOT_ROOT)/src/crc \ + $(BOOT_ROOT)/src/packet \ + $(BOOT_ROOT)/src/property \ + $(BOOT_ROOT)/src/drivers/common \ + $(BOOT_ROOT)/src/bm_usb + +CXXFLAGS := -D MACOSX -D BOOTLOADER_HOST -D LPCUSBSIO -std=c++11 +CFLAGS := -std=c99 -D MACOSX -D BOOTLOADER_HOST -D LPCUSBSIO -D _GNU_SOURCE +LD := g++ +LIBS := -framework CoreFoundation -framework IOKit + +SOURCES := $(BOOT_ROOT)/tools/blhost/src/blhost.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Blob.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Bootloader.cpp \ + $(BOOT_ROOT)/src/blfwk/src/BusPal.cpp \ + $(BOOT_ROOT)/src/blfwk/src/BusPalPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Command.cpp \ + $(BOOT_ROOT)/src/blfwk/src/DataSource.cpp \ + $(BOOT_ROOT)/src/blfwk/src/DataSourceImager.cpp \ + $(BOOT_ROOT)/src/blfwk/src/DataTarget.cpp \ + $(BOOT_ROOT)/src/blfwk/src/ELFSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/ExcludesListMatcher.cpp \ + $(BOOT_ROOT)/src/blfwk/src/format_string.cpp \ + $(BOOT_ROOT)/src/blfwk/src/GHSSecInfo.cpp \ + $(BOOT_ROOT)/src/blfwk/src/GlobMatcher.cpp \ + $(BOOT_ROOT)/src/blfwk/src/jsoncpp.cpp \ + $(BOOT_ROOT)/src/blfwk/src/LpcUsbSio.cpp \ + $(BOOT_ROOT)/src/blfwk/src/LpcUsbSioPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Logging.cpp \ + $(BOOT_ROOT)/src/blfwk/src/options.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SBSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SearchPath.cpp \ + $(BOOT_ROOT)/src/blfwk/src/serial.c \ + $(BOOT_ROOT)/src/blfwk/src/SerialPacketizer.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/SRecordSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/IntelHexSourceFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StELFFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StExecutableImage.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StSRecordFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/StIntelHexFile.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Updater.cpp \ + $(BOOT_ROOT)/src/blfwk/src/UartPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/UsbHidPacketizer.cpp \ + $(BOOT_ROOT)/src/blfwk/src/UsbHidPeripheral.cpp \ + $(BOOT_ROOT)/src/blfwk/src/utils.cpp \ + $(BOOT_ROOT)/src/blfwk/src/Value.cpp \ + $(BOOT_ROOT)/src/crc/src/crc16.c \ + $(BOOT_ROOT)/src/crc/src/crc32.c \ + +LIBS += $(BOOT_ROOT)/src/middleware/libusbsio/bin/osx/libusbsio.a + +INCLUDES := $(foreach includes, $(INCLUDES), -I $(includes)) + +ifeq "$(build)" "debug" +DEBUG_OR_RELEASE := Debug +CFLAGS += -g +CXXFLAGS += -g +LDFLAGS += -g +else +DEBUG_OR_RELEASE := Release +endif + +TARGET_OUTPUT_ROOT := $(OUTPUT_ROOT)/$(DEBUG_OR_RELEASE) +MAKE_TARGET := $(TARGET_OUTPUT_ROOT)/$(APP_NAME) + +OBJS_ROOT = $(TARGET_OUTPUT_ROOT)/obj + +# Strip sources. +SOURCES := $(strip $(SOURCES)) + +# Convert sources list to absolute paths and root-relative paths. +SOURCES_ABS := $(foreach s,$(SOURCES),$(abspath $(s))) +SOURCES_REL := $(subst $(BOOT_ROOT)/,,$(SOURCES_ABS)) + +# Get a list of unique directories containing the source files. +SOURCE_DIRS_ABS := $(sort $(foreach f,$(SOURCES_ABS),$(dir $(f)))) +SOURCE_DIRS_REL := $(subst $(BOOT_ROOT)/,,$(SOURCE_DIRS_ABS)) + +OBJECTS_DIRS := $(addprefix $(OBJS_ROOT)/,$(SOURCE_DIRS_REL)) + +# Filter source files list into separate source types. +C_SOURCES = $(filter %.c,$(SOURCES_REL)) +CXX_SOURCES = $(filter %.cpp,$(SOURCES_REL)) +ASM_s_SOURCES = $(filter %.s,$(SOURCES_REL)) +ASM_S_SOURCES = $(filter %.S,$(SOURCES_REL)) + +# Convert sources to objects. +OBJECTS_C := $(addprefix $(OBJS_ROOT)/,$(C_SOURCES:.c=.o)) +OBJECTS_CXX := $(addprefix $(OBJS_ROOT)/,$(CXX_SOURCES:.cpp=.o)) +OBJECTS_ASM := $(addprefix $(OBJS_ROOT)/,$(ASM_s_SOURCES:.s=.o)) +OBJECTS_ASM_S := $(addprefix $(OBJS_ROOT)/,$(ASM_S_SOURCES:.S=.o)) + +# Complete list of all object files. +OBJECTS_ALL := $(sort $(OBJECTS_C) $(OBJECTS_CXX) $(OBJECTS_ASM) $(OBJECTS_ASM_S)) + +#------------------------------------------------------------------------------- +# Default target +#------------------------------------------------------------------------------- + +# Note that prerequisite order is important here. The subdirectories must be built first, or you +# may end up with files in the current directory not getting added to libraries. This would happen +# if subdirs modified the library file after local files were compiled but before they were added +# to the library. +.PHONY: all +all: $(MAKE_TARGET) + +## Recipe to create the output object file directories. +$(OBJECTS_DIRS) : + $(at)mkdir -p $@ + +# Object files depend on the directories where they will be created. +# +# The dirs are made order-only prerequisites (by being listed after the '|') so they won't cause +# the objects to be rebuilt, as the modification date on a directory changes whenver its contents +# change. This would cause the objects to always be rebuilt if the dirs were normal prerequisites. +$(OBJECTS_ALL): | $(OBJECTS_DIRS) + +#------------------------------------------------------------------------------- +# Pattern rules for compilation +#------------------------------------------------------------------------------- +# We cd into the source directory before calling the appropriate compiler. This must be done +# on a single command line since make calls individual recipe lines in separate shells, so +# '&&' is used to chain the commands. +# +# Generate make dependencies while compiling using the -MMD option, which excludes system headers. +# If system headers are included, there are path problems on cygwin. The -MP option creates empty +# targets for each header file so that a rebuild will be forced if the file goes missing, but +# no error will occur. + +# Compile C sources. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.c + @$(call printmessage,c,Compiling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(CC) $(CFLAGS) $(SYSTEM_INC) $(INCLUDES) $(DEFINES) -MMD -MF $(basename $@).d -MP -o $@ -c $< + +# Compile C++ sources. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.cpp + @$(call printmessage,cxx,Compiling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(CXX) $(CXXFLAGS) $(SYSTEM_INC) $(INCLUDES) $(DEFINES) -MMD -MF $(basename $@).d -MP -o $@ -c $< + +# For .S assembly files, first run through the C preprocessor then assemble. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.S + @$(call printmessage,asm,Assembling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(CPP) -D__LANGUAGE_ASM__ $(INCLUDES) $(DEFINES) -o $(basename $@).s $< \ + && $(AS) $(ASFLAGS) $(INCLUDES) -MD $(OBJS_ROOT)/$*.d -o $@ $(basename $@).s + +# Assembler sources. +$(OBJS_ROOT)/%.o: $(BOOT_ROOT)/%.s + @$(call printmessage,asm,Assembling, $(subst $(BOOT_ROOT)/,,$<)) + $(at)$(AS) $(ASFLAGS) $(INCLUDES) -MD $(basename $@).d -o $@ $< + +#------------------------------------------------------------------------ +# Build the tagrget +#------------------------------------------------------------------------ + +# Wrap the link objects in start/end group so that ld re-checks each +# file for dependencies. Otherwise linking static libs can be a pain +# since order matters. +$(MAKE_TARGET): $(OBJECTS_ALL) + @$(call printmessage,link,Linking, $(APP_NAME)) + $(at)$(LD) $(LDFLAGS) $(LIBS) \ + $(OBJECTS_ALL) \ + -lc -lstdc++ -lm \ + -o $@ + @echo "Output binary:" ; echo " $(APP_NAME)" + +#------------------------------------------------------------------------------- +# Clean +#------------------------------------------------------------------------------- +.PHONY: clean cleanall +cleanall: clean +clean: + $(at)rm -rf $(OBJECTS_ALL) $(OBJECTS_DIRS) $(MAKE_TARGET) $(APP_NAME) + +# Include dependency files. +-include $(OBJECTS_ALL:.o=.d) + diff --git a/tools/blhost/src/.gitignore b/tools/blhost/src/.gitignore new file mode 100644 index 0000000..f63ef27 --- /dev/null +++ b/tools/blhost/src/.gitignore @@ -0,0 +1 @@ +Debug diff --git a/tools/blhost/src/blhost.cpp b/tools/blhost/src/blhost.cpp new file mode 100644 index 0000000..16489c4 --- /dev/null +++ b/tools/blhost/src/blhost.cpp @@ -0,0 +1,1098 @@ +/* + * Copyright (c) 2013-2015 Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include "blfwk/Bootloader.h" +#include "blfwk/SerialPacketizer.h" +#include "blfwk/UsbHidPacketizer.h" +#include "blfwk/options.h" +#include "blfwk/utils.h" + +#if defined(WIN32) +#include "windows.h" +#elif defined(LINUX) +#include "signal.h" +#endif + +using namespace blfwk; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//! @brief The tool's name. +const char k_toolName[] = "blhost"; + +//! @brief Current version number for the tool. +const char k_version[] = "2.6.7"; + +//! @brief Copyright string. +const char k_copyright[] = + "Copyright (c) 2013-2015 Freescale Semiconductor, Inc.\n\ +Copyright 2016-2021 NXP\nAll rights reserved."; + +//! @brief Command line option definitions. +static const char *k_optionsDefinition[] = { "?|help", + "v|version", + "p:port [,]", + "i:i2c [,
,]", + "s:spi [,,,,lsb|msb]", + "b:buspal spi[,,,,lsb|msb] | " + "i2c[,
,] | can[,,,] | sai[,]", + "l:lpcusbsio spi[,,,,,] | " + "i2c[,
,]", + "u?usb [[[,]] | []]", + "V|verbose", + "d|debug", + "j|json", + "n|noping", + "t:timeout ", + NULL }; + +//! @brief Usage text. +const char k_optionUsage[] = + "\nOptions:\n\ + -?/--help Show this help\n\ + -v/--version Display tool version\n\ + -p/--port [,] Connect to target over UART. Specify COM port\n\ + and optionally baud rate\n\ + (default=57600)\n\ + If -b, then port is BusPal port\n\ + -i/--i2c [,
,] Connect to target over I2C. Only valid for\n\ + ARM Linux blhost\n\ + name(I2C port), address(7-bit hex), speed(KHz)\n\ + (default=0x10,100)\n\ + -s/--spi [,,,,lsb|msb]\n\ + Connect to target over SPI. Only valid for ARM\n\ + Linux blhost\n\ + name(SPI port), speed(KHz),\n\ + polarity(0=active_high, 1=active_low),\n\ + phase(0=rising_edge, 1=falling_edge),\n\ + \"lsb\" | \"msb\"\n\ + (default=100,1,1,msb)\n\ + -b/--buspal spi[,,,,lsb|msb] |\n\ + i2c[,
,]\n\ + can[,,,]\n\ + sai[,]\n\ + Use SPI or I2C for BusPal<-->Target link\n\ + All parameters between square brackets are\n\ + optional, but preceding parameters must be\n\ + present or marked with a comma.\n\ + (ex. -b spi,1000,0,1) (ex. -b spi,1000,,lsb)\n\ + spi: speed(KHz),\n\ + polarity(0=active_high | 1=active_low),\n\ + phase(0=rising_edge | 1=falling_edge),\n\ + \"lsb\" | \"msb\"\n\ + (default=100,1,1,msb)\n\ + i2c: address(7-bit hex), speed(KHz)\n\ + (default=0x10,100)\n\ + can: speed(0=125K | 1=250K | 2=500K | 4=1M),\n\ + txid (11 bits ID),\n\ + rxid (11 bits ID)\n\ + (default=4,0x321,0x123)\n\ + sai: speed(Hz),\n\ + (default=8000)\n\ + -l/--lpcusbsio spi[,,,,,] |\n\ + i2c[,
,]\n\ + Connect to target over SPI or I2C of LPC USB\n\ + Serial I/O\n\ + spi: GPIO on LPC USB Serial I/O\n\ + used as SPI->SSELn, speed(KHz),\n\ + polarity(0=active_high | 1=active_low),\n\ + phase(0=rising_edge | 1=falling_edge)\n\ + (default=100,1,1)\n\ + i2c: address(7-bit hex), speed(KHz)\n\ + (default=0x10,100)\n\ + -u/--usb [[[,]] | []]\n\ + Connect to target over USB HID device denoted by\n\ + vid/pid (default=0x15a2,0x0073) or device path.\n\ + If -l, then port is LPC USB Serial I/O port\n\ + (default=0x1fc9,0x0009), and is ignored.\n\ + -V/--verbose Print extra detailed log information\n\ + -d/--debug Print really detailed log information\n\ + -j/--json Print output in JSON format to aid automation.\n\ + -n/--noping Skip the initial ping of a serial target\n\ + -t/--timeout Set packet timeout in milliseconds\n\ + (default=5000)\n"; + +//! @brief Trailer usage text that gets appended after the options descriptions. +static const char *usageTrailer = "-- command "; + +//! @brief Memory id list. +const char k_memoryId[] = + "\nMemory ID:\n\ + Internal Memory Device internal memory space\n\ + 0 Internal Memory\n\ + (Default selected memory)\n\ + 16 (0x10) Execute-only region on internal flash\n\ + (Only used for flash-erase-all)\n\ + Mapped External Memory The memories that are remapped to internal space,\n\ + and must be accessed by internal addresses.\n\ + (IDs in this group are only used for flash-erase-all and\n\ + configure-memory, and ignored by write-memory, read-memory,\n\ + flash-erase-region and flash-image(use default 0))\n\ + 1 QuadSPI Memory\n\ + 8 SEMC NOR Memory\n\ + 9 FlexSPI NOR Memory\n\ + 10 (0xa) SPIFI NOR Memory\n\ + Unmapped External Memory Memories which cannot be remapped to internal space,\n\ + and only can be accessed by memories' addresses.\n\ + (Must be specified for all commands with argument)\n\ + 256 (0x100) SEMC NAND Memory\n\ + 257 (0x101) SPI NAND Memory\n\ + 272 (0x110) SPI NOR/EEPROM Memory\n\ + 273 (0x111) I2C NOR/EEPROM Memory\n\ + 288 (0x120) uSDHC SD Memory\n\ + 289 (0x121) uSDHC MMC Memory\n\ +\n\ +** Note that not all memories are supported on all platforms.\n"; + +//! @brief Command usage string. +const char k_commandUsage[] = + "\nCommand:\n\ + reset Reset the chip\n\ + get-property [ | ]\n\ + Return bootloader specific property. \n\ + and are required by some properties.\n\ + = 0, = 0, if not specified.\n\ + and are ignored for the other properties.\n\ + If is over the range supported by the device, bootloader\n\ + will treat as = 0.\n\ +\n\ + 1 Bootloader version\n\ + 2 Available peripherals\n\ + 3 Start of program flash, is required\n\ + 4 Size of program flash, is required\n\ + 5 Size of flash sector, is required\n\ + 6 Blocks in flash array, is required\n\ + 7 Available commands\n\ + 8 Check Status, is required\n\ + 9 Last Error\n\ + 10 Verify Writes flag\n\ + 11 Max supported packet size\n\ + 12 Reserved regions\n\ + 14 Start of RAM, is required\n\ + 15 Size of RAM, is required\n\ + 16 System device identification\n\ + 17 Flash security state\n\ + 18 Unique device identification\n\ + 19 FAC support flag\n\ + 20 FAC segment size\n\ + 21 FAC segment count\n\ + 22 Read margin level of program flash\n\ + 23 QuadSpi initialization status\n\ + 24 Target version\n\ + 25 External memory attrubutes, is required\n\ + 26 Reliable update status\n\ + 27 Flash page size, is required\n\ + 28 Interrupt notifier pin\n\ + 29 FFR key store update option\n\ + 30 Byte write timeout in milliseconds\n\ + set-property \n\ + 10 Verify Writes flag\n\ + 22 Read margin level of program flash\n\ + 28 Interrupt notifier pin\n\ + :\n\ + bit[31] for enablement, 0: disable, 1: enable\n\ + bit[7:0] for GPIO pin index\n\ + bit[15:8] for GPIO port index\n\ + 29 FFR key store update option\n\ + :\n\ + 0 for Keyprovisioning\n\ + 1 for write-memory\n\ + 30 Byte write timeout in milliseconds\n\ + flash-erase-region [memory_id]\n\ + Erase a region of flash according to [memory_id].\n\ + flash-erase-all [memory_id] Erase all flash according to [memory_id],\n\ + excluding protected regions.\n\ + flash-erase-all-unsecure Erase all internal flash, including protected regions\n\ + read-memory [] [memory_id]\n\ + Read memory according to [memory_id] and write to file\n\ + or stdout if no file specified\n\ + write-memory [[,byte_count]| {{}}] [memory_id]\n\ + Write memory according to [memory_id] from file\n\ + or string of hex values,\n\ + e.g. data.bin (writes entire file)\n\ + e.g. data.bin 8 (writes first 8 bytes from file)\n\ + e.g. \"{{11 22 33 44}}\" (w/quotes)\n\ + e.g. {{11223344}} (no spaces)\n\ + fill-memory [word | short | byte]\n\ + Fill memory with pattern; size is\n\ + word (default), short or byte\n\ + receive-sb-file Receive SB file\n\ + execute \n\ + Execute at address with arg and stack pointer\n\ + call Call address with arg\n\ + flash-security-disable Flash Security Disable <8-byte-hex-key>,\n\ + e.g. 0102030405060708\n\ + flash-program-once [LSB | MSB]\n\ + Program Flash Program Once Field \n\ + is 4 or 8-byte-hex according to \n\ + output sequence is specified by LSB(Default) or MSB\n\ + The output sequence of data \"1234\" is \"4,3,2,1\" by default,\n\ + while is \"1,2,3,4\" if MSB is specified\n\ + e.g. 0 4 12345678 MSB\n\ + flash-read-once \n\ + Read Flash Program Once Field\n\ + flash-read-resource