/***********************************************************************************************************************
 *  FILE DESCRIPTION
 *  ------------------------------------------------------------------------------------------------------------------*/
/** \file
 *  \brief         Implementation of pattern/mask based non-volatile information storage
 *
 *  --------------------------------------------------------------------------------------------------------------------
 *  COPYRIGHT
 *  --------------------------------------------------------------------------------------------------------------------
 *  \par Copyright
 *  \verbatim
 *  Copyright (c) 2025 by Vector Informatik GmbH.                                                  All rights reserved.
 *
 *                This software is copyright protected and proprietary to Vector Informatik GmbH.
 *                Vector Informatik GmbH grants to you only those rights as set out in the license conditions.
 *                All other rights remain with Vector Informatik GmbH.
 *  \endverbatim
 */
/**********************************************************************************************************************/

/***********************************************************************************************************************
 *  REVISION HISTORY
 *  --------------------------------------------------------------------------------------------------------------------
 *  Version    Date        Author  Change Id        Description
 *  --------------------------------------------------------------------------------------------------------------------
 *  02.00.00   2025-03-11  virjjn  FBL-9704         Initial version
 *  02.01.00   2025-06-19  dganesh FBL-11034        Improvements in extended presence pattern
 **********************************************************************************************************************/

#define FBL_NVPATTERN_SOURCE

/***********************************************************************************************************************
 *  INCLUDES
 **********************************************************************************************************************/

#include "fbl_inc.h"
#include "fbl_nvpattern.h"

/***********************************************************************************************************************
 *  VERSION
 **********************************************************************************************************************/

#if ( FBLLIB_NVPATTERN_VERSION != 0x0201u ) || \
    ( FBLLIB_NVPATTERN_RELEASE_VERSION != 0x00u )
# error "Error in fbl_nvpattern.c: Source and header file are inconsistent!"
#endif

#if ( FBLLIB_NVPATTERN_VERSION != _FBLLIB_NVPATTERN_VERSION ) || \
    ( FBLLIB_NVPATTERN_RELEASE_VERSION != _FBLLIB_NVPATTERN_RELEASE_VERSION )
# error "Error in fbl_nvpattern.c: Source and v_ver.h are inconsistent!"
#endif

/***********************************************************************************************************************
 *  DEFINES
 **********************************************************************************************************************/

/* Definition of marker/mask values and their size */
#if !defined( kFblNvMarkerValue )
# define kFblNvMarkerValue                      {0x73u, 0x6Au, 0x29u, 0x3Eu}
#endif /* kFblNvMarkerValue */
#if !defined( kFblNvMaskValue )
# define kFblNvMaskValue                        {0x8Cu, 0x95u, 0xD6u, 0xC1u}
#endif /* kFblNvMaskValue */
#if !defined( kFblNvPatternSize )
# define kFblNvPatternSize                      4u
#endif /* kFblNvPatternSize */

/* Macro to calculate the page aligned size to hold a value of size len */
#define PAGE_ALIGN(len, align)                  (((len) + ((align) - 1u)) & ~((align) - 1u))

/* Macro to check if val is a power of two */
#define IS_POWOFTWO(val)                        (((val) & ~((val) - 1u)) == (val))

/* Size of write buffer with enough space to hold one marker/mask value */
#if ( FBL_MAX_SEGMENT_SIZE < kFblNvPatternSize )
# define kFblNvPatternSegmentSize               PAGE_ALIGN(kFblNvPatternSize, FBL_MAX_SEGMENT_SIZE)
#else
# define kFblNvPatternSegmentSize               FBL_MAX_SEGMENT_SIZE
#endif

/* Checks if tFblNvPatternId belongs to a block property or a pattern */
#define IS_VALID_PATTERN_ID(id)                 (((id) > kFblNvPatternId_Invalid)   && ((id) < kFblNvPatternId_Separator))
#define IS_VALID_PROPERTY_ID(id)                (((id) > kFblNvPatternId_Separator) && ((id) < kFblNvPatternId_Last))

/* Parameter value to skip comparison for function FblNvPatternGetPatternItemState */
#define DONT_COMPARE                            (vuint8*)0u

/* Enable the unexpected value check by default */
#if !defined( FBL_NVPATTERN_DISABLE_CHECK_UNEXPECTEDVALUE ) 
# define FBL_NVPATTERN_ENABLE_CHECK_UNEXPECTEDVALUE
#endif /* !FBL_NVPATTERN_DISABLE_CHECK_UNEXPECTEDVALUE && !FBL_NVPATTERN_ENABLE_CHECK_UNEXPECTEDVALUE */

/***********************************************************************************************************************
 *  TYPEDEFS
 **********************************************************************************************************************/

/** Buffer with alignment to platform requirements */
typedef struct
{
   vuint32  alignDummy; /**< Used to force 4Byte alignment */
   vuint8   data[kFblNvPatternSegmentSize];
} tFblNvPatternAlignedBuffer;

/***********************************************************************************************************************
 *  LOCAL DATA
 **********************************************************************************************************************/

#define FBLNVPATTERN_START_SEC_VAR
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
V_MEMRAM0 static V_MEMRAM1 tFblNvPatternAlignedBuffer V_MEMRAM2 pageBuffer;
#define FBLNVPATTERN_STOP_SEC_VAR
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */

/***********************************************************************************************************************
 *  LOCAL FUNCTION PROTOTYPES
 **********************************************************************************************************************/

#define FBLNVPATTERN_START_SEC_CODE
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */

static tFblResult FblNvPatternWritePageAligned( const vuint8* pSrcBuffer, IO_PositionType address, IO_SizeType length );
static tFblNvPatternItemState FblNvPatternGetPatternItemState( IO_PositionType patternAddress, IO_SizeType length, const vuint8* compareValue );
static tFblResult FblNvPatternGetPatternBlockDescriptorByStartAddress( IO_PositionType patternStartAddress,
                                                                       V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor );
static IO_SizeType FblNvPatternGetPatternOffset( tFblNvPatternId patternId,
                                                 V_MEMRAM1 IO_SizeType V_MEMRAM2 V_MEMRAM3 * patternLength );

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

/***********************************************************************************************************************
 *  FblNvPatternWritePageAligned
 **********************************************************************************************************************/
/*! \brief         Writes data respecting the page size of the memory device
 *  \pre           GetBaseAddr function has been called first
 *  \param[in]     pSrcBuffer pointer to source data
 *  \param[in]     address target address
 *  \param[in]     length length of data
 **********************************************************************************************************************/
static tFblResult FblNvPatternWritePageAligned( const vuint8* pSrcBuffer, IO_PositionType address, IO_SizeType length )
{
   tFblResult result;
   IO_SizeType i;
   IO_SizeType writeGranularity;
   IO_SizeType actualWriteLength;
   IO_SizeType localLength;
   IO_PositionType offset;

   /* Initialize variables */
   result = kFblOk;
   localLength = length;
   offset = 0u;

   while (localLength > 0u)
   {
      (void)FblRealTimeSupport();

      /* Determine page aligned write length, limit to buffer length */
      if (localLength > kFblNvPatternSegmentSize)
      {
         writeGranularity = kFblNvPatternSegmentSize;
         actualWriteLength = kFblNvPatternSegmentSize;
      }
      else
      {
         writeGranularity = PAGE_ALIGN(localLength, (IO_SizeType)MemDriver_SegmentSize);
         actualWriteLength = localLength;
      }

      /* Fill buffer with payload data */
      for (i = 0u; i < actualWriteLength; i++)
      {
         pageBuffer.data[i] = pSrcBuffer[i + offset];
      }
      /* Pad remaining buffer if necessary */
      if (actualWriteLength < kFblNvPatternSegmentSize)
      {
         for (i = actualWriteLength; i < writeGranularity; i++)
         {
            pageBuffer.data[i] = 0u;
         }
      }

      /* Call MIO to write data */
      if (MemDriver_RWriteSync(pageBuffer.data, writeGranularity, address + offset) != IO_E_OK)
      {
         result = kFblFailed;
         break;
      }

      /* Update variables */
      localLength -= actualWriteLength;
      offset += actualWriteLength;
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternGetPatternItemState
 **********************************************************************************************************************/
/*! \brief         Reads one marker/mask and returns the state of it
 *  \pre           Global variable memSegment is set to the block where the pattern is stored
 *  \param[in]     patternAddress address of pattern item
 *  \param[in]     length the length of the pattern item to read
 *  \param[in]     compareValue pointer to expected pattern value (return ExpectedValue), 0 if no comparison is needed
 *  \return        The state of the pattern item
 **********************************************************************************************************************/
/* PRQA S 6010, 6030, 6080 1 */ /* MD_MSR_STPTH, MD_MSR_STCYC, MD_MSR_STMIF */
static tFblNvPatternItemState FblNvPatternGetPatternItemState( IO_PositionType patternAddress, IO_SizeType length, const vuint8* compareValue)
{
   tFblNvPatternItemState itemState = FBL_NVPATTERN_STATE_READERROR;
   vuint8 memErasedValue;
   IO_SizeType currentReadLength;
   IO_SizeType localLength = length;
   IO_PositionType readOffset = 0u;
   vuint32 i;
   vuint32 countExpectedValue = 0u;
   vuint32 countErasedValue = 0u;

   /* Determine memory erased value of used device */
#if defined( FBL_ENABLE_MULTIPLE_MEM_DEVICES )
   memErasedValue = memDrvLst[FlashBlock[memSegment].device].erasedValue;
#else
   memErasedValue = FBL_FLASH_DELETED;
#endif

   while (localLength > 0u)
   {
      /* Limit read length to buffer size */
      if (localLength > kFblNvPatternSegmentSize)
      {
         currentReadLength = kFblNvPatternSegmentSize;
      }
      else
      {
         currentReadLength = localLength;
      }

      /* Read marker/mask */
      switch (MemDriver_RReadSync(pageBuffer.data, currentReadLength, patternAddress + readOffset))
      {
         case IO_E_OK:
         case IO_E_ERASED:
         {
            /* Read successful. Because some drivers don't return IO_E_ERASED,
             * we have to assume that memory is erased if all bytes show the erased value
             * and compare against the erased value later. */
            itemState = FBL_NVPATTERN_STATE_ERASED;
            break;
         }
         default:
         {
            /* Read error */
            itemState = FBL_NVPATTERN_STATE_READERROR;
            assertFbl(0u, kFblSysAssertParameterOutOfRange); /* PRQA S 2741, 4558 */ /* MD_FblNvPattern_Assertion */
            break;
         }
      }

      if (itemState == FBL_NVPATTERN_STATE_ERASED)
      {
         /* Check all bytes of marker/mask */
         for (i = 0u; i < currentReadLength; i++)
         {
            /* Compare one byte of pattern against the expected value/erased value */
            if ((compareValue != DONT_COMPARE) && (pageBuffer.data[i] == compareValue[readOffset + i]))
            {
               countExpectedValue++;
            }
            else if (pageBuffer.data[i] == memErasedValue)
            {
               countErasedValue++;
            }
            else /* (pageBuffer.data[i] != memErasedValue) */
            {
               /* This might happen in case of unintended writes to pattern area */
               itemState = FBL_NVPATTERN_STATE_UNEXPECTEDVALUE;
            }

            /* In case of an unexpected value break out of the for loop */
            if (itemState == FBL_NVPATTERN_STATE_UNEXPECTEDVALUE)
            {
               break;
            }
         }

         /* Check if there were expected and erased bytes included */
         if (   (countErasedValue != 0u)
             && (countExpectedValue != 0u))
         {
            itemState = FBL_NVPATTERN_STATE_UNEXPECTEDVALUE;
         }
      }

      /* Abort in case of error */
      if ((itemState == FBL_NVPATTERN_STATE_UNEXPECTEDVALUE) || (itemState == FBL_NVPATTERN_STATE_READERROR))
      {
         break;
      }

      /* Update variables */
      localLength -= currentReadLength;
      readOffset += currentReadLength;
   }

   /* Set itemState according to the counts */
   if (length == 0u)
   {
      itemState = FBL_NVPATTERN_STATE_NOMEMSEGMENT;
   }
   else if (countErasedValue == length)
   {
      itemState = FBL_NVPATTERN_STATE_ERASED;
   }
   else if (countExpectedValue == length)
   {
      itemState = FBL_NVPATTERN_STATE_EXPECTEDVALUE;
   }
   else
   {
      /* itemState already set correctly */
   }

   return itemState;
}


/***********************************************************************************************************************
 *  FblNvPatternGetPatternBlockDescriptorByStartAddress
 **********************************************************************************************************************/
/*! \brief       Prepares a dummy block descriptor spaning only the pattern region
 *  \param[in]   patternStartAddress Start address of pattern region
 *  \param[out]  blockDescriptor Pointer to RAM buffer in which the dummy block descriptor can be stored
 *  \return      kFblOk: block descriptor created successfully , kFblFailed: Else
 **********************************************************************************************************************/
static tFblResult FblNvPatternGetPatternBlockDescriptorByStartAddress( IO_PositionType patternStartAddress,
                                                                       V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor )
{
   tFblResult result = kFblFailed;
   IO_SizeType patternLength;
   IO_SizeType patternRegionLength = 0u;

   /* Get memory segment index */
   memSegment = FblMemSegmentNrGet(patternStartAddress);

   /* Check if a valid memory segment in flash block table was found */
   if (memSegment >= 0)
   {
      /* Get offset for last pattern/property as this offset equals size of the complete pattern region */
      /* PRQA S 4342 1 */ /* MD_FblNvPattern_4342 */
      patternRegionLength = FblNvPatternGetPatternOffset((tFblNvPatternId)((vuintx)kFblNvPatternId_Last - 1u), &patternLength);

      blockDescriptor->blockStartAddress = patternStartAddress;
      blockDescriptor->blockLength = patternRegionLength;
      result = kFblOk;
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternGetPatternOffset
 **********************************************************************************************************************/
/*! \brief       Returns the offset for a certain pattern/property
 *  \details     The returned offset is calculated from the end of the pattern region. Therefore a lower offset has
 *               the meaning that the respective pattern is located closer to the end of the pattern region.
 *  \pre         Global variable memSegment is set to the block where the pattern region is stored
 *  \param[in]   patternId ID of pattern/property for which the offset shall be calculated
 *  \param[out]  patternOffset Pointer to RAM buffer to store the offset to the requested pattern
 *  \param[out]  pPatternLength Pointer to RAM buffer to store the actual write address of the NvPattern or property
 *  \return      Offset to the requested pattern
 **********************************************************************************************************************/
static IO_SizeType FblNvPatternGetPatternOffset( tFblNvPatternId patternId,
                                                 V_MEMRAM1 IO_SizeType V_MEMRAM2 V_MEMRAM3 * patternLength )
{
   IO_SizeType patternOffset = 0u;
   IO_SizeType localPatternLength = 0u;
   vuintx i;
   vuintx upperBound;

   /* Segment size must be a power of two */
   assertFbl(IS_POWOFTWO(MemDriver_SegmentSize), kFblSysAssertParameterOutOfRange);

   /* Adjust patternLength to write segment size of device */
   localPatternLength = PAGE_ALIGN(kFblNvPatternSize, (IO_SizeType)MemDriver_SegmentSize);

   /* Add size of NvPatterns to offset...
      * Upper bound for loop is either the last NvPattern element if base address of block property has been requested
      * or patternId in case the requested element is a NvPattern */
   upperBound = (patternId > kFblNvPatternId_Separator) ? ((vuintx)kFblNvPatternId_Separator - 1u) : (vuintx)patternId;

   for (i = 0u; i < upperBound; i++)
   {
#if defined( FBL_NVPATTERN_ENABLE_MULTIPLE_VALIDATION )
      patternOffset += (localPatternLength * ((IO_SizeType)fblNvPatternMultiplicity[i] * 2u));
#else
      patternOffset += (localPatternLength * 2u);
#endif /* FBL_NVPATTERN_ENABLE_MULTIPLE_VALIDATION */
   }

   /* ... then add sizes of block property elements */
#if defined( FBL_NVPATTERN_ENABLE_BLOCK_PROPERTIES )
   /* Skip this loop (set upperBound to 0) if the requested Id is not a block property,
    * otherwise subtract kFblNvPatternId_Separator from pattern id to get the index into fblNvPatternElementLength */
   upperBound = (patternId > kFblNvPatternId_Separator) ? ((vuintx)patternId - (vuintx)kFblNvPatternId_Separator) : 0u;

   for (i = 0u; i < upperBound; i++)
   {
      patternOffset += PAGE_ALIGN(fblNvBlockProperties[i].length, (IO_SizeType)MemDriver_SegmentSize);
      localPatternLength = PAGE_ALIGN(fblNvBlockProperties[i].length, (IO_SizeType)MemDriver_SegmentSize);
   }
#endif /* FBL_NVPATTERN_ENABLE_BLOCK_PROPERTIES */

   /* Put results into arguments */
   *patternLength = localPatternLength;

   return patternOffset;
}
/***********************************************************************************************************************
 *   GLOBAL FUNCTIONS
 **********************************************************************************************************************/

/***********************************************************************************************************************
 *  FblNvPatternGetPatternState
 **********************************************************************************************************************/
/*! \brief         Determines the state of one or a set of marker/mask pairs.
 *  \details       Iterates through all sub-patterns in case of multiple validation.
 *                 Returns address and length of pattern which stores the currently relevant state.
 *  \param[in]     pBlockDescriptor descriptor of the logical block which contains the NvPattern
 *  \param[in]     patternId Id of pattern to read
 *  \param[out]    pPatternAddress Pointer to RAM buffer which keeps the pattern base address
 *  \param[out]    pPatternLength Pointer to RAM buffer which keeps the actual write length of the pattern
 *  \return        The state of the (sub) pattern which holds the current state of the pattern
 **********************************************************************************************************************/
tFblNvPatternState FblNvPatternGetPatternState( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * pBlockDescriptor,
                                                       tFblNvPatternId patternId,
                                                       V_MEMRAM1 IO_PositionType V_MEMRAM2 V_MEMRAM3 * pPatternAddress,
                                                       V_MEMRAM1 IO_SizeType V_MEMRAM2 V_MEMRAM3 * pPatternLength )
{
   vuint32              multiplicity;
   tFblNvPatternState   patternState;
   IO_PositionType      patternAddress;
   vuint8               fblMarkerValue[] = kFblNvMarkerValue; /* PRQA S 3678 */ /* MD_FblNvPattern_3678_LocalVariable */
   vuint8               fblMaskValue[] = kFblNvMaskValue; /* PRQA S 3678 */ /* MD_FblNvPattern_3678_LocalVariable */

   /* PRQA S 2742, 2880 2 */ /* MD_FblNvPattern_Assertion */
   assertFblUser(sizeof(fblMarkerValue) == kFblNvPatternSize, kFblSysAssertParameterOutOfRange);
   assertFblUser(sizeof(fblMaskValue) == kFblNvPatternSize, kFblSysAssertParameterOutOfRange);

   /* Initialize variables */
   patternAddress = *pPatternAddress;
   patternState.markerState = FBL_NVPATTERN_STATE_NOMEMSEGMENT;
   patternState.maskState = FBL_NVPATTERN_STATE_NOMEMSEGMENT;
   memSegment = -1;

   /* Get Base Address of pattern if id is valid */
   if (IS_VALID_PATTERN_ID(patternId))
   {
      memSegment = FblNvPatternGetBaseAddrByBlockDescriptor(pBlockDescriptor, patternId, &patternAddress, pPatternLength);
   }

   if (memSegment >= 0)
   {
      /* Determine number of sub-patterns */
#if defined( FBL_NVPATTERN_ENABLE_MULTIPLE_VALIDATION )
      multiplicity = (vuint32)fblNvPatternMultiplicity[((vuintx)patternId - 1u)];
#else
      multiplicity = 1u;
#endif
   }
   else
   {
      /* No memory segment found, abort */
      multiplicity = 0u;
   }

   /* Go through all configured sub-patterns until we have found the sub-pattern which holds the most recent state */
   while (multiplicity >= 1u)
   {
      /* Poll watchdog function */
      (void)FblRealTimeSupport();

      /* Read marker and mask area */
      patternState.markerState = FblNvPatternGetPatternItemState(patternAddress, kFblNvPatternSize, fblMarkerValue);
      patternState.maskState = FblNvPatternGetPatternItemState((patternAddress + *pPatternLength), kFblNvPatternSize, fblMaskValue);

      if (     ((patternState.markerState == FBL_NVPATTERN_STATE_EXPECTEDVALUE) && (patternState.maskState == FBL_NVPATTERN_STATE_ERASED))
            || ((patternState.markerState == FBL_NVPATTERN_STATE_ERASED)        && (patternState.maskState == FBL_NVPATTERN_STATE_ERASED)))
      {
         /* One pattern item is in erased state, this is the most recent state of the pattern */
         break;
      }

      /* Get base address of next sub-pattern */
      patternAddress += (*pPatternLength * 2u);

      /* Decrement number of sub-patterns to process */
      multiplicity--;
   }

   /* Write current address to passed argument pointer */
   *pPatternAddress = patternAddress;

   return patternState;
}


/***********************************************************************************************************************
 *  FblNvPatternGetPatternStateByStartAddress
 **********************************************************************************************************************/
/*! \brief         Determines the state of one or a set of marker/mask pairs.
 *  \details       Iterates through all sub-patterns in case of multiple validation.
 *                 Returns address and length of pattern which stores the currently relevant state.
 *  \param[in]     patternStartAddress Start address of pattern region
 *  \param[in]     patternId Id of pattern to read
 *  \param[out]    pPatternAddress Pointer to RAM buffer which keeps the address of the pattern
 *  \param[out]    pPatternLength Pointer to RAM buffer which keeps the actual write length of the pattern
 *  \return        The state of the (sub) pattern which holds the current state of the pattern
 **********************************************************************************************************************/
tFblNvPatternState FblNvPatternGetPatternStateByStartAddress( IO_PositionType patternStartAddress,
                                                              tFblNvPatternId patternId,
                                                              V_MEMRAM1 IO_PositionType V_MEMRAM2 V_MEMRAM3 * patternAddress,
                                                              V_MEMRAM1 IO_SizeType V_MEMRAM2 V_MEMRAM3 * patternLength )
{
   tBlockDescriptor dummyBlockDescriptor = { 0u };
   tFblNvPatternState patternState = { 0u };

   if(FblNvPatternGetPatternBlockDescriptorByStartAddress(patternStartAddress, &dummyBlockDescriptor) == kFblOk)
   {
      patternState = FblNvPatternGetPatternState(&dummyBlockDescriptor, patternId, patternAddress, patternLength);
   }

   return patternState;
}

/***********************************************************************************************************************
 *  FblNvPatternGetBaseAddr
 **********************************************************************************************************************/
/*! \brief         Calculates base address and actual (write) length of pattern and mask
 *  \pre           LBT is present
 *  \param[in]     blockNr Logical block number
 *  \param[in]     patternId ID of the pattern which shall be checked
 *  \param[out]    pPatternAddress Pointer to RAM buffer which keeps the pattern base address
 *  \param[out]    pPatternLength Pointer to RAM buffer which keeps the actual write length of the pattern
 *  \return        memSegment of the pattern location or -1 in case of an error
 **********************************************************************************************************************/
vsint16 FblNvPatternGetBaseAddr( vuint8 blockNr,
                                 tFblNvPatternId patternId,
                                 V_MEMRAM1 IO_PositionType V_MEMRAM2 V_MEMRAM3 * pPatternAddress,
                                 V_MEMRAM1 IO_SizeType V_MEMRAM2 V_MEMRAM3 * pPatternLength )
{
   tBlockDescriptor  blockDescriptor;
   tFblResult        result;

   result = FblLbtGetBlockDescriptorByNr(blockNr, &blockDescriptor);

   if (result == kFblOk)
   {
      memSegment = FblNvPatternGetBaseAddrByBlockDescriptor(&blockDescriptor, patternId, pPatternAddress, pPatternLength);
   }
   else
   {
      /* Invalid block number passed, signal failure via return value */
      memSegment = -1;
   }

   return memSegment;
}

/***********************************************************************************************************************
 *  FblNvPatternGetBaseAddrByBlockDescriptor
 **********************************************************************************************************************/
/*! \brief         Calculates base address and actual (write) length of an NvPattern or block property element
 *  \param[in]     blockDescriptor Logical block descriptor
 *  \param[in]     patternId ID of the NvPattern or block property for which the base address is requested
 *  \param[out]    pPatternAddress Pointer to RAM buffer to store the base address
 *  \param[out]    pPatternLength Pointer to RAM buffer to store the actual write address of the NvPattern or property
 *  \return        memSegment of the pattern location or -1 in case of an error
 **********************************************************************************************************************/
vsint16 FblNvPatternGetBaseAddrByBlockDescriptor( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                                  tFblNvPatternId patternId,
                                                  V_MEMRAM1 IO_PositionType V_MEMRAM2 V_MEMRAM3 * patternAddress,
                                                  V_MEMRAM1 IO_SizeType V_MEMRAM2 V_MEMRAM3 * patternLength )
{
   IO_PositionType   lastBlockAddress;

   /* Get address of last byte in block */
   lastBlockAddress = blockDescriptor->blockStartAddress + (blockDescriptor->blockLength - 1u);

   /* Get corresponding memory segment index */
   memSegment = FblMemSegmentNrGet(lastBlockAddress);

   /* Check if a valid memory segment in flash block table was found */
   if (memSegment >= 0)
   {
      IO_SizeType offsetLength = FblNvPatternGetPatternOffset(patternId, patternLength);

      /* Calculate pattern address */
      *patternAddress = (lastBlockAddress - offsetLength) + 1u;

      /* Check for range overflow */
      assertFbl(*patternAddress >= blockDescriptor->blockStartAddress, kFblSysAssertParameterOutOfRange);

      /* All NvPattern data must be stored in the same memSegment */
      assertFbl(FblMemSegmentNrGet(*patternAddress) == memSegment, kFblSysAssertParameterOutOfRange);
   }
   else
   {
      /* No memory segment found, this is a configuration error. */
      assertFbl(0u, kFblSysAssertParameterOutOfRange); /* PRQA S 2741, 4558 */ /* MD_FblNvPattern_Assertion */
   }

   return memSegment;
}

/***********************************************************************************************************************
 *  FblNvPatternGetPatternRegionSizeByStartAddress
 **********************************************************************************************************************/
/*! \brief         Calculates length of complete region containing NvPattern or block property elements
 *  \param[in]     patternStartAddress Start address of pattern region
 *  \param[out]    patternLength Pointer to RAM buffer to store the region size
 *  \return        kFblOk: Pattern region size calculated successfully , kFblFailed: Else
 **********************************************************************************************************************/
tFblResult FblNvPatternGetPatternRegionSizeByStartAddress( IO_PositionType patternStartAddress,
                                                           V_MEMRAM1 IO_SizeType V_MEMRAM2 V_MEMRAM3 * patternRegionLength)
{
   tBlockDescriptor blockDescriptor;
   tFblResult result = FblNvPatternGetPatternBlockDescriptorByStartAddress(patternStartAddress, &blockDescriptor);

   if (result == kFblOk)
   {
      *patternRegionLength = (IO_SizeType)blockDescriptor.blockLength;
   }

   return result;
}
/***********************************************************************************************************************
 *  FblNvPatternSet
 **********************************************************************************************************************/
/*! \brief       Writes the pattern into the flash memory.
 *  \details     The location of the pattern will be taken from the logical block table
 *  \param[in]   blockDescriptor Pointer to the logical block descriptor
 *  \param[in]   patternId ID of the pattern which shall be set
 *  \return      kFblOk: pattern successfully written, kFblFailed: Error writing pattern
 **********************************************************************************************************************/
tFblResult FblNvPatternSet( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor, tFblNvPatternId patternId )
{
   tBlockDescriptor localBlockDescriptor;
   tFblResult result;

   result = FblLbtGetBlockDescriptorByNr(blockDescriptor->blockNr, &localBlockDescriptor);

   if (result == kFblOk)
   {
      result = FblNvPatternSetByBlockDescriptor(&localBlockDescriptor, patternId);
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternSetByBlockDescriptor
 **********************************************************************************************************************/
/*! \brief       Writes the pattern into the flash memory.
 *  \details     The location of the pattern will be taken from the logical block descriptor.
 *  \param[in]   blockDescriptor Pointer to the logical block descriptor
 *  \param[in]   patternId ID of the pattern which shall be set
 *  \return      kFblOk: pattern successfully written or already set, kFblFailed: Error writing pattern
 **********************************************************************************************************************/
tFblResult FblNvPatternSetByBlockDescriptor( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                             tFblNvPatternId patternId )
{
   IO_PositionType               patternAddress;
   IO_SizeType                   patternLength;
   tFblResult                    result;
   tFblNvPatternState            patternState;
   vuint8                        fblMarkerValue[] = kFblNvMarkerValue; /* PRQA S 3678 */ /* MD_FblNvPattern_3678_LocalVariable */

   /* PRQA S 2742, 2880 1 */ /* MD_FblNvPattern_Assertion */
   assertFblUser(sizeof(fblMarkerValue) == kFblNvPatternSize, kFblSysAssertParameterOutOfRange);

   /* Initialize variables */
   result = kFblFailed;

   /* Check state of pattern */
   patternState = FblNvPatternGetPatternState(blockDescriptor, patternId, &patternAddress, &patternLength);

   /* Proceed if marker is blank */
   if (patternState.markerState == FBL_NVPATTERN_STATE_ERASED)
   {
      /* Write pattern */
      if (kFblOk == FblNvPatternWritePageAligned(fblMarkerValue, patternAddress, kFblNvPatternSize))
      {
         /* Verify that pattern is valid now */
         patternState = FblNvPatternGetPatternState(blockDescriptor, patternId, &patternAddress, &patternLength);

         if (patternState.markerState == FBL_NVPATTERN_STATE_EXPECTEDVALUE)
         {
#if defined( FBLNVPATTERN_ENABLE_SIGN_PRESENCE_PATTERN )
            result = FblNvPatternUpdateCmacByBlockDescriptor(blockDescriptor, patternId);
#else
            result = kFblOk;
#endif /* FBLNVPATTERN_ENABLE_SIGN_PRESENCE_PATTERN */
         }
      }
   }
   else if ((patternState.markerState == FBL_NVPATTERN_STATE_EXPECTEDVALUE) && (patternState.maskState == FBL_NVPATTERN_STATE_ERASED))
   {
      /* Pattern already has expected state */
      result = kFblOk;
   }
   else
   {
      /* For the sake of completeness */
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternSetByStartAddress
 **********************************************************************************************************************/
/*! \brief       Writes the pattern into the flash memory.
 *  \param[in]   patternStartAddress Start address of pattern region
 *  \param[in]   patternId ID of the pattern which shall be set
 *  \return      kFblOk: pattern successfully written or already set, kFblFailed: Error writing pattern
 **********************************************************************************************************************/
tFblResult FblNvPatternSetByStartAddress( IO_PositionType patternStartAddress,
                                          tFblNvPatternId patternId)
{
   tBlockDescriptor dummyBlockDescriptor = { 0u };
   tFblResult result = FblNvPatternGetPatternBlockDescriptorByStartAddress(patternStartAddress, &dummyBlockDescriptor);

   if (result == kFblOk)
   {
      result = FblNvPatternSetByBlockDescriptor(&dummyBlockDescriptor, patternId);
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternClr
 **********************************************************************************************************************/
/*! \brief       Writes the mask to invalidate the pattern
 *  \details     The location of the pattern will be taken from the logical block table
 *  \param[in]   blockDescriptor Pointer to the logical block descriptor
 *  \param[in]   patternId ID of the pattern which shall be set
 *  \return      kFblOk: Mask for invalidation successfully written, kFblFailed: Error writing invalidation mask
 **********************************************************************************************************************/
tFblResult FblNvPatternClr( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor, tFblNvPatternId patternId )
{
   tBlockDescriptor localBlockDescriptor;
   tFblResult result;

   result = FblLbtGetBlockDescriptorByNr(blockDescriptor->blockNr, &localBlockDescriptor);

   if (result == kFblOk)
   {
      result = FblNvPatternClrByBlockDescriptor(&localBlockDescriptor, patternId);
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternClrByBlockDescriptor
 **********************************************************************************************************************/
/*! \brief       Writes the mask to invalidate the pattern
 *  \details     The location of the pattern will be taken from the passed logical block descriptor
 *  \param[in]   blockDescriptor Pointer to the logical block descriptor
 *  \param[in]   patternId ID of the pattern which shall be set
 *  \return      kFblOk: Mask for invalidation successfully written, kFblFailed: Error writing invalidation mask
 **********************************************************************************************************************/
tFblResult FblNvPatternClrByBlockDescriptor( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                             tFblNvPatternId patternId )
{
   tFblResult                    result;
   IO_PositionType               patternAddress;
   IO_SizeType                   patternLength;
   tFblNvPatternState            patternState;
   vuint8                        fblMaskValue[] = kFblNvMaskValue; /* PRQA S 3678 */ /* MD_FblNvPattern_3678_LocalVariable */

   /* PRQA S 2742, 2880 1 */ /* MD_FblNvPattern_Assertion */
   assertFblUser(sizeof(fblMaskValue) == kFblNvPatternSize, kFblSysAssertParameterOutOfRange);

   /* Check state of pattern */
   patternState = FblNvPatternGetPatternState(blockDescriptor, patternId, &patternAddress, &patternLength);

   /* Only set mask if pattern is set to valid */
   if ((patternState.markerState == FBL_NVPATTERN_STATE_EXPECTEDVALUE) && (patternState.maskState == FBL_NVPATTERN_STATE_ERASED))
   {
      /* Write mask value to flash */
      result = FblNvPatternWritePageAligned(fblMaskValue, (patternAddress + patternLength), kFblNvPatternSize);
   }
   else
   {
      /* Pattern is already in invalid state, do nothing. Illegal states are detected with assertions only. */
      result = kFblOk;
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternClrByStartAddress
 **********************************************************************************************************************/
/*! \brief       Writes the mask to invalidate the pattern
 *  \param[in]   patternStartAddress Start address of pattern region
 *  \param[in]   patternId ID of the pattern which shall be cleared
 *  \return      kFblOk: Mask for invalidation successfully written, kFblFailed: Error writing invalidation mask
 **********************************************************************************************************************/
tFblResult FblNvPatternClrByStartAddress( IO_PositionType patternStartAddress, tFblNvPatternId patternId )
{
   tBlockDescriptor dummyBlockDescriptor = { 0u };
   tFblResult result = FblNvPatternGetPatternBlockDescriptorByStartAddress(patternStartAddress, &dummyBlockDescriptor);

   if (result == kFblOk)
   {
      result = FblNvPatternClrByBlockDescriptor(&dummyBlockDescriptor, patternId);
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternGet
 **********************************************************************************************************************/
/*! \brief       Checks if the pattern state is 'valid' (correct pattern value is set/mask is erased)
 *  \details     The location of the pattern and mask is taken from the logical block table
 *  \param[in]   blockDescriptor Pointer to the logical block descriptor
 *  \param[in]   patternId ID of the pattern which shall be checked
 *  \return      kFblOk:     Pattern is set and mask is erased,
 *               kFblFailed: Pattern is not set or mask not erased
 **********************************************************************************************************************/
tFblResult FblNvPatternGet( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor, tFblNvPatternId patternId )
{
   tBlockDescriptor localBlockDescriptor;
   tFblResult result;

   result = FblLbtGetBlockDescriptorByNr(blockDescriptor->blockNr, &localBlockDescriptor);

   if (result == kFblOk)
   {
      result = FblNvPatternGetByBlockDescriptor(&localBlockDescriptor, patternId);
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternGetByBlockDescriptor
 **********************************************************************************************************************/
/*! \brief       Checks if the pattern state is 'valid' (correct pattern value is set/mask is erased)
 *  \details     The location of the pattern and mask is taken from the passed logical block descriptor
 *  \param[in]   blockDescriptor Pointer to the logical block descriptor
 *  \param[in]   patternId ID of the pattern which shall be checked
 *  \return      kFblOk:     Pattern is set and mask is erased,
 *               kFblFailed: Pattern is not set or mask not erased
 **********************************************************************************************************************/
tFblResult FblNvPatternGetByBlockDescriptor( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                             tFblNvPatternId patternId )
{
   IO_PositionType      patternAddress;
   IO_SizeType          patternLength;
   tFblNvPatternState   patternState;
   tFblResult           result;

   /* Initialize result */
   result = kFblFailed;

   /* Check state of pattern */
   patternState = FblNvPatternGetPatternState(blockDescriptor, patternId, &patternAddress, &patternLength);
   if ((patternState.markerState == FBL_NVPATTERN_STATE_EXPECTEDVALUE) && (patternState.maskState == FBL_NVPATTERN_STATE_ERASED))
   {
#if defined( FBLNVPATTERN_ENABLE_SIGN_PRESENCE_PATTERN )
      result = FblNvPatternVerifyCmacByBlockDescriptor(blockDescriptor, patternId);
#else
      result = kFblOk;
#endif /* FBLNVPATTERN_ENABLE_SIGN_PRESENCE_PATTERN */
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternGetByStartAddress
 **********************************************************************************************************************/
/*! \brief       Checks if the pattern state is 'valid' (correct pattern value is set/mask is erased)
 *  \param[in]   patternStartAddress Start address of pattern region
 *  \param[in]   patternId ID of the pattern which shall be checked
 *  \return      kFblOk:     Pattern is set and mask is erased,
 *               kFblFailed: Pattern is not set or mask not erased
 **********************************************************************************************************************/
tFblResult FblNvPatternGetByStartAddress( IO_PositionType patternStartAddress, tFblNvPatternId patternId )
{
   tBlockDescriptor dummyBlockDescriptor = { 0u };
   tFblResult result = FblNvPatternGetPatternBlockDescriptorByStartAddress(patternStartAddress, &dummyBlockDescriptor);

   if (result == kFblOk)
   {
      result = FblNvPatternGetByBlockDescriptor(&dummyBlockDescriptor, patternId);
   }

   return result;
}

#if defined( FBL_NVPATTERN_ENABLE_BLOCK_PROPERTIES )
# if defined( FBLNVPATTERN_ENABLE_SIGN_PRESENCE_PATTERN )
/***********************************************************************************************************************
 *  FblNvPatternGetPropertyStateAndAddrByBlockDescriptor
 **********************************************************************************************************************/
/*! \brief        Retrieve the property state and address
 *  \details      The location of the property is taken from the passed logical block descriptor
 *  \param[in]    blockDescriptor Pointer to the logical block descriptor
 *  \param[in]    propertyId Id of the property to check the erase state
 *  \param[out]   address Pointer to the property address
 *  \param[out]   state Pointer to the property state
 *  \return       kFblOk:     Property state and address are read,
 *                kFblFailed: Property state and address are not read
 **********************************************************************************************************************/
tFblResult FblNvPatternGetPropertyStateAndAddrByBlockDescriptor(const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                                                tFblNvPatternId propertyId,
                                                                V_MEMRAM1 IO_PositionType V_MEMRAM2 V_MEMRAM3 * address,
                                                                V_MEMRAM1 tFblNvPatternItemState V_MEMRAM2 V_MEMRAM3 * state)
{
   IO_PositionType propertyAddress = 0u;
   IO_SizeType     propertyLength;
   IO_SizeType     actualPropertyLength;
   tFblResult      result = kFblFailed;

   /* Init variables */
   *state = FBL_NVPATTERN_STATE_UNEXPECTEDVALUE;
   *address = 0u;

   /* Check if requested Id is valid */
   if (IS_VALID_PROPERTY_ID(propertyId))
   {
      /* Get address of element. Length is not used. */
      if (FblNvPatternGetBaseAddrByBlockDescriptor(blockDescriptor, propertyId, &propertyAddress, &propertyLength) >= 0)
      {
         /* Get the property length */
         actualPropertyLength = fblNvBlockProperties[(vuintx)propertyId - (vuintx)kFblNvPatternId_Separator - 1u].length;
         *state = FblNvPatternGetPatternItemState(propertyAddress, actualPropertyLength, DONT_COMPARE);
         *address = propertyAddress;
         result = kFblOk;
      }
   }

   return result;
}
# endif /* FBLNVPATTERN_ENABLE_SIGN_PRESENCE_PATTERN */

/***********************************************************************************************************************
 *  FblNvPatternGetProperty
 **********************************************************************************************************************/
/*! \brief         Reads value of a block property element
 *  \details       Location of the property is taken from the logical block table
 *  \param[in]     blockDescriptor Pointer to the logical block descriptor
 *  \param[in]     propertyId Id of property to read
 *  \param[in]     bufferLength Length of destination buffer
 *  \param[out]    pDestBuffer Pointer to RAM where read property value will be stored
 *  \return        kFblOk: Property successfully read, kFblFailed: Error reading property
 **********************************************************************************************************************/
tFblResult FblNvPatternGetProperty( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                      tFblNvPatternId propertyId,
                                      IO_SizeType bufferLength,
                                      vuint8* pDestBuffer )
{
   tBlockDescriptor localBlockDescriptor;
   tFblResult result;

   result = FblLbtGetBlockDescriptorByNr(blockDescriptor->blockNr, &localBlockDescriptor);

   if (result == kFblOk)
   {
      result = FblNvPatternGetPropertyByBlockDescriptor(&localBlockDescriptor, propertyId, bufferLength, pDestBuffer);
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternGetPropertyByBlockDescriptor
 **********************************************************************************************************************/
/*! \brief         Reads value of an block property element
 *  \details       The location of the block property is taken from the passed logical block descriptor
 *  \param[in]     blockDescriptor Pointer to the logical block descriptor
 *  \param[in]     propertyId Id of the property to read
 *  \param[in]     bufferLength Length of destination buffer
 *  \param[out]    pDestBuffer Pointer to RAM where the read property value will be stored
 *  \return        kFblOk: Property successfully read, kFblFailed: Error reading property
 **********************************************************************************************************************/
tFblResult FblNvPatternGetPropertyByBlockDescriptor( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                                       tFblNvPatternId propertyId,
                                                       IO_SizeType bufferLength,
                                                       vuint8* pDestBuffer )
{
   IO_PositionType      propertyAddress;
   IO_SizeType          propertyLength;
   IO_SizeType          sizeToRead = 0u;
   tFblNvPatternId      depPattern;
   tFblResult           result;

   result = kFblFailed;
   propertyAddress = 0u;

   /* Check if requested Id is valid */
   if (IS_VALID_PROPERTY_ID(propertyId))
   {
      sizeToRead = fblNvBlockProperties[(vuintx)propertyId - (vuintx)kFblNvPatternId_Separator - 1u].length;
      depPattern = fblNvBlockProperties[(vuintx)propertyId - (vuintx)kFblNvPatternId_Separator - 1u].dependentPattern;

      /* Check if buffer length is sufficient for storing requested data */
      if (sizeToRead <= bufferLength)
      {
         /* If dependent pattern configured, check its validity */
         if (depPattern != kFblNvPatternId_Invalid)
         {
            result = FblNvPatternGetByBlockDescriptor(blockDescriptor, depPattern);
         }
         else
         {
            result = kFblOk;
         }
      }
   }

   if (result == kFblOk)
   {
      /* Get address of property (length is not needed) */
      if (FblNvPatternGetBaseAddrByBlockDescriptor(blockDescriptor, propertyId, &propertyAddress, &propertyLength) >= 0)
      {
         if (MemDriver_RReadSync(pDestBuffer, sizeToRead, propertyAddress) == IO_E_OK)
         {
            result = kFblOk;
         }
      }
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternGetPropertyByStartAddress
 **********************************************************************************************************************/
/*! \brief       Reads value of an block property element
 *  \param[in]   patternStartAddress Start address of pattern region
 *  \param[in]   propertyId Id of the property to read
 *  \param[in]   bufferLength Length of destination buffer
 *  \param[out]  pDestBuffer Pointer to RAM where the read property value will be stored
 *  \return      kFblOk: Property successfully read, kFblFailed: Error reading property
 **********************************************************************************************************************/
tFblResult FblNvPatternGetPropertyByStartAddress( IO_PositionType patternStartAddress,
                                                  tFblNvPatternId propertyId,
                                                  IO_SizeType bufferLength,
                                                  vuint8 * destBuffer )
{
   tBlockDescriptor dummyBlockDescriptor = { 0u };
   tFblResult result = FblNvPatternGetPatternBlockDescriptorByStartAddress(patternStartAddress, &dummyBlockDescriptor);

   if (result == kFblOk)
   {
      result = FblNvPatternGetPropertyByBlockDescriptor(&dummyBlockDescriptor, propertyId, bufferLength, destBuffer);
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternSetProperty
 **********************************************************************************************************************/
/*! \brief         Writes value of a block property
 *  \details       The location of the property is taken from the logical block table
 *  \param[in]     blockDescriptor Pointer to the logical block descriptor
 *  \param[in]     pSrcBuffer Pointer to RAM with the data to write
 *  \param[in]     propertyId Id of the block property to write
 *  \return        kFblOk: Property successfully written, kFblFailed: Error writing property
 **********************************************************************************************************************/
tFblResult FblNvPatternSetProperty( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                      const vuint8* pSrcBuffer,
                                      tFblNvPatternId propertyId )
{
   tBlockDescriptor localBlockDescriptor;
   tFblResult result;

   result = FblLbtGetBlockDescriptorByNr(blockDescriptor->blockNr, &localBlockDescriptor);

   if (result == kFblOk)
   {
      result = FblNvPatternSetPropertyByBlockDescriptor(&localBlockDescriptor, pSrcBuffer, propertyId);
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternSetPropertyByBlockDescriptor
 **********************************************************************************************************************/
/*! \brief         Writes value of a block property
 *  \details       The location of the property is taken from the passed logical block descriptor
 *  \param[in]     blockDescriptor Pointer to the logical block descriptor
 *  \param[in]     pSrcBuffer Pointer to RAM with the data to write
 *  \param[in]     propertyId Id of the property to write
 *  \return        kFblOk: Property successfully written, kFblFailed: Error writing property
 **********************************************************************************************************************/
tFblResult FblNvPatternSetPropertyByBlockDescriptor( const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                                       const vuint8* pSrcBuffer,
                                                       tFblNvPatternId propertyId )
{
   IO_PositionType         propertyAddress;
   IO_SizeType             propertyLength;
   IO_SizeType             actualPropertyLength;
   tFblResult              result;

   /* Initialize variables */
   result = kFblFailed;
   propertyAddress = 0u;

   /* Check if requested Id is valid */
   if (IS_VALID_PROPERTY_ID(propertyId))
   {
      /* Get address and length of element */
      if (FblNvPatternGetBaseAddrByBlockDescriptor(blockDescriptor, propertyId, &propertyAddress, &propertyLength) >= 0)
      {
         /* Only write block property to memory if it has not been set before */
         actualPropertyLength = fblNvBlockProperties[(vuintx)propertyId - (vuintx)kFblNvPatternId_Separator - 1u].length;
         if (FblNvPatternGetPatternItemState(propertyAddress, actualPropertyLength, DONT_COMPARE) == FBL_NVPATTERN_STATE_ERASED)
         {
            result = FblNvPatternWritePageAligned(pSrcBuffer, propertyAddress, actualPropertyLength);
         }
      }
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternSetPropertyByStartAddress
 **********************************************************************************************************************/
/*! \brief       Writes value of a block property
 *  \param[in]   patternStartAddress Start address of pattern region
 *  \param[in]   pSrcBuffer Pointer to RAM with the data to write
 *  \param[in]   propertyId Id of the property to write
 *  \return      kFblOk: Property successfully written, kFblFailed: Error writing property
 **********************************************************************************************************************/
tFblResult FblNvPatternSetPropertyByStartAddress( IO_PositionType patternStartAddress,
                                                  const vuint8* srcBuffer,
                                                  tFblNvPatternId propertyId )
{
   tBlockDescriptor dummyBlockDescriptor = { 0u };
   tFblResult result = FblNvPatternGetPatternBlockDescriptorByStartAddress(patternStartAddress, &dummyBlockDescriptor);

   if (result == kFblOk)
   {
      result = FblNvPatternSetPropertyByBlockDescriptor(&dummyBlockDescriptor, srcBuffer, propertyId);
   }

   return result;
}

# if defined( FBLNVPATTERN_ENABLE_SIGN_PRESENCE_PATTERN )
/***********************************************************************************************************************
 *  FblNvPatternSignedPresencePatternInit
 **********************************************************************************************************************/
/*! \brief       Init the AES key and update the CMAC for all valid logical block during the first startup of ECU
 **********************************************************************************************************************/
void FblNvPatternSignedPresencePatternInit(void)
{
   ApplFblNvPatternSignedPresencePatternInit();
}

/***********************************************************************************************************************
 *  FblNvPatternUpdateCmacByBlockDescriptor
 **********************************************************************************************************************/
/*! \brief       Calculate the CMAC for the received patternId and update it in the respective property
 *  \details     The address of the property will be fetched from the logical block descriptor
 *  \param[in]   blockDescriptor Pointer to the logical block descriptor
 *  \param[in]   patternId Id of the pattern for which the CMAC needs to be calculated and written
 *  \return      kFblOk: CMAC updated
 *               kFblFailed: CMAC not updated
 **********************************************************************************************************************/
tFblResult FblNvPatternUpdateCmacByBlockDescriptor(const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                                   tFblNvPatternId patternId)
{
   tFblResult              result = kFblFailed;
   IO_PositionType         propertyAddress = 0u;
   tFblNvPatternItemState  propertyState;
   vuint8                  signatureBuffer[FBL_NVPATTERN_PP_CMAC_SIZE];

   if (fblNvPatternProperties[patternId] != kFblNvPatternId_Invalid)
   {
      assertFblUser(fblNvBlockProperties[(vuintx)fblNvPatternProperties[patternId] - (vuintx)kFblNvPatternId_Separator - 1u].dependentPattern
                    == kFblNvPatternId_Invalid, kFblSysAssertParameterOutOfRange);

      if (FblNvPatternGetPropertyStateAndAddrByBlockDescriptor(blockDescriptor, fblNvPatternProperties[patternId],
                                                               &propertyAddress, &propertyState) == kFblOk)
      {
         if (propertyState == FBL_NVPATTERN_STATE_ERASED)
         {
            if (ApplFblNvPatternGenerateCMAC(fblNvPatternProperties[patternId], blockDescriptor, signatureBuffer) == kFblOk)
            {
               result = FblNvPatternSetPropertyByBlockDescriptor(blockDescriptor, signatureBuffer, fblNvPatternProperties[patternId]);
            }
         }
      }
   }
   else
   {
      result = kFblOk;
   }

   return result;
}

/***********************************************************************************************************************
 *  FblNvPatternVerifyCmacByBlockDescriptor
 **********************************************************************************************************************/
/*! \brief       Verify the CMAC for the received patternId
 *  \details     The address of the property will be fetched from the logical block descriptor
 *  \param[in]   blockDescriptor Pointer to the logical block descriptor
 *  \param[in]   patternId Id of the pattern for which the CMAC needs to be verified
 *  \return      kFblOk: CMAC verified successfully
 *               kFblFailed: CMAC not verified or verification failed
 **********************************************************************************************************************/
tFblResult FblNvPatternVerifyCmacByBlockDescriptor(const V_MEMRAM1 tBlockDescriptor V_MEMRAM2 V_MEMRAM3 * blockDescriptor,
                                                   tFblNvPatternId patternId)
{
   vuint8                  signatureBuffer[FBL_NVPATTERN_PP_CMAC_SIZE];
   IO_PositionType         propertyAddress = 0u;
   IO_SizeType             actualPropertyLength;
   tFblNvPatternItemState  propertyState;
   tFblResult              result = kFblFailed;

   if (fblNvPatternProperties[patternId] != kFblNvPatternId_Invalid)
   {
      assertFblUser(fblNvBlockProperties[(vuintx)fblNvPatternProperties[patternId] - (vuintx)kFblNvPatternId_Separator - 1u].dependentPattern
                    == kFblNvPatternId_Invalid, kFblSysAssertParameterOutOfRange);

      if (FblNvPatternGetPropertyStateAndAddrByBlockDescriptor(blockDescriptor, fblNvPatternProperties[patternId],
                                                               &propertyAddress, &propertyState) == kFblOk)
      {
         if (propertyState != FBL_NVPATTERN_STATE_ERASED)
         {
            actualPropertyLength = fblNvBlockProperties[(vuintx)fblNvPatternProperties[patternId] -
                                   (vuintx)kFblNvPatternId_Separator - 1u].length;

            if (MemDriver_RReadSync(signatureBuffer, actualPropertyLength, propertyAddress) == IO_E_OK)
            {
               result = ApplFblNvPatternVerifyCMAC(fblNvPatternProperties[patternId], blockDescriptor, signatureBuffer);
            }
         }
      }
   }
   else
   {
      result = kFblOk;
   }

   return result;
}
# endif /* FBLNVPATTERN_ENABLE_SIGN_PRESENCE_PATTERN */
#endif /* FBL_NVPATTERN_ENABLE_BLOCK_PROPERTIES */

#define FBLNVPATTERN_STOP_SEC_CODE
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */

/***********************************************************************************************************************
 *  ERROR CHECKS
 **********************************************************************************************************************/

#if (!IS_POWOFTWO(FBL_MAX_SEGMENT_SIZE))
# error "FBL_MAX_SEGMENT_SIZE must be a power of two."
#endif

/***********************************************************************************************************************
 *  MISRA DEVIATIONS
 **********************************************************************************************************************/

/* Justification for module-specific MISRA deviations:

   MD_FblNvPattern_Assertion:
      Reason: Assertion macros have to be disabled in production code and are used only for development.
      Risk: Assertion code may be enabled in production code unintentionally. If a assertion condition is unexpectedly
            false, the code is active. A problem may occur if the Macro is additionally changed from single statement
            to multiple statement.
      Prevention: Macro is not allowed to be changed without review. Development code is automatically disabled via
                  configuration of project state "Production".

   MD_FblNvPattern_3678_LocalVariable:
      Reason: Local variable/array is not declared as const as some compiler/platforms have issues with that.
      Risk: Content of the variable/array could be modified within the function.
      Prevention: Code is checked for unintended write accesses by code inspection.

   MD_FblNvPattern_4342:
      Reason:  An expression of essential type has been cast to an enum type.
      Risk: The essential type is not in range of enum type.
      Prevention: Code must be reviewed to ensure cast is only applied on values in valid range

*/

/***********************************************************************************************************************
 *  END OF FILE: FBL_NVPATTERN.C
 **********************************************************************************************************************/
