/*********************************************************************************************************************
 *
 * Description:     ProgM module implementation
 * FileName:        FBL_ProgM.c
 * Company:         ETAS GmbH (www.etas.com)
 * Generated        28/12/2018 at 11:22
 *
 **********************************************************************************************************************
 *
 * COPYRIGHT RESERVED, Copyright ETAS GmbH 2018. All rights reserved, also regarding any disposal,
 * exploitation, reproduction, editing, distribution, as well as in the event of applications
 * for industrial property rights.
 *
 **********************************************************************************************************************
 * History:
 **********************************************************************************************************************
 * Author        Date            Comment
 **********************************************************************************************************************
BGA1YOK         25/10/2017      First Version    
 **********************************************************************************************************************/


/*********************************************************************************************************************
 * Includes
 *********************************************************************************************************************/
#include "FBL_ProgM.h"
#include "FBL_ProgMPrvCfg.h"
#include "FBL_BootM.h"
#include "FBL_Port.h"
#ifdef __PQRA_QAC__
#pragma PRQA_MESSAGES_OFF
#endif
#include "Os_TimeServices.h"
#ifdef __PQRA_QAC__
#pragma PRQA_MESSAGES_ON
#endif
#include "Fbl_UserCode.h"
#ifdef __PQRA_QAC__
#pragma PRQA_MESSAGES_OFF
#endif
#include "DcmAppl.h"
#ifdef __PQRA_QAC__
#pragma PRQA_MESSAGES_ON
#endif
/*********************************************************************************************************************
 * Local Defines
 *********************************************************************************************************************/

/*********************************************************************************************************************
 * Local Macros
 *********************************************************************************************************************/
/*[$Satisfies $RTA_FBL_SDK_DD_PROGM 135] */
#define FBL_PROGM_VERIFYINGSTART_TIMEOUT_MS 50U
/*********************************************************************************************************************
 * Local Types
 *********************************************************************************************************************/

/*********************************************************************************************************************
 * Local Variables
 *********************************************************************************************************************/
/* The following 3 are used for saving the information 
required when a write request is handed off from the 
DCM request to the main function */
/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 21]*/
static uint32 Fbl_ProgM_PendingWriteMemoryAddress;

/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 22]*/
static uint32 Fbl_ProgM_PendingWriteDataLength;

/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 23]*/
static const uint8 * Fbl_ProgM_PendingWriteReqBuf;


/*********************************************************************************************************************
 * Local Functions Prototypes
 *********************************************************************************************************************/

/*********************************************************************************************************************
 * Local Functions Implementation
 *********************************************************************************************************************/

/*********************************************************************************************************************
 * Exported Variables Implementation
 *********************************************************************************************************************/

/*********************************************************************************************************************
 * Exported Functions Implementation
 *********************************************************************************************************************/
/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 47]*/
void Fbl_ProgM_MainFunction (void)
{
    /* Check component status */
    /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 48]*/
    if (FBL_PROGM_CMPSTATE_NOT_INIT == Fbl_ProgM_CmpState)
    {
        /* If uninitialized, do nothing */
    }
    else
    {
        /* If the component is running */
        if (FBL_PROGM_CMPSTATE_INIT == Fbl_ProgM_CmpState)
        {
            switch (Fbl_ProgM_RegionOperationMode)
            {
                case FBL_PROGM_REGION_WRITING: /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 50]*/
                {
                    Fbl_ProgM_WriteResultType writeResult = Fbl_Port_Write(Fbl_ProgM_PendingWriteMemoryAddress,Fbl_ProgM_PendingWriteDataLength,Fbl_ProgM_PendingWriteReqBuf);
                    if (FBL_PROGM_WRITE_RESULT_SUCCESS == writeResult)
                    {
                        /* Call to write was successful. Write to the update flags */
                        Fbl_ProgM_OpResultFlag = (boolean)TRUE;
                        Fbl_ProgM_OpPendingFlag = (boolean)FALSE;
                        Fbl_ProgM_RegionOperationMode = FBL_PROGM_DEFAULT;
                    }
                    else if (FBL_PROGM_WRITE_RESULT_BUSY == writeResult)
                    {
                        /* The write couldn't be done at this time. Check for timeout. */
                        if ((boolean)TRUE == OS_TimerExt_IsElapsed(Fbl_ProgM_WritingTimer, FBL_PROGM_OPERATION_WRITE_PENDING_TIMEOUT_MS))
                        {
                            /* Timeout occurred - set the pending flag to (boolean)FALSE.
                            The UDS request will return an error the next time it is called.*/
                            Fbl_ProgM_OpPendingFlag = (boolean)FALSE;
                            Fbl_ProgM_OpResultFlag = (boolean)FALSE;
                            Fbl_ProgM_RegionOperationMode = FBL_PROGM_DEFAULT;
                        }
                        else
                        {
                            /* No timeout - try again at next invocation of main */
                        }
                    }
                    else /* anything else, including FBL_PROGM_WRITE_RESULT_FAILURE */
                    {
                        /* The write failed - return error */
                        Fbl_ProgM_OpResultFlag = (boolean)FALSE;
                        Fbl_ProgM_OpPendingFlag = (boolean)FALSE;
                        Fbl_ProgM_RegionOperationMode = FBL_PROGM_DEFAULT;
                    }
                    break;
                }
                default:
                {
                    /* Illegal state - should never happen */
                    break;
                }
            }
        }
        else
        {
            /* The ProgM component is not running. Just return. */
        }
    }
}

/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 68] */
void Fbl_ProgM_Init (void)
{
    Fbl_ProgM_CmpState =FBL_PROGM_CMPSTATE_INIT;
}

/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 69] */
void Fbl_ProgM_DeInit (void)
{
    Fbl_ProgM_CmpState = FBL_PROGM_CMPSTATE_NOT_INIT;
}

/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 2] */
FUNC(Std_ReturnType, DCM_APPL_CODE) DcmGetSeedDSP_Fbl_ProgM_GetSeedCallback(
    VAR(Dcm_SecLevelType,AUTOMATIC) SecLevel_u8,
    VAR(uint32,AUTOMATIC) Seedlen_u32,
    VAR(uint32,AUTOMATIC) AccDataRecsize_u32,
    P2VAR(uint8,AUTOMATIC,DCM_INTERN_DATA) SecurityAccessDataRecord,
    P2VAR(uint8,AUTOMATIC,DCM_INTERN_DATA) Seed,
    VAR(Dcm_OpStatusType,AUTOMATIC) OpStatus,
    P2VAR(Dcm_NegativeResponseCodeType,AUTOMATIC,DCM_INTERN_DATA) ErrorCode)
{
    Std_ReturnType ret = E_OK;
    *ErrorCode = E_OK;
    ret = Fbl_Port_GetSeed(Seedlen_u32, AccDataRecsize_u32, SecurityAccessDataRecord, Seed);
    if (E_OK != ret)
    {
        ret = E_NOT_OK;
        *ErrorCode = E_NOT_OK;
    }
    return ret;
}

/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 3] */
/* PRQA S 3673 6 */ /* <Deviation: rule_8.13_3673_the form api generated from RTA BSW 5.2 */
FUNC(Std_ReturnType, DCM_APPL_CODE) DcmCompareKeyDSP_Fbl_ProgM_CompareKeyCallback(
    VAR(uint32,AUTOMATIC) KeyLen_32,
	P2CONST(uint8,AUTOMATIC,DCM_INTERN_CONST) Key,
    VAR(Dcm_OpStatusType, AUTOMATIC) OpStatus,
    P2VAR(Dcm_NegativeResponseCodeType,AUTOMATIC,DCM_INTERN_DATA) ErrorCode)
{
    Std_ReturnType ret = E_OK;
    *ErrorCode = E_OK;
    ret = Fbl_Port_CompareKey(KeyLen_32, Key);
    if (E_OK != ret)
    {
        ret = E_NOT_OK;
        *ErrorCode = DCM_E_INVALIDKEY;
    }
    return ret;
}


/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 62] */
FUNC(Dcm_ReturnWriteMemoryType,DCM_APPL_CODE) DcmAppl_Dcm_WriteMemory(
    VAR(Dcm_OpStatusType,  AUTOMATIC) Wmba_Opstatus,
    VAR(uint8,  AUTOMATIC) memoryid,
    VAR(uint32,  AUTOMATIC) memoryaddress,
    VAR(uint32,  AUTOMATIC) datalength,
    P2CONST (uint8,AUTOMATIC,DCM_INTERN_DATA) reqbuf,
    P2VAR (Dcm_NegativeResponseCodeType,AUTOMATIC,DCM_INTERN_DATA) ErrorCode)
{
    /* Default return is pending, unless operation is ready or error was detected */
    Dcm_ReturnWriteMemoryType retValue = DCM_WRITE_PENDING;
    /* Assume no error unless there is a reason */
    *ErrorCode = E_OK;
    if (Fbl_ProgM_CmpState == FBL_PROGM_CMPSTATE_INIT)
    {
        if (Wmba_Opstatus == DCM_INITIAL)
        {
            /* execute the write [$Satisfies $RTA_FBL_SDK_DD_PROGM 66] */
            Fbl_ProgM_WriteResultType writeResult = Fbl_Port_Write(memoryaddress, datalength, reqbuf);
            if (writeResult == FBL_PROGM_WRITE_RESULT_SUCCESS)
            {
                /* Call to write was successful. Return success immediately.*/
                retValue = DCM_WRITE_OK;
            } else if (writeResult == FBL_PROGM_WRITE_RESULT_BUSY)
            {
                /* The write couldn't be done at this time. Hand it over to the
                polled ProgM_MainFunction. Will return as pending. */
                Fbl_ProgM_OpResultFlag = (boolean)FALSE;
                Fbl_ProgM_OpPendingFlag = (boolean)TRUE;
                Fbl_ProgM_PendingWriteMemoryAddress = memoryaddress;
                Fbl_ProgM_PendingWriteDataLength = datalength;
                Fbl_ProgM_PendingWriteReqBuf = reqbuf;
                /* set to writing state - the ProgM will pick it up from here */
                Fbl_ProgM_RegionOperationMode = FBL_PROGM_REGION_WRITING;
                /* Start timer */
                OS_TimerExt_Start(&Fbl_ProgM_WritingTimer);
            } else /* anything else, including  FBL_PROGM_WRITE_RESULT_FAILURE */
            {
                /* The write failed - return error */
                retValue = DCM_WRITE_FAILED;
                *ErrorCode = DCM_E_GENERALPROGRAMMINGFAILURE;
            }
        }
        else if (Wmba_Opstatus == DCM_PENDING)
        {
            /* if write was completed successfully */
            /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 90] */
            if ((boolean)TRUE == Fbl_ProgM_OpResultFlag)
            {
                /* Note: No need to reset Fbl_ProgM_OpPendingFlag and Fbl_ProgM_OpResultFlag
           since they are not used by DCM_INITIAL but only set */

                retValue = DCM_WRITE_OK;
            }
            else
            { /* in this case, Fbl_ProgM_OpResultFlag == (boolean)FALSE */
                /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 92] */
                if ((boolean)FALSE == Fbl_ProgM_OpPendingFlag)
                {
                    /* Pending flag is not set, so operation must have failed, e.g. due to a timeout. */
                    retValue = DCM_WRITE_FAILED;
                    *ErrorCode = DCM_E_GENERALPROGRAMMINGFAILURE;
                } else
                { /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 91] */
                    /* In this case, the operation is still pending so the state of ProgM would be
          in the WRITING state. Check the timer to see if we should continue
          waiting or abandon the operation. */
                    if ((boolean)TRUE == OS_TimerExt_IsElapsed(Fbl_ProgM_WritingTimer, FBL_PROGM_OPERATION_WRITE_PENDING_TIMEOUT_MS))
                    {
                        /* Timeout occurred during flash timeout */
                        *ErrorCode = DCM_E_GENERALPROGRAMMINGFAILURE;
                        retValue = DCM_WRITE_FAILED;
                        Fbl_ProgM_RegionOperationMode = FBL_PROGM_DEFAULT;
                    } else {
                        /* Still have not timed out, so continue waiting */
                    }
                }
            }
        }
        else
        {
            /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 64] */
            /* unexpected DCM status...including DCM_CANCEL which is not supported */
            retValue = DCM_WRITE_FAILED;
            *ErrorCode = DCM_E_GENERALPROGRAMMINGFAILURE;
        }
    } else {
        /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 63] */
        /* Component is not initialised - return error */
        retValue = DCM_WRITE_FAILED;
        *ErrorCode = DCM_E_GENERALPROGRAMMINGFAILURE;
    }
    return retValue;
}

/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 70] */
FUNC(Std_ReturnType,DCM_APPL_CODE) DcmAppl_Dcm_ProcessRequestTransferExit(
    VAR(Dcm_OpStatusType,AUTOMATIC) OpStatus,
    P2CONST(uint8,DCM_INTERN_DATA,AUTOMATIC) transferRequestParameterRecord,
    VAR(uint32,AUTOMATIC) transferRequestParameterRecordSize,
    P2VAR(uint8,DCM_INTERN_DATA,AUTOMATIC) transferResponseParameterRecord,
    P2VAR(uint32,DCM_INTERN_DATA,AUTOMATIC) transferResponseParameterRecordSize,
    P2VAR(Dcm_NegativeResponseCodeType,DCM_INTERN_DATA,AUTOMATIC) ErrorCode)

{
    /* Default return is pending, unless operation is ready or error was detected */
    Std_ReturnType RetValue = DCM_E_PENDING;
    /* Assume no error unless there is a reason */
    *ErrorCode = E_OK;
    /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 71] */
    if (Fbl_ProgM_CmpState == FBL_PROGM_CMPSTATE_INIT)
    {
        /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 74] */
        if (DCM_INITIAL == OpStatus)
        {
            if (0U == transferRequestParameterRecordSize)
            {
                RetValue = E_OK;
            }
            else
            {
                RetValue = E_NOT_OK;
                *ErrorCode = DCM_E_INCORRECTMESSAGELENGTHORINVALIDFORMAT;
            }
        }
        else
        {   /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 72] */
            /* unexpected DCM status... */
            RetValue = E_NOT_OK;
            *ErrorCode = DCM_E_GENERALPROGRAMMINGFAILURE;
        }
    }
    else
    {
        /* [$Satisfies $RTA_FBL_SDK_DD_PROGM 71] */
        /* Component is not initialised - return error */
        RetValue = E_NOT_OK;
        *ErrorCode = DCM_E_GENERALPROGRAMMINGFAILURE;
    }
    return RetValue;
}

/* [$Satisfies $RTA_FBL_SDK_DD_PROGM 1] */
FUNC(void,DCM_APPL_CODE)  DcmAppl_Switch_DcmExecuteEcuReset(VAR(uint8,AUTOMATIC) ResetType_u8)
{
    if (0x01U == ResetType_u8) /* Hard reset type */
    {
        /* Reset the ECU */
        Fbl_Port_ResetEcu();
    }
}
