diff --git a/Target/Source/_template/GCC/cpu_comp.c b/Target/Source/_template/GCC/cpu_comp.c new file mode 100644 index 00000000..a391b2e6 --- /dev/null +++ b/Target/Source/_template/GCC/cpu_comp.c @@ -0,0 +1,71 @@ +/************************************************************************************//** +* \file Source/_template/GCC/cpu_comp.c +* \brief Bootloader cpu module source file. +* \ingroup Target__template_cpu_comp +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published by the Free +* Software Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/************************************************************************************//** +* \defgroup Target__template_cpu_comp Compiler specifics of a port +* \brief This module implements the compiler specific parts of a microcontroller +* port. +* \ingroup Target__template +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include "boot.h" /* bootloader generic header */ + + +/************************************************************************************//** +** \brief Disable global interrupts. +** \return none. +** +****************************************************************************************/ +void CpuIrqDisable(void) +{ + /* TODO ##Port Disable the global interrupts. For safety and stability reasons, the + * bootloader does not use interrupts. This function is called to disable the + * generation of interrupt. It is called upon bootloader initialization by CpuInit(). + */ +} /*** end of CpuIrqDisable ***/ + + +/************************************************************************************//** +** \brief Enable global interrupts. +** \return none. +** +****************************************************************************************/ +void CpuIrqEnable(void) +{ + /* TODO ##Port Enable the global interrupts. Not all, but some microcontrollers have + * global interrupts enabled right after reset. In this case the bootloader should + * enable the global interrupts again, right before the user program is started in + * function CpuStartUserProgram(). + */ +} /*** end of CpuIrqEnable ***/ + + +/*********************************** end of cpu_comp.c *********************************/ diff --git a/Target/Source/_template/can.c b/Target/Source/_template/can.c new file mode 100644 index 00000000..8bd3d481 --- /dev/null +++ b/Target/Source/_template/can.c @@ -0,0 +1,273 @@ +/************************************************************************************//** +* \file Source/_template/can.c +* \brief Bootloader CAN communication interface source file. +* \ingroup Target__template_can +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published by the Free +* Software Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/************************************************************************************//** +* \defgroup Target__template_can CAN driver of a port +* \brief This module implements the CAN driver of a microcontroller port. +* \details For the most parts, this driver is already implemented. The only parts that +* need porting are the UART initialization, byte reception and byte +* transmission. +* \ingroup Target__template +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include "boot.h" /* bootloader generic header */ +#if (BOOT_COM_CAN_ENABLE > 0) +/* TODO ##Port Include microcontroller peripheral driver header files here. */ + + +/**************************************************************************************** +* Macro definitions +****************************************************************************************/ +/** \brief Timeout for transmitting a CAN message in milliseconds. */ +#define CAN_MSG_TX_TIMEOUT_MS (50u) + + +/**************************************************************************************** +* Type definitions +****************************************************************************************/ +/** \brief Structure type for grouping CAN bus timing related information. */ +typedef struct t_can_bus_timing +{ + blt_int8u tseg1; /**< CAN time segment 1 */ + blt_int8u tseg2; /**< CAN time segment 2 */ +} tCanBusTiming; + + +/**************************************************************************************** +* Local constant declarations +****************************************************************************************/ +/** \brief CAN bittiming table for dynamically calculating the bittiming settings. + * \details According to the CAN protocol 1 bit-time can be made up of between 8..25 + * time quanta (TQ). The total TQ in a bit is SYNC + TSEG1 + TSEG2 with SYNC + * always being 1. The sample point is (SYNC + TSEG1) / (SYNC + TSEG1 + SEG2) * + * 100%. This array contains possible and valid time quanta configurations with + * a sample point between 68..78%. + */ +static const tCanBusTiming canTiming[] = +{ + /* TQ | TSEG1 | TSEG2 | SP */ + /* ------------------------- */ + { 5, 2 }, /* 8 | 5 | 2 | 75% */ + { 6, 2 }, /* 9 | 6 | 2 | 78% */ + { 6, 3 }, /* 10 | 6 | 3 | 70% */ + { 7, 3 }, /* 11 | 7 | 3 | 73% */ + { 8, 3 }, /* 12 | 8 | 3 | 75% */ + { 9, 3 }, /* 13 | 9 | 3 | 77% */ + { 9, 4 }, /* 14 | 9 | 4 | 71% */ + { 10, 4 }, /* 15 | 10 | 4 | 73% */ + { 11, 4 }, /* 16 | 11 | 4 | 75% */ + { 12, 4 }, /* 17 | 12 | 4 | 76% */ + { 12, 5 }, /* 18 | 12 | 5 | 72% */ + { 13, 5 }, /* 19 | 13 | 5 | 74% */ + { 14, 5 }, /* 20 | 14 | 5 | 75% */ + { 15, 5 }, /* 21 | 15 | 5 | 76% */ + { 15, 6 }, /* 22 | 15 | 6 | 73% */ + { 16, 6 }, /* 23 | 16 | 6 | 74% */ + { 16, 7 }, /* 24 | 16 | 7 | 71% */ + { 16, 8 } /* 25 | 16 | 8 | 68% */ +}; + + +/************************************************************************************//** +** \brief Search algorithm to match the desired baudrate to a possible bus +** timing configuration. +** \param baud The desired baudrate in kbps. Valid values are 10..1000. +** \param prescaler Pointer to where the value for the prescaler will be stored. +** \param tseg1 Pointer to where the value for TSEG2 will be stored. +** \param tseg2 Pointer to where the value for TSEG2 will be stored. +** \return BLT_TRUE if the CAN bustiming register values were found, BLT_FALSE +** otherwise. +** +****************************************************************************************/ +static blt_bool CanGetSpeedConfig(blt_int16u baud, blt_int16u *prescaler, + blt_int8u *tseg1, blt_int8u *tseg2) +{ + blt_int8u cnt; + blt_int32u canClockFreqkHz; + + /* TODO ##Port This helper function assists with getting a compatible bittiming + * configuration, based on the specified 'baud' communication speed on the CAN bus in + * kbps. This function needs two microcontroller specific values: (1) the speed of + * the clock that sources the CAN peripheral and (2) the supported range of the + * prescaler that for scaling down the CAN peripheral clock speed. + */ + + /* TODO ##Port Set the clock speed of the CAN peripheral in kHz. You can used the + * macros BOOT_CPU_XTAL_SPEED_KHZ and BOOT_CPU_SYSTEM_SPEED_KHZ if applicable. + */ + canClockFreqkHz = BOOT_CPU_XTAL_SPEED_KHZ; + + /* loop through all possible time quanta configurations to find a match */ + for (cnt=0; cnt < sizeof(canTiming)/sizeof(canTiming[0]); cnt++) + { + if ((canClockFreqkHz % (baud*(canTiming[cnt].tseg1+canTiming[cnt].tseg2+1))) == 0) + { + /* compute the prescaler that goes with this TQ configuration */ + *prescaler = canClockFreqkHz/(baud*(canTiming[cnt].tseg1+canTiming[cnt].tseg2+1)); + + /* TODO ##Port Update the prescaler range that is supported by the CAN peripheral + * on the microcontroller. The example implementation is for a prescaler that can + * be in the 1 - 1024 range. + */ + /* make sure the prescaler is valid */ + if ((*prescaler > 0) && (*prescaler <= 1024)) + { + /* store the bustiming configuration */ + *tseg1 = canTiming[cnt].tseg1; + *tseg2 = canTiming[cnt].tseg2; + /* found a good bus timing configuration */ + return BLT_TRUE; + } + } + } + /* could not find a good bus timing configuration */ + return BLT_FALSE; +} /*** end of CanGetSpeedConfig ***/ + + +/************************************************************************************//** +** \brief Initializes the CAN controller and synchronizes it to the CAN bus. +** \return none. +** +****************************************************************************************/ +void CanInit(void) +{ + blt_int16u prescaler = 0; + blt_int8u tseg1 = 0, tseg2 = 0; + + /* TODO ##Port Perform compile time assertion to check that the configured CAN channel + * is actually supported by this driver. The example is for a driver where CAN + * channels 0 - 1 are supported. + */ + ASSERT_CT((BOOT_COM_CAN_CHANNEL_INDEX == 0 || BOOT_COM_CAN_CHANNEL_INDEX == 1)); + + /* obtain bittiming configuration information. */ + if (CanGetSpeedConfig(BOOT_COM_CAN_BAUDRATE/1000, &prescaler, &tseg1, &tseg2) == BLT_FALSE) + { + /* Incorrect configuration. The specified baudrate is not supported for the given + * clock configuration. Verify the following settings in blt_conf.h: + * - BOOT_COM_CAN_BAUDRATE + * - BOOT_CPU_XTAL_SPEED_KHZ + * - BOOT_CPU_SYSTEM_SPEED_KHZ + */ + ASSERT_RT(BLT_FALSE); + } + + /* TODO ##Vg Perform the configuration and initialization of the CAN controller. Note + * that the bittiming related values are already stored in 'prescaler, 'tseg1', and + * 'tseg2'. There values are ready to be used. Typically, the following tasks need + * to be performed: + * (1) Place the CAN controller in initialization mode. + * (2) Disable all CAN related interrupts as the bootloader runs in polling mode. + * (3) Configure the bittiming based on: 'prescaler', 'tseg1' and 'tseg2'. It is okay + * to configure 1 time quanta for the synchronization jump width (SWJ). + * (4) Configure one transmit message object. It will be used in CanTransmitPacket() + * to transmit a CAN message with identifier BOOT_COM_CAN_TX_MSG_ID. Note that if + * the 0x80000000 bit is set in this identifier, it means that it is a 29-bit CAN + * identifier instead of an 11-bit. + * (5) Configure at least one reception message object and configure its reception + * acceptance filter such that only the CAN identifier BOOT_COM_CAN_RX_MSG_ID is + * received. Note that if the 0x80000000 bit is set in this identifier, it means + * that it is a 29-bit CAN identifier instead of an 11-bit. + * (6) Leave the initialization mode and place the CAN controller in operational mode. + */ +} /*** end of CanInit ***/ + + +/************************************************************************************//** +** \brief Transmits a packet formatted for the communication interface. +** \param data Pointer to byte array with data that it to be transmitted. +** \param len Number of bytes that are to be transmitted. +** \return none. +** +****************************************************************************************/ +void CanTransmitPacket(blt_int8u *data, blt_int8u len) +{ + blt_int32u timeout; + + /* TODO ##Port Configure the transmit message object for transmitting a CAN message + * with CAN identifier BOOT_COM_CAN_TX_MSG_ID. Note that if the 0x80000000 bit is set + * in this identifier, it means that it is a 29-bit CAN identifier instead of an + * 11-bit. Next, copy the message data to the transmit message object. The number + * of data bytes is in 'len' and the actual data byte values are in array 'data'. + * Once done, start the transmission of the message that was just stored in the + * transmit message object. + */ + + /* TODO ##Port Wait for the message transmission to complete, with timeout though to + * make sure this function doesn't hang in case of an error. This is typically achieved + * by evaluating a transmit complete flag in a register of the transmit message object. + */ + /* determine timeout time for the transmit completion. */ + timeout = TimerGet() + CAN_MSG_TX_TIMEOUT_MS; + /* poll for completion of the transmit operation. */ + while (1 == 0) + { + /* service the watchdog. */ + CopService(); + /* break loop upon timeout. this would indicate a hardware failure or no other + * nodes connected to the bus. + */ + if (TimerGet() > timeout) + { + break; + } + } +} /*** end of CanTransmitPacket ***/ + + +/************************************************************************************//** +** \brief Receives a communication interface packet if one is present. +** \param data Pointer to byte array where the data is to be stored. +** \param len Pointer where the length of the packet is to be stored. +** \return BLT_TRUE is a packet was received, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool CanReceivePacket(blt_int8u *data, blt_int8u *len) +{ + blt_bool result = BLT_FALSE; + + /* TODO ##Port Check for the reception of a new CAN message with identifier + * BOOT_COM_CAN_RX_MSG_ID. Note that if the 0x80000000 bit is set in this identifier, + * it means that it is a 29-bit CAN identifier instead of an 11-bit. + * If a new message with this CAN identifier was received, store the data byte values + * in array 'data' and store the number of data bytes in 'len'. Finally, set 'result' + * to BLT_TRUE to indicate to the caller of this function that a new CAN message was + * received and stored. + */ + + /* give the result back to the caller */ + return result; +} /*** end of CanReceivePacket ***/ +#endif /* BOOT_COM_CAN_ENABLE > 0 */ + + +/*********************************** end of can.c **************************************/ diff --git a/Target/Source/_template/cpu.c b/Target/Source/_template/cpu.c new file mode 100644 index 00000000..1cee1fad --- /dev/null +++ b/Target/Source/_template/cpu.c @@ -0,0 +1,232 @@ +/************************************************************************************//** +* \file Source/_template/cpu.c +* \brief Bootloader cpu module source file. +* \ingroup Target__template_cpu +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published by the Free +* Software Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/************************************************************************************//** +* \defgroup Target__template_cpu CPU driver of a port +* \brief This module implements the CPU driver of a microcontroller port. +* \ingroup Target__template +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include "boot.h" /* bootloader generic header */ + + +/**************************************************************************************** +* Macro definitions +****************************************************************************************/ +/** \brief Pointer to the user program's vector table. */ +#define CPU_USER_PROGRAM_VECTABLE_OFFSET ((blt_addr)NvmGetUserProgBaseAddress()) + + +/**************************************************************************************** +* Hook functions +****************************************************************************************/ +#if (BOOT_CPU_USER_PROGRAM_START_HOOK > 0) +extern blt_bool CpuUserProgramStartHook(void); +#endif + + +/************************************************************************************//** +** \brief Initializes the CPU module. +** \return none. +** +****************************************************************************************/ +void CpuInit(void) +{ + /* bootloader runs in polling mode so disable the global interrupts. this is done for + * safety reasons. if the bootloader was started from a running user program, it could + * be that the user program did not properly disable the interrupt generation of + * peripherals. */ + CpuIrqDisable(); +} /*** end of CpuInit ***/ + + +/************************************************************************************//** +** \brief Starts the user program, if one is present. In this case this function +** does not return. +** \return none. +** +****************************************************************************************/ +void CpuStartUserProgram(void) +{ + void (*pProgResetHandler)(void); + + /* check if a user program is present by verifying the checksum */ + if (NvmVerifyChecksum() == BLT_FALSE) + { +#if (BOOT_COM_DEFERRED_INIT_ENABLE > 0) && (BOOT_COM_ENABLE > 0) + /* bootloader will stay active so perform deferred initialization to make sure + * the communication interface that were not yet initialized are now initialized. + * this is needed to make sure firmware updates via these communication interfaces + * will be possible. + */ + ComDeferredInit(); +#endif + /* not a valid user program so it cannot be started */ + return; + } +#if (BOOT_CPU_USER_PROGRAM_START_HOOK > 0) + /* invoke callback */ + if (CpuUserProgramStartHook() == BLT_FALSE) + { + #if (BOOT_COM_DEFERRED_INIT_ENABLE > 0) && (BOOT_COM_ENABLE > 0) + /* bootloader will stay active so perform deferred initialization to make sure + * the communication interface that were not yet initialized are now initialized. + * this is needed to make sure firmware updates via these communication interfaces + * will be possible. + */ + ComDeferredInit(); + #endif + /* callback requests the user program to not be started */ + return; + } +#endif +#if (BOOT_COM_ENABLE > 0) + /* release the communication interface */ + ComFree(); +#endif + /* reset the timer */ + TimerReset(); + + /* TODO ##Port Prepare to start the user program. This typically consists of remapping + * the base address of the vector table, since the user program is typically moved + * forward to make space for the bootloader itself. + * Some microcontrollers to not support changing the base address of the vector + * table. In this the bootloader would need to reroute all interrupt vectors, except + * the reset vector, to the location in memory where the user program has its vector + * table. This was done in the HCS12 port. + * If the microcontroller does not support remapping the vector table base address in + * flash, it might support remapping it to RAM. In this case you would not only need + * to do the remapping, but also copy the user program's vector table to this area + * in RAM. This was done in the STM32F0 port. + */ + + /* TODO ##Port Enable the global interrupts by calling function CpuIrqEnable(). Note + * that this should only be done if the microcontroller normally has global interrupts + * enabled after a reset event. Otherwise, you can skip this part. + */ + CpuIrqEnable(); + + /* TODO ##Port Start the user program. This is achieved by reading out the address + * of the user program's reset handler from its vector table and jumping to it. + * The following example implementation shows how this is done in case the reset + * handler is located in the first entry of the interrupt vector table and the + * interrupt vector table is at the start of the user program. + * Note that for a lot of ARM Cortex CPUs, the first entry is the stackpointer and the + * second entry is the reset handler. In this case an extra 4 bytes need to be added + * to get to the address of where the reset handler pointer is located. In this case + * the user program should also explicitly initialize the stackpointer as the first + * thing in the reset handler. + */ + + /* set the address where the bootloader needs to jump to. this is the address of + * the 1st entry in the user program's vector table. this address points to the + * user program's reset handler. + */ + pProgResetHandler = (void(*)(void))(*((blt_addr *)NvmGetUserProgBaseAddress())); + /* start the user program by calling its reset interrupt service routine */ + pProgResetHandler(); +#if (BOOT_COM_DEFERRED_INIT_ENABLE > 0) && (BOOT_COM_ENABLE > 0) + /* theoretically, the code never gets here because the user program should now be + * running and the previous function call should not return. In case it did return + * for whatever reason, make sure all communication interfaces are initialized so that + * firmware updates can be started. + */ + ComDeferredInit(); +#endif +} /*** end of CpuStartUserProgram ***/ + + +/************************************************************************************//** +** \brief Copies data from the source to the destination address. +** \param dest Destination address for the data. +** \param src Source address of the data. +** \param len length of the data in bytes. +** \return none. +** +****************************************************************************************/ +void CpuMemCopy(blt_addr dest, blt_addr src, blt_int16u len) +{ + blt_int8u *from, *to; + + /* TODO ##Port Implements similar functionality as the C library's memcpy() function. + * For most ports you can simply leave this function as is. If desired you can optimize + * the implementation, for example by copying 32-bits at a time for 32-bit CPU + * architectures. Alternativly, you could just use memcpy(). + */ + + /* set casted pointers */ + from = (blt_int8u *)src; + to = (blt_int8u *)dest; + + /* copy all bytes from source address to destination address */ + while (len-- > 0) + { + /* store byte value from source to destination */ + *to++ = *from++; + /* keep the watchdog happy */ + CopService(); + } +} /*** end of CpuMemCopy ***/ + + +/************************************************************************************//** +** \brief Sets the bytes at the destination address to the specified value. +** \param dest Destination address for the data. +** \param value Value to write. +** \param len Number of bytes to write. +** \return none. +** +****************************************************************************************/ +void CpuMemSet(blt_addr dest, blt_int8u value, blt_int16u len) +{ + blt_int8u *to; + + /* TODO ##Port Implements similar functionality as the C library's memset() function. + * For most ports you can simply leave this function as is. If desired you can optimize + * the implementation, for example by setting 32-bits at a time for 32-bit CPU + * architectures. Alternativly, you could just use memset(). + */ + + /* set casted pointer */ + to = (blt_int8u *)dest; + + /* set all bytes at the destination address to the specified value */ + while (len-- > 0) + { + /* set byte value */ + *to++ = value; + /* keep the watchdog happy */ + CopService(); + } +} /*** end of CpuMemSet ***/ + + +/*********************************** end of cpu.c **************************************/ diff --git a/Target/Source/_template/flash.c b/Target/Source/_template/flash.c new file mode 100644 index 00000000..da8f0070 --- /dev/null +++ b/Target/Source/_template/flash.c @@ -0,0 +1,867 @@ +/************************************************************************************//** +* \file Source/_template/flash.c +* \brief Bootloader flash driver source file. +* \ingroup Target__template_flash +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published by the Free +* Software Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/************************************************************************************//** +* \defgroup Target__template_flash Flash driver of a port +* \brief This module implements the flash EEPROM memory driver of a microcontroller +* port. +* \details The flash driver manages the actual erase and program operations on the +* flash EEPROM and the signature checksum. +* The signature checksum is a 32-bit value in the user program. It is used as +* a flag to determine if a user program is present or not. +* Newly programmed data is always first buffered in RAM buffers with a size +* of FLASH_WRITE_BLOCK_SIZE. +* This driver manages a second RAM buffer of the same size, called the +* bootBlock. The bootBlock buffers program data that includes the interrupt +* vector table and the 32-bit signature checksum. The signature checksum +* value is written as the last step during a firmware update, hence the need +* for the bootBlock. +* Note that the majority of this flash driver can be used as is. The only +* parts that need to be updated / implemented are: +* * Macros FLASH_WRITE_BLOCK_SIZE and BOOT_FLASH_VECTOR_TABLE_CS_OFFSET. +* * The flashLayout[]-array contents. +* * The functions FlashEraseSectors() and FlashWriteBlock(). +* * The functions FlashWriteChecksum() and FlashVerifyChecksum(). +* \ingroup Target__template +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include "boot.h" /* bootloader generic header */ + + +/**************************************************************************************** +* Macro definitions +****************************************************************************************/ +/** \brief Value for an invalid flash sector. */ +#define FLASH_INVALID_SECTOR (0xff) +/** \brief Value for an invalid flash address. */ +#define FLASH_INVALID_ADDRESS (0xffffffff) +/** \brief Standard size of a flash block for writing. */ +/* TODO ##Port The FLASH_WRITE_BLOCK_SIZE should be at least 512. If for some reason this + * is not large enough, double the size so: 512 -> 1024 -> 2048 -> 4096 etc. + */ +#define FLASH_WRITE_BLOCK_SIZE (512) +/** \brief Total numbers of sectors in array flashLayout[]. */ +#define FLASH_TOTAL_SECTORS (sizeof(flashLayout)/sizeof(flashLayout[0])) +/** \brief End address of the bootloader programmable flash. */ +#define FLASH_END_ADDRESS (flashLayout[FLASH_TOTAL_SECTORS-1].sector_start + \ + flashLayout[FLASH_TOTAL_SECTORS-1].sector_size - 1) +/** \brief Offset into the user program's vector table where the checksum is located. + * For this target it is set to the end of the vector table. Note that the + * value can be overriden in blt_conf.h, because the size of the vector table + * could vary. When changing this value, don't forget to update the location + * of the checksum in the user program accordingly. Otherwise the checksum + * verification will always fail. + */ +#ifndef BOOT_FLASH_VECTOR_TABLE_CS_OFFSET +/* TODO ##Port The bootloader uses a 32-bit checksum signature value to determine if a + * a valid user program is present or not. This checksum value is written by the + * bootloader at the end of a firmware update with function FlashWriteChecksum(). Right + * before a user program is about to be started, function FlashVerifyChecksum() is called + * to verify the presence of a user program. Space must be reserved in the user program + * for the checksum signature value and the bootloader needs to know where this space + * is reserved. It is recommended to place the signature checksum right after the + * user program's vector table. Using this approach it is easy to reserved space for the + * checksum signature in the user program by simply adding one more dummy entry into the + * vector table. This macro should be set to the size of the vector table, which can then + * be used to determine the memory address of the signature checksum. + */ +#define BOOT_FLASH_VECTOR_TABLE_CS_OFFSET (0x188) +#endif + + +/**************************************************************************************** +* Plausibility checks +****************************************************************************************/ +#if (BOOT_FLASH_VECTOR_TABLE_CS_OFFSET >= FLASH_WRITE_BLOCK_SIZE) +#error "BOOT_FLASH_VECTOR_TABLE_CS_OFFSET is set too high. It must be located in the first writable block." +#endif + +#ifndef BOOT_FLASH_CUSTOM_LAYOUT_ENABLE +#define BOOT_FLASH_CUSTOM_LAYOUT_ENABLE (0u) +#endif + + +/**************************************************************************************** +* Type definitions +****************************************************************************************/ +/** \brief Flash sector descriptor type. */ +typedef struct +{ + blt_addr sector_start; /**< sector start address */ + blt_int32u sector_size; /**< sector size in bytes */ + blt_int8u sector_num; /**< sector number */ +} tFlashSector; + +/** \brief Structure type for grouping flash block information. + * \details Programming is done per block of max FLASH_WRITE_BLOCK_SIZE. for this a + * flash block manager is implemented in this driver. this flash block manager + * depends on this flash block info structure. It holds the base address of + * the flash block and the data that should be programmed into the flash + * block. The .base_addr must be a multiple of FLASH_WRITE_BLOCK_SIZE. + */ +typedef struct +{ + blt_addr base_addr; + blt_int8u data[FLASH_WRITE_BLOCK_SIZE]; +} tFlashBlockInfo; + + +/**************************************************************************************** +* Hook functions +****************************************************************************************/ +#if (BOOT_FLASH_CRYPTO_HOOKS_ENABLE > 0) +extern blt_bool FlashCryptoDecryptDataHook(blt_int8u * data, blt_int32u size); +#endif + + +/**************************************************************************************** +* Function prototypes +****************************************************************************************/ +static blt_bool FlashInitBlock(tFlashBlockInfo *block, blt_addr address); +static tFlashBlockInfo *FlashSwitchBlock(tFlashBlockInfo *block, blt_addr base_addr); +static blt_bool FlashAddToBlock(tFlashBlockInfo *block, blt_addr address, + blt_int8u *data, blt_int32u len); +static blt_bool FlashWriteBlock(tFlashBlockInfo *block); +static blt_bool FlashEraseSectors(blt_int8u first_sector, blt_int8u last_sector); +static blt_int8u FlashGetSector(blt_addr address); +static blt_addr FlashGetSectorBaseAddr(blt_int8u sector); +static blt_addr FlashGetSectorSize(blt_int8u sector); + + +/**************************************************************************************** +* Local constant declarations +****************************************************************************************/ +/** \brief If desired, it is possible to set BOOT_FLASH_CUSTOM_LAYOUT_ENABLE to > 0 + * in blt_conf.h and then implement your own version of the flashLayout[] table + * in a source-file with the name flash_layout.c. This way you customize the + * flash memory size reserved for the bootloader, without having to modify + * the flashLayout[] table in this file directly. This file will then include + * flash_layout.c so there is no need to compile it additionally with your + * project. + */ +#if (BOOT_FLASH_CUSTOM_LAYOUT_ENABLE == 0) +/** \brief Array wit the layout of the flash memory. + * \details Also controls what part of the flash memory is reserved for the bootloader. + * If the bootloader size changes, the reserved sectors for the bootloader + * might need adjustment to make sure the bootloader doesn't get overwritten. + */ +static const tFlashSector flashLayout[] = +{ + /* TODO ##Port Update the contents of this array with the erase sector sizes as defined + * in the microcontroller's reference manual. The flash sector erase sizes are + * hardware specific and must therefore match, otherwise erase operations cannot be + * performed properly. + * Besides controlling the flash erase size, this array also controls which sectors + * are reserved for the bootloader and will therefore never be erased. The current + * fictive implementation is for a microcontroller that can only erase flash memory + * in chunks of 16 KB and the first 32 KB are reserved for the bootloader. Its flash + * memory starts at 0x08000000 in the memory map. + */ + /* { 0x08000000, 0x04000, 0}, flash sector 0 - reserved for bootloader */ + /* { 0x08004000, 0x04000, 1}, flash sector 1 - reserved for bootloader */ + { 0x08008000, 0x04000, 2}, /* flash sector 2 - 16kb */ + { 0x0800C000, 0x04000, 3}, /* flash sector 3 - 16kb */ +#if (BOOT_NVM_SIZE_KB > 64) + { 0x08010000, 0x4000, 4}, /* flash sector 4 - 16kb */ + { 0x08014000, 0x4000, 5}, /* flash sector 5 - 16kb */ + { 0x08018000, 0x4000, 6}, /* flash sector 6 - 16kb */ + { 0x0801C000, 0x4000, 7}, /* flash sector 7 - 16kb */ +#endif +#if (BOOT_NVM_SIZE_KB > 128) + { 0x08020000, 0x4000, 8}, /* flash sector 8 - 16kb */ + { 0x08024000, 0x4000, 9}, /* flash sector 9 - 16kb */ + { 0x08028000, 0x4000, 10}, /* flash sector 10 - 16kb */ + { 0x0802C000, 0x4000, 11}, /* flash sector 11 - 16kb */ + { 0x08030000, 0x4000, 12}, /* flash sector 12 - 16kb */ + { 0x08034000, 0x4000, 13}, /* flash sector 13 - 16kb */ + { 0x08038000, 0x4000, 14}, /* flash sector 14 - 16kb */ + { 0x0803C000, 0x4000, 15}, /* flash sector 15 - 16kb */ +#endif +#if (BOOT_NVM_SIZE_KB > 256) +#error "BOOT_NVM_SIZE_KB > 256 is currently not supported." +#endif +}; +#else +#include "flash_layout.c" +#endif /* BOOT_FLASH_CUSTOM_LAYOUT_ENABLE == 0 */ + + +/**************************************************************************************** +* Local data declarations +****************************************************************************************/ +/** \brief Local variable with information about the flash block that is currently + * being operated on. + * \details The smallest amount of flash that can be programmed is + * FLASH_WRITE_BLOCK_SIZE. A flash block manager is implemented in this driver + * and stores info in this variable. Whenever new data should be flashed, it + * is first added to a RAM buffer, which is part of this variable. Whenever + * the RAM buffer, which has the size of a flash block, is full or data needs + * to be written to a different block, the contents of the RAM buffer are + * programmed to flash. The flash block manager requires some software + * overhead, yet results is faster flash programming because data is first + * harvested, ideally until there is enough to program an entire flash block, + * before the flash device is actually operated on. + */ +static tFlashBlockInfo blockInfo; + +/** \brief Local variable with information about the flash boot block. + * \details The first block of the user program holds the vector table, which on the + * STM32 is also the where the checksum is written to. Is it likely that + * the vector table is first flashed and then, at the end of the programming + * sequence, the checksum. This means that this flash block need to be written + * to twice. Normally this is not a problem with flash memory, as long as you + * write the same values to those bytes that are not supposed to be changed + * and the locations where you do write to are still in the erased 0xFF state. + * Unfortunately, writing twice to flash this way, does not work reliably on + * all micros. This is why we need to have an extra block, the bootblock, + * placed under the management of the block manager. This way is it possible + * to implement functionality so that the bootblock is only written to once + * at the end of the programming sequence. + */ +static tFlashBlockInfo bootBlockInfo; + + +/************************************************************************************//** +** \brief Initializes the flash driver. +** \return none. +** +****************************************************************************************/ +void FlashInit(void) +{ + /* init the flash block info structs by setting the address to an invalid address */ + blockInfo.base_addr = FLASH_INVALID_ADDRESS; + bootBlockInfo.base_addr = FLASH_INVALID_ADDRESS; +} /*** end of FlashInit ***/ + + +/************************************************************************************//** +** \brief Reinitializes the flash driver. +** \return none. +** +****************************************************************************************/ +void FlashReinit(void) +{ + /* init the flash block info structs by setting the address to an invalid address */ + blockInfo.base_addr = FLASH_INVALID_ADDRESS; + bootBlockInfo.base_addr = FLASH_INVALID_ADDRESS; +} /*** end of FlashReinit ***/ + + +/************************************************************************************//** +** \brief Writes the data to flash through a flash block manager. Note that this +** function also checks that no data is programmed outside the flash +** memory region, so the bootloader can never be overwritten. +** \param addr Start address. +** \param len Length in bytes. +** \param data Pointer to the data buffer. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool FlashWrite(blt_addr addr, blt_int32u len, blt_int8u *data) +{ + blt_addr base_addr; + + /* validate the len parameter */ + if ((len - 1) > (FLASH_END_ADDRESS - addr)) + { + return BLT_FALSE; + } + + /* make sure the addresses are within the flash device */ + if ((FlashGetSector(addr) == FLASH_INVALID_SECTOR) || \ + (FlashGetSector(addr+len-1) == FLASH_INVALID_SECTOR)) + { + return BLT_FALSE; + } + + /* if this is the bootblock, then let the boot block manager handle it */ + base_addr = (addr/FLASH_WRITE_BLOCK_SIZE)*FLASH_WRITE_BLOCK_SIZE; + if (base_addr == flashLayout[0].sector_start) + { + /* let the boot block manager handle it */ + return FlashAddToBlock(&bootBlockInfo, addr, data, len); + } + /* let the block manager handle it */ + return FlashAddToBlock(&blockInfo, addr, data, len); +} /*** end of FlashWrite ***/ + + +/************************************************************************************//** +** \brief Erases the flash memory. Note that this function also checks that no +** data is erased outside the flash memory region, so the bootloader can +** never be erased. +** \param addr Start address. +** \param len Length in bytes. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool FlashErase(blt_addr addr, blt_int32u len) +{ + blt_int8u first_sector; + blt_int8u last_sector; + + /* validate the len parameter */ + if ((len - 1) > (FLASH_END_ADDRESS - addr)) + { + return BLT_FALSE; + } + + /* obtain the first and last sector number */ + first_sector = FlashGetSector(addr); + last_sector = FlashGetSector(addr+len-1); + /* check them */ + if ((first_sector == FLASH_INVALID_SECTOR) || (last_sector == FLASH_INVALID_SECTOR)) + { + return BLT_FALSE; + } + /* erase the sectors */ + return FlashEraseSectors(first_sector, last_sector); +} /*** end of FlashErase ***/ + + +/************************************************************************************//** +** \brief Writes a checksum of the user program to non-volatile memory. This is +** performed once the entire user program has been programmed. Through +** the checksum, the bootloader can check if the programming session +** was completed, which indicates that a valid user programming is +** present and can be started. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool FlashWriteChecksum(void) +{ + blt_int32u signature_checksum = 0; + + /* TODO ##Port Calculate and write the signature checksum such that it appears at the + * address configured with macro BOOT_FLASH_VECTOR_TABLE_CS_OFFSET. Use the + * FlashWrite() function for the actual write operation. For a typical microcontroller, + * the bootBlock holds the program code that includes the user program's interrupt + * vector table and after which the 32-bit for the signature checksum is reserved. + * + * Note that this means one extra dummy entry must be added at the end of the user + * program's vector table to reserve storage space for the signature checksum value, + * which is then overwritten by this function. + * + * The example here calculates a signature checksum by summing up the first 32-bit + * values in the bootBlock (so the first 7 interrupt vectors) and then taking the + * Two's complement of this sum. You can modify this to anything you like as long as + * the signature checksum is based on program code present in the bootBlock. + */ + + /* first check that the bootblock contains valid data. if not, this means the + * bootblock is not part of the reprogramming this time and therefore no + * new checksum needs to be written + */ + if (bootBlockInfo.base_addr == FLASH_INVALID_ADDRESS) + { + return BLT_TRUE; + } + +#if (BOOT_FLASH_CRYPTO_HOOKS_ENABLE > 0) + /* perform decryption of the bootblock, before calculating the checksum and writing it + * to flash memory. + */ + if (FlashCryptoDecryptDataHook(bootBlockInfo.data, FLASH_WRITE_BLOCK_SIZE) == BLT_FALSE) + { + return BLT_FALSE; + } +#endif + + /* compute the checksum. note that the user program's vectors are not yet written + * to flash but are present in the bootblock data structure at this point. + */ + signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x00])); + signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x04])); + signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x08])); + signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x0C])); + signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x10])); + signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x14])); + signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x18])); + signature_checksum = ~signature_checksum; /* one's complement */ + signature_checksum += 1; /* two's complement */ + + /* write the checksum */ + return FlashWrite(flashLayout[0].sector_start+BOOT_FLASH_VECTOR_TABLE_CS_OFFSET, + sizeof(blt_addr), (blt_int8u *)&signature_checksum); +} /*** end of FlashWriteChecksum ***/ + + +/************************************************************************************//** +** \brief Verifies the checksum, which indicates that a valid user program is +** present and can be started. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool FlashVerifyChecksum(void) +{ + blt_int32u signature_checksum = 0; + + /* TODO ##Port Implement code here that basically does the reverse of + * FlashWriteChecksum(). Just make sure to read the values directory from flash memory + * and NOT from the bootBlock. + * The example implementation reads the first 7 32-bit from the user program flash + * memory and sums them up. The signature checksum written by FlashWriteChecksum() was + * the Two complement's value. This means that if you add the previously written + * signature checksum value to the sum of the first 7 32-bit values, the result is + * a value of 0 in case the signature checksum is valid. + */ + + /* verify the checksum based on how it was written by FlashWriteChecksum(). */ + signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start)); + signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x04)); + signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x08)); + signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x0C)); + signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x10)); + signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x14)); + signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x18)); + /* add the checksum value that was written by FlashWriteChecksum(). Since this was a + * Two complement's value, the resulting value should equal 0. + */ + signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+BOOT_FLASH_VECTOR_TABLE_CS_OFFSET)); + /* sum should add up to an unsigned 32-bit value of 0 */ + if (signature_checksum == 0) + { + /* checksum okay */ + return BLT_TRUE; + } + /* checksum incorrect */ + return BLT_FALSE; +} /*** end of FlashVerifyChecksum ***/ + + +/************************************************************************************//** +** \brief Finalizes the flash driver operations. There could still be data in +** the currently active block that needs to be flashed. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool FlashDone(void) +{ + /* check if there is still data waiting to be programmed in the boot block */ + if (bootBlockInfo.base_addr != FLASH_INVALID_ADDRESS) + { + if (FlashWriteBlock(&bootBlockInfo) == BLT_FALSE) + { + return BLT_FALSE; + } + } + + /* check if there is still data waiting to be programmed */ + if (blockInfo.base_addr != FLASH_INVALID_ADDRESS) + { + if (FlashWriteBlock(&blockInfo) == BLT_FALSE) + { + return BLT_FALSE; + } + } + /* still here so all is okay */ + return BLT_TRUE; +} /*** end of FlashDone ***/ + + +/************************************************************************************//** +** \brief Obtains the base address of the flash memory available to the user program. +** This is basically the first address in the flashLayout table. +** \return Base address. +** +****************************************************************************************/ +blt_addr FlashGetUserProgBaseAddress(void) +{ + return flashLayout[0].sector_start; +} /*** end of FlashGetUserProgBaseAddress ***/ + + +/************************************************************************************//** +** \brief Copies data currently in flash to the block->data and sets the +** base address. +** \param block Pointer to flash block info structure to operate on. +** \param address Base address of the block data. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +static blt_bool FlashInitBlock(tFlashBlockInfo *block, blt_addr address) +{ + /* check address alignment */ + if ((address % FLASH_WRITE_BLOCK_SIZE) != 0) + { + return BLT_FALSE; + } + /* make sure that we are initializing a new block and not the same one */ + if (block->base_addr == address) + { + /* block already initialized, so nothing to do */ + return BLT_TRUE; + } + /* set the base address and copies the current data from flash */ + block->base_addr = address; + CpuMemCopy((blt_addr)block->data, address, FLASH_WRITE_BLOCK_SIZE); + return BLT_TRUE; +} /*** end of FlashInitBlock ***/ + + +/************************************************************************************//** +** \brief Switches blocks by programming the current one and initializing the +** next. +** \param block Pointer to flash block info structure to operate on. +** \param base_addr Base address of the next block. +** \return The pointer of the block info struct that is no being used, or a NULL +** pointer in case of error. +** +****************************************************************************************/ +static tFlashBlockInfo *FlashSwitchBlock(tFlashBlockInfo *block, blt_addr base_addr) +{ + /* check if a switch needs to be made away from the boot block. in this case the boot + * block shouldn't be written yet, because this is done at the end of the programming + * session by FlashDone(), this is right after the checksum was written. + */ + if (block == &bootBlockInfo) + { + /* switch from the boot block to the generic block info structure */ + block = &blockInfo; + } + /* check if a switch back into the bootblock is needed. in this case the generic block + * doesn't need to be written here yet. + */ + else if (base_addr == flashLayout[0].sector_start) + { + /* switch from the generic block to the boot block info structure */ + block = &bootBlockInfo; + base_addr = flashLayout[0].sector_start; + } + else + { + /* need to switch to a new block, so program the current one and init the next */ + if (FlashWriteBlock(block) == BLT_FALSE) + { + return BLT_NULL; + } + } + + /* initialize tne new block when necessary */ + if (FlashInitBlock(block, base_addr) == BLT_FALSE) + { + return BLT_NULL; + } + + /* still here to all is okay */ + return block; +} /*** end of FlashSwitchBlock ***/ + + +/************************************************************************************//** +** \brief Programming is done per block. This function adds data to the block +** that is currently collecting data to be written to flash. If the +** address is outside of the current block, the current block is written +** to flash an a new block is initialized. +** \param block Pointer to flash block info structure to operate on. +** \param address Flash destination address. +** \param data Pointer to the byte array with data. +** \param len Number of bytes to add to the block. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +static blt_bool FlashAddToBlock(tFlashBlockInfo *block, blt_addr address, + blt_int8u *data, blt_int32u len) +{ + blt_addr current_base_addr; + blt_int8u *dst; + blt_int8u *src; + + /* determine the current base address */ + current_base_addr = (address/FLASH_WRITE_BLOCK_SIZE)*FLASH_WRITE_BLOCK_SIZE; + + /* make sure the blockInfo is not uninitialized */ + if (block->base_addr == FLASH_INVALID_ADDRESS) + { + /* initialize the blockInfo struct for the current block */ + if (FlashInitBlock(block, current_base_addr) == BLT_FALSE) + { + return BLT_FALSE; + } + } + + /* check if the new data fits in the current block */ + if (block->base_addr != current_base_addr) + { + /* need to switch to a new block, so program the current one and init the next */ + block = FlashSwitchBlock(block, current_base_addr); + if (block == BLT_NULL) + { + return BLT_FALSE; + } + } + + /* add the data to the current block, but check for block overflow */ + dst = &(block->data[address - block->base_addr]); + src = data; + do + { + /* keep the watchdog happy */ + CopService(); + /* buffer overflow? */ + if ((blt_addr)(dst-&(block->data[0])) >= FLASH_WRITE_BLOCK_SIZE) + { + /* need to switch to a new block, so program the current one and init the next */ + block = FlashSwitchBlock(block, current_base_addr+FLASH_WRITE_BLOCK_SIZE); + if (block == BLT_NULL) + { + return BLT_FALSE; + } + /* reset destination pointer */ + dst = &(block->data[0]); + } + /* write the data to the buffer */ + *dst = *src; + /* update pointers */ + dst++; + src++; + /* decrement byte counter */ + len--; + } + while (len > 0); + /* still here so all is good */ + return BLT_TRUE; +} /*** end of FlashAddToBlock ***/ + + +/************************************************************************************//** +** \brief Programs FLASH_WRITE_BLOCK_SIZE bytes to flash from the block->data +** array. +** \param block Pointer to flash block info structure to operate on. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +static blt_bool FlashWriteBlock(tFlashBlockInfo *block) +{ + blt_addr prog_addr; + blt_int32u prog_data; + blt_int32u word_cnt; + blt_bool result = BLT_TRUE; + +#if (BOOT_FLASH_CRYPTO_HOOKS_ENABLE > 0) + #if (BOOT_NVM_CHECKSUM_HOOKS_ENABLE == 0) + /* note that the bootblock is already decrypted in FlashWriteChecksum(), if the + * internal checksum mechanism is used. Therefore don't decrypt it again. + */ + if (block != &bootBlockInfo) + #endif + { + /* perform decryption of the program data before writing it to flash memory. */ + if (FlashCryptoDecryptDataHook(block->data, FLASH_WRITE_BLOCK_SIZE) == BLT_FALSE) + { + return BLT_FALSE; + } + } +#endif + + /* TODO ##Port Program the data contents in 'block' to flash memory here and read the + * programmed data values back directory from flash memory to verify that the flash + * program operation was successful. The example implementation assumes that flash + * data can be written 32-bits at a time. + */ + + /* program all words in the block one by one */ + for (word_cnt=0; word_cnt<(FLASH_WRITE_BLOCK_SIZE/sizeof(blt_int32u)); word_cnt++) + { + prog_addr = block->base_addr + (word_cnt * sizeof(blt_int32u)); + prog_data = *(volatile blt_int32u *)(&block->data[word_cnt * sizeof(blt_int32u)]); + /* keep the watchdog happy */ + CopService(); + /* TODO ##Port Program 32-bit 'prog_data' data value to memory address 'prog_addr'. + * In case an error occured, set result to BLT_FALSE and break the loop. + */ + if (1 == 0) + { + result = BLT_FALSE; + break; + } + /* verify that the written data is actually there */ + if (*(volatile blt_int32u *)prog_addr != prog_data) + { + /* TODO ##Port Uncomment the following two lines again. It was commented out so + * that a dry run with the flash driver is possible without it reporting errors. + */ + /*result = BLT_FALSE;*/ + /*break;*/ + } + } + + /* Give the result back to the caller. */ + return result; +} /*** end of FlashWriteBlock ***/ + + +/************************************************************************************//** +** \brief Erases the flash sectors from first_sector up until last_sector. +** \param first_sector First flash sector number. +** \param last_sector Last flash sector number. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +static blt_bool FlashEraseSectors(blt_int8u first_sector, blt_int8u last_sector) +{ + blt_bool result = BLT_TRUE; + blt_int8u sectorIdx; + blt_addr sectorBaseAddr; + blt_int32u sectorSize; + + /* validate the sector numbers */ + if (first_sector > last_sector) + { + result = BLT_FALSE; + } + if ((first_sector < flashLayout[0].sector_num) || \ + (last_sector > flashLayout[FLASH_TOTAL_SECTORS-1].sector_num)) + { + result = BLT_FALSE; + } + + /* only move forward with the erase operation if all is okay so far */ + if (result == BLT_TRUE) + { + /* erase all sectors one by one */ + for (sectorIdx=first_sector; sectorIdx<= last_sector; sectorIdx++) + { + /* keep the watchdog happy */ + CopService(); + /* get information about the sector */ + sectorBaseAddr = FlashGetSectorBaseAddr(sectorIdx); + sectorSize = FlashGetSectorSize(sectorIdx); + /* validate the sector information */ + if ( (sectorBaseAddr == FLASH_INVALID_ADDRESS) || (sectorSize == 0) ) + { + /* invalid sector information. flag error and abort erase operation */ + result = BLT_FALSE; + break; + } + + /* TODO ##Port Perform the flash erase operation of a sector that starts at + * 'sectorBaseAddr' and has a length of 'sectorSize' bytes. In case an error + * occured, set result to BLT_FALSE and break the loop. + */ + if(1 == 0) + { + /* could not perform erase operation */ + result = BLT_FALSE; + /* error detected so don't bother continuing with the loop */ + break; + } + } + } + + /* give the result back to the caller */ + return result; +} /*** end of FlashEraseSectors ***/ + + +/************************************************************************************//** +** \brief Determines the flash sector the address is in. +** \param address Address in the flash sector. +** \return Flash sector number or FLASH_INVALID_SECTOR. +** +****************************************************************************************/ +static blt_int8u FlashGetSector(blt_addr address) +{ + blt_int8u result = FLASH_INVALID_SECTOR; + blt_int8u sectorIdx; + + /* search through the sectors to find the right one */ + for (sectorIdx = 0; sectorIdx < FLASH_TOTAL_SECTORS; sectorIdx++) + { + /* keep the watchdog happy */ + CopService(); + /* is the address in this sector? */ + if ((address >= flashLayout[sectorIdx].sector_start) && \ + (address < (flashLayout[sectorIdx].sector_start + \ + flashLayout[sectorIdx].sector_size))) + { + /* found the sector we are looking for so store it */ + result = flashLayout[sectorIdx].sector_num; + /* all done so no need to continue looping */ + break; + } + } + /* give the result back to the caller */ + return result; +} /*** end of FlashGetSector ***/ + + +/************************************************************************************//** +** \brief Determines the flash sector base address. +** \param sector Sector to get the base address of. +** \return Flash sector base address or FLASH_INVALID_ADDRESS. +** +****************************************************************************************/ +static blt_addr FlashGetSectorBaseAddr(blt_int8u sector) +{ + blt_int8u sectorIdx; + + /* search through the sectors to find the right one */ + for (sectorIdx = 0; sectorIdx < FLASH_TOTAL_SECTORS; sectorIdx++) + { + /* keep the watchdog happy */ + CopService(); + if (flashLayout[sectorIdx].sector_num == sector) + { + return flashLayout[sectorIdx].sector_start; + } + } + /* still here so no valid sector found */ + return FLASH_INVALID_ADDRESS; +} /*** end of FlashGetSectorBaseAddr ***/ + + +/************************************************************************************//** +** \brief Determines the flash sector size. +** \param sector Sector to get the size of. +** \return Flash sector size or 0. +** +****************************************************************************************/ +static blt_addr FlashGetSectorSize(blt_int8u sector) +{ + blt_int8u sectorIdx; + + /* search through the sectors to find the right one */ + for (sectorIdx = 0; sectorIdx < FLASH_TOTAL_SECTORS; sectorIdx++) + { + /* keep the watchdog happy */ + CopService(); + if (flashLayout[sectorIdx].sector_num == sector) + { + return flashLayout[sectorIdx].sector_size; + } + } + /* still here so no valid sector found */ + return 0; +} /*** end of FlashGetSectorSize ***/ + + +/*********************************** end of flash.c ************************************/ diff --git a/Target/Source/_template/flash.h b/Target/Source/_template/flash.h new file mode 100644 index 00000000..dea3a9f4 --- /dev/null +++ b/Target/Source/_template/flash.h @@ -0,0 +1,45 @@ +/************************************************************************************//** +* \file Source/_template/flash.h +* \brief Bootloader flash driver header file. +* \ingroup Target__template_flash +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published by the Free +* Software Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ +#ifndef FLASH_H +#define FLASH_H + +/**************************************************************************************** +* Function prototypes +****************************************************************************************/ +void FlashInit(void); +void FlashReinit(void); +blt_bool FlashWrite(blt_addr addr, blt_int32u len, blt_int8u *data); +blt_bool FlashErase(blt_addr addr, blt_int32u len); +blt_bool FlashWriteChecksum(void); +blt_bool FlashVerifyChecksum(void); +blt_bool FlashDone(void); +blt_addr FlashGetUserProgBaseAddress(void); + + +#endif /* FLASH_H */ +/*********************************** end of flash.h ************************************/ diff --git a/Target/Source/_template/nvm.c b/Target/Source/_template/nvm.c new file mode 100644 index 00000000..3d5e7ea1 --- /dev/null +++ b/Target/Source/_template/nvm.c @@ -0,0 +1,257 @@ +/************************************************************************************//** +* \file Source/_template/nvm.c +* \brief Bootloader non-volatile memory driver source file. +* \ingroup Target__template_nvm +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published by the Free +* Software Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/************************************************************************************//** +* \defgroup Target__template_nvm Non-volatile memory driver of a port +* \brief This module implements the non-volatile memory driver of a microcontroller +* port. Note that the default implementation if for a microcontroller that +* has internal flash memory. At the time of this writing pretty much all +* microcontrollers use flash EEPROM as non-volatile memory to store the +* program code. Assuming that this is also the case for the microcontroller +* for which the port is developed, nothing needs to be modified in this +* source file. +* \ingroup Target__template +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include "boot.h" /* bootloader generic header */ +#include "flash.h" + + +/**************************************************************************************** +* Hook functions +****************************************************************************************/ +#if (BOOT_NVM_HOOKS_ENABLE > 0) +extern void NvmInitHook(void); +extern void NvmReinitHook(void); +extern blt_int8u NvmWriteHook(blt_addr addr, blt_int32u len, blt_int8u *data); +extern blt_int8u NvmEraseHook(blt_addr addr, blt_int32u len); +extern blt_bool NvmDoneHook(void); +#endif + +#if (BOOT_NVM_CHECKSUM_HOOKS_ENABLE > 0) +extern blt_bool NvmWriteChecksumHook(void); +extern blt_bool NvmVerifyChecksumHook(void); +#endif + + +/************************************************************************************//** +** \brief Initializes the NVM driver. +** \return none. +** +****************************************************************************************/ +void NvmInit(void) +{ +#if (BOOT_NVM_HOOKS_ENABLE > 0) + /* give the application a chance to initialize a driver for operating on NVM + * that is not by default supported by this driver. + */ + NvmInitHook(); +#endif + + /* init the internal driver */ + FlashInit(); +} /*** end of NvmInit ***/ + + +/************************************************************************************//** +** \brief Reinitializes the NVM driver. This function is called at the start of each +** firmware update as opposed to NvmInit, which is only called once during +** power on. +** \return none. +** +****************************************************************************************/ +void NvmReinit(void) +{ +#if (BOOT_NVM_HOOKS_ENABLE > 0) + /* give the application a chance to re-initialize a driver for operating on NVM + * that is not by default supported by this driver. + */ + NvmReinitHook(); +#endif + + /* reinitialize the internal driver */ + FlashReinit(); +} /*** end of NvmReinit ***/ + + +/************************************************************************************//** +** \brief Programs the non-volatile memory. +** \param addr Start address. +** \param len Length in bytes. +** \param data Pointer to the data buffer. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool NvmWrite(blt_addr addr, blt_int32u len, blt_int8u *data) +{ +#if (BOOT_NVM_HOOKS_ENABLE > 0) + blt_int8u result = BLT_NVM_NOT_IN_RANGE; +#endif + +#if (BOOT_NVM_HOOKS_ENABLE > 0) + /* give the application a chance to operate on memory that is not by default supported + * by this driver. + */ + result = NvmWriteHook(addr, len, data); + + /* process the return code */ + if (result == BLT_NVM_OKAY) + { + /* data was within range of the additionally supported memory and succesfully + * programmed, so we are all done. + */ + return BLT_TRUE; + } + else if (result == BLT_NVM_ERROR) + { + /* data was within range of the additionally supported memory and attempted to be + * programmed, but an error occurred, so we can't continue. + */ + return BLT_FALSE; + } +#endif + + /* still here so the internal driver should try and perform the program operation */ + return FlashWrite(addr, len, data); +} /*** end of NvmWrite ***/ + + +/************************************************************************************//** +** \brief Erases the non-volatile memory. +** \param addr Start address. +** \param len Length in bytes. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool NvmErase(blt_addr addr, blt_int32u len) +{ +#if (BOOT_NVM_HOOKS_ENABLE > 0) + blt_int8u result = BLT_NVM_NOT_IN_RANGE; +#endif + +#if (BOOT_NVM_HOOKS_ENABLE > 0) + /* give the application a chance to operate on memory that is not by default supported + * by this driver. + */ + result = NvmEraseHook(addr, len); + + /* process the return code */ + if (result == BLT_NVM_OKAY) + { + /* address was within range of the additionally supported memory and succesfully + * erased, so we are all done. + */ + return BLT_TRUE; + } + else if (result == BLT_NVM_ERROR) + { + /* address was within range of the additionally supported memory and attempted to be + * erased, but an error occurred, so we can't continue. + */ + return BLT_FALSE; + } +#endif + + /* still here so the internal driver should try and perform the erase operation */ + return FlashErase(addr, len); +} /*** end of NvmErase ***/ + + +/************************************************************************************//** +** \brief Verifies the checksum, which indicates that a valid user program is +** present and can be started. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool NvmVerifyChecksum(void) +{ +#if (BOOT_NVM_CHECKSUM_HOOKS_ENABLE > 0) + /* check checksum using the application specific method. */ + return NvmVerifyChecksumHook(); +#else + /* check checksum using the interally supported method. */ + return FlashVerifyChecksum(); +#endif +} /*** end of NvmVerifyChecksum ***/ + + +/************************************************************************************//** +** \brief Obtains the base address of the non-volatile memory available to the user +** program. This is typically that start of the vector table. +** \return Base address. +** +****************************************************************************************/ +blt_addr NvmGetUserProgBaseAddress(void) +{ + return FlashGetUserProgBaseAddress(); +} /*** end of NvmGetUserProgBaseAddress ***/ + + +/************************************************************************************//** +** \brief Once all erase and programming operations are completed, this +** function is called, so at the end of the programming session and +** right before a software reset is performed. It is used to calculate +** a checksum and program this into flash. This checksum is later used +** to determine if a valid user program is present in flash. +** \return BLT_TRUE if successful, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool NvmDone(void) +{ +#if (BOOT_NVM_HOOKS_ENABLE > 0) + /* give the application's NVM driver a chance to finish up */ + if (NvmDoneHook() == BLT_FALSE) + { + /* error so no need to continue */ + return BLT_FALSE; + } +#endif + +#if (BOOT_NVM_CHECKSUM_HOOKS_ENABLE > 0) + /* compute and write checksum, using the application specific method. */ + if (NvmWriteChecksumHook() == BLT_FALSE) + { + return BLT_FALSE; + } +#else + /* compute and write checksum, which is programmed by the internal driver. */ + if (FlashWriteChecksum() == BLT_FALSE) + { + return BLT_FALSE; + } +#endif + + /* finish up internal driver operations */ + return FlashDone(); +} /*** end of NvmDone ***/ + + +/*********************************** end of nvm.c **************************************/ diff --git a/Target/Source/_template/target.dox b/Target/Source/_template/target.dox new file mode 100644 index 00000000..f3ffaba8 --- /dev/null +++ b/Target/Source/_template/target.dox @@ -0,0 +1,12 @@ +/** +\defgroup Target__template Target Port Template +\ingroup Ports +\brief Target dependent code as a template for new microcontroller ports. +\details This module serves as a template to implement the bootloader's target + dependent part for a specific microcontroller family. It can be copied and + used as a foundation when developing new microcontroller ports. Note that + the parts of a port that need to be implemented are described as a source + code comment that starts with "TODO ##Port". +*/ + + diff --git a/Target/Source/_template/timer.c b/Target/Source/_template/timer.c new file mode 100644 index 00000000..4843f234 --- /dev/null +++ b/Target/Source/_template/timer.c @@ -0,0 +1,125 @@ +/************************************************************************************//** +* \file Source/_template/timer.c +* \brief Bootloader timer driver source file. +* \ingroup Target__template_timer +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published by the Free +* Software Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/************************************************************************************//** +* \defgroup Target__template_timer Timer driver of a port +* \brief This module implements the timer memory driver of a microcontroller port. +* \details The timer driver implements a polling based 1 millisecond timer. It +* provides the time base for all timing related parts of the bootloader. The +* bootloader calls the function TimerUpdate() continuously to check if the +* next millisecond period passed. +* \ingroup Target__template +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include "boot.h" /* bootloader generic header */ + + +/**************************************************************************************** +* Local data declarations +****************************************************************************************/ +/** \brief Local variable for storing the number of milliseconds that have elapsed since + * startup. + */ +static blt_int32u millisecond_counter; + + +/************************************************************************************//** +** \brief Initializes the polling based millisecond timer driver. +** \return none. +** +****************************************************************************************/ +void TimerInit(void) +{ + /* Reset the timer configuration. */ + TimerReset(); + + /* TODO ##Port Configure a timer peripheral such that 1 millisecond events can be + * detected. Note that the bootloader does not use interrupts, so this driver should + * also not generate timer related interrupts. + */ + + /* Reset the millisecond counter value. */ + millisecond_counter = 0; +} /*** end of TimerInit ***/ + + +/************************************************************************************//** +** \brief Reset the timer by placing the timer back into it's default reset +** configuration. +** \return none. +** +****************************************************************************************/ +void TimerReset(void) +{ + /* TODO ##Port Set the timer peripheral back into the default reset value. */ +} /* end of TimerReset */ + + +/************************************************************************************//** +** \brief Updates the millisecond timer. +** \return none. +** +****************************************************************************************/ +void TimerUpdate(void) +{ + /* TODO ##Port Check with the timer peripheral if the 1 millisecond event occured. This + * is typically done by looking at a flag bit this is set by the timer peripheral. An + * alternative solution would use the timer peripheral's free running counter. Just + * keep in mind that with the latter case, you would have to store the free running + * counter value of the last time the millisecond event occured. This you can compare + * it with the current value of the free running counter to determine if a millisecond + * passed. + */ + if (1 == 0) + { + /* Increment the millisecond counter. */ + millisecond_counter++; + } +} /*** end of TimerUpdate ***/ + + +/************************************************************************************//** +** \brief Obtains the counter value of the millisecond timer. +** \return Current value of the millisecond timer. +** +****************************************************************************************/ +blt_int32u TimerGet(void) +{ + /* Updating timer here allows this function to be called in a loop with timeout + * detection. + */ + TimerUpdate(); + /* Read and return the amount of milliseconds that passed since initialization. */ + return millisecond_counter; +} /*** end of TimerGet ***/ + + +/*********************************** end of timer.c ************************************/ diff --git a/Target/Source/_template/types.h b/Target/Source/_template/types.h new file mode 100644 index 00000000..eabe0091 --- /dev/null +++ b/Target/Source/_template/types.h @@ -0,0 +1,68 @@ +/************************************************************************************//** +* \file Source/_template/types.h +* \brief Bootloader types header file. +* \ingroup Target__template_types +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published by the Free +* Software Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ +#ifndef TYPES_H +#define TYPES_H + +/************************************************************************************//** +* \defgroup Target__template_types Type definitions of a port +* \brief This module implements the variable type definitions of a microcontroller +* port. +* \ingroup Target__template +****************************************************************************************/ + +/**************************************************************************************** +* Macro definitions +****************************************************************************************/ +/** \brief Boolean true value. */ +#define BLT_TRUE (1) +/** \brief Boolean false value. */ +#define BLT_FALSE (0) +/** \brief NULL pointer value. */ +#define BLT_NULL ((void *)0) + + +/**************************************************************************************** +* Type definitions +****************************************************************************************/ +/* TODO ##Port Adjust the type definitions such that they match the microcontroller + * architecture for which this port is being developed. These type definitions are quite + * generic and can typically be left as is. + */ +typedef unsigned char blt_bool; /**< boolean type */ +typedef char blt_char; /**< character type */ +typedef unsigned long blt_addr; /**< memory address type */ +typedef unsigned char blt_int8u; /**< 8-bit unsigned integer */ +typedef signed char blt_int8s; /**< 8-bit signed integer */ +typedef unsigned short blt_int16u; /**< 16-bit unsigned integer */ +typedef signed short blt_int16s; /**< 16-bit signed integer */ +typedef unsigned int blt_int32u; /**< 32-bit unsigned integer */ +typedef signed int blt_int32s; /**< 32-bit signed integer */ + + +#endif /* TYPES_H */ +/*********************************** end of types.h ************************************/ diff --git a/Target/Source/_template/uart.c b/Target/Source/_template/uart.c new file mode 100644 index 00000000..a7f25f0a --- /dev/null +++ b/Target/Source/_template/uart.c @@ -0,0 +1,253 @@ +/************************************************************************************//** +* \file Source/_template/uart.c +* \brief Bootloader UART communication interface source file. +* \ingroup Target__template_uart +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published by the Free +* Software Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/************************************************************************************//** +* \defgroup Target__template_uart RS232 UART driver of a port +* \brief This module implements the RS232 UART driver of a microcontroller port. +* \details For the most parts, this driver is already implemented. The only parts that +* need porting are the UART initialization, byte reception and byte +* transmission. +* \ingroup Target__template +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include "boot.h" /* bootloader generic header */ +#if (BOOT_COM_UART_ENABLE > 0) +/* TODO ##Port Include microcontroller peripheral driver header files here. */ + + +/**************************************************************************************** +* Macro definitions +****************************************************************************************/ +/** \brief Timeout time for the reception of a CTO packet. The timer is started upon + * reception of the first packet byte. + */ +#define UART_CTO_RX_PACKET_TIMEOUT_MS (100u) +/** \brief Timeout for transmitting a byte in milliseconds. */ +#define UART_BYTE_TX_TIMEOUT_MS (10u) + + +/**************************************************************************************** +* Function prototypes +****************************************************************************************/ +static blt_bool UartReceiveByte(blt_int8u *data); +static void UartTransmitByte(blt_int8u data); + + +/************************************************************************************//** +** \brief Initializes the UART communication interface. +** \return none. +** +****************************************************************************************/ +void UartInit(void) +{ + /* TODO ##Port Perform compile time assertion to check that the configured UART channel + * is actually supported by this driver. The example is for a driver where UART + * channels 0 - 2 are supported. + */ + ASSERT_CT((BOOT_COM_UART_CHANNEL_INDEX == 0) || + (BOOT_COM_UART_CHANNEL_INDEX == 1) || + (BOOT_COM_UART_CHANNEL_INDEX == 2)); + + /* TODO ##Port Configure and initialize the UART peripheral for the configured UART + * channel. The communication speed should be set to the value configured with + * BOOT_COM_UART_BAUDRATE. Further communication settings are: 8 databits, no parity, + * and 1 stopbit. Keep in mind that the bootloader runs in polling mode so without + * interrupts. For this reason make sure not to configure the UART peripheral for + * interrupt driven operation. + */ +} /*** end of UartInit ***/ + + +/************************************************************************************//** +** \brief Transmits a packet formatted for the communication interface. +** \param data Pointer to byte array with data that it to be transmitted. +** \param len Number of bytes that are to be transmitted. +** \return none. +** +****************************************************************************************/ +void UartTransmitPacket(blt_int8u *data, blt_int8u len) +{ + blt_int16u data_index; + + /* verify validity of the len-paramenter */ + ASSERT_RT(len <= BOOT_COM_UART_TX_MAX_DATA); + + /* first transmit the length of the packet */ + UartTransmitByte(len); + + /* transmit all the packet bytes one-by-one */ + for (data_index = 0; data_index < len; data_index++) + { + /* keep the watchdog happy */ + CopService(); + /* write byte */ + UartTransmitByte(data[data_index]); + } +} /*** end of UartTransmitPacket ***/ + + +/************************************************************************************//** +** \brief Receives a communication interface packet if one is present. +** \param data Pointer to byte array where the data is to be stored. +** \param len Pointer where the length of the packet is to be stored. +** \return BLT_TRUE if a packet was received, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool UartReceivePacket(blt_int8u *data, blt_int8u *len) +{ + static blt_int8u xcpCtoReqPacket[BOOT_COM_UART_RX_MAX_DATA+1]; /* one extra for length */ + static blt_int8u xcpCtoRxLength; + static blt_bool xcpCtoRxInProgress = BLT_FALSE; + static blt_int32u xcpCtoRxStartTime = 0; + + /* start of cto packet received? */ + if (xcpCtoRxInProgress == BLT_FALSE) + { + /* store the message length when received */ + if (UartReceiveByte(&xcpCtoReqPacket[0]) == BLT_TRUE) + { + if ( (xcpCtoReqPacket[0] > 0) && + (xcpCtoReqPacket[0] <= BOOT_COM_UART_RX_MAX_DATA) ) + { + /* store the start time */ + xcpCtoRxStartTime = TimerGet(); + /* reset packet data count */ + xcpCtoRxLength = 0; + /* indicate that a cto packet is being received */ + xcpCtoRxInProgress = BLT_TRUE; + } + } + } + else + { + /* store the next packet byte */ + if (UartReceiveByte(&xcpCtoReqPacket[xcpCtoRxLength+1]) == BLT_TRUE) + { + /* increment the packet data count */ + xcpCtoRxLength++; + + /* check to see if the entire packet was received */ + if (xcpCtoRxLength == xcpCtoReqPacket[0]) + { + /* copy the packet data */ + CpuMemCopy((blt_int32u)data, (blt_int32u)&xcpCtoReqPacket[1], xcpCtoRxLength); + /* done with cto packet reception */ + xcpCtoRxInProgress = BLT_FALSE; + /* set the packet length */ + *len = xcpCtoRxLength; + /* packet reception complete */ + return BLT_TRUE; + } + } + else + { + /* check packet reception timeout */ + if (TimerGet() > (xcpCtoRxStartTime + UART_CTO_RX_PACKET_TIMEOUT_MS)) + { + /* cancel cto packet reception due to timeout. note that that automaticaly + * discards the already received packet bytes, allowing the host to retry. + */ + xcpCtoRxInProgress = BLT_FALSE; + } + } + } + /* packet reception not yet complete */ + return BLT_FALSE; +} /*** end of UartReceivePacket ***/ + + +/************************************************************************************//** +** \brief Receives a communication interface byte if one is present. +** \param data Pointer to byte where the data is to be stored. +** \return BLT_TRUE if a byte was received, BLT_FALSE otherwise. +** +****************************************************************************************/ +static blt_bool UartReceiveByte(blt_int8u *data) +{ + blt_bool result = BLT_FALSE; + + /* TODO ##Port Check if a new byte was received on the configured channel. This is + * typically done by checking the reception register not empty flag. If a new byte + * was received, read it out and store it in '*data'. Next, clear the reception flag + * such that a new byte can be received again. Finally, set 'result' to BLT_TRUE to + * indicate to the caller of this function that a new byte was received and stored. + */ + if (1 == 0) + { + /* retrieve and store the newly received byte */ + *data = 0; + /* update the result */ + result = BLT_TRUE; + } + + /* give the result back to the caller */ + return result; +} /*** end of UartReceiveByte ***/ + + +/************************************************************************************//** +** \brief Transmits a communication interface byte. +** \param data Value of byte that is to be transmitted. +** \return none. +** +****************************************************************************************/ +static void UartTransmitByte(blt_int8u data) +{ + blt_int32u timeout; + + /* TODO ##Port Write the byte value in 'data' to the transmit register of the UART + * peripheral such that the transmission of the byte value is started. + */ + + /* set timeout time to wait for transmit completion. */ + timeout = TimerGet() + UART_BYTE_TX_TIMEOUT_MS; + + /* TODO ##Port Wait in a loop, with timeout, until the UART peripheral reports that the + * data was successfully completed. This is typically done by reading out a transmit + * register empty flag. + */ + + /* wait for tx holding register to be empty */ + while (1 == 0) + { + /* keep the watchdog happy */ + CopService(); + /* break loop upon timeout. this would indicate a hardware failure. */ + if (TimerGet() > timeout) + { + break; + } + } +} /*** end of UartTransmitByte ***/ +#endif /* BOOT_COM_UART_ENABLE > 0 */ + + +/*********************************** end of uart.c *************************************/ diff --git a/Target/Source/_template/usb.c b/Target/Source/_template/usb.c new file mode 100644 index 00000000..8561749b --- /dev/null +++ b/Target/Source/_template/usb.c @@ -0,0 +1,532 @@ +/************************************************************************************//** +* \file Source/_template/usb.c +* \brief Bootloader USB communication interface source file. +* \ingroup Target__template_usb +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or +* modify it under the terms of the GNU General Public License as published by the Free +* Software Foundation, either version 3 of the License, or (at your option) any later +* version. +* +* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/************************************************************************************//** +* \defgroup Target__template_usb USB driver of a port +* \brief This module implements the USB driver of a microcontroller port. +* \details The USB driver makes user of bulk communications only, by means of two +* endpoints. This driver already implements FIFO buffers for the endpoint +* data including utility functions for managing these FIFOs. Note that the +* USB descriptor configuration and other enumeration related configuration +* is typically stored with the bootloader demo application and is not +* implemented in this driver directly. +* Basically, whenever the USB communication stack is available to transmit +* new data on the IN-endpoint, function UsbTransmitPipeBulkIN() can be +* called, which checks if there is data to transmit in the FIFO buffer and +* then needs to copy it to the IN-endpoint buffer. Whenever the USB +* communication stack signals that new data was received on the OUT-endpoint, +* UsbReceivePipeBulkOUT() can be called which then copies the data from the +* OUT-endpoint buffer into the FIFO buffer. +* \ingroup Target__template +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include "boot.h" /* bootloader generic header */ +#if (BOOT_COM_USB_ENABLE > 0) +#include "usb.h" /* USB bootloader driver */ +/* TODO ##Port Include microcontroller peripheral driver header files here. */ + + +/**************************************************************************************** +* Configuration macros +****************************************************************************************/ +/* For a USB bootloader, the backdoor needs to stay open long enough for the USB device + * to enumerate on the host PC. Therefore the default backdoor open time needs to be + * extended. Note that this won't be long enough for a first time USB driver install + * on the host PC. In this case the bootloader should be started with the backup backdoor + * that uses, for example, a digital input to force the bootloader to stay active. This + * can be implemented in CpuUserProgramStartHook(). Feel free to shorten/lengthen this + * time for finetuning. Note that adding this configuration macro to blt_conf.h overrides + * the value here. + */ +#ifndef BOOT_COM_USB_BACKDOOR_EXTENSION_MS +#define BOOT_COM_USB_BACKDOOR_EXTENSION_MS (2000) +#endif + + +/**************************************************************************************** +* Macro definitions +****************************************************************************************/ +/** \brief Total number of fifo buffers. */ +#define FIFO_MAX_BUFFERS (2) +/** \brief Invalid value for a fifo buffer handle. */ +#define FIFO_ERR_INVALID_HANDLE (255) +/** \brief Number of bytes that fit in the fifo pipe. */ +#define FIFO_PIPE_SIZE (64) + +/** \brief Endpoint IN & OUT Packet size. */ +#define BULK_DATA_MAX_PACKET_SIZE (64) + + +/**************************************************************************************** +* Type definitions +****************************************************************************************/ +/** \brief Structure type for fifo control. */ +typedef struct t_fifo_ctrl +{ + blt_int8u *startptr; /**< pointer to start of buffer */ + blt_int8u *endptr; /**< pointer to end of buffer */ + blt_int8u *readptr; /**< pointer to next read location */ + blt_int8u *writeptr; /**< pointer to next free location */ + blt_int8u length; /**< number of buffer elements */ + blt_int8u entries; /**< # of full buffer elements */ + blt_int8u handle; /**< handle of the buffer */ + struct t_fifo_ctrl *fifoctrlptr; /**< pointer to free buffer control */ +} tFifoCtrl; + +/** \brief Structure type for a fifo pipe. */ +typedef struct +{ + blt_int8u handle; /**< fifo handle */ + blt_int8u data[FIFO_PIPE_SIZE]; /**< fifo data buffer */ +} tFifoPipe; /**< USB pipe fifo type */ + + +/**************************************************************************************** +* Function prototypes +****************************************************************************************/ +static blt_bool UsbReceiveByte(blt_int8u *data); +static blt_bool UsbTransmitByte(blt_int8u data); +static void UsbFifoMgrInit(void); +static blt_int8u UsbFifoMgrCreate(blt_int8u *buffer, blt_int8u length); +static blt_bool UsbFifoMgrWrite(blt_int8u handle, blt_int8u data); +static blt_bool UsbFifoMgrRead(blt_int8u handle, blt_int8u *data); +static blt_int8u UsbFifoMgrScan(blt_int8u handle); + + +/**************************************************************************************** +* Local data declarations +****************************************************************************************/ +/** \brief Local variable that holds the fifo control structures. */ +static tFifoCtrl fifoCtrl[FIFO_MAX_BUFFERS]; +/** \brief Local pointer that points to the next free fifo control structure. */ +static tFifoCtrl *fifoCtrlFree; +/** \brief Fifo pipe used for the bulk in endpoint. */ +static tFifoPipe fifoPipeBulkIN; +/** \brief Fifo pipe used for the bulk out endpoint. */ +static tFifoPipe fifoPipeBulkOUT; + + +/************************************************************************************//** +** \brief Initializes the USB communication interface. +** \return none. +** +****************************************************************************************/ +void UsbInit(void) +{ + /* initialize the FIFO manager */ + UsbFifoMgrInit(); + /* place 2 buffers under FIFO management */ + fifoPipeBulkIN.handle = UsbFifoMgrCreate(fifoPipeBulkIN.data, FIFO_PIPE_SIZE); + fifoPipeBulkOUT.handle = UsbFifoMgrCreate(fifoPipeBulkOUT.data, FIFO_PIPE_SIZE); + /* validate fifo handles */ + ASSERT_RT((fifoPipeBulkIN.handle != FIFO_ERR_INVALID_HANDLE) && \ + (fifoPipeBulkOUT.handle != FIFO_ERR_INVALID_HANDLE)); + + /* TODO ##Port Initialize the USB communication stack library. */ + + /* inform application about the connect event */ + UsbConnectHook(BLT_TRUE); + /* extend the time that the backdoor is open in case the default timed backdoor + * mechanism is used. + */ +#if (BOOT_BACKDOOR_HOOKS_ENABLE == 0) + if (BackDoorGetExtension() < BOOT_COM_USB_BACKDOOR_EXTENSION_MS) + { + BackDoorSetExtension(BOOT_COM_USB_BACKDOOR_EXTENSION_MS); + } +#endif /* BOOT_BACKDOOR_HOOKS_ENABLE == 0 */ +} /*** end of UsbInit ***/ + + +/************************************************************************************//** +** \brief Releases the USB communication interface. +** \return none. +** +****************************************************************************************/ +void UsbFree(void) +{ + /* TODO ##Port Unitialize the USB communication stack library. */ + + /* inform application about the disconnect event */ + UsbConnectHook(BLT_FALSE); +} /*** end of UsbFree ***/ + + +/************************************************************************************//** +** \brief Transmits a packet formatted for the communication interface. +** \param data Pointer to byte array with data that it to be transmitted. +** \param len Number of bytes that are to be transmitted. +** \return none. +** +****************************************************************************************/ +void UsbTransmitPacket(blt_int8u *data, blt_int8u len) +{ + blt_int16u data_index; + blt_bool result; + + /* verify validity of the len-paramenter */ + ASSERT_RT(len <= BOOT_COM_USB_TX_MAX_DATA); + + /* first transmit the length of the packet */ + result = UsbTransmitByte(len); + ASSERT_RT(result == BLT_TRUE); + + /* transmit all the packet bytes one-by-one */ + for (data_index = 0; data_index < len; data_index++) + { + /* keep the watchdog happy */ + CopService(); + /* write byte */ + result = UsbTransmitByte(data[data_index]); + ASSERT_RT(result == BLT_TRUE); + } +} /*** end of UsbTransmitPacket ***/ + + +/************************************************************************************//** +** \brief Receives a communication interface packet if one is present. +** \param data Pointer to byte array where the data is to be stored. +** \param len Pointer where the length of the packet is to be stored. +** \return BLT_TRUE if a packet was received, BLT_FALSE otherwise. +** +****************************************************************************************/ +blt_bool UsbReceivePacket(blt_int8u *data, blt_int8u *len) +{ + static blt_int8u xcpCtoReqPacket[BOOT_COM_USB_RX_MAX_DATA+1]; /* one extra for length */ + static blt_int8u xcpCtoRxLength; + static blt_bool xcpCtoRxInProgress = BLT_FALSE; + + /* TODO ##Port Poll USB interrupt flags to process USB related events. This is a good + * location for this, since this function is called continuously by the bootloader. + */ + + /* start of cto packet received? */ + if (xcpCtoRxInProgress == BLT_FALSE) + { + /* store the message length when received */ + if (UsbReceiveByte(&xcpCtoReqPacket[0]) == BLT_TRUE) + { + if ( (xcpCtoReqPacket[0] > 0) && + (xcpCtoReqPacket[0] <= BOOT_COM_USB_RX_MAX_DATA) ) + { + /* indicate that a cto packet is being received */ + xcpCtoRxInProgress = BLT_TRUE; + /* reset packet data count */ + xcpCtoRxLength = 0; + } + } + } + else + { + /* store the next packet byte */ + if (UsbReceiveByte(&xcpCtoReqPacket[xcpCtoRxLength+1]) == BLT_TRUE) + { + /* increment the packet data count */ + xcpCtoRxLength++; + + /* check to see if the entire packet was received */ + if (xcpCtoRxLength == xcpCtoReqPacket[0]) + { + /* copy the packet data */ + CpuMemCopy((blt_int32u)data, (blt_int32u)&xcpCtoReqPacket[1], xcpCtoRxLength); + /* done with cto packet reception */ + xcpCtoRxInProgress = BLT_FALSE; + /* set the packet length */ + *len = xcpCtoRxLength; + /* packet reception complete */ + return BLT_TRUE; + } + } + } + /* packet reception not yet complete */ + return BLT_FALSE; +} /*** end of UsbReceivePacket ***/ + + +/************************************************************************************//** +** \brief Receives a communication interface byte if one is present. +** \param data Pointer to byte where the data is to be stored. +** \return BLT_TRUE if a byte was received, BLT_FALSE otherwise. +** +****************************************************************************************/ +static blt_bool UsbReceiveByte(blt_int8u *data) +{ + blt_bool result; + + /* obtain data from the fifo */ + result = UsbFifoMgrRead(fifoPipeBulkOUT.handle, data); + return result; +} /*** end of UsbReceiveByte ***/ + + +/************************************************************************************//** +** \brief Transmits a communication interface byte. +** \param data Value of byte that is to be transmitted. +** \return BLT_TRUE if the byte was transmitted, BLT_FALSE otherwise. +** +****************************************************************************************/ +static blt_bool UsbTransmitByte(blt_int8u data) +{ + blt_bool result; + + /* write data from to fifo */ + result = UsbFifoMgrWrite(fifoPipeBulkIN.handle, data); + return result; +} /*** end of UsbTransmitByte ***/ + + +/************************************************************************************//** +** \brief Checks if there is still data left to transmit and if so submits it +** for transmission with the USB endpoint. +** \return none. +** +****************************************************************************************/ +void UsbTransmitPipeBulkIN(void) +{ + /* USB_Tx_Buffer is static for run-time optimalization */ + static blt_int8u USB_Tx_Buffer[BULK_DATA_MAX_PACKET_SIZE]; + blt_int8u nr_of_bytes_for_tx_endpoint; + blt_int8u byte_counter; + blt_int8u byte_value; + blt_bool result; + + /* read how many bytes should be transmitted */ + nr_of_bytes_for_tx_endpoint = UsbFifoMgrScan(fifoPipeBulkIN.handle); + /* only continue if there is actually data left to transmit */ + if (nr_of_bytes_for_tx_endpoint == 0) + { + return; + } + /* make sure to not transmit more than the USB endpoint can handle */ + if (nr_of_bytes_for_tx_endpoint > BULK_DATA_MAX_PACKET_SIZE) + { + nr_of_bytes_for_tx_endpoint = BULK_DATA_MAX_PACKET_SIZE; + } + /* copy the transmit data to the transmit buffer */ + for (byte_counter=0; byte_counter < nr_of_bytes_for_tx_endpoint; byte_counter++) + { + /* obtain data from the fifo */ + result = UsbFifoMgrRead(fifoPipeBulkIN.handle, &byte_value); + ASSERT_RT(result == BLT_TRUE); + /* store it in the endpoint's RAM */ + USB_Tx_Buffer[byte_counter] = byte_value; + } + /* TODO ##Port Copy data to IN-endpoint's RAM and start the transmission. The data is + * located in local array 'USB_Tx_Buffer' and the number of bytes is stored in + * 'nr_of_bytes_for_tx_endpoint' + */ +} /*** end of UsbTransmitPipeBulkIN ***/ + + +/************************************************************************************//** +** \brief Stores data that was received on the Bulk OUT pipe in the fifo. +** \return none. +** +****************************************************************************************/ +void UsbReceivePipeBulkOUT(void) +{ + blt_int16u USB_Rx_Cnt=0; + blt_int8u *usbRxBufferPtr; + blt_int16u byte_counter; + blt_bool result; + + /* TODO ##Port Get the number of received bytes and set the pointer to where the + * received bytes are currently located. + */ + usbRxBufferPtr = BLT_NULL; + USB_Rx_Cnt = 0; + + /* USB data will be immediately processed, this allows next USB traffic being + * NAKed till the end of the USART Xfer + */ + for (byte_counter=0; byte_counterhandle = i; + pbc1->fifoctrlptr = pbc2; + pbc1++; + pbc2++; + } + /* initialize handle for the last one and use null-pointer for the next free fifo */ + pbc1->handle = i; + pbc1->fifoctrlptr = (tFifoCtrl *)0; + fifoCtrlFree = &fifoCtrl[0]; +} /*** end of UsbFifoMgrInit ***/ + + +/************************************************************************************//** +** \brief Places a data storage array under fifo management control. A handle +** for identifying the fifo in subsequent fifo management function +** calls is returned, if successful. +** \param buffer Pointer to the first element in the data storage fifo. +** \param length Maximum number of data elements that can be stored in the fifo. +** \return Fifo handle if successfull, or FIFO_ERR_INVALID_HANDLE. +** +****************************************************************************************/ +static blt_int8u UsbFifoMgrCreate(blt_int8u *buffer, blt_int8u length) +{ + tFifoCtrl *pbc; + + /* first determine if these is still a free fifo control available */ + if (fifoCtrlFree == (tFifoCtrl *)0) + { + return FIFO_ERR_INVALID_HANDLE; + } + /* store pointer to free fifo and update pointer to next free one */ + pbc = fifoCtrlFree; + fifoCtrlFree = pbc->fifoctrlptr; + + /* initialize the buffer control */ + pbc->length = length; + pbc->readptr = buffer; + pbc->writeptr = buffer; + pbc->entries = 0; + pbc->startptr = buffer; + pbc->endptr = (blt_int8u *)(buffer + length - 1); + + /* return the handle to the successfully created fifo control */ + return pbc->handle; +} /*** end of UsbFifoMgrCreate ***/ + + +/************************************************************************************//** +** \brief Stores data in the fifo. +** \param handle Identifies the fifo to write data to. +** \param data Pointer to the data that is to be written to the fifo. +** \return BLT_TRUE if the data was successfully stored in the fifo, BLT_FALSE +** otherwise. +** +****************************************************************************************/ +static blt_bool UsbFifoMgrWrite(blt_int8u handle, blt_int8u data) +{ + /* check the validity of the handle parameter */ + ASSERT_RT(handle < FIFO_MAX_BUFFERS); + /* check if fifo is full */ + if (fifoCtrl[handle].entries == fifoCtrl[handle].length) + { + return BLT_FALSE; + } + /* copy data to fifo */ + *fifoCtrl[handle].writeptr = data; + /* data written so update number of entries */ + fifoCtrl[handle].entries++; + /* update write pointer */ + fifoCtrl[handle].writeptr++; + /* check end of fifo */ + if (fifoCtrl[handle].writeptr > fifoCtrl[handle].endptr) + { + /* set write pointer to start of the cyclic fifo */ + fifoCtrl[handle].writeptr = fifoCtrl[handle].startptr; + } + /* still here so all is okay */ + return BLT_TRUE; +} /*** end of UsbFifoMgrWrite ***/ + + +/************************************************************************************//** +** \brief Retrieves data from the fifo. +** \param handle Identifies the fifo to read data from. +** \param data Pointer to where the read data is to be stored. +** \return BLT_TRUE if the data was successfully read from the fifo, BLT_FALSE +** otherwise. +** +****************************************************************************************/ +static blt_bool UsbFifoMgrRead(blt_int8u handle, blt_int8u *data) +{ + /* check the validity of the handle parameter */ + ASSERT_RT(handle < FIFO_MAX_BUFFERS); + /* check if fifo is empty */ + if (fifoCtrl[handle].entries == 0) + { + return BLT_FALSE; + } + /* read the data */ + *data = *fifoCtrl[handle].readptr; + /* data read so update number of entries */ + fifoCtrl[handle].entries--; + /* update read pointer */ + fifoCtrl[handle].readptr++; + /* check end of fifo */ + if (fifoCtrl[handle].readptr > fifoCtrl[handle].endptr) + { + /* set read pointer to start of the cyclic fifo */ + fifoCtrl[handle].readptr = fifoCtrl[handle].startptr; + } + /* still here so all is good */ + return BLT_TRUE; +} /*** end of UsbFifoMgrRead ***/ + + +/************************************************************************************//** +** \brief Returns the number of data entries currently present in the fifo. +** \param handle Identifies the fifo that is to be scanned. +** \return Number of data entries in the fifo if successful, otherwise 0. +** +****************************************************************************************/ +static blt_int8u UsbFifoMgrScan(blt_int8u handle) +{ + /* check the validity of the handle parameter */ + ASSERT_RT(handle < FIFO_MAX_BUFFERS); + /* read and return the number of data entries */ + return fifoCtrl[handle].entries; +} /*** end of UsbFifoMgrScan ***/ +#endif /* BOOT_COM_USB_ENABLE > 0 */ + + +/*********************************** end of usb.c **************************************/