/******************************************************************************

 @file oad_storage.c

 @brief OAD Storage

 Group: CMCU LPRF
 Target Device: cc13x0

 ******************************************************************************
 
 Copyright (c) 2016-2021, Texas Instruments Incorporated
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 *  Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

 *  Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

 *  Neither the name of Texas Instruments Incorporated nor the names of
    its contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 ******************************************************************************
 
 
 *****************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include <string.h>

#include <ti/devices/DeviceFamily.h>
#include DeviceFamily_constructPath(driverlib/flash.h)

#include "oad/native_oad/oad_protocol.h"
#include "oad/native_oad/oad_storage.h"
#include "oad/native_oad/oad_image_header.h"
#include "oad/native_oad/flash_interface.h"

//================================================================================= AD:CRC error print
#include "RadioProtocol.h"
#include "ConcentratorTask.h"
#include "ConcentratorRadioTask.h"

/*********************************************************************
 * CONSTANTS
 */
#define ERROR_BLOCK     0xFFFF

#define HAL_FLASH_WORD_SIZE  4

#define CRC32_BUF_SZ          256  // 256 //BD: OAD 128
#define CRC32_POLYNOMIAL      ((uint32_t)0xEDB88320)  /* reveresed version or 802.3 polynomial 0x04C11DB7 */ //BD: OAD
/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */

/*******************************************************************************
 * PRIVATE VARIABLES
 */
//static bool isOpen = false;
//static NVS_Handle nvsHandle;
//static NVS_Attrs regionAttrs;
//static NVS_Params nvsParams;

/*********************************************************************
 * LOCAL VARIABLES
 */
uint8_t crcBuf[CRC32_BUF_SZ]; //BD:OAD

// Page that metadata is stored on
uint16_t metaPage = 0; //BD:OAD
static imgHdr_t candidateImageHeader;//BD:OAD

static uint16_t oadImgBytesPerBlock = OADStorage_BLOCK_SIZE - OADStorage_BLK_NUM_HDR_SZ; //BD:OAD
static uint8_t numBlksInImgHdr = 0; //BD:OAD
static bool useExternalFlash = false; //BD:OAD

static uint16_t oadBlkTot = 0xFFFF;
static uint16_t oadRemianingBytes = 0;
static uint32_t imageAddress;
static uint16_t imagePage;
static uint32_t candidateImageLength = 0xFFFFFFFF; //BD:OAD
static uint32_t candidateImageType = 0xFFFFFFFF; //BD:OAD

static uint32_t flashPageSize;
static uint32_t flashNumPages;

#ifndef FEATURE_OAD_ONCHIP
// Used to keep track of images written.
static uint8_t flagRecord = 0;
#endif //FEATURE_OAD_ONCHIP

static uint8_t OADStorage_imageIdLen = 0;

// Flash consists of 32 pages of 4 KB.
#define HAL_FLASH_WORD_SIZE       4

/*********************************************************************
 * LOCAL FUNCTIONS
 */

#if !defined FEATURE_OAD_ONCHIP
//static uint8_t checkDL(void);
//static uint16_t crcCalcDL(void);
//static uint16_t crc16(uint16_t crc, uint8_t val);
#endif  // !FEATURE_OAD_ONCHIP

struct CrcPrint crcPrint;       //============================================================= AD : print crc error

//BD:OAD
static uint8_t oadCheckImageID(OADStorage_imgIdentifyPld_t *idPld);

static uint8_t oadCheckDL(void);
static OADStorage_Status_t oadValidateCandidateHdr(imgHdr_t *receivedHeader);

static uint32_t CRC32_calc(uint8_t page, uint32_t pageSize, uint16_t offset, uint32_t len, bool useExtFl);
static void *CRC32_memCpy(void *dest, const void *src, uint16_t len);
static uint32_t CRC32_value(uint32_t inCRC);
//BD:OAD

/*********************************************************************
 * @fn      OADStorage_register
 *
 * @brief   Register a callback function with the OAD Target Profile.
 *
 * @param   *pfnOadCBs - write callback function container.
 *
 * @return  None.
 */
void OADStorage_init(void)
{

  // Set flash size variables
  flashPageSize = FlashSectorSizeGet();

    flash_init();
    flash_open();

    // This variable controls whether the OAD module uses internal or external
     // flash memory
     useExternalFlash = hasExternalFlash();
}

/*********************************************************************
 * @fn      OADStorage_imgIdentifyRead
 *
* @brief   Read Image header and return number of blocks.
 *
 * @param   imageType   - image type indicating which image to read
 * @param   pImgHdr     - pointer to image header data
 *
 * @return  Total Blocks if image accepted, 0 if Image invalid
 */
//uint16_t OADStorage_imgIdentifyRead(uint8_t imageType, OADTarget_ImgHdr_t* pImgHdr) //BD:OAD
uint16_t OADStorage_imgIdentifyRead(uint8_t imageType, OADStorage_imgIdentifyPld_t* pImgId) //BD:OAD
{
//    uint16_t oadBlkTot;
//    uint16_t remianingBytes;

//    OADTarget_getImageHeader(imageType, pImgHdr);

//    if(pImgHdr->len != 0x0000)
//    {
//        OADTarget_open();
//
//        // Determine where image will be read from.
//        imageAddress = OADTarget_imageAddress((uint8_t*)pImgHdr);
//        imagePage = imageAddress / FlashSectorSizeGet();
//
//        // Calculate block total of the new image.
//        oadBlkTot = pImgHdr->len /
//                    (OAD_BLOCK_SIZE / 4);
//
//        // Calculate remaining bytes
//        remianingBytes = (pImgHdr->len * 4) - (oadBlkTot * OAD_BLOCK_SIZE);
//
//        // Check for remaining in last block
//        if(remianingBytes != 0)
//        {
//            oadBlkTot += 1;
//        }
//
//        return (int32_t) oadBlkTot;
//    }
//    else
//    {
//        return 0;
//    }

//BD:OAD
    uint16_t oadBlkTot;
    uint8_t imgID[] = OAD_IMG_ID_VAL;
    uint8_t imgvmID[] = OAD_EXTFL_ID_VAL;
    imgHdr_t imgHdr;

    if(pImgId == NULL)
    {
        return 0;
    }

    imageAddress = 0 ; //oadFindExtFlImgAddr(imageType); //TODO:

    // Determine where image will be read from.
    imagePage = 0 ; // 0x1000 ; //EXT_FLASH_PAGE(imageAddress); //TODO:

    readFlashPg(imagePage, 0,(uint8_t * ) &imgHdr,sizeof(imgHdr_t));

    //copy image identify payload
    memcpy(pImgId->imgID, imgHdr.fixedHdr.imgID, 8);
    pImgId->bimVer = imgHdr.fixedHdr.bimVer;
    pImgId->metaVer = imgHdr.fixedHdr.metaVer;
    pImgId->imgCpStat = imgHdr.fixedHdr.imgCpStat;
    pImgId->crcStat = imgHdr.fixedHdr.crcStat;
    pImgId->imgType = imgHdr.fixedHdr.imgType;
    pImgId->imgNo = imgHdr.fixedHdr.imgNo;
    pImgId->len = imgHdr.fixedHdr.len;
    memcpy(pImgId->softVer, imgHdr.fixedHdr.softVer, 4);

    // Validate the Image header preamble
    if( (memcmp(&imgID, pImgId->imgID, OAD_IMG_ID_LEN) != 0) ||
        (memcmp(&imgvmID, pImgId->imgID, OAD_IMG_ID_LEN) != 0)  )
   {

        // Calculate block total of the new image.
        oadBlkTot = pImgId->len /
                    (OADStorage_BLOCK_SIZE - OADStorage_BLK_NUM_HDR_SZ);

        // If there is a remainder after division, round up
        if( 0 != (pImgId->len % (OADStorage_BLOCK_SIZE - OADStorage_BLK_NUM_HDR_SZ)))
        {
            oadBlkTot += 1;
        }

        return (int32_t) oadBlkTot;
    }
    else
    {
        return 0;
    }
}

/*********************************************************************
 * @fn      OADStorage_imgIdentifyWrite
 *
 * @brief   Process the Image Identify Write.  Determine from the received OAD
 *          Image Header if the Downloaded Image should be acquired.
 *
 * @param   pValue     - pointer to image header data
 *
 * @return  Total Blocks if image accepted, 0 if Image rejected
 */
//uint16_t OADStorage_imgIdentifyWrite(uint8_t *pValue)
uint16_t OADStorage_imgIdentifyWrite(uint8_t *pBlockData) //BD:OAD
{
//    OADTarget_ImgHdr_t ImgHdr;
//    uint8_t hdrOffset = OADStorage_imageIdLen == 8 ? 0 : 4;
//
//    // Set flash size variables
//    flashPageSize = FlashSectorSizeGet();
//
//    // Store the new image's header
//    OADTarget_storeImageHeader(pValue);
//
//    // Read out running image's header.
//    OADTarget_getCurrentImageHeader(&ImgHdr);
//
//    // Calculate block total of the new image
//    oadBlkTot = OADTarget_BUILD_UINT16(pValue[hdrOffset + 2], pValue[hdrOffset + 3]) /
//              (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE);
//
//    // Calculate remaining bytes
//    oadRemianingBytes = ((OADTarget_BUILD_UINT16(pValue[hdrOffset + 2], pValue[hdrOffset + 3])) * HAL_FLASH_WORD_SIZE) - (oadBlkTot * OAD_BLOCK_SIZE);
//
//    // Check for remaining in last block
//    if(oadRemianingBytes != 0)
//    {
//        oadBlkTot += 1;
//    }
//
//#ifndef FEATURE_OAD_ONCHIP
//    flagRecord = 0;
//#endif
//
//    /* Requirements to begin OAD:
//     * 1) LSB of image version cannot be the same, this would imply a code overlap
//     *    between currently running image and new image.
//     * 2) Total blocks of new image must not exceed maximum blocks supported, else
//     *    the new image cannot fit.
//     * 3) Block total must be greater than 0.
//     * 4) Optional: Add additional criteria for initiating OAD here.
//     */
//    if (OADTarget_validateNewImage(pValue + hdrOffset, &ImgHdr, oadBlkTot))
//    {
//        // Determine where image will be stored.
//        imageAddress = OADTarget_imageAddress(pValue+hdrOffset);
//        imagePage = imageAddress / FlashSectorSizeGet();
//
//        // Open the target interface
//        if(OADTarget_open() == 0)
//        {
//            oadBlkTot = 0;
//        }
//    }
//    else
//    {
//        oadBlkTot = 0;
//    }
//
//    return oadBlkTot;

    //BD:OAD
    uint8_t idStatus;

     // Find the number of blocks in the image header, round up if necessary
     numBlksInImgHdr = sizeof(imgHdr_t) / (oadImgBytesPerBlock)  +  \
                         (sizeof(imgHdr_t) % (oadImgBytesPerBlock) != 0);

     // Cast the pBlockData byte array to OADStorage_imgIdentifyPld_t
     OADStorage_imgIdentifyPld_t *idPld = (OADStorage_imgIdentifyPld_t *)(pBlockData);

     // Validate the ID
     idStatus = oadCheckImageID(idPld);

     // If image ID is accepted, set variables and pre-erase flash pages
     if(idStatus == OADStorage_Status_Success)
     {
         if(!useExternalFlash)
         {
             imageAddress = 0;  //0
             imagePage = 0;   // 0x1000
//             metaPage = 0;
         }
         else
         {
//             imageAddress = oadFindExtFlImgAddr(idPld->imgType);
//             imagePage = EXT_FLASH_PAGE(imageAddress);
//             metaPage = oadFindExtFlMetaPage();
         }

         // Calculate total number of OAD blocks, round up if needed
         oadBlkTot = candidateImageLength / (oadImgBytesPerBlock);

         // If there is a remainder after division, round up
         if( 0 != (candidateImageLength % (oadImgBytesPerBlock)))
         {
             oadBlkTot += 1;
         }
     }
     else
     {
         oadBlkTot = 0;
     }
     crcPrint.oadTotBlks = oadBlkTot;   //===================================== AD:
     return oadBlkTot;
}

/*********************************************************************
 * @fn      OADStorage_imgBlockWrite
 *
 * @brief   Write an Image Block.
 *
 * @param   blockNum   - block number to be written
 * @param   pBlockData - pointer to data to be written
 * @param   len        - length of data to be written
 *
 * @return  none
 */
//void OADStorage_imgBlockWrite(uint16_t blockNum, uint8_t *pBlockData) //BD:OAD
uint8_t OADStorage_imgBlockWrite(uint32_t blkNum, uint8_t *pBlockData, uint8_t len) //BD:OAD
{
//    // Calculate address to write as (start of OAD range) + (offset into range)
//    uint32_t addr = imageAddress + (blockNum * OAD_BLOCK_SIZE);
//
//    // If address starts a new page, erase that page first.
//    if ((addr % flashPageSize) == 0)
//    {
//      OADTarget_eraseFlash(addr / flashPageSize);
//    }
//
//    // Write a 16 byte block to Flash.
//    OADTarget_writeFlash(imagePage, (blockNum * OAD_BLOCK_SIZE), pBlockData,
//                         OAD_BLOCK_SIZE);

//BD: OAD
// Write image block to internal flash
    OADStorage_Status_t status = OADStorage_Status_Success;
     uint8_t flashStat;
     uint8_t page;

     uint8_t offset = (oadImgBytesPerBlock)*blkNum;

     // Remaining bytes that are included in this packet that are not
     // part of the image header
     uint8_t nonHeaderBytes = 0;

     // Number of bytes in this packet that need to be copied to header
     uint8_t remainder = 0;

     // The destination in RAM to copy the payload info
     uint8_t *destAddr = (uint8_t *)(&candidateImageHeader) + offset;

     // Don't start store to flash until entire header is received
     if((blkNum < (numBlksInImgHdr - 1)) && (sizeof(imgHdr_t) >= oadImgBytesPerBlock))
     {
         memcpy(destAddr, pBlockData + OADStorage_BLK_NUM_HDR_SZ,
                 (oadImgBytesPerBlock));
     }
     else if (blkNum == (numBlksInImgHdr - 1))
     {
         if((oadImgBytesPerBlock)  >= sizeof(imgHdr_t))
         {
             // if we can fit the entire header in a single block
             memcpy(destAddr,
                     pBlockData+OADStorage_BLK_NUM_HDR_SZ,
                     sizeof(imgHdr_t));

             nonHeaderBytes = (oadImgBytesPerBlock) - sizeof(imgHdr_t);
             remainder = sizeof(imgHdr_t);
         }
         else
         {
             // Find out how much of the block contains header data
             remainder = sizeof(imgHdr_t) % (oadImgBytesPerBlock);
             if(remainder == 0)
             {
                 remainder = oadImgBytesPerBlock;
             }

             nonHeaderBytes = (oadImgBytesPerBlock) - remainder;
             // if this block contains the last part of the header
             memcpy(destAddr,
                     pBlockData+OADStorage_BLK_NUM_HDR_SZ,
                     remainder);
         }

         status = oadValidateCandidateHdr((imgHdr_t * )&candidateImageHeader);
         if(status == OADStorage_Status_Success)
         {
             // Calculate number of flash pages to pre-erase
             uint8_t numFlashPages;
             if(!useExternalFlash)
             {
                 numFlashPages = candidateImageHeader.fixedHdr.len / flashPageSize;
                 if(0 != (candidateImageHeader.fixedHdr.len % flashPageSize))
                 {
                     numFlashPages += 1;
                 }
             }
             else
             {
                 numFlashPages = candidateImageHeader.fixedHdr.len / EFL_PAGE_SIZE;
                 if(0 != (candidateImageHeader.fixedHdr.len % EFL_PAGE_SIZE))
                 {
                     numFlashPages += 1;
                 }
             }

             // Pre-erase the correct amount of pages before starting OAD
             for(page = imagePage; page < (imagePage + numFlashPages); ++page)
             {
                 flashStat = eraseFlashPg(page);
                 if(flashStat == FLASH_FAILURE)
                 {
                     // If we fail to pre-erase, then halt the OAD process
                     status = OADStorage_FlashError;
                     break;
                 }

             }

             // Write a OAD_BLOCK to Flash.
             flashStat = writeFlashPg(imagePage, 0,
                                     (uint8_t * ) &candidateImageHeader,
                                     sizeof(imgHdr_t));

             // Cancel OAD due to flash program error
             if(FLASH_SUCCESS != flashStat)
             {
                 return (OADStorage_FlashError);
             }

             // If there are non header (image data) bytes in this packet
             // write them to flash as well
             if(nonHeaderBytes)
             {
                 // Write a OAD_BLOCK to Flash.
                 flashStat = writeFlashPg(imagePage,
                                         sizeof(imgHdr_t),
                                         (pBlockData+OADStorage_BLK_NUM_HDR_SZ+remainder),
                                         nonHeaderBytes);

                 // Cancel OAD due to flash program error
                 if(FLASH_SUCCESS != flashStat)
                 {
                     return (OADStorage_FlashError);
                 }
             }
         }
         else
         {
             // Cancel OAD due to boundary error
             return (status);
         }
     }
     else
     {
         // Calculate address to write as (start of OAD range) + (offset into range)
         uint32_t blkStartAddr = (oadImgBytesPerBlock)*blkNum + imageAddress;

         if (!useExternalFlash)
         {
             flashStat = writeFlash(blkStartAddr,
                                    pBlockData+OADStorage_BLK_NUM_HDR_SZ,
                                    (len - OADStorage_BLK_NUM_HDR_SZ));
         }
         else
         {
             uint8_t page = (blkStartAddr >> 12);
             uint32_t offset = (blkStartAddr & 0x00000FFF);

             // Write a OAD_BLOCK to Flash.
             flashStat = writeFlashPg(page, offset,
                                      pBlockData+OADStorage_BLK_NUM_HDR_SZ,
                                      (len - OADStorage_BLK_NUM_HDR_SZ));
         }

         // Cancel OAD due to flash program error
         if(FLASH_SUCCESS != flashStat)
         {
             return (OADStorage_FlashError);
         }
     }

     // Return and request the next block
     return (status);
}

/*********************************************************************
 * @fn      OADStorage_imgBlockRead
 *
 * @brief   Read an Image block.
 *
 * @param   blockNum   - block number to be read
 * @param   pBlockData - pointer for data to be read
 *
 * @return  none
 */
void OADStorage_imgBlockRead(uint16_t blockNum, uint8_t *pBlockData)
{
//    // Read a block from Flash.
//    OADTarget_readFlash(imagePage, (blockNum * OAD_BLOCK_SIZE), pBlockData,
//                         OAD_BLOCK_SIZE);
    // Calculate address to write as (start of OAD range) + (offset into range)
     uint32_t blkStartAddr = (oadImgBytesPerBlock)*blockNum + imageAddress;

     uint8_t page = (blkStartAddr >> 12);
     uint32_t offset = (blkStartAddr & 0x00000FFF);

     // Read a block from Flash.
     readFlashPg(page, offset, pBlockData,
                           (OADStorage_BLOCK_SIZE - OADStorage_BLK_NUM_HDR_SZ));

}

/*********************************************************************
 * @fn      OADStorage_imgInfoRead
 *
 * @brief   Read an Image info.
 *
 * @param   pimgInfo - pointer for data to be read
 *
 * @return  none
 */
//void OADStorage_imgInfoRead(uint8_t *pimgInfo)
//{
//    // Read a block from Flash.
//    OADTarget_readFlash(imagePage, 0, pimgInfo,
//                         16);
//}

/*********************************************************************
 * @fn      OADStorage_imgFinalise
 *
 * @brief   Process the Image Block Write.
 *
 * @param  none
 *
 * @return  OADStorage_Status_t
 */
OADStorage_Status_t OADStorage_imgFinalise(void)
{
//    OADStorage_Status_t status = OADStorage_Status_Success;
//
//// For onchip Handle CRC verification in BIM.
//#if !defined FEATURE_OAD_ONCHIP
//    // Run CRC check on new image.
//    if (checkDL())
//    {
//        // Store the flag of the downloaded image.
//        flagRecord |= getImageFlag();
//
//        // Store the image information.
//        saveImageInfo();
//
//        status = OADStorage_Status_Success;
//    }
//    else
//    {
//        // CRC error
//        status = OADStorage_CrcError;
//    }
//    flagRecord = 0;
//#endif //!FEATURE_OAD_ONCHIP
//    OADTarget_close();
//
//    return status;
    
    // Run CRC check on new image.
    if (OADStorage_Status_Success != oadCheckDL())
    {
        // CRC error
        return (OADStorage_CrcError);
    }

    // Indicate a successful download and CRC
     return (OADStorage_Status_Success);
}

/*********************************************************************
 * @fn      OADStorage_imgFinalise
 *
 * @brief   Process the Image Block Write.
 *
 * @param  none
 *
 * @return none
 */
void OADStorage_close(void)
{
//    OADTarget_close();
    // close the flash interface
     flash_close();
}

#if !defined FEATURE_OAD_ONCHIP
/*********************************************************************
 * @fn      crcCalcDL
 *
 * @brief   Run the CRC16 Polynomial calculation over the DL image.
 *
 * @param   None
 *
 * @return  The CRC16 calculated.
 */
//static uint16_t crcCalcDL(void)
//{
//  uint16_t imageCRC = 0;
//  uint8_t page;
//  uint32_t flashPageSize = FlashSectorSizeGet();
//  uint32_t blocksPerPage = flashPageSize / OAD_BLOCK_SIZE;
//
//  uint8_t lastPage = oadBlkTot / blocksPerPage;
//
//  // Calculate remaining blocks in last page.
//  uint16_t numRemBytes = (oadBlkTot - (lastPage * blocksPerPage))
//                         * OAD_BLOCK_SIZE;
//
//  // Is the last block partially filled
//  if(oadRemianingBytes != 0)
//  {
//      numRemBytes -= (OAD_BLOCK_SIZE - oadRemianingBytes);
//  }
//
//  // Set last page to end of OAD image address range.
//  lastPage += imagePage;
//
//  // Read over downloaded pages
//  for (page = imagePage; page <= lastPage; page++)
//  {
//    uint16_t offset;
//
//    // Read over all flash words in a page, excluding the CRC section of the
//    // first page and all bytes after remainder bytes on the last page.
//    for (offset = (page == imagePage) ? HAL_FLASH_WORD_SIZE : 0;
//         offset < flashPageSize &&
//         (page < lastPage || offset < numRemBytes);
//         offset += HAL_FLASH_WORD_SIZE)
//    {
//      uint8_t buf[HAL_FLASH_WORD_SIZE];
//      uint8_t idx;
//
//      // Read a word from flash.
//      OADTarget_readFlash(page, offset, buf, HAL_FLASH_WORD_SIZE);
//
//      // Calculate CRC of word, byte by byte.
//      for (idx = 0; idx < HAL_FLASH_WORD_SIZE; idx++)
//      {
//        imageCRC = crc16(imageCRC, buf[idx]);
//      }
//    }
//  }
//
//  // IAR note explains that poly must be run with value zero for each byte of
//  // the crc.
//  imageCRC = crc16(imageCRC, 0);
//  imageCRC = crc16(imageCRC, 0);
//
//  // Return the CRC calculated over the image.
//  return imageCRC;
//}

/*********************************************************************
 * @fn      checkDL
 *
 * @brief   Check validity of the downloaded image.
 *
 * @param   None.
 *
 * @return  TRUE or false for image valid.
 */
//static uint8_t checkDL(void)
static uint8_t oadCheckDL(void)
{
//  uint16_t crc[2];
//
//  OADTarget_getCrc(crc);
//
//  if ((crc[0] == 0xFFFF) || (crc[0] == 0x0000))
//  {
//    return false;
//  }
//
//  // Calculate CRC of downloaded image.
//  crc[1] = crcCalcDL();
//
//  if (crc[1] == crc[0])
//  {
//    // Set the CRC shadow as equivalent to the CRC.
//    OADTarget_setCrc(crc);
//  }
//
//  return (crc[0] == crc[1]);

//    uint32_t crcFromHdr;
//            uint8_t crcFromHdr1[4];
//         uint32_t crcCalculated;
//         uint8_t crcStatus;
//         imagePage = 0x0000 ;
         uint8_t first_line[16];

    uint32_t crcFromHdr;
     uint32_t crcCalculated;
     uint8_t crcStatus;


     uint8_t status = OADStorage_Status_Success;

     //===================================================================================== AD: crc error
//     readFlashPg(imagePage, 0, (uint8_t *)(&first_line),
//                      sizeof(first_line));
     readFlashPg(0x0000, 0x0010, (uint8_t *)(&first_line), sizeof(first_line));


     // Read in the CRC
     readFlashPg(imagePage, CRC_OFFSET, (uint8_t *)(&crcFromHdr),
                 sizeof(uint32_t));

     // Read in the image info word
     readFlashPg(imagePage, CRC_STAT_OFFSET, &crcStatus,
                 sizeof(uint8_t));

     // If for some reason the header shows the CRC is invalid reject the image now
     if (crcStatus == CRC_INVALID)
     {
         return (OADStorage_CrcError);
     }

     // Calculate CRC of downloaded image.
     if(useExternalFlash)
     {
         crcCalculated = CRC32_calc(imagePage, EFL_PAGE_SIZE, 0, candidateImageLength, useExternalFlash);
     }
     else
     {
//         crcCalculated = CRC32_calc(imagePage, flashPageSize, 0, candidateImageLength, useExternalFlash);
         crcCalculated = CRC32_calc(0x04, 0x1000, 0, candidateImageLength, useExternalFlash);  //=============== AD:
     }

     if (crcCalculated == crcFromHdr)
     {
         // Set CRC stat to valid
         crcStatus = CRC_VALID;

         // Only write to the CRC flag if using internal flash
         if(!useExternalFlash)
         {
             // Write CRC status back to flash  ================================================== AD:
//             writeFlashPg(imagePage, CRC_STAT_OFFSET, &crcStatus,
//                             sizeof(uint8_t));
         }
         status = OADStorage_Status_Success;

     }
     else
     {
         status = OADStorage_CrcError;
     }

     crcPrint.Calculated_crc = crcCalculated;
     crcPrint.Header_crc = crcFromHdr;
     crcPrint.Status_crc = crcStatus;
     crcPrint.Crc_offset = CRC_OFFSET;
     memcpy(crcPrint.first_line, first_line, sizeof(first_line));
     crcPrint.flashpagesize = flashPageSize;
    crcPrint.candidateImageLength = candidateImageLength;

     ConcentratorTask_printCrcStore(crcPrint);   //============================================================= AD : print crc error

     return (status);
}

/*********************************************************************
 * @fn          crc16
 *
 * @brief       Run the CRC16 Polynomial calculation over the byte parameter.
 *
 * @param       crc - Running CRC calculated so far.
 * @param       val - Value on which to run the CRC16.
 *
 * @return      crc - Updated for the run.
 */
//static uint16_t crc16(uint16_t crc, uint8_t val)
//{
//  const uint16_t poly = 0x1021;
//  uint8_t cnt;
//
//  for (cnt = 0; cnt < 8; cnt++, val <<= 1)
//  {
//    uint8_t msb = (crc & 0x8000) ? 1 : 0;
//
//    crc <<= 1;
//
//    if (val & 0x80)
//    {
//      crc |= 0x0001;
//    }
//
//    if (msb)
//    {
//      crc ^= poly;
//    }
//  }
//
//  return crc;
//}

#endif // !FEATURE_OAD_ONCHIP

/*********************************************************************
 * @fn      oadCheckImageID
 *
 * @brief   Check image identify header (determines OAD start)
 *
 * @param   idPld - pointer to imageID payload
 *
 * @return  headerValid - SUCCESS or fail code
 */
static uint8_t oadCheckImageID(OADStorage_imgIdentifyPld_t *idPld)
{
    uint8_t status = OADStorage_Status_Success;
    uint8_t imgID[] = OAD_IMG_ID_VAL;
    uint8_t imgvmID[] = OAD_EXTFL_ID_VAL;

    // Validate the Image header preamble
    if( (memcmp(&imgID, idPld->imgID, OAD_IMG_ID_LEN) != 0)  &&
        (memcmp(&imgvmID, idPld->imgID, OAD_IMG_ID_LEN) != 0) )
    {
        status = OADStorage_Rejected;
    } else if (idPld->imgType == OAD_IMG_TYPE_PERSISTENT_APP     ||
            idPld->imgType == OAD_IMG_TYPE_BIM         ||
            idPld->imgType == OAD_IMG_TYPE_FACTORY ||
            idPld->imgType >= OAD_IMG_TYPE_RSVD_BEGIN)
   {
       // Persistent app, BIM, factory are not currently upgradeable
       // Image type must also not be in the reserved range
       status = OADStorage_Rejected;
   }

   if(status == OADStorage_Status_Success)
   {
       // If we are about to accept the image, store the image data
       candidateImageLength = idPld->len;
       candidateImageType = idPld->imgType;
   }
   return (status);
}

/*********************************************************************
 * @fn      oadValidateCandidateHdr
 *
 * @brief   Validate the header of the incoming image
 *
 * @param   receivedHeader - pointer to the candidate image's header
 *
 * @return  Status - OADStorage_Status_Success if segment valid or not a segment
                     OADStorage_Rejected - if the boundary check fails
 */
static OADStorage_Status_t oadValidateCandidateHdr(imgHdr_t *receivedHeader)
{
    OADStorage_Status_t status = OADStorage_Status_Success;

    // Check that the incoming image is page aligned
    if((receivedHeader->imgPayload.startAddr & (flashPageSize - 1)) != 0)
    {
        status = OADStorage_Rejected;
    }

    // Merged image types and images not targed to run on CC26xx are not checked
    return (status);
}

/*******************************************************************************
 * @fn          crcCalc
 *
 * @brief       Run the CRC32 Polynomial calculation over the image specified.
 *
 * @param       page   - Flash page on which to beginning the CRC calculation.
 *
 * @param       offset - offset of first byte of image within first flash page
 *                       of the image.
 *              useExtFl - calculate crc on external or internal flash
 *
 * @return      The CRC32 calculated.
 */
static uint32_t CRC32_calc(uint8_t page, uint32_t pageSize, uint16_t offset, uint32_t len, bool useExtFl)
{
    uint8_t pageIdx;
    uint8_t pageBeg = page;
    uint8_t pageEnd;
    uint16_t numBytesInLastPg;
    uint32_t temp1 = pageSize, temp2, crc = 0;
    uint16_t oset = 0;
    uint8_t bufNum;

    /* Check for invalid length */
    if((len == 0) || (len == 0xFFFFFFFF) ||
       (useExtFl == true && len > EFL_FLASH_SIZE) ||
       (useExtFl == false && len > (0x20000)))//MAX_ONCHIP_FLASH_PAGES*INTFLASH_PAGE_SIZE)))
    {
        return crc;
    }
    /* Read first page of the image into the buffer. */
    if(!useExtFl)
    {
        CRC32_memCpy(crcBuf, (uint8_t *)(page * pageSize), CRC32_BUF_SZ);
    }
    else
    {
        readFlashPg(page, 0, crcBuf, CRC32_BUF_SZ);
    }

    pageEnd = ((len - 1) / (pageSize) + pageBeg);

    /* Determine the number of bytes in the last page */
    numBytesInLastPg = ((len - 1) % pageSize) + 1;

    crc = 0xFFFFFFFF;

    /* Read over image pages. */
    for (pageIdx = pageBeg; pageIdx <= pageEnd; pageIdx++)
    {
        uint8_t numBufInCurPg;

        /* Find the number of buffers within this page */
        if(pageIdx == pageEnd)
        {
            /* Number of bytes divided by buf_sz is the number of buffers */
            numBufInCurPg = numBytesInLastPg / CRC32_BUF_SZ;

            /* Round up a buffer if a partial buffer must be used */
            if(numBytesInLastPg % CRC32_BUF_SZ != 0)
            {
                numBufInCurPg++;
            }
        }
        else
        {
            /* Note this requires that pageSize is an integer multiple of
              CRC32_BUF_SZ */
            numBufInCurPg = pageSize / CRC32_BUF_SZ;
        }
        /* Read over buffers within each page */
        for(bufNum = 0; bufNum < numBufInCurPg; bufNum++)
        {
            /* Find ending offset in bytes last buffer. */
            uint16_t osetEnd;
            /* Calculate the ending offset for this buffer */
            if(bufNum == (numBufInCurPg - 1) && pageIdx == pageEnd)
            {
                if(numBytesInLastPg % CRC32_BUF_SZ != 0)
                {
                    osetEnd = (numBytesInLastPg % CRC32_BUF_SZ );
                }
                else
                {
                    osetEnd = CRC32_BUF_SZ;
                }
            }
            else
            {
                osetEnd = CRC32_BUF_SZ;
            }

            /* Read over all flash words in a buffer, excluding the CRC section
             * of the first page and all bytes after the remainder bytes in the
             * last buffer
             */
            for (oset = ((pageIdx == pageBeg && bufNum == 0) ? offset + IMG_DATA_OFFSET : 0);
                     oset < osetEnd;
                     oset++)
            {
                temp1 = (crc >> 8) & 0x00FFFFFFL;
                temp2 = CRC32_value(((uint32_t)crc ^ crcBuf[oset]) & 0xFF);
                crc = temp1 ^ temp2;
            }

            /* Read data into the next buffer */
            if(!useExtFl)
            {
                CRC32_memCpy(crcBuf, (uint8_t *)((pageIdx*pageSize) + ((bufNum + 1)*CRC32_BUF_SZ)),
                        CRC32_BUF_SZ);
            }
            else
            {
                /* Check to see    if the next buffer is on the next page */
                if(bufNum    == (numBufInCurPg - 1))
                {
                    readFlashPg((pageIdx + 1), 0, crcBuf, CRC32_BUF_SZ);
                }
                else
                {
                    readFlashPg(pageIdx, ((bufNum + 1)*CRC32_BUF_SZ), crcBuf,
                                      CRC32_BUF_SZ);
                }
            }
        } /* for(uint8_t bufNum = 0; bufNum < numBufInCurPg; bufNum++) */
        crcPrint.crc_numBufInCurPg = numBufInCurPg; //========================================== AD:
    } /* for (uint8_t pageIdx = pageBeg; pageIdx <= pageEnd; pageIdx++) */

    /* XOR CRC with all bits on */
    crc = crc ^ 0xFFFFFFFF;

    /*****************************************///================================================ AD:
    crcPrint.crc_page_end = pageEnd;
    crcPrint.crc_bytInLstPg = numBytesInLastPg;


    return(crc);
    }

/*******************************************************************************
 * @fn          CRC32_memCpy
 *
 * @brief       Copies source buffer to destination buffer.
 *
 * @param       dest  - Destination buffer.
 * @param       src   - Destination buffer.
 * @param       len   - Destination buffer.
 *
 * @return      pointer to destination buffer.
 */
static void *CRC32_memCpy(void *dest, const void *src, uint16_t len)
{
    if(dest == NULL)
    {
        return(NULL);
    }
    while(len--)
    {
        ((uint8_t *)dest)[len] = ((uint8_t *)src)[len];
    }
    return(dest);
}

/*******************************************************************************
 * @fn          CRC32_value
 *
 * @brief       Calculates crc32 value.
 *
 * @param       inCRC  - input word.
 *
 * @return      calculated crc32 word.
 */
static uint32_t CRC32_value(uint32_t inCRC)
{
    uint8_t  j;
    uint32_t ulCRC = inCRC;

    /* for this byte (inCRC).. */
    for (j = 8; j; j--)
    {
        /* lsb on? yes -- shift right and XOR with poly. else just shift */
        if (ulCRC & 1)
        {
            ulCRC = (ulCRC >> 1) ^ CRC32_POLYNOMIAL;
        }
        else
        {
            ulCRC >>= 1;
        }
    }

    return(ulCRC);
}
/*********************************************************************
*********************************************************************/
