/*!
* @file     Fee.c
*
* @version  1.0.0
*
* @brief    AUTOSAR Module Fee Driver - Fee driver source file
*
* @attention
*
* Project Name      : AUTOSAR MCAL
*
* Platform          : Arm
*
* Autosar Standard  : Classic Platform 4.4.0
*
* Compiled Version  : G32A10xx_MCAL_1_0_0_01-Otc-25
*
* Copyright (C) 2026 Geehy Semiconductor
*
* You may not use this file except in compliance with the
* GEEHY COPYRIGHT NOTICE (Geehy Semiconductor Software License Agreement).
*
* The program is only for reference, which is distributed in the hope
* that it will be usefull and instructional for customers to develop
* their software. Unless required by applicable law or agreed to in
* writing, the program is distributed on an "AS IS" BASIS, WITHOUT
* ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
* See the Geehy Semiconductor Software License Agreement for the governing permissions
* and limitations under the License.
*/

#ifdef __cplusplus
extern "C"{
#endif

#include "Mcal.h"
#include "Fee.h"
#include "Fee_InternalTypes.h"
#include "Fee_Cbk.h"
#include "SchM_Fee.h"
#if (FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    #include "Det.h"
#endif

/*
* MISRA-C:2012 compliance checks
*
*      Fee_c_MISRA_REF1
*          Breaks the required Rule-8.4 of MISRA 2012 guidelines,
*          A compatible declaration shall be visible when an object or function with external linkage is defined
*
*      Fee_c_MISRA_REF2
*          Breaks the required Rule-14.3 of MISRA 2012 guidelines,
*          Controlling expressions shall not be invariant
*
*      Fee_c_MISRA_REF3
*          Breaks the required Rule-2.2 of MISRA 2012 guidelines,
*          A project shall not contain dead code
*
*      Fee_c_MISRA_REF4
*          Breaks the required Rule-1.3 of MISRA 2012 guidelines,
*          There shall be no occurrence of undefined or critical unspecified behaviour
*
*      Fee_c_MISRA_REF5
*          Breaks the required Rule-18.1 of MISRA 2012 guidelines,
*          A pointer resulting from arithmetic on a pointer operand shall address an element of the same array as that pointer operand
*
*      Fee_c_MISRA_REF6
*          Breaks the required Dir-4.1 of MISRA 2012 guidelines,
*          Run-time failures shall be minimized
*/

/** @addtogroup G32A10xx_StdPeriphDriver
  @{
*/

/** @defgroup FEE_Driver
  * @brief FEE driver modules
  @{
*/

/** @defgroup FEE_Macros
  @{
*/
/**********************************************************************************************************************
                    VENDOR ID & AUTOSAR VERSION INFO
**********************************************************************************************************************/
/** Vendor ID (vendorId) of the dedicated implementation of this module according to the AUTOSAR vendor list. */
#define FEE_VENDOR_ID                    0x00C1U
/** Major version number of AUTOSAR release on which the appropriate implementation is based on. */
#define FEE_AR_RELEASE_MAJOR_VERSION     0x04U
/** Minor version number of AUTOSAR release on which the appropriate implementation is based on. */
#define FEE_AR_RELEASE_MINOR_VERSION     0x04U
/** Revision version number of AUTOSAR release on which the appropriate implementation is based on. */
#define FEE_AR_RELEASE_REVISION_VERSION  0x00U
/** Major version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define FEE_SW_MAJOR_VERSION             0x01U
/** Minor version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define FEE_SW_MINOR_VERSION             0x00U
/** Patch level version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define FEE_SW_PATCH_VERSION             0x00U

/**@} end of group FEE_Macros */

/**********************************************************************************************************************
                    VERSION INFOMATION VERIFICATION
**********************************************************************************************************************/
/* Make sure the vendor id is the same between Fee.c and Fee.h */
#if (FEE_VENDOR_ID != FEE_H_VENDOR_ID)
    #error "Vendor id mismatch between Fee.c and Fee.h"
#endif
/* Make sure the AUTOSAR major version number is the same between Fee.c and Fee.h */
#if (FEE_AR_RELEASE_MAJOR_VERSION != FEE_H_AR_RELEASE_MAJOR_VERSION)
    #error "AUTOSAR Major Version Number mismatch between Fee.c and Fee.h"
#endif
/* Make sure the AUTOSAR minor version number is the same between Fee.c and Fee.h */
#if (FEE_AR_RELEASE_MINOR_VERSION != FEE_H_AR_RELEASE_MINOR_VERSION)
    #error "AUTOSAR minor Version Number mismatch between Fee.c and Fee.h"
#endif
/* Make sure the AUTOSAR revision version number is the same between Fee.c and Fee.h */
#if (FEE_AR_RELEASE_REVISION_VERSION != FEE_H_AR_RELEASE_REVISION_VERSION)
    #error "AUTOSAR revision Version Number mismatch between Fee.c and Fee.h"
#endif
/* Make sure the software major version number is the same between Fee.c and Fee.h */
#if (FEE_SW_MAJOR_VERSION != FEE_H_SW_MAJOR_VERSION)
    #error "Software Major Version Number mismatch between Fee.c and Fee.h"
#endif
/* Make sure the software minor version number is the same between Fee.c and Fee.h */
#if (FEE_SW_MINOR_VERSION != FEE_H_SW_MINOR_VERSION)
    #error "Software Minor Version Number mismatch between Fee.c and Fee.h"
#endif
/* Make sure the software patch version number is the same between Fee.c and Fee.h */
#if (FEE_SW_PATCH_VERSION != FEE_H_SW_PATCH_VERSION)
    #error "Software Patch Version Number mismatch between Fee.c and Fee.h"
#endif

/* Make sure the vendor id is the same between Fee.c and Fee_InternalTypes.h */
#if (FEE_VENDOR_ID != FEE_INTERNALTYPES_H_VENDOR_ID)
    #error "Vendor id mismatch between Fee.c and Fee_InternalTypes.h"
#endif
/* Make sure the AUTOSAR major version number is the same between Fee.c and Fee_InternalTypes.h */
#if (FEE_AR_RELEASE_MAJOR_VERSION != FEE_INTERNALTYPES_H_AR_RELEASE_MAJOR_VERSION)
    #error "AUTOSAR Major Version Number mismatch between Fee.c and Fee_InternalTypes.h"
#endif
/* Make sure the AUTOSAR minor version number is the same between Fee.c and Fee_InternalTypes.h */
#if (FEE_AR_RELEASE_MINOR_VERSION != FEE_INTERNALTYPES_H_AR_RELEASE_MINOR_VERSION)
    #error "AUTOSAR minor Version Number mismatch between Fee.c and Fee_InternalTypes.h"
#endif
/* Make sure the AUTOSAR revision version number is the same between Fee.c and Fee_InternalTypes.h */
#if (FEE_AR_RELEASE_REVISION_VERSION != FEE_INTERNALTYPES_H_AR_RELEASE_REVISION_VERSION)
    #error "AUTOSAR revision Version Number mismatch between Fee.c and Fee_InternalTypes.h"
#endif
/* Make sure the software major version number is the same between Fee.c and Fee_InternalTypes.h */
#if (FEE_SW_MAJOR_VERSION != FEE_INTERNALTYPES_H_SW_MAJOR_VERSION)
    #error "Software Major Version Number mismatch between Fee.c and Fee_InternalTypes.h"
#endif
/* Make sure the software minor version number is the same between Fee.c and Fee_InternalTypes.h */
#if (FEE_SW_MINOR_VERSION != FEE_INTERNALTYPES_H_SW_MINOR_VERSION)
    #error "Software Minor Version Number mismatch between Fee.c and Fee_InternalTypes.h"
#endif
/* Make sure the software patch version number is the same between Fee.c and Fee_InternalTypes.h */
#if (FEE_SW_PATCH_VERSION != FEE_INTERNALTYPES_H_SW_PATCH_VERSION)
    #error "Software Patch Version Number mismatch between Fee.c and Fee_InternalTypes.h"
#endif

/* Make sure the vendor id is the same between Fee.c and Fee_Cbk.h */
#if (FEE_VENDOR_ID != FEE_CBK_H_VENDOR_ID)
    #error "Vendor id mismatch between Fee.c and Fee_Cbk.h"
#endif
/* Make sure the AUTOSAR major version number is the same between Fee.c and Fee_Cbk.h */
#if (FEE_AR_RELEASE_MAJOR_VERSION != FEE_CBK_H_AR_RELEASE_MAJOR_VERSION)
    #error "AUTOSAR Major Version Number mismatch between Fee.c and Fee_Cbk.h"
#endif
/* Make sure the AUTOSAR minor version number is the same between Fee.c and Fee_Cbk.h */
#if (FEE_AR_RELEASE_MINOR_VERSION != FEE_CBK_H_AR_RELEASE_MINOR_VERSION)
    #error "AUTOSAR minor Version Number mismatch between Fee.c and Fee_Cbk.h"
#endif
/* Make sure the AUTOSAR revision version number is the same between Fee.c and Fee_Cbk.h */
#if (FEE_AR_RELEASE_REVISION_VERSION != FEE_CBK_H_AR_RELEASE_REVISION_VERSION)
    #error "AUTOSAR revision Version Number mismatch between Fee.c and Fee_Cbk.h"
#endif
/* Make sure the software major version number is the same between Fee.c and Fee_Cbk.h */
#if (FEE_SW_MAJOR_VERSION != FEE_CBK_H_SW_MAJOR_VERSION)
    #error "Software Major Version Number mismatch between Fee.c and Fee_Cbk.h"
#endif
/* Make sure the software minor version number is the same between Fee.c and Fee_Cbk.h */
#if (FEE_SW_MINOR_VERSION != FEE_CBK_H_SW_MINOR_VERSION)
    #error "Software Minor Version Number mismatch between Fee.c and Fee_Cbk.h"
#endif
/* Make sure the software patch version number is the same between Fee.c and Fee_Cbk.h */
#if (FEE_SW_PATCH_VERSION != FEE_CBK_H_SW_PATCH_VERSION)
    #error "Software Patch Version Number mismatch between Fee.c and Fee_Cbk.h"
#endif

#ifdef ENABLE_INTERMODULE_MCAL_CHECK
    /* Make sure the AUTOSAR major version number is the same between Fee.c and Mcal.h */
    #if(FEE_AR_RELEASE_MAJOR_VERSION != MCAL_H_AR_RELEASE_MAJOR_VERSION)
        #error "AUTOSAR Major Version Number mismatch between Fee.c and Mcal.h"
    #endif
    /* Make sure the AUTOSAR minor version number is the same between Fee.c and Mcal.h */
    #if(FEE_AR_RELEASE_MINOR_VERSION != MCAL_H_AR_RELEASE_MINOR_VERSION)
        #error "AUTOSAR Minor Version Number mismatch between Fee.c and Mcal.h"
    #endif
    
    /* Make sure the AUTOSAR major version number is the same between Fee.c and SchM_Fee.h */
    #if(FEE_AR_RELEASE_MAJOR_VERSION != SCHM_FEE_H_AR_RELEASE_MAJOR_VERSION)
        #error "AUTOSAR Major Version Number mismatch between Fee.c and SchM_Fee.h"
    #endif
    /* Make sure the AUTOSAR minor version number is the same between Fee.c and SchM_Fee.h */
    #if(FEE_AR_RELEASE_MINOR_VERSION != SCHM_FEE_H_AR_RELEASE_MINOR_VERSION)
        #error "AUTOSAR Minor Version Number mismatch between Fee.c and SchM_Fee.h"
    #endif
    
    /* Make sure the AUTOSAR major version number is the same between Fee.c and Det.h */
    #if(FEE_AR_RELEASE_MAJOR_VERSION != DET_H_AR_RELEASE_MAJOR_VERSION)
        #error "AUTOSAR Major Version Number mismatch between Fee.c and Det.h"
    #endif
    /* Make sure the AUTOSAR minor version number is the same between Fee.c and Det.h */
    #if(FEE_AR_RELEASE_MINOR_VERSION != DET_H_AR_RELEASE_MINOR_VERSION)
        #error "AUTOSAR Minor Version Number mismatch between Fee.c and Det.h"
    #endif
#endif
/***********************************************************************************************************************
                    MACRO DEFINETION
***********************************************************************************************************************/
/** @defgroup FEE_Macros
  @{
*/

/*!
* @brief             set the highest bit of checksum use for Block Header
*
* @param[in]         ImmediateBlock    the block support Immediate will be 1,otherwise will be 0
* @param[out]        None
* @param[in,out]     CheckSum          the checksum of block header
*
* @retval            None
*/
#define FEE_CHECKSUM_HIGHESTBIT_SET(ImmediateBlock, CheckSum)                                                          \
    do                                                                                                                 \
    {                                                                                                                  \
        if(ImmediateBlock == (boolean)TRUE)                                                                            \
        {                                                                                                              \
            CheckSum += 1U;                                                                                            \
            CheckSum |= 0x80000000U;                                                                                   \
        }                                                                                                              \
        else                                                                                                           \
        {                                                                                                              \
            CheckSum &= 0x7fffffffU;                                                                                   \
        }                                                                                                              \
    }                                                                                                                  \
    while(0);

/**@} end of group FEE_Macros */

/***********************************************************************************************************************
                    LOCAL VARIABLE
***********************************************************************************************************************/
#define FEE_START_SEC_VAR_INIT_32
#include "Fee_MemMap.h"

/** @defgroup FEE_Variable
  @{
*/

#if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT ) && (STD_OFF == FEE_LEGACY_MODE_SUPPORT )
/** the information of whether blocks touching the Reserved Area */
static VAR(uint32, FEE_VAR)
    gs_feeReservedAreaTouched[(FEE_MAX_NUMBER_OF_BLOCKS + ((sizeof(uint32) * 8U) - 1U)) / (sizeof(uint32) * 8U)] = {(uint32)0x0};
#endif

/**@} end of group FEE_Variable */

#define FEE_STOP_SEC_VAR_INIT_32
#include "Fee_MemMap.h"

#define FEE_START_SEC_VAR_INIT_8
#include "Fee_MemMap.h"

/** @defgroup FEE_Variable
  @{
*/

/** Pointer to user data buffer used by Fee read/write Job */
static P2VAR(uint8, FEE_VAR, FEE_APPL_DATA) gs_feeReadJobDataDestPtr       = NULL_PTR;
static P2CONST(uint8, FEE_CONST, FEE_APPL_CONST) gs_feeWriteJobDataDestPtr = NULL_PTR;

/** Internal cluster group index used for scan and swap jobs */
static VAR(uint8, FEE_VAR) gs_feeJobIntClrGrpId                            = (uint8)0x0;

/** Internal cluster index used for scan and swap jobs */
static VAR(uint8, FEE_VAR) gs_feeJobIntClrId                               = (uint8)0x0;

/** Internal Data buffer used by all jobs to store immediate data */
static VAR(uint8, FEE_VAR) gs_feeIntDataBuffer[FEE_DATA_BUFFER_SIZE]       = {0};

/**@} end of group FEE_Variable */

#define FEE_STOP_SEC_VAR_INIT_8
#include "Fee_MemMap.h"

#define FEE_START_SEC_VAR_INIT_16
#include "Fee_MemMap.h"

/** @defgroup FEE_Variable
  @{
*/

/** Fee block index use for all jobs */
static VAR(uint16, FEE_VAR) gs_feeJobBlockIndex    = (uint16)0x0;

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
/** number of elements from the gs_feeForeignBlockConfig array. */
static VAR(uint16, FEE_VAR) gs_feeForeignBlocksNumber = (uint16)0x0;
#endif

/** Fee internal block index use for swap jobs */
static VAR(uint16, FEE_VAR) gs_feeIntJobBlockIndex = (uint16)0x0;

/**@} end of group FEE_Variable */

#define FEE_STOP_SEC_VAR_INIT_16
#include "Fee_MemMap.h"

#define FEE_START_SEC_VAR_INIT_UNSPECIFIED
#include "Fee_MemMap.h"

/** @defgroup FEE_Variable
  @{
*/

/** the Internal state of Fee module */
static VAR(MemIf_StatusType, FEE_VAR) gs_feeIntModuleStatus  = MEMIF_UNINIT;

/** the job result of last execute job */
static VAR(MemIf_JobResultType, FEE_VAR) gs_feeJobResult     = MEMIF_JOB_OK;

/** the current job  */
static VAR(Fee_JobType, FEE_VAR) gs_feeJob                   = FEE_COMPLETE_JOB;

/** the original job of swap block operate */
static VAR(Fee_JobType, FEE_VAR) gs_feeIntOriginalJob        = FEE_COMPLETE_JOB;

/** Fee block offset used by read job */
static VAR(Fls_LengthType, FEE_VAR) gs_feeJobBlockOffset     = 0x0;

/** the number of bytes to read job or internal write job */
static VAR(Fls_LengthType, FEE_VAR) gs_feeJobBlockLength     = 0x0;

/** the addr for current internal job operate */
static VAR(Fls_AddressType, FEE_VAR) gs_feeIntJobCurrentAddr = 0x0;

/** the header addr for current internal job operate */
static VAR(Fls_AddressType, FEE_VAR) gs_feeIntJobHdrAddr     = 0x0;

/** the Data addr for current internal job operate */
static VAR(Fls_AddressType, FEE_VAR) gs_feeIntJobDataAddr    = 0x0;

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
/** store the config of the foreign blocks */
static VAR(Fee_BlkCfgType, FEE_VAR) gs_feeForeignBlockConfig[(uint32)FEE_MAX_NUMBER_OF_BLOCKS - (uint32)FEE_CONFIGURED_NUMBER_OF_BLOCKS] = {{0U}};
#endif

/**@} end of group FEE_Variable */

#define FEE_STOP_SEC_VAR_INIT_UNSPECIFIED
#include "Fee_MemMap.h"

#define FEE_START_SEC_VAR_NO_INIT_UNSPECIFIED
#include "Fee_MemMap.h"

/** @defgroup FEE_Variable
  @{
*/

/** store the runtime infomation of the Fee blocks */
static VAR(Fee_BlkInfoType, FEE_VAR) gs_feeBlockInfo[FEE_MAX_NUMBER_OF_BLOCKS];

/** store the runtime infomation of the Fee cluster group */
/*** @warning Fee_c_MISRA_REF1 'gs_feeClusterInfo' has external linkage and is being defined without any previous declaration. */
VAR(Fee_ClrGrpInfoType, FEE_VAR) gs_feeClusterInfo[FEE_CLUSTER_GROUPS_NUMBER];

/**@} end of group FEE_Variable */

#define FEE_STOP_SEC_VAR_NO_INIT_UNSPECIFIED
#include "Fee_MemMap.h"

/***********************************************************************************************************************
                    GLOBAL FUNCTION DECLARATION
***********************************************************************************************************************/
#define FEE_START_SEC_CODE
#include "Fee_MemMap.h"

/** @defgroup FEE_Fuctions
  @{
*/

#if !defined(__DOXYGEN__)

/** Fee Job end notification callback provided by the upper layer module */
FEE_NVM_JOB_END_NOTIFICATION_DECLARE

/** Fee Job error notification callback provided by the upper layer module */
FEE_NVM_JOB_ERROR_NOTIFICATION_DECLARE

#endif

#if !defined(__DOXYGEN__)

/** Fee cluster format during Initialize notification callback provided by the upper layer module */
FEE_NVM_CLUSTER_FORMAT_NOTIFICATION_DECLARE

#endif

/***********************************************************************************************************************
                    LOCAL FUNCTION DECLARATION
***********************************************************************************************************************/
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
static FUNC(void, FEE_CODE) Fee_SerializeU8(P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) buf,
                                            P2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) off,
                                              VAR(uint8, AUTOMATIC) v);
#endif

static FUNC(void, FEE_CODE) Fee_SerializeU16LE(P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) buf,
                                               P2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) off,
                                                 VAR(uint16, AUTOMATIC) v);

static FUNC(void, FEE_CODE) Fee_SerializeU32LE(P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) buf,
                                               P2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) off,
                                                 VAR(uint32, AUTOMATIC) v);

static FUNC(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST), FEE_CODE)
Fee_DeserializeU8(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) p, P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) v);

static FUNC(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST), FEE_CODE)
Fee_DeserializeU16(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) p, P2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) v);

static FUNC(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST), FEE_CODE)
Fee_DeserializeU32(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) p, P2VAR(uint32, AUTOMATIC, FEE_APPL_DATA) v);

static FUNC(void, FEE_CODE) Fee_BlockHdrSerializeOut(CONST(uint16, AUTOMATIC) BlockNumber,
                            CONST(uint16, AUTOMATIC) Length,CONST(Fls_AddressType, AUTOMATIC) TargetAddress,
                            CONST(boolean, AUTOMATIC) ImmediateBlock,
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                            CONST(Fee_BlkPrjAsgmtType, AUTOMATIC) BlockAssignment,
#endif
                            P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) BlockHdrPtr);

static FUNC(Fee_BlockStatusType, FEE_CODE)
    Fee_BlockHdrDeserializeOut(CONSTP2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) BlockNumberPtr,
                               CONSTP2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) LengthPtr,
                               CONSTP2VAR(Fls_AddressType, AUTOMATIC, FEE_APPL_DATA) TargetAddressPtr,
                               CONSTP2VAR(boolean, AUTOMATIC, FEE_APPL_DATA) ImmediateBlockPtr,
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                               CONSTP2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) BlockAssignmentPtr,
#endif
                               P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) BlockHdrPtr);

static FUNC(void, FEE_CODE)
    Fee_FlagSerializeOut(P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) TargetPtr, CONST(uint8, AUTOMATIC) FlagExpected);

static FUNC(Std_ReturnType, FEE_CODE)
    Fee_BlockFlagDeserializeOut(CONSTP2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) TargetPtr,
                                CONST(uint8, AUTOMATIC) FlagExpected,
                                P2VAR(boolean, AUTOMATIC, FEE_APPL_DATA) FlagValue);

static FUNC(void, FEE_CODE) Fee_ClusterHdrSerializeOut(CONST(uint32, AUTOMATIC) ClrID,
                        CONST(Fls_AddressType, AUTOMATIC) StartAddress,CONST(Fls_LengthType, AUTOMATIC) ClusteSize,
                        P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) ClrHdrPtr);

static FUNC(Fee_ClusterStatusType, FEE_CODE)
    Fee_ClusterHdrDeserializeOut(CONSTP2VAR(uint32, AUTOMATIC, FEE_APPL_DATA) ClrIDPtr,
                                 CONSTP2VAR(Fls_AddressType, AUTOMATIC, FEE_APPL_DATA) StartAddressPtr,
                                 CONSTP2VAR(Fls_LengthType, AUTOMATIC, FEE_APPL_DATA) ClusteSizePtr,
                                 P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) ClrHdrPtr);

static FUNC(Std_ReturnType, FEE_CODE) Fee_CheckBlockBlank(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) TargetPtr,
                                                          CONSTP2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) TargetEndPtr);

static FUNC(uint16, FEE_CODE) Fee_GetBlockIndexByBlockNumber(CONST(uint16, AUTOMATIC) BlockNumber);

LOCAL_INLINE FUNC(uint16, FEE_CODE) Fee_GetBlockNumberByBlockId(CONST(uint16, AUTOMATIC) BlockRuntimeInfoIndex);
LOCAL_INLINE FUNC(uint16, FEE_CODE) Fee_GetBlockSizeByBlockId(CONST(uint16, AUTOMATIC) BlockRuntimeInfoIndex);
LOCAL_INLINE FUNC(uint8, FEE_CODE) Fee_GetBlockClusterGrpByBlockId(CONST(uint16, AUTOMATIC) BlockRuntimeInfoIndex);
LOCAL_INLINE FUNC(boolean, FEE_CODE)
    Fee_CheckBlockImmediateSupportByBlockId(CONST(uint16, AUTOMATIC) BlockRuntimeInfoIndex);

static FUNC(uint16, FEE_CODE) Fee_AlignSizeToVirtualPage(VAR(uint16, AUTOMATIC) BlockSize);

static FUNC(void, FEE_CODE)
    Fee_CopyToPageBuffer(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) SourcePtr,
                         P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) TargetPtr, CONST(uint16, AUTOMATIC) Length);

static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapClrVld(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapClrVldDone(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapBlock(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapBlockVld(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapClrFmt(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapClrErase(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapDataWrite(CONST(boolean, AUTOMATIC) BufferValid);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapDataRead(CONST(boolean, AUTOMATIC) BufferValid);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwap(void);

static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanBlockHdrRead(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClrErase(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClr(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClrFmt(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClrFmtDone(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanBlockHdrParse(CONST(boolean, AUTOMATIC) BufferValid);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClrHdrRead(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClrHdrParse(CONST(boolean, AUTOMATIC) BufferValid);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScan(void);

static FUNC(MemIf_JobResultType, FEE_CODE) Fee_ReadJob(void);

static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteHdrJob(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteDataJob(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteUnalignedDataJob(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteValidateJob(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteDoneJob(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteJob(void);

static FUNC(MemIf_JobResultType, FEE_CODE) Fee_InvalBlockDoneJob(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_InvalBlockJob(void);

static FUNC(MemIf_JobResultType, FEE_CODE) Fee_EraseImmediateDoneJob(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_EraseImmediateJob(void);

static FUNC(MemIf_JobResultType, FEE_CODE) Fee_ScheduleForJob_One(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_ScheduleForJob_Two(void);
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_ScheduleForJob(void);

#if(STD_OFF == FEE_LEGACY_MODE_SUPPORT)
static FUNC(boolean, FEE_CODE) Fee_ReservedAreaEnableWrite(void);
    #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        static INLINE FUNC(uint32, FEE_CODE) Fee_PowerOf2Mask5LSB(CONST(uint32, AUTOMATIC) InVal);
        static INLINE FUNC(sint8, FEE_CODE) Fee_CheckReservedAreaTouchedByBlock(CONST(uint16, AUTOMATIC) BlockNumber);
        static INLINE FUNC(sint8, FEE_CODE) Fee_CheckReservedAreaTargetedInClrGrp(CONST(uint8, AUTOMATIC) ClrGrpIndex);
        static INLINE FUNC(void, FEE_CODE) Fee_MaskTouchReservedAreaByBlock(CONST(uint16, AUTOMATIC) BlockNumber);
        static INLINE FUNC(void, FEE_CODE) Fee_MaskUntouchReservedAreaByClrGrp(CONST(uint8, AUTOMATIC) ClrGrpIndex);
        static INLINE FUNC(void, FEE_CODE) Fee_MaskUntouchReservedAreaAll(void);
    #endif
#endif

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
static FUNC(uint16, FEE_CODE) Fee_GetForeignBlockIndexByBlockNumber(CONST(uint16, AUTOMATIC) BlockNumber);

LOCAL_INLINE FUNC(Fee_BlkPrjAsgmtType, FEE_CODE)
    Fee_GetBlockProjectAssignment(CONST(uint16, AUTOMATIC) BlockRuntimeInfoIndex);
#endif

/**@} end of group FEE_Fuctions */

#define FEE_STOP_SEC_CODE
#include "Fee_MemMap.h"

/***********************************************************************************************************************
                    LOCAL FUNCTION
***********************************************************************************************************************/
#define FEE_START_SEC_CODE
#include "Fee_MemMap.h"

/** @defgroup FEE_Fuctions
  @{
*/

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
/*!
* @brief            Serialize parameter uint8 type value into the buffer .
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    buf     pointer to target buffer that used for store serialized result
* @param[in,out]    off     used for offset
* @param[in,out]    v       the parameter value wait to be process
*
* @retval           None
*/
static FUNC(void, FEE_CODE) Fee_SerializeU8(P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) buf,
                                            P2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) off,
                                              VAR(uint8, AUTOMATIC) v)
{
    buf[*off] = v;
    *off = (uint16)(*off + 1U);
}
#endif

/*!
* @brief            Serialize parameter uint16 type value into the buffer .
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    buf     pointer to target buffer that used for store serialized result
* @param[in,out]    off     used for offset
* @param[in,out]    v       the parameter value wait to be process
*
* @retval           None
*/
static FUNC(void, FEE_CODE) Fee_SerializeU16LE(P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) buf,
                                               P2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) off,
                                                 VAR(uint16, AUTOMATIC) v)
{
    buf[*off] = (uint8)(v & 0xFFU);
    *off = (uint16)(*off + 1U);

    buf[*off] = (uint8)((v >> 8U) & 0xFFU);
    *off = (uint16)(*off + 1U);
}

/*!
* @brief            Serialize parameter uint32 type value into the buffer .
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    buf     pointer to target buffer that used for store serialized result
* @param[in,out]    off     used for offset
* @param[in,out]    v       the parameter value wait to be process
*
* @retval           None
*/
static FUNC(void, FEE_CODE) Fee_SerializeU32LE(P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) buf,
                                               P2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) off,
                                                 VAR(uint32, AUTOMATIC) v)
{
    buf[*off] = (uint8)(v & 0xFFU);
    *off = (uint16)(*off + 1U);

    buf[*off] = (uint8)((v >> 8U) & 0xFFU);
    *off = (uint16)(*off + 1U);

    buf[*off] = (uint8)((v >> 16U) & 0xFFU);
    *off = (uint16)(*off + 1U);

    buf[*off] = (uint8)((v >> 24U) & 0xFFU);
    *off = (uint16)(*off + 1U);
}

/*!
* @brief            Deserialize parameter uint8 type value into the buffer .
*
* @param[in]        None
* @param[out]       v       pointer to target buffer that used for store deserialize result
* @param[in,out]    p       the address of parameter value wait to be process
*
* @retval           source address
*/
static FUNC(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST), FEE_CODE)
Fee_DeserializeU8(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) p, P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) v)
{
    *v = p[0];
    return &p[1];
}

/*!
* @brief            Deserialize parameter uint16 type value into the buffer .
*
* @param[in]        None
* @param[out]       v       pointer to target buffer that used for store deserialize result
* @param[in,out]    p       the address of parameter value wait to be process
*
* @retval           source address
*/
static FUNC(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST), FEE_CODE)
Fee_DeserializeU16(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) p, P2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) v)
{
    uint16 tmp;
    tmp  = (uint16)p[0];
    tmp |= (uint16)((uint16)p[1] << 8);
    *v = tmp;

    return &p[2];
}

/*!
* @brief            Deserialize parameter uint32 type value into the buffer .
*
* @param[in]        None
* @param[out]       v       pointer to target buffer that used for store deserialize result
* @param[in,out]    p       the address of parameter value wait to be process
*
* @retval           source address
*/
static FUNC(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST), FEE_CODE)
Fee_DeserializeU32(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) p, P2VAR(uint32, AUTOMATIC, FEE_APPL_DATA) v)
{
    uint32 tmp;
    tmp  = (uint32)p[0];
    tmp |= (uint32)((uint32)p[1] << 8);
    tmp |= (uint32)((uint32)p[2] << 16);
    tmp |= (uint32)((uint32)p[3] << 24);
    *v = tmp;

    return &p[4];
}

/*!
* @brief            put Fee block header parameters Serialize into a buffer
*
* @param[in]        BlockNumber         the number of the block
* @param[in]        Length              the lengt of the block
* @param[in]        TargetAddress       the target address of block
* @param[in]        ImmediateBlock      the flag indicate whether block is a immediate block or not.
* @param[in]        BlockAssignment     the project Assignment flag of this block
* @param[out]       BlockHdrPtr         point to the buffer store Serialize output data
* @param[in,out]    None
*
* @retval           None
*/
static FUNC(void, FEE_CODE)
    Fee_BlockHdrSerializeOut(CONST(uint16, AUTOMATIC) BlockNumber, CONST(uint16, AUTOMATIC) Length,
                             CONST(Fls_AddressType, AUTOMATIC) TargetAddress, CONST(boolean, AUTOMATIC) ImmediateBlock,
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                             CONST(Fee_BlkPrjAsgmtType, AUTOMATIC) BlockAssignment,
#endif
                             P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) BlockHdrPtr)
{
    VAR(uint32, AUTOMATIC) checkSum = 0U;
    VAR(uint16, AUTOMATIC) off = 0U;

    checkSum = ((uint32)BlockNumber) + ((uint32)Length) + (uint32)TargetAddress;

    /* the highest bit of the checksum use for the immediate block flag */
    FEE_CHECKSUM_HIGHESTBIT_SET(ImmediateBlock, checkSum);

    Fee_SerializeU16LE(BlockHdrPtr, &off, BlockNumber);
    Fee_SerializeU16LE(BlockHdrPtr, &off, Length);
    Fee_SerializeU32LE(BlockHdrPtr, &off, (uint32)TargetAddress);
    Fee_SerializeU32LE(BlockHdrPtr, &off, checkSum);

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    Fee_SerializeU8(BlockHdrPtr, &off, (uint8)BlockAssignment);
#endif

    /* Fill FEE_DEFAULT_VALUE to reverse space */
    while (off < (uint16)FEE_BLOCK_OVERHEAD_SIZE)
    {
        BlockHdrPtr[off] = (uint8)FEE_DEFAULT_VALUE;
        off = (uint16)(off + 1U);
    }
}

/*!
* @brief            get block header params by deserialize output read buff
*
* @param[in]        BlockHdrPtr            point to the buffer to be deserialize
* @param[out]       BlockNumberPtr         point to the address to store the number of the block header
* @param[out]       LengthPtr              point to the address to store the length of the block
* @param[out]       TargetAddressPtr       point to the address to store the target address of block header
* @param[out]       ImmediateBlockPtr      point to the address to store the flag indicate whether
*                                          this block is a immediate block or not.
* @param[out]       BlockAssignmentPtr     point to the address to the project Assignment flag of this block
* @param[in,out]    None

* @retval           Fee_BlockStatusType      FEE_BLKST_VALID            - this block is valid
*                                            FEE_BLKST_INVALID          - this block is invalid
*                                            FEE_BLKST_INCONSISTENT     - this block is inconsistent
*                                            FEE_BLKST_HEADER_INVALID   - this block header is invalid
*                                            FEE_BLKST_HEADER_BLANK     - this block header is only contains FEE_DEFAULT_VALUE
*/
static FUNC(Fee_BlockStatusType, FEE_CODE)
    Fee_BlockHdrDeserializeOut(CONSTP2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) BlockNumberPtr,
                               CONSTP2VAR(uint16, AUTOMATIC, FEE_APPL_DATA) LengthPtr,
                               CONSTP2VAR(Fls_AddressType, AUTOMATIC, FEE_APPL_DATA) TargetAddressPtr,
                               CONSTP2VAR(boolean, AUTOMATIC, FEE_APPL_DATA) ImmediateBlockPtr,
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                               CONSTP2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) BlockAssignmentPtr,
#endif
                               P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) BlockHdrPtr)
{
    /* avoid BlockHdrPtr change warning */
    P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) pptr = BlockHdrPtr;
    VAR(Std_ReturnType, AUTOMATIC) retReturn          = E_OK;
    VAR(Fee_BlockStatusType, AUTOMATIC) retStatus          = FEE_BLKST_HEADER_INVALID;
    P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) targetEndPtr = NULL_PTR;
#if(FEE_SWAP_FOREIGN_BLOCKS_ENABLED == STD_OFF)
    VAR(uint8, AUTOMATIC) reservedSpace = 0U;
#endif
    VAR(uint32, AUTOMATIC) readCheckSum                    = (uint32)0x0;
    VAR(uint32, AUTOMATIC) calcCheckSum                    = (uint32)0x0;
    VAR(boolean, AUTOMATIC) flagValid                      = (boolean)FALSE;
    VAR(boolean, AUTOMATIC) flagInvalid                    = (boolean)FALSE;

    retReturn = Fee_CheckBlockBlank(pptr, &pptr[FEE_BLOCK_OVERHEAD_SIZE]);

    switch (retReturn)
    {
        case (Std_ReturnType)E_OK :
          {
                retStatus = FEE_BLKST_HEADER_BLANK;
                break;
          }
        default :
          {
                targetEndPtr = &pptr[FEE_BLOCK_OVERHEAD_SIZE - (2U * FEE_VIRTUAL_PAGE_SIZE)];

                pptr = Fee_DeserializeU16(pptr, BlockNumberPtr);
                pptr = Fee_DeserializeU16(pptr, LengthPtr);
                pptr = Fee_DeserializeU32(pptr, TargetAddressPtr);
                pptr = Fee_DeserializeU32(pptr, &readCheckSum);

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                pptr = Fee_DeserializeU8(pptr, BlockAssignmentPtr);
#else
                /* must read 1 bytes even though this byte is not used when FEE_SWAP_FOREIGN_BLOCKS_ENABLED is false*/
                pptr = Fee_DeserializeU8(pptr, &reservedSpace);
                (void)reservedSpace;
#endif
                /* the highest bit of the checksum use for the immediate block flag,low 31-bit use for the checksum */
                calcCheckSum = *LengthPtr + *TargetAddressPtr + *BlockNumberPtr;

                *ImmediateBlockPtr = (0U == (readCheckSum & 0x80000000U)) ? (boolean)FALSE : (boolean)TRUE;

                if((boolean)TRUE == *ImmediateBlockPtr)
                {
                    calcCheckSum       = calcCheckSum + 1U;
                }
                else
                {
                    /* nothing */
                }

                if((readCheckSum & 0x7fffffffU) == (calcCheckSum & 0x7fffffffU))
                {
                    retReturn = Fee_CheckBlockBlank(pptr, targetEndPtr);
                    if(((Std_ReturnType)E_OK) == retReturn)
                    {
                        retReturn = Fee_BlockFlagDeserializeOut(targetEndPtr, FEE_VALIDATED_FLAG, &flagValid);
                        if(((Std_ReturnType)E_OK) == retReturn)
                        {
                            retReturn = Fee_BlockFlagDeserializeOut(&targetEndPtr[FEE_VIRTUAL_PAGE_SIZE], FEE_INVALIDATED_FLAG,
                                                                    &flagInvalid);
                            if(((Std_ReturnType)E_OK) == retReturn)
                            {
                                if((boolean)FALSE == flagInvalid)
                                {
                                    if((boolean)FALSE == flagValid)
                                    {
                                        retStatus = FEE_BLKST_INCONSISTENT;
                                    }
                                    else
                                    {
                                        retStatus = FEE_BLKST_VALID;
                                    }
                                }
                                else
                                {
#if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
                                    if((boolean)TRUE == flagValid)
                                    {
                                        retStatus = FEE_BLKST_INVALIDATED;
                                    }
                                    else
#endif
                                    {
                                        retStatus = FEE_BLKST_INVALID;
                                    }
                                }
                            }
                            else
                            {
                                /* nothing */
                            }
                        }
                        else
                        {
                            /* nothing */
                        }
                    }
                    else
                    {
                        /* nothing */
                    }
                }
                else
                {
                    /* nothing */
                }
                break;
          }
    }
    return (retStatus);
}

/*!
* @brief            put validation or invalidation flag Serialize into a buffer
*
* @param[in]        FlagExpected    expect Flag value
* @param[out]       TargetPtr       point to the buffer store Serialize output data
* @param[in,out]    None
*
* @retval           None
*/
static FUNC(void, FEE_CODE)
    Fee_FlagSerializeOut(P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) TargetPtr, CONST(uint8, AUTOMATIC) FlagExpected)
{
    VAR(uint16, AUTOMATIC) index = 0;
    
    /* Set the first byte to the desired flag value   */
    TargetPtr[index] = FlagExpected;
    index++;

    /* Fill the remaining space */
    while(index < FEE_VIRTUAL_PAGE_SIZE)
    {
        TargetPtr[index] = FEE_DEFAULT_VALUE;
        index++;
    }
}

/*!
* @brief            deserialize block flag value for read buffer,check whether that flag value is equel to expect flag value
*
* @param[in]        TargetPtr         point to start address of deserialize target buffer
* @param[in]        FlagExpected      expect Flag value
* @param[out]       FlagValue         TRUE  - block flag value deserialize output equel to expect flag value
*                                     FLASE - block flag value deserialize output not equel to expect flag value
* @param[in,out]    None
*
* @retval           Std_ReturnType    E_OK     - block data area is blank
*                                     E_NOT_OK - block data area is no blank
*/
static FUNC(Std_ReturnType, FEE_CODE)
    Fee_BlockFlagDeserializeOut(CONSTP2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) TargetPtr,
                                CONST(uint8, AUTOMATIC) FlagExpected,
                                P2VAR(boolean, AUTOMATIC, FEE_APPL_DATA) FlagValue)
{
    VAR(Std_ReturnType, AUTOMATIC) retReturn = (Std_ReturnType)E_OK;

    if((FlagExpected != *TargetPtr) && (FEE_DEFAULT_VALUE != *TargetPtr))

    {
        retReturn = (Std_ReturnType)E_NOT_OK;
    }
    else
    {
        *FlagValue = (FlagExpected == *TargetPtr) ? (boolean)TRUE : (boolean)FALSE;

        /* Fill FEE_DEFAULT_VALUE to reverse space */
        retReturn = Fee_CheckBlockBlank(&TargetPtr[1], &TargetPtr[FEE_VIRTUAL_PAGE_SIZE]);
    }
    return (retReturn);
}

/*!
* @brief            put Fee cluster header parameters Serialize into a buffer
*
* @param[in]        ClrID           the index of the cluster
* @param[in]        StartAddress    the logical address of Fee cluster in Flash address space
* @param[in]        ClusteSize      the length of cluster
* @param[out]       ClrHdrPtr       point to the buffer store Serialize output data
* @param[in,out]    None
*
* @retval           None
*/
static FUNC(void, FEE_CODE)
    Fee_ClusterHdrSerializeOut(CONST(uint32, AUTOMATIC) ClrID, CONST(Fls_AddressType, AUTOMATIC) StartAddress,
                               CONST(Fls_LengthType, AUTOMATIC) ClusteSize,
                               P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) ClrHdrPtr)
{
    VAR(uint32, AUTOMATIC) checkSum = 0U;
    VAR(uint16, AUTOMATIC) off = 0U;

    checkSum = (uint32)ClrID + (uint32)StartAddress + (uint32)ClusteSize;

    Fee_SerializeU32LE(ClrHdrPtr, &off, (uint32)ClrID);
    Fee_SerializeU32LE(ClrHdrPtr, &off, (uint32)StartAddress);
    Fee_SerializeU32LE(ClrHdrPtr, &off, (uint32)ClusteSize);
    Fee_SerializeU32LE(ClrHdrPtr, &off, (uint32)checkSum);

    /* Fill FEE_DEFAULT_VALUE to reverse space */
    while (off < (uint16)FEE_CLUSTER_OVERHEAD_SIZE)
    {
        ClrHdrPtr[off] = (uint8)FEE_DEFAULT_VALUE;
        off = (uint16)(off + 1U);
    }
}

/*!
* @brief            get cluster header deserialize output by deserialize output read buff
*
* @param[in]        ClrHdrPtr          point to the buffer to be deserialize
* @param[out]       ClrIDPtr           point to the address to store the cluster id
* @param[out]       StartAddressPtr    point to the address to store logical address of Fee cluster in Flash address space
* @param[out]       ClusteSizePtr      point to the address to store the size of cluster
* @param[in,out]    None
*
* @retval           Fee_ClusterStatusType    FEE_CLRST_VALID            - this cluster is valid
*                                            FEE_CLRST_INVALID          - this cluster is invalid
*                                            FEE_CLRST_INCONSISTENT     - this cluster is inconsistent
*                                            FEE_CLRST_HEADER_INVALID   - this cluster header is invalid
*/
static FUNC(Fee_ClusterStatusType, FEE_CODE)
    Fee_ClusterHdrDeserializeOut(CONSTP2VAR(uint32, AUTOMATIC, FEE_APPL_DATA) ClrIDPtr,
                                 CONSTP2VAR(Fls_AddressType, AUTOMATIC, FEE_APPL_DATA) StartAddressPtr,
                                 CONSTP2VAR(Fls_LengthType, AUTOMATIC, FEE_APPL_DATA) ClusteSizePtr,
                                 P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) ClrHdrPtr)
{
    /* avoid ClrHdrPtr change warning */
    P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) pptr = ClrHdrPtr;
    VAR(Std_ReturnType, AUTOMATIC) retReturn               = (Std_ReturnType)E_OK;
    VAR(Fee_ClusterStatusType, AUTOMATIC) retStatus        = FEE_CLRST_HEADER_INVALID;
    P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) targetEndPtr = NULL_PTR;
    VAR(uint32, AUTOMATIC) checkSum                        = (uint32)0x0;
    VAR(boolean, AUTOMATIC) flagValid                      = (boolean)FALSE;
    VAR(boolean, AUTOMATIC) flagInvalid                    = (boolean)FALSE;

    targetEndPtr = &pptr[FEE_CLUSTER_OVERHEAD_SIZE - (2U * FEE_VIRTUAL_PAGE_SIZE)];

    pptr = Fee_DeserializeU32(pptr, ClrIDPtr);
    pptr = Fee_DeserializeU32(pptr, StartAddressPtr);
    pptr = Fee_DeserializeU32(pptr, ClusteSizePtr);
    pptr = Fee_DeserializeU32(pptr, &checkSum);

    if((*ClrIDPtr + *StartAddressPtr + *ClusteSizePtr) == checkSum)
    {
        retReturn = Fee_CheckBlockBlank(pptr, targetEndPtr);
        /*** @warning Fee_c_MISRA_REF2 The value of this 'if' controlling expression is always 'true'. */
        /*** @warning Fee_c_MISRA_REF3 The value of this 'if' controlling expression is always 'true'. */
        if((Std_ReturnType)E_OK == retReturn)
        {
            retReturn = Fee_BlockFlagDeserializeOut(targetEndPtr, FEE_VALIDATED_FLAG, &flagValid);
            if((Std_ReturnType)E_OK == retReturn)
            {
                retReturn = Fee_BlockFlagDeserializeOut(&targetEndPtr[FEE_VIRTUAL_PAGE_SIZE], FEE_INVALIDATED_FLAG,
                                                        &flagInvalid);
                if((Std_ReturnType)E_OK == retReturn)
                {
                    if((boolean)FALSE == flagInvalid)
                    {
                        if((boolean)FALSE == flagValid)
                        {
                            retStatus = FEE_CLRST_INCONSISTENT;
                        }
                        else
                        {
                            retStatus = FEE_CLRST_VALID;
                        }
                    }
                    else
                    {
                        retStatus = FEE_CLRST_INVALID;
                    }
                }
                else
                {
                    /* nothing */
                }
            }
            else
            {
                /* nothing */
            }
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        /* nothing */
    }

    return (retStatus);
}

/*!
* @brief            Check whether the block specified by user only contains the FEE_DEFAULT_VALUE value
*
* @param[in]        TargetPtr         point to start address of block data area
* @param[in]        TargetEndPtr      point to end address + 1 of block data area
* @param[out]       None
* @param[in,out]    None
*
* @retval           Std_ReturnType    E_OK     - block data area is blank
*                                     E_NOT_OK - block data area is no blank
*/
static FUNC(Std_ReturnType, FEE_CODE) Fee_CheckBlockBlank(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) TargetPtr,
                                                          CONSTP2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) TargetEndPtr)
{
    /* avoid TargetPtr change warning */
    P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) ptrT = TargetPtr;

    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_OK;

    while(ptrT < TargetEndPtr)
    {
        if(FEE_DEFAULT_VALUE != *ptrT)
        {
            result = (Std_ReturnType)E_NOT_OK;
            break;
        }
        else
        {
            /* nothing */
        }
        ptrT++;
    }

    return (result);
}

/*!
* @brief            get the block index by specified block number
*
* @param[in]        BlockNumber    the number of the block want to get block id
* @param[out]       None
* @param[in,out]    None
*
* @retval           uint16         0xffff - block number is invalid
*                                  other  - Block id
*/
static FUNC(uint16, FEE_CODE) Fee_GetBlockIndexByBlockNumber(CONST(uint16, AUTOMATIC) BlockNumber)
{
    VAR(uint32, AUTOMATIC) lowId    = 0L;
    VAR(uint32, AUTOMATIC) highId   = (uint32)FEE_CONFIGURED_NUMBER_OF_BLOCKS - 1U;
    VAR(uint32, AUTOMATIC) middleId = 0;
    VAR(uint16, AUTOMATIC) retId    = (uint16)0xFFFF;

    middleId                        = lowId + (uint32)((highId - lowId) / 2U);

    while( lowId <= highId )
    {
        if(BlockNumber == Fee_BlockConfig[middleId].blkNum)
        {
            retId = (uint16)middleId;
            break;
        }
        else if(BlockNumber < Fee_BlockConfig[middleId].blkNum)
        {
            highId = middleId - 1U;
        }
        else
        {
            lowId = middleId + 1U;
        }

        middleId = lowId + ((highId - lowId) / 2U);
    }

    return (retId);
}

/*!
* @brief            get the block number by specified block index
*
* @param[in]        BlockRuntimeInfoIndex     the index of the block runtime infomation array
* @param[out]       None
* @param[in,out]    None
*
* @retval           uint16    block number
*/
LOCAL_INLINE FUNC(uint16, FEE_CODE) Fee_GetBlockNumberByBlockId(CONST(uint16, AUTOMATIC) BlockRuntimeInfoIndex)
{
    VAR(uint16, AUTOMATIC) blockNum = (uint16)0;

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(BlockRuntimeInfoIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
        /*** @warning Fee_c_MISRA_REF4 Suspicious: Dereference of an invalid pointer value. */
        /*** @warning Fee_c_MISRA_REF5 Suspicious: Dereference of an invalid pointer value. */
        /*** @warning Fee_c_MISRA_REF6 Suspicious: Dereference of an invalid pointer value. */
        blockNum = Fee_BlockConfig[BlockRuntimeInfoIndex].blkNum;
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
        blockNum = gs_feeForeignBlockConfig[BlockRuntimeInfoIndex - FEE_CONFIGURED_NUMBER_OF_BLOCKS].blkNum;
    }
#endif

    return blockNum;
}

/*!
* @brief            get the block size by specified block index
*
* @param[in]        BlockRuntimeInfoIndex     the index of the block runtime infomation array
* @param[out]       None
* @param[in,out]    None
*
* @retval           uint16    block size
*/
LOCAL_INLINE FUNC(uint16, FEE_CODE) Fee_GetBlockSizeByBlockId(CONST(uint16, AUTOMATIC) BlockRuntimeInfoIndex)
{
    VAR(uint16, AUTOMATIC) size = 0;

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(BlockRuntimeInfoIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
        /*** @warning Fee_c_MISRA_REF4 Suspicious: Dereference of an invalid pointer value. */
        /*** @warning Fee_c_MISRA_REF5 Suspicious: Dereference of an invalid pointer value. */
        /*** @warning Fee_c_MISRA_REF6 Suspicious: Dereference of an invalid pointer value. */
        size = Fee_BlockConfig[BlockRuntimeInfoIndex].blkSize;
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
        size = gs_feeForeignBlockConfig[BlockRuntimeInfoIndex - FEE_CONFIGURED_NUMBER_OF_BLOCKS].blkSize;
    }
#endif

    return size;
}

/*!
* @brief            get the cluster group id by specified index of the gs_feeBlockInfo array
*
* @param[in]        BlockRuntimeInfoIndex    index to get the gs_feeBlockInfo array element
* @param[out]       None
* @param[in,out]    None
*
* @retval           uint8    block Cluster Group index
*/
LOCAL_INLINE FUNC(uint8, FEE_CODE) Fee_GetBlockClusterGrpByBlockId(CONST(uint16, AUTOMATIC) BlockRuntimeInfoIndex)
{
    VAR(uint8, AUTOMATIC) blockClusterGrp = 0U;

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(BlockRuntimeInfoIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
        blockClusterGrp = Fee_BlockConfig[BlockRuntimeInfoIndex].clrGrp;
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
        /* this block is foreign Block */
        blockClusterGrp = gs_feeForeignBlockConfig[BlockRuntimeInfoIndex - FEE_CONFIGURED_NUMBER_OF_BLOCKS].clrGrp;
    }
#endif

    return blockClusterGrp;
}

/*!
* @brief            check if the block specified by a block index support immediate data feature
*
* @param[in]        BlockRuntimeInfoIndex     the index of the block runtime infomation array
* @param[out]       None
* @param[in,out]    None
*
* @retval           TRUE  - support immediate data feature
*                   FALSE - do not support immediate data feature
*/
LOCAL_INLINE FUNC(boolean, FEE_CODE)
    Fee_CheckBlockImmediateSupportByBlockId(CONST(uint16, AUTOMATIC) BlockRuntimeInfoIndex)
{
    VAR(boolean, AUTOMATIC) ret = (boolean)FALSE;

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(BlockRuntimeInfoIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
        /*** @warning Fee_c_MISRA_REF4 Suspicious: Dereference of an invalid pointer value. */
        /*** @warning Fee_c_MISRA_REF5 Suspicious: Dereference of an invalid pointer value. */
        /*** @warning Fee_c_MISRA_REF6 Suspicious: Dereference of an invalid pointer value. */
        ret = Fee_BlockConfig[BlockRuntimeInfoIndex].iDataFlg;
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
        ret = gs_feeForeignBlockConfig[BlockRuntimeInfoIndex - FEE_CONFIGURED_NUMBER_OF_BLOCKS].iDataFlg;
    }
#endif

    return ret;
}

/*!
* @brief            return size that to align with Virtual Page size
*
* @param[in]        BlockSize    the size of the block data
* @param[out]       None
* @param[in,out]    None
*
* @retval           uint16       the size after align with Virtual Page size
*/
static FUNC(uint16, FEE_CODE) Fee_AlignSizeToVirtualPage(VAR(uint16, AUTOMATIC) BlockSize)
{
    /* avoid BlockSize change warning */
    VAR(uint16, AUTOMATIC) size = BlockSize;
    /* Check whether the block size is not integer multiple of FEE_VIRTUAL_PAGE_SIZE */
    size = (0U == (size % FEE_VIRTUAL_PAGE_SIZE)) ?
                size :
                (((size / FEE_VIRTUAL_PAGE_SIZE) + 1U) * FEE_VIRTUAL_PAGE_SIZE);

    return ((uint16)size);
}

/*!
* @brief            copy user data to the internal write buffer,fill FEE_DEFAULT_VALUE to of the write buffer
*
* @param[in]        SourcePtr    point to the source buffer wait copy
* @param[in]        Length       the length need to copy
* @param[out]       TargetPtr    point to the target buffer store copt data
* @param[in,out]    None
*
* @retval           None
*/
static FUNC(void, FEE_CODE)
    Fee_CopyToPageBuffer(P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) SourcePtr,
                         P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) TargetPtr, CONST(uint16, AUTOMATIC) Length)
{
    VAR(uint16, AUTOMATIC) i = 0U;

    /* Copy the source data to the destination buffer */
    for(i = 0U; i < Length; i++)
    {
        TargetPtr[i] = SourcePtr[i];
    }
    
    /* Fill the remaining space with default values */
    for(i = Length; i < FEE_VIRTUAL_PAGE_SIZE; i++)
    {
        TargetPtr[i] = FEE_DEFAULT_VALUE;
    }
}

/*!
* @brief            the internal job to set current actiove cluster valid.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED  - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapClrVld(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retWrite    = E_OK;
    VAR(Fls_AddressType, AUTOMATIC) startAddr  = 0x0;

    startAddr                                  = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].srcAddr;

    /* Serialize cluster validation flag to data buffer */
    Fee_FlagSerializeOut(gs_feeIntDataBuffer, FEE_VALIDATED_FLAG);

    /* update cluster header to flash */
    retWrite  = Fls_Write((startAddr + FEE_CLUSTER_OVERHEAD_SIZE) - (2U * FEE_VIRTUAL_PAGE_SIZE), 
                          gs_feeIntDataBuffer,
                          FEE_VIRTUAL_PAGE_SIZE);

    retJob    = ((Std_ReturnType)E_OK == retWrite) ? MEMIF_JOB_PENDING : MEMIF_JOB_FAILED;

    gs_feeJob = FEE_INT_SWAP_CLR_VLD_DONE_JOB;

    return (retJob);
}

/*!
* @brief            the internal job to set cluster valid done in swap process.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapClrVldDone(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob  = MEMIF_JOB_OK;
    VAR(Fls_AddressType, AUTOMATIC) hdrAddrCur  = (Fls_AddressType)0;
    VAR(Fls_AddressType, AUTOMATIC) dataAddrCur = (Fls_AddressType)0;
    VAR(uint16, AUTOMATIC) blockId              = (uint16)0;
    VAR(uint16, AUTOMATIC) configBlockNumber    = (uint16)0;
    VAR(uint16, AUTOMATIC) blockSize            = (uint16)0;
    VAR(uint16, AUTOMATIC) alignedBlockSize     = (uint16)0;
#if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
    VAR(boolean, AUTOMATIC) immediateBlockFlag = (boolean)FALSE;
#endif
    VAR(uint8, AUTOMATIC) blockClusterGrp = (uint8)0;

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    configBlockNumber = FEE_CONFIGURED_NUMBER_OF_BLOCKS + gs_feeForeignBlocksNumber;
#else
    configBlockNumber  = FEE_CONFIGURED_NUMBER_OF_BLOCKS;
#endif

#if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT) && (STD_OFF == FEE_LEGACY_MODE_SUPPORT)
    /* re-initialize info about the blocks touching the Reserved Area because the swap has happened.*/
    Fee_MaskUntouchReservedAreaByClrGrp(gs_feeJobIntClrGrpId);
#endif

    hdrAddrCur  = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].srcAddr + FEE_CLUSTER_OVERHEAD_SIZE;
    dataAddrCur = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].srcAddr + Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].len;

    /* update block info */
    for(blockId = 0U; blockId < configBlockNumber; blockId++)
    {
        blockClusterGrp = Fee_GetBlockClusterGrpByBlockId(blockId);

        if(gs_feeJobIntClrGrpId != blockClusterGrp)
        {
            continue;
        }
        else
        {
            /* nothing */
        }

        switch(gs_feeBlockInfo[blockId].blockStatus)
        {
            case FEE_BLKST_VALID :
            case FEE_BLKST_INCONSISTENT :
            case FEE_BLKST_INCONSISTENT_COPY :
                {
                    gs_feeBlockInfo[blockId].invalidAddr =
                        (hdrAddrCur + FEE_BLOCK_OVERHEAD_SIZE) - FEE_VIRTUAL_PAGE_SIZE;
#if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
                    immediateBlockFlag = Fee_CheckBlockImmediateSupportByBlockId(blockId);
#endif
                    if((FEE_BLKST_VALID == gs_feeBlockInfo[blockId].blockStatus)
                       || (FEE_BLKST_INCONSISTENT_COPY == gs_feeBlockInfo[blockId].blockStatus)
#if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
                       || (((boolean)TRUE == immediateBlockFlag)
                           && (FEE_BLKST_INCONSISTENT == gs_feeBlockInfo[blockId].blockStatus))
#endif
                    )
                    {
                        blockSize        = Fee_GetBlockSizeByBlockId(blockId);
                        alignedBlockSize = Fee_AlignSizeToVirtualPage(blockSize);
                        dataAddrCur      = dataAddrCur - alignedBlockSize;
                        gs_feeBlockInfo[blockId].dataAddr =
                            (FEE_BLKST_INCONSISTENT_COPY == gs_feeBlockInfo[blockId].blockStatus) ? 0U : dataAddrCur;
                        if(FEE_BLKST_INCONSISTENT_COPY == gs_feeBlockInfo[blockId].blockStatus)
                        {
                            /* data is damaged by read during copy,so it's inconsistent */
                            gs_feeBlockInfo[blockId].blockStatus = FEE_BLKST_INCONSISTENT;
                        }
                        else
                        {
                            /* nothing */
                        }
                    }
                    else
                    {
                        gs_feeBlockInfo[blockId].dataAddr = 0;
                    }

                    hdrAddrCur = hdrAddrCur + FEE_BLOCK_OVERHEAD_SIZE;
                }
                break;
            default :
                {
                    /* this block is not usable */
                    gs_feeBlockInfo[blockId].dataAddr    = 0;
                    gs_feeBlockInfo[blockId].invalidAddr = 0;
                }
                break;
        }
    }

    gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClr = gs_feeJobIntClrId;
    gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClrID++;
    gs_feeClusterInfo[gs_feeJobIntClrGrpId].hdrAddrCur  = hdrAddrCur;
    gs_feeClusterInfo[gs_feeJobIntClrGrpId].dataAddrCur = dataAddrCur;

    /* restore to original job */
    gs_feeJobIntClrGrpId =
        (FEE_INT_SCAN_CLR_JOB == gs_feeIntOriginalJob) ? (gs_feeJobIntClrGrpId + 1U) : gs_feeJobIntClrGrpId;

    /* transition from MEMIF_BUSYINTERNAL to MEMIF_BUSY */
    gs_feeIntModuleStatus = MEMIF_BUSY;

    gs_feeJob             = gs_feeIntOriginalJob;
    retJob                = Fee_ScheduleForJob();

    return (retJob);
}

/*!
* @brief            the internal job to swap block.(copy block form source to target cluster)
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED  - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapBlock(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retWrite    = E_OK;
    VAR(uint16, AUTOMATIC) configBlockNumber   = (uint16)0;
    VAR(uint8, AUTOMATIC) swapNeedFlag         = (uint8)0;
    VAR(Fls_AddressType, AUTOMATIC) dataAddr   = 0x0;
    VAR(uint16, AUTOMATIC) blockSize           = (uint16)0x0;
    VAR(uint16, AUTOMATIC) alignedBlockSize    = (uint16)0x0;
    VAR(uint16, AUTOMATIC) blockNumber         = (uint16)0x0;
    VAR(uint8, AUTOMATIC) blockClusterGrp      = (uint8)0x0;
    VAR(boolean, AUTOMATIC) immediateBlock     = (boolean)FALSE;
    VAR(boolean, AUTOMATIC) tempFlag           = (boolean)FALSE;
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    VAR(Fee_BlkPrjAsgmtType, AUTOMATIC) blockAssignment = FEE_PRJASGMT_RESERVED;
#endif

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    configBlockNumber = FEE_CONFIGURED_NUMBER_OF_BLOCKS + gs_feeForeignBlocksNumber;
#else
    configBlockNumber  = FEE_CONFIGURED_NUMBER_OF_BLOCKS;
#endif

    if((FEE_BLKST_VALID == gs_feeBlockInfo[gs_feeIntJobBlockIndex].blockStatus)
       || (FEE_BLKST_INCONSISTENT == gs_feeBlockInfo[gs_feeIntJobBlockIndex].blockStatus)
       || (FEE_BLKST_INCONSISTENT_COPY == gs_feeBlockInfo[gs_feeIntJobBlockIndex].blockStatus))
    {
        swapNeedFlag = 1;
    }
    else
    {
        swapNeedFlag = 0;
    }

    /* find first valid or inconsistent block in cluster group need to swap cluster */
    while( gs_feeIntJobBlockIndex < configBlockNumber )
    {
        blockClusterGrp = Fee_GetBlockClusterGrpByBlockId(gs_feeIntJobBlockIndex);

        /* check if the block is need to be swap(FEE_BLKST_VALID,FEE_BLKST_INCONSISTENT,FEE_BLKST_INCONSISTENT_COPY) */
        if((gs_feeJobIntClrGrpId == blockClusterGrp) && (swapNeedFlag != (uint8)0))
        {
            break;
        }
        else
        {
            /* nothing */
        }

        gs_feeIntJobBlockIndex++;
    }
    tempFlag = (configBlockNumber == gs_feeIntJobBlockIndex) ? (boolean)TRUE : (boolean)FALSE;
    switch (tempFlag)
    {
        case (boolean)TRUE :
            {
                retJob = Fee_IntJobSwapClrVld();
            }
            break;
        default:
            {
                blockSize      = Fee_GetBlockSizeByBlockId(gs_feeIntJobBlockIndex);
                immediateBlock = Fee_CheckBlockImmediateSupportByBlockId(gs_feeIntJobBlockIndex);
                blockNumber    = Fee_GetBlockNumberByBlockId(gs_feeIntJobBlockIndex);
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                blockAssignment = Fee_GetBlockProjectAssignment(gs_feeIntJobBlockIndex);
#endif

#if(STD_OFF == FEE_LEGACY_MODE_SUPPORT)
                /* if Block status is not FEE_BLKST_VALID,no need  valid allocated the data spece for it */
                /*** @warning Fee_c_MISRA_REF4 Suspicious: Dereference of an invalid pointer value. */
                /*** @warning Fee_c_MISRA_REF5 Suspicious: Dereference of an invalid pointer value. */
                /*** @warning Fee_c_MISRA_REF6 Suspicious: Dereference of an invalid pointer value. */
                if(FEE_BLKST_VALID == gs_feeBlockInfo[gs_feeIntJobBlockIndex].blockStatus)
#else
                /* if Block status is FEE_BLKST_VALID or block is immediate data block,allocated the data spece for it */
                if((FEE_BLKST_VALID == gs_feeBlockInfo[gs_feeIntJobBlockIndex].blockStatus) || ((boolean)TRUE == immediateBlock))
#endif
                {
                    /* align to virtual page boundary */
                    alignedBlockSize        = Fee_AlignSizeToVirtualPage(blockSize);
                    dataAddr                = gs_feeIntJobDataAddr - alignedBlockSize;
                    gs_feeIntJobDataAddr    = dataAddr;
                    gs_feeIntJobCurrentAddr = dataAddr;
                }
                else
                {
                    dataAddr = 0U;
                }

                /* put block header Serialize to the write buffer */
                Fee_BlockHdrSerializeOut(blockNumber, blockSize, dataAddr, immediateBlock,
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                                         blockAssignment,
#endif
                                         gs_feeIntDataBuffer);
                /*** @warning Fee_c_MISRA_REF4 Suspicious: Dereference of an invalid pointer value. */
                /*** @warning Fee_c_MISRA_REF5 Suspicious: Dereference of an invalid pointer value. */
                /*** @warning Fee_c_MISRA_REF6 Suspicious: Dereference of an invalid pointer value. */
                if(FEE_BLKST_VALID == gs_feeBlockInfo[gs_feeIntJobBlockIndex].blockStatus)
                {
                    /* read data save in block */
                    gs_feeJob = FEE_INT_SWAP_DATA_READ_JOB;
                }
                else
                {
                    /* nothing to read. process next block */
                    gs_feeIntJobBlockIndex++;
                    gs_feeJob = FEE_INT_SWAP_BLOCK_JOB;
                }

                retWrite =
                    Fls_Write(gs_feeIntJobHdrAddr, gs_feeIntDataBuffer, FEE_BLOCK_OVERHEAD_SIZE - (2U * FEE_VIRTUAL_PAGE_SIZE));
                retJob              = (((Std_ReturnType)E_OK) != retWrite) ? MEMIF_JOB_FAILED : MEMIF_JOB_PENDING;

                /* read next block header */
                gs_feeIntJobHdrAddr = gs_feeIntJobHdrAddr + FEE_BLOCK_OVERHEAD_SIZE;  
            }
            break;
    }
    return (retJob);
}

/*!
* @brief            the internal job to change block status to valid in swap process.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapBlockVld(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retWrite    = E_OK;

    Fee_FlagSerializeOut(gs_feeIntDataBuffer, FEE_VALIDATED_FLAG);
    retWrite = Fls_Write(gs_feeIntJobHdrAddr - (2U * (uint8)FEE_VIRTUAL_PAGE_SIZE), gs_feeIntDataBuffer, (uint8)FEE_VIRTUAL_PAGE_SIZE);
    retJob   = (((Std_ReturnType)E_OK) != retWrite) ? MEMIF_JOB_FAILED : MEMIF_JOB_PENDING;

    gs_feeJob = FEE_INT_SWAP_BLOCK_JOB;

    return (retJob);
}

/*!
* @brief            the internal job to format cluster in swap process.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED  - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapClrFmt(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retWrite    = E_OK;
    VAR(Fls_AddressType, AUTOMATIC) startAddr  = (Fls_AddressType)0;
    VAR(Fls_LengthType, AUTOMATIC) length      = (Fls_LengthType)0;
    VAR(uint32, AUTOMATIC) actClrID            = (uint32)0;

    startAddr                                  = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].srcAddr;
    length                                     = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].len;
    actClrID                                   = gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClrID;

    /* create cluster header with new actClrID + 1 */
    Fee_ClusterHdrSerializeOut(actClrID + 1U, startAddr, length, gs_feeIntDataBuffer);

    retWrite  = Fls_Write(startAddr, gs_feeIntDataBuffer, FEE_CLUSTER_OVERHEAD_SIZE - (2U * FEE_VIRTUAL_PAGE_SIZE));
    retJob    = (((Std_ReturnType)E_OK) == retWrite) ? MEMIF_JOB_PENDING : MEMIF_JOB_FAILED;

    gs_feeJob = FEE_INT_SWAP_BLOCK_JOB;

    return (retJob);
}

/*!
* @brief            the internal job to erase new cluster in swap process.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapClrErase(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retErase    = E_OK;
    VAR(Fls_AddressType, AUTOMATIC) startAddr  = 0UL;
    VAR(Fls_LengthType, AUTOMATIC) length      = 0UL;

    startAddr                                  = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].srcAddr;
    length                                     = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].len;

    retErase                                   = Fls_Erase(startAddr, length);
    retJob    = (((Std_ReturnType)E_OK) == retErase) ? MEMIF_JOB_PENDING : MEMIF_JOB_FAILED;

    gs_feeJob = FEE_INT_SWAP_CLR_FMT_JOB;

    return (retJob);
}

/*!
* @brief            the internal job to write block data to new block in swap process.
*
* @param[in]        BufferValid    when Fls_Read read block data failed,it will set to FALSE,otherwise it will be TRUE
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING           - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED            - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapDataWrite(CONST(boolean, AUTOMATIC) BufferValid)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retWrite    = E_OK;
    if(BufferValid != (boolean)0)
    {
        retWrite = Fls_Write(gs_feeIntJobCurrentAddr, gs_feeIntDataBuffer, gs_feeJobBlockLength);
        retJob  = (((Std_ReturnType)E_OK) == retWrite) ? MEMIF_JOB_PENDING : MEMIF_JOB_FAILED;

        gs_feeIntJobCurrentAddr += gs_feeJobBlockLength;

        gs_feeJob = FEE_INT_SWAP_DATA_READ_JOB;
    }
    else
    {
        retJob = Fee_IntJobSwapDataRead((boolean)FALSE);
    }
    return (retJob);
}

/*!
* @brief            the internal job to read block data in swap process.
*
* @param[in]        BufferValid    when Fls_Read read block header failed,it will set to FALSE,otherwise it will be TRUE
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwapDataRead(CONST(boolean, AUTOMATIC) BufferValid)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_FAILED;
    VAR(uint16, AUTOMATIC) blockSize           = (uint16)0;
    VAR(uint16, AUTOMATIC) alignedBlockSize    = (uint16)0;
    VAR(Fls_AddressType, AUTOMATIC) readAddr   = (Fls_AddressType)0;

    blockSize                                  = Fee_GetBlockSizeByBlockId(gs_feeIntJobBlockIndex);

    alignedBlockSize                           = Fee_AlignSizeToVirtualPage(blockSize);

    /* get remaining length to read */
    gs_feeJobBlockLength                       = (gs_feeIntJobDataAddr + alignedBlockSize) - gs_feeIntJobCurrentAddr;
    if(((boolean)TRUE == BufferValid) && (0U != gs_feeJobBlockLength))
    {
        if(gs_feeJobBlockLength > FEE_DATA_BUFFER_SIZE)
        {
            gs_feeJobBlockLength = FEE_DATA_BUFFER_SIZE;
        }
        else
        {
            /* nothing to do */
        }

        readAddr = (gs_feeBlockInfo[gs_feeIntJobBlockIndex].dataAddr + gs_feeIntJobCurrentAddr) - gs_feeIntJobDataAddr;

        if(((Std_ReturnType)E_OK) != Fls_Read(readAddr, gs_feeIntDataBuffer, gs_feeJobBlockLength))
        {
            retJob = MEMIF_JOB_FAILED;
        }
        else
        {
            retJob = MEMIF_JOB_PENDING;
        }

        gs_feeJob = FEE_INT_SWAP_DATA_WRITE_JOB;
    }
    else
    {
        if((boolean)FALSE == BufferValid)
        {
            /*
            *    there was some error occur while reading the source cluster. so change the block status
            *    to FEE_BLKST_INCONSISTENT_COPY to mark the status change during swap, leave the block
            *    (in flash) INCONSITENT
            */
            gs_feeBlockInfo[gs_feeIntJobBlockIndex].blockStatus = FEE_BLKST_INCONSISTENT_COPY;
            gs_feeIntJobBlockIndex++;
            retJob = Fee_IntJobSwapBlock();
        }
        else
        {
            if(0U == gs_feeJobBlockLength)
            {
                /* No data to copy. */
                gs_feeIntJobBlockIndex++;
                retJob = Fee_IntJobSwapBlockVld();
            }
            else
            {
                /* nothing */
            }
        }
    }

    return (retJob);
}

/*!
* @brief            the internal job to start swap cluster for cluster group.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED  - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobSwap(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;

    /* temporary save original job so Fee can re-schedule this job once the swap is complete */
    gs_feeIntOriginalJob                       = gs_feeJob;
    gs_feeIntJobBlockIndex                     = 0;

    /* get current cluster index */
    gs_feeJobIntClrId =
        (((uint32)gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClr + 1U) == Fee_ClrGrps[gs_feeJobIntClrGrpId].clrCount) ?
            0U :
            (gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClr + 1U);

    gs_feeIntJobHdrAddr   = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].srcAddr;
    gs_feeIntJobDataAddr  = gs_feeIntJobHdrAddr + Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].len;

    /* point to the first block header */
    gs_feeIntJobHdrAddr  += FEE_CLUSTER_OVERHEAD_SIZE;

    /* Erase the cluster will swap to */
    retJob                = Fee_IntJobSwapClrErase();

    return (retJob);
}

/*!
* @brief            the internal job to scan block header.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanBlockHdrRead(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retRead     = E_OK;

    gs_feeJob                                  = FEE_INT_SCAN_BLOCK_HDR_PARSE_JOB;

    /* read the block header into internal read buffer*/
    retRead = Fls_Read(gs_feeIntJobCurrentAddr, gs_feeIntDataBuffer, FEE_BLOCK_OVERHEAD_SIZE);
    retJob  = (((Std_ReturnType)E_OK) != retRead) ? MEMIF_JOB_FAILED : MEMIF_JOB_PENDING;

    return (retJob);
}

/*!
* @brief            the internal job to erase first cluster in current cluster group.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClrErase(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retErase    = E_OK;
    VAR(Fls_AddressType, AUTOMATIC) startAddr  = (Fls_AddressType)0x0;
    VAR(Fls_LengthType, AUTOMATIC) length      = (Fls_LengthType)0x0;

    startAddr                                  = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[0].srcAddr;
    length                                     = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[0].len;

    retErase                                   = Fls_Erase(startAddr, length);
    if(((Std_ReturnType)E_OK) == retErase)
    {
        retJob = MEMIF_JOB_PENDING;
    }
    else
    {
        retJob = MEMIF_JOB_FAILED;
    }

    gs_feeJob = FEE_INT_SCAN_CLR_FMT_JOB;

    return (retJob);
}

/*!
* @brief            the internal job to scan cluster in current cluster group.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClr(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob   = MEMIF_JOB_OK;
    VAR(uint8, AUTOMATIC) clrId                  = 0;
    VAR(Fls_AddressType, AUTOMATIC) clrStartAddr = 0;
    VAR(Fls_LengthType, AUTOMATIC) clrLength     = 0;

    if(FEE_CLUSTER_GROUPS_NUMBER == gs_feeJobIntClrGrpId)
    {
        /* no more cluster groups to scan */
        gs_feeJob = FEE_COMPLETE_JOB;
    }
    else
    {
        if(0U == gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClrID)
        {
            /* call notification callback function that user configuration */
            FEE_NVM_CLUSTER_FORMAT_NOTIFICATION
            /* No active cluster found,format the first cluster */
            retJob = Fee_IntJobScanClrErase();
        }
        else
        {
            /* found active cluster */
            clrId                   = gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClr;

            clrStartAddr            = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[clrId].srcAddr;

            /* get first block header address */
            gs_feeIntJobCurrentAddr = FEE_CLUSTER_OVERHEAD_SIZE + clrStartAddr;

            gs_feeClusterInfo[gs_feeJobIntClrGrpId].hdrAddrCur  = gs_feeIntJobCurrentAddr;

            /* get block data address */
            clrLength                                           = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[clrId].len;
            gs_feeClusterInfo[gs_feeJobIntClrGrpId].dataAddrCur = clrStartAddr + clrLength;

            retJob                                              = Fee_IntJobScanBlockHdrRead();
        }
    }

    return (retJob);
}

/*!
* @brief            the internal job to erase cluster in scan cluster process.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClrFmt(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retWrite    = E_OK;
    VAR(Fls_AddressType, AUTOMATIC) startAddr  = 0UL;
    VAR(Fls_LengthType, AUTOMATIC) length      = 0UL;

    startAddr                                  = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[0].srcAddr;
    length                                     = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[0].len;

    /* create cluster header with clusterID = 1 */
    Fee_ClusterHdrSerializeOut(1U, startAddr, length, gs_feeIntDataBuffer);

    Fee_FlagSerializeOut(&gs_feeIntDataBuffer[FEE_CLUSTER_OVERHEAD_SIZE - (2U * FEE_VIRTUAL_PAGE_SIZE)],
                         FEE_VALIDATED_FLAG);

    retWrite = Fls_Write(startAddr, gs_feeIntDataBuffer, FEE_CLUSTER_OVERHEAD_SIZE - FEE_VIRTUAL_PAGE_SIZE);
    retJob   = (((Std_ReturnType)E_OK) == retWrite) ? MEMIF_JOB_PENDING : MEMIF_JOB_FAILED;

    gs_feeJob = FEE_INT_SCAN_CLR_FMT_DONE_JOB;

    return (retJob);
}

/*!
* @brief            the internal job to process cluster erase done in scan cluster process.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*                   MEMIF_JOB_OK         - the job is done,no more cluster need to scan
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClrFmtDone(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob          = MEMIF_JOB_OK;
    VAR(Fls_AddressType, AUTOMATIC) clrStartAddr        = (Fls_AddressType)0x0;
    VAR(Fls_LengthType, AUTOMATIC) clrLength            = (Fls_LengthType)0x0;

    /* Assign Index and ID of active cluster */
    gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClr      = 0x0;
    gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClrID    = 0x1;

    clrStartAddr                                        = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[0].srcAddr;
    clrLength                                           = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[0].len;

    /* set the block header pointer */
    gs_feeClusterInfo[gs_feeJobIntClrGrpId].hdrAddrCur  = clrStartAddr + FEE_CLUSTER_OVERHEAD_SIZE;

    /* set the block data pointer */
    gs_feeClusterInfo[gs_feeJobIntClrGrpId].dataAddrCur = clrStartAddr + clrLength;

    /* process next cluster group */
    gs_feeJobIntClrGrpId++;

    retJob = Fee_IntJobScanClr();

    return (retJob);
}

/*!
* @brief            the internal job to parse block header in scan process.
*
* @param[in]        BufferValid    when Fls_Read read block header failed,it will set to FALSE,otherwise it will be TRUE
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED  - the job is refuse by underlying driver
*                   MEMIF_JOB_OK      - the job is done,no more cluster need to scan
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanBlockHdrParse(CONST(boolean, AUTOMATIC) BufferValid)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_FAILED;
    VAR(Fee_BlockStatusType, AUTOMATIC) blockStatus;
    VAR(uint16, AUTOMATIC) blockNumber           = (uint16)0x0;
    VAR(uint16, AUTOMATIC) blockSize             = (uint16)0x0;
    VAR(Fls_AddressType, AUTOMATIC) dataAddr     = (Fls_AddressType)0x0;
    VAR(boolean, AUTOMATIC) immediateData        = (boolean)FALSE;
    VAR(uint16, AUTOMATIC) blockId               = (uint16)0x0;
    VAR(uint16, AUTOMATIC) alignedBlockSize      = (uint16)0x0;
    VAR(uint16, AUTOMATIC) blockRuntimeInfoIndex = (uint16)0x0;
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    VAR(Fee_BlkPrjAsgmtType, AUTOMATIC) blockAssignment  = FEE_PRJASGMT_RESERVED;
    VAR(uint16, AUTOMATIC) foreignBlockId        = (uint16)0x0;
    VAR(boolean, AUTOMATIC) foreignFlag          = (boolean)FALSE;
    VAR(boolean, AUTOMATIC) foreignBlockOverflow = (boolean)FALSE;
#endif
    static VAR(boolean, AUTOMATIC) swapToBePerformedFlag = (boolean)FALSE;
    VAR(boolean, AUTOMATIC) feeSwapNeededFlag              = (boolean)FALSE;

    /* deserialize block header params from read buffer */
    blockStatus = Fee_BlockHdrDeserializeOut(&blockNumber, &blockSize, &dataAddr, &immediateData,
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                                     (uint8*)&blockAssignment,
#endif
                                             gs_feeIntDataBuffer);

#if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
    /*
    *   set swap variable if this block is the last header (set swapToBePerformedFlag when previously processed
    *   block wasn't properly recognized. perform a swap to prevent a risk that this block overlaps the Reserved
    *   Area. )
    */
    if(FEE_BLKST_HEADER_BLANK != blockStatus)
    {
        swapToBePerformedFlag = (boolean)FALSE;
    }
    else
    {
        /* nothing */
    }
#endif

    if((FEE_BLKST_HEADER_INVALID == blockStatus) || ((boolean)FALSE == BufferValid))
    {
        /* mask block invalidate with set the block index wo 0xffff */
        gs_feeJobBlockIndex                                 = 0xffff;

        /* point to next block header */
        gs_feeIntJobCurrentAddr                            += FEE_BLOCK_OVERHEAD_SIZE;

        gs_feeClusterInfo[gs_feeJobIntClrGrpId].hdrAddrCur  = gs_feeIntJobCurrentAddr;

        swapToBePerformedFlag                               = (boolean)TRUE;

        /* read next block header */
        retJob                                              = Fee_IntJobScanBlockHdrRead();
    }
    else
    {
        switch(blockStatus)
        {
            case FEE_BLKST_HEADER_BLANK :
                {
                    /* if SwapToBePerformed is TRUE, it means it is the last block header of this cluster.*/
                    if(((boolean)TRUE) == swapToBePerformedFlag)
                    {
                        swapToBePerformedFlag = (boolean)FALSE;
                        gs_feeClusterInfo[gs_feeJobIntClrGrpId].dataAddrCur =
                            gs_feeIntJobCurrentAddr + (2U * FEE_BLOCK_OVERHEAD_SIZE);
                    }
                    else
                    {
                        /* nothing */
                    }

                    /* point to next cluster group */
                    gs_feeJobIntClrGrpId++;

                    retJob = Fee_IntJobScanClr();
                }
                break;
            default :
                {
                    blockId = Fee_GetBlockIndexByBlockNumber(blockNumber);
    #if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                    foreignFlag = (boolean)FALSE;

                    /* block not found,but it may be a foreign block */
                    if(((uint16)0xFFFF == blockId) &&
        #if(FEE_PROJECT_ASSIGNMENT_CONFIG == STD_ON)
                       (FEE_PRJASGMT_APP == blockAssignment)
        #else
                       (FEE_PRJASGMT_BTL == blockAssignment)
        #endif
                    )
                    {
                        foreignFlag    = (boolean)TRUE;
                        foreignBlockId = Fee_GetForeignBlockIndexByBlockNumber(blockNumber);

                        if((uint16)0xFFFF == foreignBlockId)
                        {
                            /* this is  a new foreign block */
                            if(gs_feeForeignBlocksNumber < ((uint32)FEE_MAX_NUMBER_OF_BLOCKS - (uint32)FEE_CONFIGURED_NUMBER_OF_BLOCKS))
                            {
                                foreignBlockId                                    = gs_feeForeignBlocksNumber;
                                gs_feeForeignBlockConfig[foreignBlockId].blkAsgmt = blockAssignment;
                                gs_feeForeignBlockConfig[foreignBlockId].clrGrp   = gs_feeJobIntClrGrpId;
                                gs_feeForeignBlockConfig[foreignBlockId].blkNum   = blockNumber;
                                gs_feeForeignBlockConfig[foreignBlockId].iDataFlg = immediateData;
                                gs_feeForeignBlockConfig[foreignBlockId].blkSize  = blockSize;
                                gs_feeForeignBlocksNumber++;
                            }
                            else
                            {
        #if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID,
                                                FEE_E_FOREIGN_BLOCKS_OVERFLOW);
        #endif
                                FEE_NVM_JOB_ERROR_NOTIFICATION
                                foreignBlockOverflow = (boolean)TRUE;
                            }
                        }
                        else
                        {
                            /* nothing */
                        }
                    }
                    else
                    {
                        /* nothing */
                    }
    #endif

    #if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                    if((boolean)TRUE == foreignBlockOverflow)
                    {
                        retJob = MEMIF_JOB_FAILED;
                    }
                    else
                    {
    #endif
    #if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                    if((0xFFFFU == blockId) && ((boolean)FALSE == foreignFlag))
    #else
                    if(0xFFFFU == blockId)
    #endif
                        {
                            swapToBePerformedFlag = (boolean)TRUE;
                        }
                        else
                        {
    #if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                            blockRuntimeInfoIndex = ((boolean)TRUE == foreignFlag) ?
                                                        (FEE_CONFIGURED_NUMBER_OF_BLOCKS + foreignBlockId) :
                                                        blockId;
    #else
                            blockRuntimeInfoIndex = blockId;
    #endif

                            alignedBlockSize  = Fee_AlignSizeToVirtualPage(blockSize);
                            /* make sure data pointer points to an acceptable area */
                            if((dataAddr <= (gs_feeClusterInfo[gs_feeJobIntClrGrpId].dataAddrCur - alignedBlockSize))
                               && (dataAddr >= (gs_feeClusterInfo[gs_feeJobIntClrGrpId].hdrAddrCur
                                                + (2U * FEE_BLOCK_OVERHEAD_SIZE))))
                            {
    #if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                                if((boolean)FALSE == foreignFlag)
                                {
    #endif
                                    /* make sure block infomation is match the configuration info */
                                    if((Fee_BlockConfig[blockId].clrGrp == gs_feeJobIntClrGrpId)
                                       && (Fee_BlockConfig[blockId].blkSize == blockSize)
                                       && (Fee_BlockConfig[blockId].iDataFlg == immediateData))
                                    {
                                        feeSwapNeededFlag = (boolean)FALSE;
                                    }
                                    else
                                    {
                                        feeSwapNeededFlag = (boolean)TRUE;
                                    }
    #if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                                }
                                else
                                {
                                    /* nothing */
                                }
    #endif
                            }
                            else
                            {
                                feeSwapNeededFlag = (boolean)TRUE;
                            }

                            if((boolean)TRUE == feeSwapNeededFlag)
                            {
                                swapToBePerformedFlag = (boolean)TRUE;
                            }
                            else
                            {
                                /* update block runtime information */
    #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        #if(STD_OFF == FEE_LEGACY_MODE_SUPPORT)
                                if(FEE_BLKST_INCONSISTENT != blockStatus)
        #else
                                if((FEE_BLKST_INCONSISTENT != blockStatus) || (((boolean)TRUE) == immediateData))
        #endif
                                {
                                    if(FEE_BLKST_INVALIDATED == blockStatus)
                                    {
                                        blockStatus = FEE_BLKST_INVALID;
                                    }
                                    else
                                    {
                                        /* nothing */
                                    }
    #endif

                                    gs_feeBlockInfo[blockRuntimeInfoIndex].invalidAddr =
                                        (gs_feeIntJobCurrentAddr + FEE_BLOCK_OVERHEAD_SIZE) - FEE_VIRTUAL_PAGE_SIZE;
                                    gs_feeBlockInfo[blockRuntimeInfoIndex].blockStatus  = blockStatus;
                                    gs_feeBlockInfo[blockRuntimeInfoIndex].dataAddr     = dataAddr;

                                    gs_feeClusterInfo[gs_feeJobIntClrGrpId].dataAddrCur = dataAddr;
    #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
                                }
                                else
                                {
                                    gs_feeClusterInfo[gs_feeJobIntClrGrpId].dataAddrCur = dataAddr;
                                }
    #endif

    #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT) && (STD_OFF == FEE_LEGACY_MODE_SUPPORT)
                                gs_feeClusterInfo[gs_feeJobIntClrGrpId].hdrAddrCur =
                                    gs_feeIntJobCurrentAddr + FEE_BLOCK_OVERHEAD_SIZE;
                                if(FALSE != Fee_CheckReservedAreaTargetedInClrGrp(gs_feeJobIntClrGrpId))
                                {
                                    if(immediateData)
                                    {
                                        Fee_MaskTouchReservedAreaByBlock(blockRuntimeInfoIndex);
                                    }
                                    else
                                    {
                                        /* no immediateData block has touch the reserve space for immediateData block,so swap */
                                        swapToBePerformedFlag = (boolean)TRUE;
                                    }
                                }
    #endif
                            }
                        }

                        gs_feeIntJobCurrentAddr = gs_feeIntJobCurrentAddr + FEE_BLOCK_OVERHEAD_SIZE;
                        gs_feeClusterInfo[gs_feeJobIntClrGrpId].hdrAddrCur = gs_feeIntJobCurrentAddr;
                        retJob                                             = Fee_IntJobScanBlockHdrRead();
    #if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                    }
    #endif
                }
                break;
        }
    }
    return (retJob);
}

/*!
* @brief            the internal job to read cluster header in scan process.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED  - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClrHdrRead(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob  = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retRead      = E_OK;
    VAR(Fls_AddressType, AUTOMATIC) readAddress = 0UL;

    /* Get read address of current cluster */
    readAddress                                 = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].srcAddr;

    retRead                                     = Fls_Read(readAddress, gs_feeIntDataBuffer, FEE_CLUSTER_OVERHEAD_SIZE);
    retJob    = (((Std_ReturnType)E_OK) == retRead) ? MEMIF_JOB_PENDING : MEMIF_JOB_FAILED;

    gs_feeJob = FEE_INT_SCAN_CLR_HDR_PARSE_JOB;

    return (retJob);
}

/*!
* @brief            the internal job to prase cluster header in scan process.
*
* @param[in]        BufferValid    when Fls_Read read cluster header failed,it will set to FALSE,otherwise it will be TRUE
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScanClrHdrParse(CONST(boolean, AUTOMATIC) BufferValid)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob      = MEMIF_JOB_OK;
    VAR(Fls_AddressType, AUTOMATIC) clrStartAddr    = (Fls_AddressType)0x0;
    VAR(Fls_LengthType, AUTOMATIC) cfgClrSize       = (Fls_LengthType)0x0;
    VAR(Fee_ClusterStatusType, AUTOMATIC) clrStatus = FEE_CLRST_VALID;
    VAR(uint32, AUTOMATIC) clrID                    = (uint32)0x0;
    VAR(Fls_LengthType, AUTOMATIC) clrSize          = (Fls_LengthType)0x0;
    VAR(Fls_AddressType, AUTOMATIC) cfgStartAddr    = (Fls_AddressType)0x0;

    /* get cluster start address and size in configuration parameter */
    cfgStartAddr = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].srcAddr;
    cfgClrSize   = Fee_ClrGrps[gs_feeJobIntClrGrpId].clrPtr[gs_feeJobIntClrId].len;

    clrStatus    = Fee_ClusterHdrDeserializeOut(&clrID, &clrStartAddr, &clrSize, gs_feeIntDataBuffer);

    if(((boolean)TRUE == BufferValid) && (FEE_CLRST_VALID == clrStatus))
    {
        if((clrStartAddr == cfgStartAddr) && (clrSize == cfgClrSize)
           && (clrID > gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClrID))
        {
            /* found a valid and active cluster */
            gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClr   = gs_feeJobIntClrId;
            gs_feeClusterInfo[gs_feeJobIntClrGrpId].actClrID = clrID;
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        /* nothing */
    }

    gs_feeJobIntClrId++;

    /* get next cluster if it's not the last cluster of this cluster group */
    if(gs_feeJobIntClrId == Fee_ClrGrps[gs_feeJobIntClrGrpId].clrCount)
    {
        gs_feeJobIntClrId = 0;
        gs_feeJobIntClrGrpId++;
    }
    else
    {
        /* nothing */
    }

    if(FEE_CLUSTER_GROUPS_NUMBER != gs_feeJobIntClrGrpId)
    {
        /* read next cluster header */
        retJob = Fee_IntJobScanClrHdrRead();
    }
    else
    {
        gs_feeJobIntClrGrpId = 0;

        /* all cluster header has been read*/
        retJob               = Fee_IntJobScanClr();
    }

    return (retJob);
}

/*!
* @brief            the internal job to Scan all cluster & block belong this Fee module.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED  - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_IntJobScan(void)
{
    gs_feeJobIntClrGrpId = 0U;
    gs_feeJobIntClrId    = 0U;

    return (Fee_IntJobScanClrHdrRead());
}

/*!
* @brief            the internal job to read block data.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING           - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED            - the job is refuse by underlying driver
*                   MEMIF_BLOCK_INVALID         - the block that Requested is invalid.
*                   MEMIF_BLOCK_INCONSISTENT    - the block that Requested is inconsistent.
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_ReadJob(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob      = MEMIF_JOB_FAILED;
    VAR(Fee_BlockStatusType, AUTOMATIC) blockStatus = FEE_BLKST_VALID;
    VAR(Fls_AddressType, AUTOMATIC) blockAddress    = (Fls_AddressType)0;

    blockStatus                                     = gs_feeBlockInfo[gs_feeJobBlockIndex].blockStatus;
    blockAddress                                    = gs_feeBlockInfo[gs_feeJobBlockIndex].dataAddr;

    switch(blockStatus)
    {
        case FEE_BLKST_VALID :
            {
                if(((Std_ReturnType)E_OK)
                   != Fls_Read(blockAddress + gs_feeJobBlockOffset, gs_feeReadJobDataDestPtr, gs_feeJobBlockLength))

                {
                    retJob = MEMIF_JOB_FAILED;
                }
                else
                {
                    retJob = MEMIF_JOB_PENDING;
                }
            }
            break;
#if(FEE_MARK_EMPTY_BLOCKS_INVALID_SUPPORT == STD_OFF)
        case FEE_BLKST_NEVER_WRITTEN :
            {
                retJob = MEMIF_BLOCK_INCONSISTENT;
            }
            break;
#endif
        case FEE_BLKST_INVALID :
            {
                retJob = MEMIF_BLOCK_INVALID;
            }
            break;
        case FEE_BLKST_INCONSISTENT :
        case FEE_BLKST_INCONSISTENT_COPY :
            {
#if(STD_OFF == FEE_LEGACY_MODE_SUPPORT)
                retJob = MEMIF_BLOCK_INCONSISTENT;
#else
    #if(STD_ON == FEE_MARK_EMPTY_BLOCKS_INVALID_SUPPORT)
                    if((Std_ReturnType)E_OK != Fee_BlockConfig[gs_feeJobBlockIndex].iDataFlg)
                    {
                        retJob = MEMIF_BLOCK_INVALID;
                    }
                    else
    #endif
                    {
                        retJob = MEMIF_BLOCK_INCONSISTENT;
                    }
#endif
            }
            break;
        default :
            {
                /* read failed */
            }
            break;
    }

    gs_feeJob = FEE_COMPLETE_JOB;

    return (retJob);
}

/*!
* @brief            the internal job to write block header.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteHdrJob(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_FAILED;
    VAR(Std_ReturnType, AUTOMATIC) retWrite    = E_OK;
    VAR(uint8, AUTOMATIC) clrGrpIndex          = (uint8)0x0;
    VAR(uint16, AUTOMATIC) blockSize           = (uint16)0x0;
    VAR(uint16, AUTOMATIC) alignedBlockSize    = (uint16)0x0;
    VAR(Fls_AddressType, AUTOMATIC) dataAddr   = (Fls_AddressType)0x0;
    VAR(Fls_AddressType, AUTOMATIC) hdrAddr    = (Fls_AddressType)0x0;
#if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
    VAR(Fls_LengthType, AUTOMATIC) availClrSpace = (Fls_LengthType)0x0;
#endif

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(gs_feeJobBlockIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
        clrGrpIndex      = Fee_BlockConfig[gs_feeJobBlockIndex].clrGrp;

        blockSize        = Fee_BlockConfig[gs_feeJobBlockIndex].blkSize;

        /* Aalign size to the virtual page boundary */
        alignedBlockSize = Fee_AlignSizeToVirtualPage(blockSize);

#if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
        availClrSpace = gs_feeClusterInfo[clrGrpIndex].dataAddrCur - gs_feeClusterInfo[clrGrpIndex].hdrAddrCur;
        /*
         *       every FEE_BLOCK_OVERHEAD_SIZE must be left blank to have a clear separation between
         *       header block space and data block space
         */
        if((((uint32)alignedBlockSize) + (2U * FEE_BLOCK_OVERHEAD_SIZE)) > availClrSpace)
#else
        if((boolean)FALSE == Fee_ReservedAreaEnableWrite())
#endif
        {
            /* if space is no enough,start swap process */
            gs_feeJobIntClrGrpId = clrGrpIndex;

            retJob               = Fee_IntJobSwap();
        }
        else
        {
            /* get data and header addresses */
            hdrAddr  = gs_feeClusterInfo[clrGrpIndex].hdrAddrCur;
            dataAddr = gs_feeClusterInfo[clrGrpIndex].dataAddrCur - alignedBlockSize;

            Fee_BlockHdrSerializeOut(Fee_BlockConfig[gs_feeJobBlockIndex].blkNum, blockSize, dataAddr,
                                     Fee_BlockConfig[gs_feeJobBlockIndex].iDataFlg,
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
                                     Fee_BlockConfig[gs_feeJobBlockIndex].blkAsgmt,
#endif
                                     gs_feeIntDataBuffer);

            retWrite = Fls_Write(hdrAddr, gs_feeIntDataBuffer, FEE_BLOCK_OVERHEAD_SIZE - (2U * FEE_VIRTUAL_PAGE_SIZE));
            retJob   = (((Std_ReturnType)E_OK) != retWrite) ? MEMIF_JOB_FAILED : MEMIF_JOB_PENDING;
        }
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
        (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID, FEE_E_INVALID_BLOCK_NO);
#endif
        retJob = MEMIF_JOB_FAILED;
        FEE_NVM_JOB_ERROR_NOTIFICATION
    }
#endif
    return (retJob);
}

/*!
* @brief            the internal job to write block data.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteDataJob(void)
{
    VAR(Std_ReturnType, AUTOMATIC) retWrite                = E_OK;
    VAR(Fls_AddressType, AUTOMATIC) dataAddr               = (Fls_AddressType)0;
    VAR(MemIf_JobResultType, AUTOMATIC) retJob             = MEMIF_JOB_OK;
    VAR(uint16, AUTOMATIC) blockSize                       = (uint16)0x0;
    VAR(uint16, AUTOMATIC) alignedBlockSize                = (uint16)0x0;
    VAR(Fls_LengthType, AUTOMATIC) writeLength             = (Fls_LengthType)0x0;
    VAR(uint8, AUTOMATIC) clrGrp                           = (uint8)0x0;
    P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) writeDataPtr = NULL_PTR;
#if(FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT == STD_OFF)
    VAR(Fls_AddressType, AUTOMATIC) hdrAddr = 0U;
    VAR(uint8, AUTOMATIC) clrGrpIndex       = (uint8)0;
#endif

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(gs_feeJobBlockIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
    #if(FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT == STD_OFF)
        #if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
        if((boolean)FALSE == Fee_BlockConfig[gs_feeJobBlockIndex].iDataFlg)
        {
        #endif
            blockSize         = Fee_BlockConfig[gs_feeJobBlockIndex].blkSize;
            alignedBlockSize  = Fee_AlignSizeToVirtualPage(blockSize);
            clrGrpIndex       = Fee_BlockConfig[gs_feeJobBlockIndex].clrGrp;
            dataAddr          = gs_feeClusterInfo[clrGrpIndex].dataAddrCur - alignedBlockSize;
            hdrAddr           = gs_feeClusterInfo[clrGrpIndex].hdrAddrCur;

            gs_feeBlockInfo[gs_feeJobBlockIndex].dataAddr = dataAddr;
            gs_feeBlockInfo[gs_feeJobBlockIndex].invalidAddr = (hdrAddr + FEE_BLOCK_OVERHEAD_SIZE) - FEE_VIRTUAL_PAGE_SIZE;
        #if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
        }
        else
        {
            /* nothing */
        }
        #endif
        /* mark the block as inconsistent when the block header will start write */
        gs_feeBlockInfo[gs_feeJobBlockIndex].blockStatus = FEE_BLKST_INCONSISTENT;
    #endif

        blockSize        = Fee_BlockConfig[gs_feeJobBlockIndex].blkSize;
        alignedBlockSize = Fee_AlignSizeToVirtualPage(blockSize);

        clrGrp           = Fee_BlockConfig[gs_feeJobBlockIndex].clrGrp;

    #if(STD_OFF == FEE_LEGACY_MODE_SUPPORT)
        /* update both header and data addresses */
        gs_feeClusterInfo[clrGrp].hdrAddrCur  += FEE_BLOCK_OVERHEAD_SIZE;
        gs_feeClusterInfo[clrGrp].dataAddrCur -= alignedBlockSize;
        #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)

        /* when STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT , Fee_BlockInfo is not updated immediatelly upon a header write. */
        if(0 != Fee_BlockConfig[gs_feeJobBlockIndex].iDataFlg)
        {
            if (FALSE != Fee_CheckReservedAreaTargetedInClrGrp(clrGrp))
            {
                Fee_MaskTouchReservedAreaByBlock(gs_feeJobBlockIndex);
            }
        }
        #endif
    #else
        if((Std_ReturnType)E_OK != Fee_BlockConfig[gs_feeJobBlockIndex].iDataFlg)
        {
            gs_feeClusterInfo[clrGrp].hdrAddrCur  += FEE_BLOCK_OVERHEAD_SIZE;
            gs_feeClusterInfo[clrGrp].dataAddrCur -= alignedBlockSize;
        }
        else
        {
            /* immediate write no need to update header and data pointes */
        }
    #endif

        if (alignedBlockSize == blockSize)
        {
            writeDataPtr = gs_feeWriteJobDataDestPtr;
            writeLength = blockSize;

            gs_feeJob   = FEE_WRITE_VALIDATE_JOB;
        }
        else
        {
            if (blockSize < FEE_VIRTUAL_PAGE_SIZE)
            {
                Fee_CopyToPageBuffer(gs_feeWriteJobDataDestPtr, gs_feeIntDataBuffer, blockSize);
                writeDataPtr = gs_feeIntDataBuffer;
                writeLength  = FEE_VIRTUAL_PAGE_SIZE;

                gs_feeJob    = FEE_WRITE_VALIDATE_JOB;
            }
            else
            {
                writeDataPtr = gs_feeWriteJobDataDestPtr;
                writeLength = ((uint32)alignedBlockSize) - FEE_VIRTUAL_PAGE_SIZE;
                gs_feeJob   = FEE_WRITE_UNALIGNED_DATA_JOB;
            }
        }

    #if(STD_OFF == FEE_LEGACY_MODE_SUPPORT)
        dataAddr = gs_feeClusterInfo[clrGrp].dataAddrCur;
    #else
        #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        if((Std_ReturnType)E_OK == Fee_BlockConfig[gs_feeJobBlockIndex].iDataFlg)
        {
            dataAddr = gs_feeClusterInfo[clrGrp].dataAddrCur;
        }
        else
        #endif
        {
            dataAddr = gs_feeBlockInfo[gs_feeJobBlockIndex].dataAddr;
        }
    #endif

        retWrite = Fls_Write(dataAddr, writeDataPtr, writeLength);
        retJob   = (((Std_ReturnType)E_OK) == retWrite) ? MEMIF_JOB_PENDING : MEMIF_JOB_FAILED;
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
    #if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
        (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID, FEE_E_INVALID_BLOCK_NO);
    #endif
        retJob = MEMIF_JOB_FAILED;
        FEE_NVM_JOB_ERROR_NOTIFICATION
    }
#endif
    return (retJob);
}

/*!
* @brief            the internal job to write block unaligned data.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteUnalignedDataJob(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retWrite    = E_OK;
    VAR(Fls_AddressType, AUTOMATIC) dataAddr   = (Fls_AddressType)0x0;
    VAR(uint16, AUTOMATIC) blockSize           = (uint16)0x0;
    VAR(uint16, AUTOMATIC) writeLength         = (uint16)0x0;
    VAR(uint16, AUTOMATIC) writeOffset         = (uint16)0x0;

#if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
    VAR(uint16, AUTOMATIC) clrGrp = (uint16)0x0;
#endif
    
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(gs_feeJobBlockIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
    #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        clrGrp = Fee_BlockConfig[gs_feeJobBlockIndex].clrGrp;
    #endif

        blockSize   = Fee_BlockConfig[gs_feeJobBlockIndex].blkSize;

        writeOffset = (blockSize / FEE_VIRTUAL_PAGE_SIZE) * FEE_VIRTUAL_PAGE_SIZE;

        writeLength = blockSize % FEE_VIRTUAL_PAGE_SIZE;

        Fee_CopyToPageBuffer(&gs_feeWriteJobDataDestPtr[writeOffset], gs_feeIntDataBuffer, writeLength);

    #if(STD_OFF == FEE_LEGACY_MODE_SUPPORT)
        #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        dataAddr = gs_feeClusterInfo[clrGrp].dataAddrCur;
        #else
        dataAddr = gs_feeBlockInfo[gs_feeJobBlockIndex].dataAddr;
        #endif
    #else
        #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        if((Std_ReturnType)E_OK == Fee_BlockConfig[gs_feeJobBlockIndex].iDataFlg)
        {
            dataAddr = gs_feeClusterInfo[clrGrp].dataAddrCur;
        }
        else
        #endif
        {
            dataAddr = gs_feeBlockInfo[gs_feeJobBlockIndex].dataAddr;
        }
    #endif

        retWrite  = Fls_Write(dataAddr + writeOffset, gs_feeIntDataBuffer, FEE_VIRTUAL_PAGE_SIZE);
        retJob    = (((Std_ReturnType)E_OK) == retWrite) ? MEMIF_JOB_PENDING : MEMIF_JOB_FAILED;

        gs_feeJob = FEE_WRITE_VALIDATE_JOB;
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
    #if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
        (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID, FEE_E_INVALID_BLOCK_NO);
    #endif
        retJob = MEMIF_JOB_FAILED;
        FEE_NVM_JOB_ERROR_NOTIFICATION
    }
#endif
    return (retJob);
}

/*!
* @brief            the internal job to write block header make block valid.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteValidateJob(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(Std_ReturnType, AUTOMATIC) retWrite    = E_OK;
    VAR(Fls_AddressType, AUTOMATIC) hdrAddr    = (Fls_AddressType)0;

#if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
    VAR(uint8, AUTOMATIC) clrGrpId = (uint8)0;
#endif

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(gs_feeJobBlockIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
    #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        clrGrpId = Fee_BlockConfig[gs_feeJobBlockIndex].clrGrp;
    #endif

        Fee_FlagSerializeOut(gs_feeIntDataBuffer, FEE_VALIDATED_FLAG);

    #if(STD_OFF == FEE_LEGACY_MODE_SUPPORT)
        #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        hdrAddr = gs_feeClusterInfo[clrGrpId].hdrAddrCur - (2U * FEE_VIRTUAL_PAGE_SIZE);
        #else
        hdrAddr  = gs_feeBlockInfo[gs_feeJobBlockIndex].invalidAddr - FEE_VIRTUAL_PAGE_SIZE;
        #endif
    #else
        #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        if((Std_ReturnType)E_OK == Fee_BlockConfig[gs_feeJobBlockIndex].iDataFlg)
        {
            hdrAddr = gs_feeClusterInfo[clrGrpId].hdrAddrCur - (2U * FEE_VIRTUAL_PAGE_SIZE);
        }
        else
        #endif
        {
            hdrAddr = gs_feeBlockInfo[gs_feeJobBlockIndex].invalidAddr - FEE_VIRTUAL_PAGE_SIZE;
        }
    #endif

        retWrite  = Fls_Write(hdrAddr, gs_feeIntDataBuffer, FEE_VIRTUAL_PAGE_SIZE);
        retJob    = (((Std_ReturnType)E_OK) == retWrite) ? MEMIF_JOB_PENDING : MEMIF_JOB_FAILED;

        gs_feeJob = FEE_WRITE_DONE_JOB;
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
    #if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
        (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID, FEE_E_INVALID_BLOCK_NO);
    #endif
        retJob = MEMIF_JOB_FAILED;
        FEE_NVM_JOB_ERROR_NOTIFICATION
    }
#endif
    return (retJob);
}

/*!
* @brief            the internal job to write block done process.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteDoneJob(void)
{
#if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
    VAR(uint8, AUTOMATIC) clrGrpId           = (uint8)0x0;
    VAR(Fls_AddressType, AUTOMATIC) dataAddr = (Fls_AddressType)0x0;
    VAR(Fls_AddressType, AUTOMATIC) hdrAddr  = (Fls_AddressType)0x0;
#endif

#if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
    clrGrpId = Fee_BlockConfig[gs_feeJobBlockIndex].clrGrp;

    /* get data and header addresses */
    dataAddr = gs_feeClusterInfo[clrGrpId].dataAddrCur;
    hdrAddr  = gs_feeClusterInfo[clrGrpId].hdrAddrCur;

    #if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
    if((boolean)FALSE == Fee_BlockConfig[gs_feeJobBlockIndex].iDataFlg)
    {
    #endif
        gs_feeBlockInfo[gs_feeJobBlockIndex].dataAddr    = dataAddr;
        gs_feeBlockInfo[gs_feeJobBlockIndex].invalidAddr = hdrAddr - FEE_VIRTUAL_PAGE_SIZE;
    #if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
    }
    else
    {
        /* nothing */
    }
    #endif
#endif

    /* mark block as valid */
    gs_feeBlockInfo[gs_feeJobBlockIndex].blockStatus = FEE_BLKST_VALID;

    gs_feeJob                                        = FEE_COMPLETE_JOB;

    return (MEMIF_JOB_OK);
}

/*!
* @brief            the internal job to start write block.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_WriteJob(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_FAILED;

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(gs_feeJobBlockIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
    #if(FEE_LEGACY_MODE_SUPPORT == STD_ON)
        if((Std_ReturnType)E_OK != Fee_BlockConfig[gs_feeJobBlockIndex].iDataFlg)
        {
            retJob = (FEE_BLKST_INCONSISTENT == gs_feeBlockInfo[gs_feeJobBlockIndex].blockStatus) ? Fee_WriteDataJob() :
                                                                                                    MEMIF_JOB_FAILED;
        }
        else
    #endif
        {
            /* write block header */
            retJob = Fee_WriteHdrJob();

            if(FEE_INT_SWAP_CLR_FMT_JOB != gs_feeJob)
            {
                gs_feeJob = FEE_WRITE_DATA_JOB;
            }
            else
            {
                /* nothing */
            }
        }
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
    #if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
        (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID, FEE_E_INVALID_BLOCK_NO);
    #endif
        retJob = MEMIF_JOB_FAILED;
        FEE_NVM_JOB_ERROR_NOTIFICATION
    }
#endif
    return (retJob);
}

/*!
* @brief            the internal job to set block invalid done process.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_OK      - the job is done success.
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_InvalBlockDoneJob(void)
{
    gs_feeBlockInfo[gs_feeJobBlockIndex].blockStatus = FEE_BLKST_INVALID;
    gs_feeJob                                        = FEE_COMPLETE_JOB;

    return (MEMIF_JOB_OK);
}

/*!
* @brief            the internal job to set block invalid.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*                   MEMIF_JOB_OK          - the job is done success
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_InvalBlockJob(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;

    switch(gs_feeBlockInfo[gs_feeJobBlockIndex].blockStatus)
    {
        case FEE_BLKST_INVALID :
            {
                /* it has been invalid now */
                gs_feeJob = FEE_COMPLETE_JOB;

                retJob    = MEMIF_JOB_OK;
            }
            break;
#if(FEE_MARK_EMPTY_BLOCKS_INVALID_SUPPORT == STD_OFF)
        case FEE_BLKST_NEVER_WRITTEN :
            {
                /* it's a blank block,has been invalid now */
                gs_feeBlockInfo[gs_feeJobBlockIndex].blockStatus = FEE_BLKST_INVALID;

                gs_feeJob                                        = FEE_COMPLETE_JOB;

                retJob                                           = MEMIF_JOB_OK;
            }
            break;
#endif
        default :
            {
                Fee_FlagSerializeOut(gs_feeIntDataBuffer, FEE_INVALIDATED_FLAG);

                if(((Std_ReturnType)E_OK)
                   == Fls_Write(gs_feeBlockInfo[gs_feeJobBlockIndex].invalidAddr, gs_feeIntDataBuffer,
                                FEE_VIRTUAL_PAGE_SIZE))
                {
                    retJob = MEMIF_JOB_PENDING;
                }
                else
                {
                    retJob = MEMIF_JOB_FAILED;
                }

                gs_feeJob = FEE_INVAL_BLOCK_DONE_JOB;
            }
            break;
    }

    return (retJob);
}

/*!
* @brief            the internal job to erase immediate data block.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING    - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED     - the job is refuse by underlying driver
*                   MEMIF_JOB_OK         - the job is done success
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_EraseImmediateDoneJob(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;
    VAR(uint16, AUTOMATIC) blockSize           = (uint16)0x0;
    VAR(uint16, AUTOMATIC) alignedBlockSize    = (uint16)0x0;
    VAR(uint16, AUTOMATIC) clrGrp              = (uint16)0x0;

#if(FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT == STD_OFF)
    VAR(uint8, AUTOMATIC) clrGrpId           = (uint8)0;
    VAR(Fls_AddressType, AUTOMATIC) dataAddr = (Fls_AddressType)0;
    VAR(Fls_AddressType, AUTOMATIC) hdrAddr  = (Fls_AddressType)0;
#endif

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(gs_feeJobBlockIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
        /* mark block as inconsistent */
        gs_feeBlockInfo[gs_feeJobBlockIndex].blockStatus = FEE_BLKST_INCONSISTENT;
        blockSize                                        = Fee_BlockConfig[gs_feeJobBlockIndex].blkSize;
        alignedBlockSize                                 = Fee_AlignSizeToVirtualPage(blockSize);
        clrGrp                                           = Fee_BlockConfig[gs_feeJobBlockIndex].clrGrp;

    #if(FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT == STD_OFF)

        /* update the cluster info */
        clrGrpId                                         = Fee_BlockConfig[gs_feeJobBlockIndex].clrGrp;
        dataAddr                                         = gs_feeClusterInfo[clrGrpId].dataAddrCur - alignedBlockSize;
        hdrAddr                                          = gs_feeClusterInfo[clrGrpId].hdrAddrCur;

        /* update the block info */
        gs_feeBlockInfo[gs_feeJobBlockIndex].dataAddr    = dataAddr;
        gs_feeBlockInfo[gs_feeJobBlockIndex].invalidAddr = (hdrAddr + FEE_BLOCK_OVERHEAD_SIZE) - FEE_VIRTUAL_PAGE_SIZE;

    #endif

        /* update the cluster info */
        gs_feeClusterInfo[clrGrp].hdrAddrCur  += FEE_BLOCK_OVERHEAD_SIZE;
        gs_feeClusterInfo[clrGrp].dataAddrCur -= alignedBlockSize;

    #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        /* update the block info */
        gs_feeBlockInfo[gs_feeJobBlockIndex].invalidAddr = gs_feeClusterInfo[clrGrp].hdrAddrCur - FEE_VIRTUAL_PAGE_SIZE;
        gs_feeBlockInfo[gs_feeJobBlockIndex].dataAddr    = gs_feeClusterInfo[clrGrp].dataAddrCur;
    #endif
        gs_feeJob = FEE_COMPLETE_JOB;
        retJob    = MEMIF_JOB_OK;
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
    #if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
        (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID,
                        FEE_E_INVALID_BLOCK_NO);
    #endif
        retJob = MEMIF_JOB_FAILED;
        FEE_NVM_JOB_ERROR_NOTIFICATION
    }
#endif
    return (retJob);
}

/*!
* @brief            the internal job to erase immediate data block.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING     - the job is accepted by underlying driver
*                   MEMIF_JOB_FAILED      - the job is refuse by underlying driver
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_EraseImmediateJob(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_OK;

    
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    if(gs_feeJobBlockIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
    {
#endif
    #if(STD_OFF == FEE_LEGACY_MODE_SUPPORT)
        #if(FEE_LEGACY_IMMEDIATE_ERASE_MODE_SUPPORT == STD_ON)
        /* when FEE_LEGACY_IMMEDIATE_ERASE_MODE_SUPPORT == STD_ON, Fee_EraseImmediateJob shall set the block to invalidate*/
        if((boolean)TRUE == Fee_ReservedAreaEnableWrite())
        {
            /* the reserve area ok to write,so only invalidate the block */
            retJob = Fee_InvalBlockJob();
        }
        else
        {
            /* when FEE_LEGACY_IMMEDIATE_ERASE_MODE_SUPPORT == STD_ON, force the swap when pronounce the block to be erased as invalidated */
            gs_feeBlockInfo[gs_feeJobBlockIndex].blockStatus = FEE_BLKST_INVALID;
            gs_feeJobIntClrGrpId                             = Fee_BlockConfig[gs_feeJobBlockIndex].clrGrp;
            retJob                                           = Fee_IntJobSwap();
        }
        #else
        /* perform swap when reserved area has been touched. */
        if((boolean)TRUE == Fee_ReservedAreaEnableWrite())
        {
            gs_feeJob = FEE_COMPLETE_JOB;
        }
        else
        {
            gs_feeJobIntClrGrpId = Fee_BlockConfig[gs_feeJobBlockIndex].clrGrp;
            retJob               = Fee_IntJobSwap();
        }
        #endif
    #else
        /* erase block by write block header */
        retJob = Fee_WriteHdrJob();

        if(FEE_INT_SWAP_CLR_FMT_JOB != gs_feeJob)
        {
            gs_feeJob = FEE_ERASE_IMMEDIATE_DONE_JOB;
        }
        else
        {
            /* nothing */
        }
    #endif
#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    }
    else
    {
    #if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
        (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID,
                        FEE_E_INVALID_BLOCK_NO);
    #endif
        retJob = MEMIF_JOB_FAILED;
        FEE_NVM_JOB_ERROR_NOTIFICATION
    }
#endif
    return (retJob);
}

/*!
* @brief            the schedule for fee job.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING           - detail see MemIf_Types.h
*                   MEMIF_JOB_FAILED            - detail see MemIf_Types.h
*                   MEMIF_JOB_OK                - detail see MemIf_Types.h
*                   MEMIF_BLOCK_INCONSISTENT    - detail see MemIf_Types.h
*                   MEMIF_BLOCK_INVALID         - detail see MemIf_Types.h
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_ScheduleForJob_One(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_FAILED;

    switch(gs_feeJob)
    {
            /* deal jobs relate for Fee_Init() */
        case FEE_INT_SCAN_JOB :
            {
                retJob = Fee_IntJobScan();
            }
            break;

        case FEE_INT_SCAN_CLR_JOB :
            {
                retJob = Fee_IntJobScanClr();
            }
            break;

        case FEE_INT_SCAN_CLR_HDR_PARSE_JOB :
            {
                retJob = Fee_IntJobScanClrHdrParse((boolean)TRUE);
            }
            break;

        case FEE_INT_SCAN_CLR_FMT_JOB :
            {
                retJob = Fee_IntJobScanClrFmt();
            }
            break;

        case FEE_INT_SCAN_CLR_FMT_DONE_JOB :
            {
                retJob = Fee_IntJobScanClrFmtDone();
            }
            break;

        case FEE_INT_SCAN_BLOCK_HDR_PARSE_JOB :
            {
                retJob = Fee_IntJobScanBlockHdrParse((boolean)TRUE);
            }
            break;

            /* deal jobs relate for cluster swap or block swap */
        case FEE_INT_SWAP_CLR_FMT_JOB :
            {
                retJob = Fee_IntJobSwapClrFmt();
            }
            break;

        case FEE_INT_SWAP_BLOCK_JOB :
            {
                retJob = Fee_IntJobSwapBlock();
            }
            break;

        case FEE_INT_SWAP_DATA_READ_JOB :
            {
                retJob = Fee_IntJobSwapDataRead((boolean)TRUE);
            }
            break;

        case FEE_INT_SWAP_DATA_WRITE_JOB :
            {
                retJob = Fee_IntJobSwapDataWrite((boolean)TRUE);
            }
            break;

        case FEE_INT_SWAP_CLR_VLD_DONE_JOB :
            {
                retJob = Fee_IntJobSwapClrVldDone();
            }
            break;
            
        case FEE_COMPLETE_JOB :
            {
                /* nothing to do  */
            }
            break;
            
        default :
            {
                /* nothing to do  */
            }
            break;
    }
    return (retJob);
}

/*!
* @brief            the schedule for two fee job.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING           - detail see MemIf_Types.h
*                   MEMIF_JOB_FAILED            - detail see MemIf_Types.h
*                   MEMIF_JOB_OK                - detail see MemIf_Types.h
*                   MEMIF_BLOCK_INCONSISTENT    - detail see MemIf_Types.h
*                   MEMIF_BLOCK_INVALID         - detail see MemIf_Types.h
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_ScheduleForJob_Two(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_FAILED;

    switch(gs_feeJob)
    {
        case FEE_WRITE_DATA_JOB :
            {
                retJob = Fee_WriteDataJob();
            }
            break;

        case FEE_WRITE_UNALIGNED_DATA_JOB :
            {
                retJob = Fee_WriteUnalignedDataJob();
            }
            break;

        case FEE_WRITE_VALIDATE_JOB :
            {
                retJob = Fee_WriteValidateJob();
            }
            break;

        case FEE_WRITE_DONE_JOB :
            {
                retJob = Fee_WriteDoneJob();
            }
            break;
        /* deal jobs relate for Fee_Write() */
        case FEE_WRITE_JOB :
            {
                retJob = Fee_WriteJob();
            }
            break;

        /* deal jobs relate for Fee_Read() */
        case FEE_READ_JOB :
            {
                retJob = Fee_ReadJob();
            }
            break;

        case FEE_INVAL_BLOCK_DONE_JOB :
            {
                retJob = Fee_InvalBlockDoneJob();
            }
            break;

        /* deal jobs relate for Fee_InvalidateBlock() */
        case FEE_INVAL_BLOCK_JOB :
            {
                retJob = Fee_InvalBlockJob();
            }
            break;

        case FEE_ERASE_IMMEDIATE_DONE_JOB :
            {
                retJob = Fee_EraseImmediateDoneJob();
            }
            break;

        /* deal jobs relate for Fee_EraseImmediateBlock() */
        case FEE_ERASE_IMMEDIATE_JOB :
            {
                retJob = Fee_EraseImmediateJob();
            }
            break;
        default :
            {
                /* nothing to do  */
            }
            break;
    }

    return (retJob);
}

/*!
* @brief            the schedule for fee job.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MEMIF_JOB_PENDING           - detail see MemIf_Types.h
*                   MEMIF_JOB_FAILED            - detail see MemIf_Types.h
*                   MEMIF_JOB_OK                - detail see MemIf_Types.h
*                   MEMIF_BLOCK_INCONSISTENT    - detail see MemIf_Types.h
*                   MEMIF_BLOCK_INVALID         - detail see MemIf_Types.h
*/
static FUNC(MemIf_JobResultType, FEE_CODE) Fee_ScheduleForJob(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = MEMIF_JOB_FAILED;

    retJob = ((gs_feeJob <= FEE_ERASE_IMMEDIATE_DONE_JOB) ? Fee_ScheduleForJob_Two() : Fee_ScheduleForJob_One());

    return (retJob);
}

#if (STD_OFF == FEE_LEGACY_MODE_SUPPORT )
/*!
* @brief            Checks whether the reserved area has been touched by gs_feeJobBlockIndex.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           TRUE     - this block is writable into the reserved area.
*                   FALSE    - this block is not writable into the reserved area.
*/
static FUNC(boolean, FEE_CODE) Fee_ReservedAreaEnableWrite(void)
{
    VAR(boolean, AUTOMATIC) retValue             = TRUE;
    VAR(uint8, AUTOMATIC) clrGrpIndex            = (uint8)0x0;
    VAR(boolean, AUTOMATIC) immediateDataFlag    = (boolean)FALSE;
    VAR(uint16, AUTOMATIC) blockSize             = (uint16)0x0;
    VAR(uint32, AUTOMATIC) reservedSpace         = (uint32)0x0;
    VAR(uint16, AUTOMATIC) alignedBlockSize      = (uint16)0x0;
    VAR(Fls_LengthType, AUTOMATIC) availClrSpace = (Fls_LengthType)0x0;
    #if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
    VAR(boolean, AUTOMATIC) isReservedAreaTouchedFlag = TRUE;
    #endif

    clrGrpIndex      = Fee_GetBlockClusterGrpByBlockId(gs_feeJobBlockIndex);

    reservedSpace    = Fee_ClrGrps[clrGrpIndex].rsvdSize;

    /* get available space in active cluster */
    availClrSpace    = gs_feeClusterInfo[clrGrpIndex].dataAddrCur - gs_feeClusterInfo[clrGrpIndex].hdrAddrCur;

    blockSize        = Fee_GetBlockSizeByBlockId(gs_feeJobBlockIndex);
    alignedBlockSize = Fee_AlignSizeToVirtualPage(blockSize);

    if(((((uint32)alignedBlockSize) + (2U * FEE_BLOCK_OVERHEAD_SIZE)) <= availClrSpace) &&
       ((((uint32)alignedBlockSize) + (2U * FEE_BLOCK_OVERHEAD_SIZE) + reservedSpace) > availClrSpace))
    {
        immediateDataFlag = Fee_CheckBlockImmediateSupportByBlockId(gs_feeJobBlockIndex);
#if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
        isReservedAreaTouchedFlag = Fee_CheckReservedAreaTouchedByBlock(gs_feeJobBlockIndex);
        if(((boolean)TRUE == immediateDataFlag) && (FALSE == isReservedAreaTouchedFlag))
#else
        if((boolean)TRUE == immediateDataFlag)
#endif
        {
            /* Immediate block present in the reserved area,ready to write */
            if((0U != gs_feeBlockInfo[gs_feeJobBlockIndex].dataAddr)
               || (0U != gs_feeBlockInfo[gs_feeJobBlockIndex].invalidAddr))
            {
                /* check if this block is it written in the reserved area*/
                if((FEE_BLOCK_OVERHEAD_SIZE + reservedSpace)
                   > (gs_feeBlockInfo[gs_feeJobBlockIndex].dataAddr
                      - (gs_feeBlockInfo[gs_feeJobBlockIndex].invalidAddr + FEE_VIRTUAL_PAGE_SIZE)))
                {
                    /* already written in the reserved area */
                    retValue = FALSE;
                }
                else
                {
                    /* no written in the reserved area */
                    retValue = TRUE;
                }
            }
            else
            {
                retValue = TRUE;
            }
        }
        else
        {
            retValue = FALSE;
        }
    }
    else if ((((uint32)alignedBlockSize) + (2U * FEE_BLOCK_OVERHEAD_SIZE)) <= availClrSpace)
    {
        retValue = TRUE;
    }
    else    
    {
        retValue = FALSE;
    }
    return retValue;
}

#if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT)
/*!
* @brief            compute the value of power of 2 InVal value & Get 5 LSB bits
*
* @param[in]        ClrGrpIndex    the index of Cluster Group
* @param[out]       None
* @param[in,out]    None
*
* @retval           uint32    the value of power of 2 InVal value & Get 5 LSB bits
*/
static INLINE FUNC(uint32, FEE_CODE) Fee_PowerOf2Mask5LSB(CONST(uint32, AUTOMATIC) InVal)
{
    return (uint32)(0x00000001UL << (InVal & 0x1FUL));
}

/*!
* @brief            check if the block that specified by BlockNumber has touched the Reserved Area
*
* @param[in]        BlockNumber    the number of the block want to check
* @param[out]       None
* @param[in,out]    None
*
* @retval           TRUE     - The Reserved Area is touched.
*                   FALSE    - The Reserved Area is not touched.
*/
static INLINE FUNC(sint8, FEE_CODE) Fee_CheckReservedAreaTouchedByBlock(CONST(uint16, AUTOMATIC) BlockNumber)
{
    VAR(uint32, AUTOMATIC) index = (uint32)0x0;
    VAR(sint8, AUTOMATIC) retValue = FALSE;

    index = ((uint32)BlockNumber) >> 5U;

    retValue = (0 == (gs_feeReservedAreaTouched[index] & Fee_PowerOf2Mask5LSB((uint32)BlockNumber))) ? FALSE : TRUE;

    return retValue;
}

/*!
* @brief            Checks whether the area has been touched by gs_feeClusterInfo[ClrGrpIndex].dataAddrCur
*                   or gs_feeClusterInfo[ClrGrpIndex].hdrAddrCur
*
* @param[in]        ClrGrpIndex    the index of Cluster Group
* @param[out]       None
* @param[in,out]    None
*
* @retval           TRUE     - The Reserved Area is touched.
*                   FALSE    - The Reserved Area is not touched.
*/
static INLINE FUNC(sint8, FEE_CODE) Fee_CheckReservedAreaTargetedInClrGrp(CONST(uint8, AUTOMATIC) ClrGrpIndex)
{
    VAR(Fls_LengthType, AUTOMATIC) remainSpace = (Fls_LengthType)0x0;
    VAR(uint32, AUTOMATIC) reservedSpace         = (uint32)0x0;

    reservedSpace                                = Fee_ClrGrps[ClrGrpIndex].rsvdSize;

    /* get available space in current active cluster */
    remainSpace = gs_feeClusterInfo[ClrGrpIndex].dataAddrCur - gs_feeClusterInfo[ClrGrpIndex].hdrAddrCur;

    if((FEE_BLOCK_OVERHEAD_SIZE + reservedSpace) > remainSpace)
    {
        return TRUE;
    }

    return FALSE;
}

/*!
* @brief            mask the block that specified by uBlockuNumber has touched the Reserved Area
*
* @param[in]        BlockNumber     the number of block user specified
* @param[out]       None
* @param[in,out]    None
*
* @retval           None
*/
static INLINE FUNC(void, FEE_CODE) Fee_MaskTouchReservedAreaByBlock(CONST(uint16, AUTOMATIC) BlockNumber)
{
    VAR(uint32, AUTOMATIC) index      = 0UL;

    index                             = ((uint32)BlockNumber) >> 5U;

    gs_feeReservedAreaTouched[index] |= Fee_PowerOf2Mask5LSB((uint32)BlockNumber);
}

/*!
* @brief            mask the all block belongs Cluster Group untouch the Reserved Area
*
* @param[in]        ClrGrpIndex    the index of user specified Cluster Group
* @param[out]       None
* @param[in,out]    None
*
* @retval           None
*/
static INLINE FUNC(void, FEE_CODE) Fee_MaskUntouchReservedAreaByClrGrp(CONST(uint8, AUTOMATIC) ClrGrpIndex)
{
    VAR(uint32, AUTOMATIC) blockId        = (uint32)0x0;
    VAR(uint32, AUTOMATIC) index          = (uint32)0x0;
    VAR(uint32, AUTOMATIC) mask           = (uint32)0x00000001;

        #if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    while(blockId < ((uint32)FEE_CONFIGURED_NUMBER_OF_BLOCKS + (uint32)gs_feeForeignBlocksNumber))
        #else
    while(blockId < FEE_CONFIGURED_NUMBER_OF_BLOCKS)
        #endif
    {
        if(ClrGrpIndex == Fee_GetBlockClusterGrpByBlockId((uint16)blockId))
        {
            gs_feeReservedAreaTouched[index] &= (~mask);
        }

        if(0x80000000 != mask)
        {
            mask = mask << 0x1;
        }
        else
        {
            mask = 0x00000001UL;
            index++;
        }

        blockId++;
    }
}

/*!
* @brief            mask the all Reserved Area are untouch
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           None
*/
static INLINE FUNC(void, FEE_CODE) Fee_MaskUntouchReservedAreaAll(void)
{
    VAR(uint32, AUTOMATIC) index = (uint32)0x0;

    while(index < (sizeof(gs_feeReservedAreaTouched) / sizeof(*gs_feeReservedAreaTouched)))
    {
        gs_feeReservedAreaTouched[index] = 0x00000000UL;
        index++;
    }
}
#endif
#endif

#if (STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
/*!
* @brief            get the foreign block index by specified block number
*
* @param[in]        BlockNumber    the number of the foreign block want to get block id
* @param[out]       None
* @param[in,out]    None
*
* @retval           uint16    0xffff - block number is invalid
*                             other  - Block id
*/
static FUNC(uint16, FEE_CODE) Fee_GetForeignBlockIndexByBlockNumber(CONST(uint16, AUTOMATIC) BlockNumber)
{
    VAR(uint16, AUTOMATIC) i     = (uint16)0x0;
    VAR(uint16, AUTOMATIC) retId = (uint16)0xFFFF;

    if(gs_feeForeignBlocksNumber >= (uint16)0x1)
    {
        while(i < gs_feeForeignBlocksNumber)
        {
            if(BlockNumber == gs_feeForeignBlockConfig[i].blkNum)
            {
                retId = i;
                break;
            }
            else
            {
                /* nothing */
            }
            i++;
        }
    }
    else
    {
        /* nothing */
    }

    return (retId);
}

/*!
* @brief            get the block project assignement flag by block runtime info index
*
* @param[in]        BlockRuntimeInfoIndex    the index of the block runtime info array
* @param[out]       None
* @param[in,out]    None
*
* @retval           Fee_BlkPrjAsgmtType      FEE_PRJASGMT_SHR        - shared for all project
*                                            FEE_PRJASGMT_APP        - only used for application project
*                                            FEE_PRJASGMT_BTL        - only used for bootloader project
*                                            FEE_PRJASGMT_RESERVED   - not used
*/
LOCAL_INLINE FUNC(Fee_BlkPrjAsgmtType, FEE_CODE)
    Fee_GetBlockProjectAssignment(CONST(uint16, AUTOMATIC) BlockRuntimeInfoIndex)
{
    VAR(Fee_BlkPrjAsgmtType, AUTOMATIC) blockAssignment = (Fee_BlkPrjAsgmtType)FEE_PRJASGMT_RESERVED;

    #if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
    blockAssignment = (BlockRuntimeInfoIndex < FEE_CONFIGURED_NUMBER_OF_BLOCKS) ?
                          Fee_BlockConfig[BlockRuntimeInfoIndex].blkAsgmt :
                          gs_feeForeignBlockConfig[BlockRuntimeInfoIndex - FEE_CONFIGURED_NUMBER_OF_BLOCKS].blkAsgmt;
    #else
    blockAssignment = Fee_BlockConfig[BlockRuntimeInfoIndex].blkAsgmt;
    #endif

    return blockAssignment;
}
#endif


/***********************************************************************************************************************
                    GLOBAL FUNCTIONS
***********************************************************************************************************************/
/*!
* @brief            Service to initialize the FEE module.
*
* @param[in]        ConfigPtr    Pointer to the selected configuration set.
* @param[out]       None
* @param[in,out]    None
*
* @retval           None
*/
FUNC(void, FEE_CODE) Fee_Init(P2CONST(Fee_BlkCfgType, AUTOMATIC, FEE_APPL_CONST) ConfigPtr)
{
    VAR(uint32, AUTOMATIC) invalIndex = 0;
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    VAR(uint8, AUTOMATIC) errorId = 0;
#endif

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    if(NULL_PTR != ConfigPtr)
    {
        (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_INIT_SRVID, FEE_E_PARAM_POINTER);
    }
    else
    {
        switch(gs_feeIntModuleStatus)
        {
            case MEMIF_BUSY :
                {
                    errorId = FEE_E_BUSY;
                }
                break;
            case MEMIF_BUSY_INTERNAL :
                {
                    errorId = FEE_E_BUSY_INTERNAL;
                }
                break;
            default :
                {
                    /* nothing */
                }
                break;
        }
        if (errorId != (uint8)0)
        {
            if(FEE_E_BUSY == errorId)
            {
                (void)Det_ReportRuntimeError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_INIT_SRVID, errorId);
            }
            else
            {
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_INIT_SRVID, errorId);
            }
        }
        else
        {
#else
        (void)ConfigPtr;
#endif
            /* initialize block infomation records */
            while(invalIndex < (uint32)FEE_MAX_NUMBER_OF_BLOCKS)
            {
#if(STD_OFF == FEE_LEGACY_MODE_SUPPORT)
                gs_feeBlockInfo[invalIndex].dataAddr    = 0x0;
                gs_feeBlockInfo[invalIndex].invalidAddr = 0x0;
#endif

#if(STD_ON == FEE_MARK_EMPTY_BLOCKS_INVALID_SUPPORT)
                /* mask blocks which were never written to FEE_BLKST_INVALID */
                gs_feeBlockInfo[invalIndex].blockStatus = FEE_BLKST_INVALID;
#else
                gs_feeBlockInfo[invalIndex].blockStatus = FEE_BLKST_NEVER_WRITTEN;
#endif

                invalIndex++;
            }

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
            /* initialize foreign block configuration records */
            invalIndex = 0;
            while(invalIndex < ((uint32)FEE_MAX_NUMBER_OF_BLOCKS - (uint32)FEE_CONFIGURED_NUMBER_OF_BLOCKS))
            {
                gs_feeForeignBlockConfig[invalIndex].blkNum   = 0x0;
                gs_feeForeignBlockConfig[invalIndex].iDataFlg = (boolean)FALSE;
                gs_feeForeignBlockConfig[invalIndex].blkSize  = 0x0;
                gs_feeForeignBlockConfig[invalIndex].blkAsgmt = FEE_PRJASGMT_RESERVED;
                gs_feeForeignBlockConfig[invalIndex].clrGrp   = 0x0;
                invalIndex++;
            }
#endif
            /* set all cluster groups invalidate*/
            invalIndex = 0;
            while(invalIndex < FEE_CLUSTER_GROUPS_NUMBER)
            {
                gs_feeClusterInfo[invalIndex].actClrID = 0U;
                invalIndex++;
            }

#if(STD_ON == FEE_BLOCK_ALWAYS_AVAILABLE_SUPPORT) && (STD_OFF == FEE_LEGACY_MODE_SUPPORT)
            Fee_MaskUntouchReservedAreaAll();
#endif

#if(STD_ON == FEE_SWAP_FOREIGN_BLOCKS_ENABLED)
            gs_feeForeignBlocksNumber = 0U;
#endif

            gs_feeJob             = FEE_INT_SCAN_JOB;

            gs_feeIntModuleStatus = MEMIF_BUSY_INTERNAL;

            gs_feeJobResult       = MEMIF_JOB_PENDING;  
            
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
        }
    }
#endif
}

#if ((STD_ON == FEE_SET_MODE_API_ENABLE) || defined(__DOXYGEN__))
/*!
* @brief            Function to switch the mode of the underlying Flash Driver.
*
* @param[in]        Mode       Desired mode for the underlying flash driver.
* @param[out]       None
* @param[in,out]    None
*
* @retval           None
*/
FUNC(void, FEE_CODE) Fee_SetMode(VAR(MemIf_ModeType, AUTOMATIC) Mode)
{
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    VAR(uint8, AUTOMATIC) errorId = 0;

    switch(gs_feeIntModuleStatus)
    {
        case MEMIF_UNINIT :
            {
                errorId = FEE_E_UNINIT;
            }
            break;
        case MEMIF_BUSY :
            {
                errorId = FEE_E_BUSY;
            }
            break;
        case MEMIF_BUSY_INTERNAL :
            {
                errorId = FEE_E_BUSY_INTERNAL;
            }
            break;
        default :
            {
                /* nothing */
            }
            break;
    }
    if (errorId != (uint8)0)
    {
        if (FEE_E_BUSY == errorId)
        {
            (void)Det_ReportRuntimeError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_SETMODE_SRVID, errorId);
        }
        else
        {
            (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_SETMODE_SRVID, errorId);
        }
    }
    else
    {
#else
        if(MEMIF_IDLE == gs_feeIntModuleStatus)
        {
#endif
            Fls_SetMode(Mode);
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_OFF)
        }
        else
        {
            /* nothing */
        }
#endif
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    }
#endif
}
#endif

/*!
* @brief            Service to initiate a read job.
*
* @param[in]        BlockNumber       Number of logical block, also denoting start address of that block in flash memory.
* @param[in]        BlockOffset       Read address offset inside the block.
* @param[in]        Length            Number of bytes to read
* @param[out]       DataBufferPtr     Pointer to data buffer.
* @param[in,out]    None
*
* @retval           E_OK        - The requested job has been accepted by the module.
*                   E_NOT_OK    - The requested job has not been accepted by the module.
*/
FUNC(Std_ReturnType, FEE_CODE)
Fee_Read(VAR(uint16, AUTOMATIC) BlockNumber, VAR(uint16, AUTOMATIC) BlockOffset,
         P2VAR(uint8, AUTOMATIC, FEE_APPL_DATA) DataBufferPtr, VAR(uint16, AUTOMATIC) Length)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_OK;
    VAR(uint16, AUTOMATIC) blockId        = Fee_GetBlockIndexByBlockNumber(BlockNumber);

    SchM_Enter_Fee_FEE_CRITICAL_SECTION0();

    switch(gs_feeIntModuleStatus)
    {
        case MEMIF_UNINIT :
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_READ_SRVID, FEE_E_UNINIT);
#endif
                result = (Std_ReturnType)E_NOT_OK;
            }
            break;
        case MEMIF_BUSY :
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                (void)Det_ReportRuntimeError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_READ_SRVID, FEE_E_BUSY);
#endif
                result = (Std_ReturnType)E_NOT_OK;
            }
            break;
        case MEMIF_BUSY_INTERNAL :
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_READ_SRVID, FEE_E_BUSY_INTERNAL);
#endif
                result = (Std_ReturnType)E_NOT_OK;
            }
            break;
        default :
            {
                /* nothing */
            }
            break;
    }

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    if(E_OK == result)
    {
        if(0xFFFFU == blockId)
        {
            (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_READ_SRVID, FEE_E_INVALID_BLOCK_NO);
            result = (Std_ReturnType)E_NOT_OK;
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        /* nothing */
    }
#endif

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    if(E_OK == result)
    {
        if(BlockOffset >= Fee_BlockConfig[blockId].blkSize)
        {
            (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_READ_SRVID, FEE_E_INVALID_BLOCK_OFS);
            result = (Std_ReturnType)E_NOT_OK;
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        /* nothing */
    }
#endif

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    if(E_OK == result)
    {
        if((0U == Length) || ((BlockOffset + Length) > Fee_BlockConfig[blockId].blkSize))
        {
            (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_READ_SRVID, FEE_E_INVALID_BLOCK_LEN);
            result = (Std_ReturnType)E_NOT_OK;
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        /* nothing */
    }
#endif

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    if(E_OK == result)
    {
        if(NULL_PTR == DataBufferPtr)
        {
            (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_READ_SRVID, FEE_E_PARAM_POINTER);
            result = (Std_ReturnType)E_NOT_OK;
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        /* nothing */
    }
#endif

    if(E_OK == result)
    {
        gs_feeJobBlockIndex      = blockId;
        gs_feeJobBlockOffset     = BlockOffset;

        gs_feeJobBlockLength     = Length;
        gs_feeReadJobDataDestPtr = DataBufferPtr;

        gs_feeJob                = FEE_READ_JOB;

        gs_feeIntModuleStatus    = MEMIF_BUSY;
        gs_feeJobResult          = MEMIF_JOB_PENDING;
    }
    else
    {
        /* nothing */
    }

    SchM_Exit_Fee_FEE_CRITICAL_SECTION0();

    return (result);
}

/*!
* @brief            Service to initiate a write job.
*
* @param[in]        BlockNumber       Number of logical block, also denoting start address of that block in EEPROM.
* @param[in]        DataBufferPtr     Pointer to data buffer.
* @param[out]       None
* @param[in,out]    None
*
* @retval           E_OK        - The requested job has been accepted by the module.
*                   E_NOT_OK    - The requested job has not been accepted by the module.
*/
FUNC(Std_ReturnType, FEE_CODE)
Fee_Write(VAR(uint16, AUTOMATIC) BlockNumber, P2CONST(uint8, AUTOMATIC, FEE_APPL_CONST) DataBufferPtr)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_OK;
    VAR(uint16, AUTOMATIC) blockId        = Fee_GetBlockIndexByBlockNumber(BlockNumber);

    SchM_Enter_Fee_FEE_CRITICAL_SECTION1();

    switch(gs_feeIntModuleStatus)
    {
        case MEMIF_UNINIT :
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID, FEE_E_UNINIT);
#endif
                result = (Std_ReturnType)E_NOT_OK;
            }
            break;
        case MEMIF_BUSY :
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                (void)Det_ReportRuntimeError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID, FEE_E_BUSY);
#endif
                result = (Std_ReturnType)E_NOT_OK;
            }
            break;
        default :
            {
                /* nothing */
            }
            break;
    }

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    if(E_OK == result)
    {
        if(0xFFFFU == blockId)
        {
            (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID, FEE_E_INVALID_BLOCK_NO);
            result = (Std_ReturnType)E_NOT_OK;
        }
        else
        {
            /* nothing to do */
        }
    }
    else
    {
        /* nothing to do */
    }
#endif

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    if(E_OK == result)
    {
        if(MEMIF_BUSY_INTERNAL == gs_feeIntModuleStatus)
        {
            (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID, FEE_E_BUSY_INTERNAL);
            result = (Std_ReturnType)E_NOT_OK;
        }
        else
        {
            /* nothing to do */
        }
    }
    else
    {
        /* nothing to do */
    }
#endif

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    if(E_OK == result)
    {
        if(NULL_PTR == DataBufferPtr)
        {
            (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_WRITE_SRVID, FEE_E_PARAM_POINTER);
            result = (Std_ReturnType)E_NOT_OK;
        }
        else
        {
            /* nothing to do */
        }
    }
    else
    {
        /* nothing to do */
    }
#endif

    if(E_OK == result)
    {
        gs_feeJobBlockIndex       = blockId;
        gs_feeWriteJobDataDestPtr = DataBufferPtr;

        gs_feeJob                 = FEE_WRITE_JOB;

        gs_feeIntModuleStatus     = MEMIF_BUSY;
        gs_feeJobResult           = MEMIF_JOB_PENDING;
    }
    else
    {
        /* nothing to do */
    }
    SchM_Exit_Fee_FEE_CRITICAL_SECTION1();

    return (result);
}

/*!
* @brief            Service to call the cancel function of the underlying flash driver.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           None
*/
FUNC(void, FEE_CODE) Fee_Cancel(void)
{
#if(FEE_CANCEL_API_ENABLE == STD_OFF)
    #if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_CANCEL_SRVID, FEE_E_CANCEL_API_DISABLE);
    #endif
#else
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    switch(gs_feeIntModuleStatus)
    {
        case MEMIF_UNINIT :
            {
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_CANCEL_SRVID, FEE_E_UNINIT);
            }
            break;
        default :
            {
#endif
                if(MEMIF_JOB_PENDING == gs_feeJobResult)
                {
                    if(MEMIF_BUSY == gs_feeIntModuleStatus)
                    {
                        gs_feeJobResult = MEMIF_JOB_CANCELED;
                        gs_feeJob       = FEE_COMPLETE_JOB;
                        Fls_Cancel();
                        gs_feeIntModuleStatus = MEMIF_IDLE;
                    }
                    else
                    {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                        (void)Det_ReportRuntimeError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_CANCEL_SRVID,
                                               FEE_E_INVALID_CANCEL);
#endif
                        FEE_NVM_JOB_ERROR_NOTIFICATION
                    }
                }
                else
                {
                    /* nothing to do */
                }
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
            }
            break;
    }
#endif
#endif
}

/*!
* @brief            Service to return the status.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MemIf_StatusType    MEMIF_UNINIT
*                                       MEMIF_IDLE
*                                       MEMIF_BUSY
*                                       MEMIF_BUSY_INTERNAL
*/
FUNC(MemIf_StatusType, FEE_CODE) Fee_GetStatus(void)
{
    return (gs_feeIntModuleStatus);
}

/*!
* @brief            Service to query the result of the last accepted job issued by the upper layer software.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           MemIf_JobResultType    MEMIF_JOB_OK
*                                          MEMIF_JOB_FAILED
*                                          MEMIF_JOB_PENDING
*                                          MEMIF_JOB_CANCELED
*                                          MEMIF_BLOCK_INCONSISTENT
*                                          MEMIF_BLOCK_INVALID
*/
FUNC(MemIf_JobResultType, FEE_CODE) Fee_GetJobResult(void)
{
    VAR(MemIf_JobResultType, AUTOMATIC) retJob = gs_feeJobResult;

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    switch(gs_feeIntModuleStatus)
    {
        case MEMIF_UNINIT :
            {
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_GETJOBRESULT_SRVID, FEE_E_UNINIT);
                retJob = MEMIF_JOB_FAILED;
            }
            break;
        default :
            {
                /* nothing to do */
            }
            break;
    }
#endif

    return (retJob);
}

/*!
* @brief            Service to invalidate a logical block
*
* @param[in]        BlockNumber    Number of logical block, also denoting start address of that block in flash memory
* @param[out]       None
* @param[in,out]    None
*
* @retval           E_OK        - The requested job has been accepted by the module.
*                   E_NOT_OK    - only if DET is enabled: The requested job has not been accepted by the module.
*/
FUNC(Std_ReturnType, FEE_CODE) Fee_InvalidateBlock(VAR(uint16, AUTOMATIC) BlockNumber)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_NOT_OK;
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    VAR(uint8, AUTOMATIC) errorId         = 0;
#endif
    VAR(uint16, AUTOMATIC) blockId        = Fee_GetBlockIndexByBlockNumber(BlockNumber);

    SchM_Enter_Fee_FEE_CRITICAL_SECTION2();

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    if(MEMIF_UNINIT == gs_feeIntModuleStatus)
    {
        errorId = FEE_E_UNINIT;
    }
    else
    {
        /* nothing to do */
    }

    if((0xFFFFU == blockId))
    {
        errorId = FEE_E_INVALID_BLOCK_NO;
    }
    else
    {
        /* nothing to do */
    }

    if((MEMIF_BUSY == gs_feeIntModuleStatus) && (0xFFFFU != blockId))
    {
        errorId = FEE_E_BUSY;
    }
    else
    {
        /* nothing to do */
    }

    if((MEMIF_BUSY_INTERNAL == gs_feeIntModuleStatus) && (0xFFFFU != blockId))
    {
        errorId = FEE_E_BUSY_INTERNAL;
    }
    else
    {
        /* nothing to do */
    }

    if(errorId != (uint8)0)
    {
        if (FEE_E_BUSY == errorId)
        {
            (void)Det_ReportRuntimeError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_INVALIDATEBLOCK_SRVID, errorId);
        }
        else
        {
            (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_INVALIDATEBLOCK_SRVID, errorId);
        }
    }
    else
    {
#else
        if(MEMIF_IDLE == gs_feeIntModuleStatus)
        {
#endif
            gs_feeJobBlockIndex   = blockId;
            gs_feeJob             = FEE_INVAL_BLOCK_JOB;
            gs_feeIntModuleStatus = MEMIF_BUSY;
            gs_feeJobResult       = MEMIF_JOB_PENDING;

            result                = (Std_ReturnType)E_OK;
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_OFF)
        }
        else
        {
            /* nothing */
        }
#endif
        SchM_Exit_Fee_FEE_CRITICAL_SECTION2(); 
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    }
#endif

    return (result);
}

#if ((STD_ON == FEE_GET_VERSION_INFO_API_ENABLE ) || defined(__DOXYGEN__))
/*!
* @brief            Service to return the version information of the FEE module.
*
* @param[out]       VersionInfoPtr    Pointer to standard version information structure.
* @param[out]       None
* @param[in,out]    None
*
* @retval           None
*/
FUNC(void, FEE_CODE) Fee_GetVersionInfo(P2VAR(Std_VersionInfoType, AUTOMATIC, FEE_APPL_DATA) VersionInfoPtr)
{
    #if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    if(NULL_PTR != VersionInfoPtr)
    {
    #endif
        VersionInfoPtr->moduleID         = (uint16)FEE_H_MODULE_ID;
        VersionInfoPtr->vendorID         = (uint16)FEE_H_VENDOR_ID;
        VersionInfoPtr->sw_major_version = (uint8)FEE_H_SW_MAJOR_VERSION;
        VersionInfoPtr->sw_minor_version = (uint8)FEE_H_SW_MINOR_VERSION;
        VersionInfoPtr->sw_patch_version = (uint8)FEE_H_SW_PATCH_VERSION;
    #if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    }
    else
    {
        (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_GETVERSIONINFO_SRVID, FEE_E_PARAM_POINTER);
    }
    #endif
}
#endif

/*!
* @brief            Service to erase a logical block.
*
* @param[in]        BlockNumber    Number of logical block, also denoting start address of that block in flash memory
* @param[out]       None
* @param[in,out]    None
*
* @retval           E_OK        - The requested job has been accepted by the module.
*                   E_NOT_OK    - only if DET is enabled: The requested job has not been accepted by the module.
*/
FUNC(Std_ReturnType, FEE_CODE) Fee_EraseImmediateBlock(VAR(uint16, AUTOMATIC) BlockNumber)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_OK;
    VAR(uint16, AUTOMATIC) blockId        = Fee_GetBlockIndexByBlockNumber(BlockNumber);

    SchM_Enter_Fee_FEE_CRITICAL_SECTION3();

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    switch(gs_feeIntModuleStatus)
    {
        case MEMIF_UNINIT :
            {
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID, FEE_E_UNINIT);
                result = (Std_ReturnType)E_NOT_OK;
            }
            break;
        case MEMIF_BUSY :
            {
                if(0xFFFFU == blockId)
                {
                    (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID, FEE_E_INVALID_BLOCK_NO);
                }
                else if((boolean)FALSE == Fee_BlockConfig[blockId].iDataFlg)
                {
                    (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID, FEE_E_INVALID_BLOCK_NO);
                }
                else
                {
                    (void)Det_ReportRuntimeError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID, FEE_E_BUSY);
                }
                result = (Std_ReturnType)E_NOT_OK;
            }
            break;
        case MEMIF_BUSY_INTERNAL :
            {
                if(0xFFFFU == blockId)
                {
                    (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID, FEE_E_INVALID_BLOCK_NO);
                }
                else if((boolean)FALSE == Fee_BlockConfig[blockId].iDataFlg)
                {
                    (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID, FEE_E_INVALID_BLOCK_NO);
                }
                else
                {
                    (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID, FEE_E_BUSY_INTERNAL);
                }
                result = (Std_ReturnType)E_NOT_OK;
            }
            break;
        default :
            {
                if(0xFFFFU == blockId)
                {
                    (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID, FEE_E_INVALID_BLOCK_NO);
                    result = (Std_ReturnType)E_NOT_OK;
                }
                else if((boolean)FALSE == Fee_BlockConfig[blockId].iDataFlg)
                {
                    (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_ERASEIMMEDIATEBLOCK_SRVID, FEE_E_INVALID_BLOCK_NO);
                    result = (Std_ReturnType)E_NOT_OK;
                }
                else
                {
                    /* nothing to do */
                }
            }
            break;
    }
#endif
    
    if (E_NOT_OK == result)
    {
        /* nothing to do */
    }
    else
    {
        gs_feeJobBlockIndex   = blockId;

        gs_feeJob             = FEE_ERASE_IMMEDIATE_JOB;

        gs_feeIntModuleStatus = MEMIF_BUSY;

        gs_feeJobResult       = MEMIF_JOB_PENDING;

        SchM_Exit_Fee_FEE_CRITICAL_SECTION3();
    }

    return (result);
}

/*!
* @brief            Service to report to this module the successful end of an asynchronous operation.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           None
*/
FUNC(void, FEE_CODE) Fee_JobEndNotification(void)
{
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    switch (gs_feeIntModuleStatus)
    {
        case MEMIF_UNINIT :
            {
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_JOBENDNOTIFICATION_SRVID, FEE_E_UNINIT);
            }
            break;
        default :
            {
#endif
                if(FEE_COMPLETE_JOB == gs_feeJob)
                {
                    gs_feeJobResult       = Fls_GetJobResult();
                    gs_feeIntModuleStatus = MEMIF_IDLE;
                    FEE_NVM_JOB_END_NOTIFICATION
                }
                else
                {
                    gs_feeJobResult = Fee_ScheduleForJob();

                    switch(gs_feeJobResult)
                    {
                        case MEMIF_JOB_OK :
                            {
                                gs_feeIntModuleStatus = MEMIF_IDLE;
                                FEE_NVM_JOB_END_NOTIFICATION
                            }
                            break;
                        case MEMIF_JOB_PENDING :
                            {
                            }
                            break;
                        default :
                            {
                                gs_feeIntModuleStatus = MEMIF_IDLE;
                                FEE_NVM_JOB_ERROR_NOTIFICATION
                            }
                            break;
                    }
                }
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
            }
            break;
    }
#endif
}

/*!
* @brief            Service to report to this module the failure of an asynchronous operation.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           None
*/
FUNC(void, FEE_CODE) Fee_JobErrorNotification(void)
{
    VAR(uint8, AUTOMATIC) uClrGrpIndex = (uint8)0x0;

#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    switch (gs_feeIntModuleStatus)
    {
        case MEMIF_UNINIT :
            {
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_JOBERRORNOTIFICATION_SRVID, FEE_E_UNINIT);
            }
            break;

        default :
            {
#endif
                if(MEMIF_JOB_CANCELED != gs_feeJobResult)
                {
                    if(FEE_INT_SCAN_BLOCK_HDR_PARSE_JOB == gs_feeJob)
                    {
                        /* error when reading block header. */
                        gs_feeJobResult = Fee_IntJobScanBlockHdrParse((boolean)FALSE);
                    }
                    else if(FEE_INT_SCAN_CLR_HDR_PARSE_JOB == gs_feeJob)
                    {
                        /* error when reading cluster header. */
                        gs_feeJobResult = Fee_IntJobScanClrHdrParse((boolean)FALSE);
                    }
                    else if(FEE_INT_SWAP_DATA_WRITE_JOB == gs_feeJob)
                    {
                        /* error when reading data from source cluster. */
                        gs_feeJobResult = Fee_IntJobSwapDataWrite((boolean)FALSE);
                    }
                    else if((FEE_WRITE_JOB == gs_feeJob) || (FEE_WRITE_DATA_JOB == gs_feeJob)
                            || (FEE_WRITE_UNALIGNED_DATA_JOB == gs_feeJob) || (FEE_ERASE_IMMEDIATE_JOB == gs_feeJob)
                            || (FEE_ERASE_IMMEDIATE_DONE_JOB == gs_feeJob) || (FEE_WRITE_VALIDATE_JOB == gs_feeJob)
                            || (FEE_WRITE_DONE_JOB == gs_feeJob))
                    {
                        uClrGrpIndex = Fee_GetBlockClusterGrpByBlockId(gs_feeJobBlockIndex);

                        /* something wrong in Flash Memory ,so cluster will swap in next write */
                        gs_feeClusterInfo[uClrGrpIndex].dataAddrCur =
                        gs_feeClusterInfo[uClrGrpIndex].hdrAddrCur + (2U * FEE_BLOCK_OVERHEAD_SIZE);
                    }
                    else
                    {
                        gs_feeJobResult = Fls_GetJobResult();

                        gs_feeIntModuleStatus = MEMIF_IDLE;
                        FEE_NVM_JOB_ERROR_NOTIFICATION
                    }
                }
                else
                {
                    /* nothing to do */
                }
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
            }
            break;
    }
#endif
}

/*!
* @brief            Service to handle the requested read / write / erase jobs and the internal management operations.
*
* @param[in]        None
* @param[out]       None
* @param[in,out]    None
*
* @retval           None
*/
FUNC(void, FEE_CODE) Fee_MainFunction(void)
{
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
    switch (gs_feeIntModuleStatus)
    {
        case MEMIF_UNINIT :
            {
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_MAINFUNCTION_SRVID, FEE_E_UNINIT);  
            }
            break;
        default :
            {
#endif
                if(MEMIF_JOB_PENDING == gs_feeJobResult)
                {
                    if((FEE_INT_SCAN_JOB == gs_feeJob) || (FEE_READ_JOB == gs_feeJob) || (FEE_WRITE_JOB == gs_feeJob)
                    || (FEE_INVAL_BLOCK_JOB == gs_feeJob) || (FEE_ERASE_IMMEDIATE_JOB == gs_feeJob))
                    {
                        /* all the following jobs has the subsequent jobs will be called in Fee job schedule function based on Job */
                        gs_feeJobResult = Fee_ScheduleForJob();
                    }
                    else if((FEE_WRITE_DATA_JOB == gs_feeJob) \
                            || (FEE_WRITE_UNALIGNED_DATA_JOB == gs_feeJob) || (FEE_WRITE_VALIDATE_JOB == gs_feeJob) \
                            || (FEE_WRITE_DONE_JOB == gs_feeJob) || (FEE_INVAL_BLOCK_DONE_JOB == gs_feeJob) \
                            || (FEE_ERASE_IMMEDIATE_DONE_JOB == gs_feeJob) || (FEE_INT_SCAN_CLR_HDR_PARSE_JOB == gs_feeJob) \
                            || (FEE_INT_SCAN_CLR_JOB == gs_feeJob) || (FEE_INT_SCAN_CLR_FMT_JOB == gs_feeJob) \
                            || (FEE_INT_SCAN_CLR_FMT_DONE_JOB == gs_feeJob) || (FEE_INT_SCAN_BLOCK_HDR_PARSE_JOB == gs_feeJob) \
                            || (FEE_INT_SWAP_BLOCK_JOB == gs_feeJob) || (FEE_INT_SWAP_CLR_FMT_JOB == gs_feeJob) \
                            || (FEE_INT_SWAP_DATA_READ_JOB == gs_feeJob) || (FEE_INT_SWAP_DATA_WRITE_JOB == gs_feeJob) \
                            || (FEE_INT_SWAP_CLR_VLD_DONE_JOB == gs_feeJob) || (FEE_COMPLETE_JOB == gs_feeJob))
                    {
                        /* all the following jobs job end or job error notification will be called based on the job result */
                    }
                    else
                    {
                        /* nothing to do */
                    }

                    switch(gs_feeJobResult)
                    {
                        case MEMIF_JOB_PENDING :
                            break;
                        case MEMIF_JOB_OK :
                            {
                                gs_feeIntModuleStatus = MEMIF_IDLE;
                                FEE_NVM_JOB_END_NOTIFICATION
                            }
                            break;
                        default :
                            {
                                gs_feeIntModuleStatus = MEMIF_IDLE;
                                FEE_NVM_JOB_ERROR_NOTIFICATION
                            }
                            break;
                    }
                } 
                else
                {
                    /* nothing to do */
                }
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
            }
            break;
    }
#endif
}

/*!
* @brief            Service to get cluster group runtime infomation
*
* @param[in]        ClrGrpIndex         the index of user specified cluster group
* @param[out]       ClrGrpRTInfoPtr     point to the struct store cluster group infomation
* @param[in,out]    None
*
* @retval           None
*/
FUNC(void, FEE_CODE)
Fee_GetClrGrpRunTimeInfo(VAR(uint8, AUTOMATIC) ClrGrpIndex,
                         P2VAR(Fee_ClrGrpRTInfoType, AUTOMATIC, FEE_APPL_DATA) ClrGrpRTInfoPtr)
{
    VAR(uint32, AUTOMATIC) activeClrIndex = (uint32)0x0;

    if (NULL_PTR == ClrGrpRTInfoPtr)
    {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
        (void)Det_ReportRuntimeError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_GETCLRGRPRUNTIMEINFO_SRVID, FEE_E_BUSY);
#endif
    }
    else
    {
        switch (gs_feeIntModuleStatus)
        {
            case MEMIF_UNINIT:
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_GETCLRGRPRUNTIMEINFO_SRVID, FEE_E_UNINIT);
#endif
                break;
            }
            case MEMIF_BUSY:
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                (void)Det_ReportRuntimeError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_GETCLRGRPRUNTIMEINFO_SRVID, FEE_E_BUSY);
#endif
                break;
            }
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
            case MEMIF_BUSY_INTERNAL:
            {
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_GETCLRGRPRUNTIMEINFO_SRVID, FEE_E_BUSY_INTERNAL);
                break;
            }
#endif
            default :
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                if(FEE_CLUSTER_GROUPS_NUMBER <= ClrGrpIndex)
                {
                    (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_GETCLRGRPRUNTIMEINFO_SRVID,FEE_E_CLUSTER_GROUP_ID);
                }
                else
                {
#endif
                    /* get current active cluster index */
                    ClrGrpRTInfoPtr->blockHeaderOverheadSize = (uint16)FEE_CLUSTER_OVERHEAD_SIZE;
                    ClrGrpRTInfoPtr->virtualPageSize         = (uint16)FEE_VIRTUAL_PAGE_SIZE;
                    activeClrIndex                           = gs_feeClusterInfo[ClrGrpIndex].actClr;
                    ClrGrpRTInfoPtr->swapNumber              = gs_feeClusterInfo[ClrGrpIndex].actClrID - 1U;
                    ClrGrpRTInfoPtr->clusterTotalSize        = Fee_ClrGrps[ClrGrpIndex].clrPtr[activeClrIndex].len;
                    ClrGrpRTInfoPtr->clusterFreeSize =
                        gs_feeClusterInfo[ClrGrpIndex].dataAddrCur
                        - (gs_feeClusterInfo[ClrGrpIndex].hdrAddrCur + (2U * FEE_BLOCK_OVERHEAD_SIZE));
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                }
#endif
                break;
            }
        }
    }
}

/*!
* @brief            Service to force swap cluster in next write job
*
* @param[in]        ClrGrpIndex    the index of user specified cluster group
* @param[out]       None
* @param[in,out]    None
*
* @retval           E_OK        - No more space available in the selected cluster.
*                   E_NOT_OK    - module is not initialized, busy or ClrGrpIndex is not in the valid range.
*/
FUNC(Std_ReturnType, FEE_CODE) Fee_ForceSwapNextWrite(VAR(uint8, AUTOMATIC) ClrGrpIndex)
{
    VAR(Std_ReturnType, AUTOMATIC) result = (Std_ReturnType)E_OK;

    switch (gs_feeIntModuleStatus)
    {
        case MEMIF_UNINIT :
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_FORCESWAPNEXTWRITE_SRVID, FEE_E_UNINIT);
#endif
                result = (Std_ReturnType)E_NOT_OK;
            }
            break;

        case MEMIF_BUSY :
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                (void)Det_ReportRuntimeError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_FORCESWAPNEXTWRITE_SRVID, FEE_E_BUSY);
#endif
                result = (Std_ReturnType)E_NOT_OK;
            }
            break;

        default :
            {
#if(FEE_DEV_ERROR_DETECT_ENABLE == STD_ON)
                if(FEE_CLUSTER_GROUPS_NUMBER <= ClrGrpIndex)
                {
                    (void)Det_ReportError((uint16)FEE_H_MODULE_ID, FEE_INSTANCE_ID, FEE_FORCESWAPNEXTWRITE_SRVID, FEE_E_CLUSTER_GROUP_ID);
                    result = (Std_ReturnType)E_NOT_OK;
                }
                else
#endif
                {
                    gs_feeClusterInfo[ClrGrpIndex].dataAddrCur =
                    gs_feeClusterInfo[ClrGrpIndex].hdrAddrCur + (2U * FEE_BLOCK_OVERHEAD_SIZE);
                }
            }
            break;
    }

    return (result);
}


#if (STD_ON == FEE_ECC_ISR_ENABLE)
FUNC(void, FEE_CODE) Fee_EccCompletionNotificaiton(void)
{
    uint32 clusterIndex = 0U;
    uint32 addrCount = 0U;
    uint32 ret = 0U;
    uint32 i = 0U;
    uint32 blockId = 0U;

    (void)Fls_DsiHandler();

    blockId = *(volatile uint32 *)(gs_feeClusterInfo[0].hdrAddrCur - FEE_BLOCK_OVERHEAD_SIZE + FLS_ABSOLUTE_BASE_ADDRESS) & 0xFFFFU;

    /* Get the cluster index */
    clusterIndex = (gs_feeClusterInfo[0].hdrAddrCur - FEE_RALATIVE_BASE_ADDRESS) / FEE_CLUSTER_SIZE;

    for(addrCount = gs_feeClusterInfo[0].hdrAddrCur;
        addrCount > (FEE_RALATIVE_BASE_ADDRESS + (FEE_CLUSTER_SIZE * clusterIndex));
        addrCount -= FEE_BLOCK_OVERHEAD_SIZE)
    {
        if(((*(volatile uint32 *)(FLS_ABSOLUTE_BASE_ADDRESS + addrCount)) & 0xFFFFU) == blockId)
        {
            if((*(volatile uint8 *)(FLS_ABSOLUTE_BASE_ADDRESS + addrCount + 16u)) == FEE_VALIDATED_FLAG)
            {
                /* Return the starting address of the data when successfully written */
                ret = (*(volatile uint32 *)(FLS_ABSOLUTE_BASE_ADDRESS + addrCount + 4u));
                break;
            }
            else
            {
                /* nothing to do */
            }
        }
        else
        {
            /* nothing to do */
        }
    }

    for(i = 0; i < FEE_CONFIGURED_NUMBER_OF_BLOCKS; i++)
    {
        if(Fee_BlockConfig[i].blkNum == blockId)
        {
            if(Fee_BlockConfig[i].eccCallback != NULL_PTR)
            {
                (Fee_BlockConfig[i].eccCallback)(ret);
            }
            else
            {
                /* nothing to do */
            }
        }
        else
        {
            /* nothing to do */
        }
    }
}
#endif



/**@} end of group FEE_Fuctions */

#define FEE_STOP_SEC_CODE
#include "Fee_MemMap.h"

#ifdef __cplusplus
}
#endif

/**@} end of group FEE_Driver */
/**@} end of group G32A10xx_StdPeriphDriver */
