/**
*   @file       Crypto_Util.c
*   @version    1.0.0
*   @brief      AUTOSAR Crypto - Cryptographic Services Engine (CRYPTO) functions
*   @details    Contains utility CRYPTO driver functions
*
*   @addtogroup  Crypto
*   @{
*/
/*==================================================================================================
*   Project              : AUTOSAR 4.3 MCAL
*   Platform             : PA
*   Peripheral           : Crypto
*   Dependencies         : none
*
*   Autosar Version      : 4.3.1
*   Autosar Revision     : ASR_REL_4_3_REV_0001
*   Autosar Conf.Variant :
*   SW Version           : 1.0.0
*   Build Version        : MPC5777C_MCAL_1_0_0_RTM_ASR_REL_4_3_REV_0001_20190315
*
*   (c) Copyright 2006-2016 Freescale Semiconductor, Inc. 
*       Copyright 2017-2019 NXP
*   All Rights Reserved.
==================================================================================================*/
/*==================================================================================================
==================================================================================================*/

#ifdef __cplusplus
extern "C"{
#endif

/*==================================================================================================
*                                        INCLUDE FILES
* 1) system and project includes
* 2) needed interfaces from external units
* 3) internal and external interfaces from this unit
==================================================================================================*/
#include "Crypto_Util.h"

/*==================================================================================================
*                              SOURCE FILE VERSION INFORMATION
==================================================================================================*/
#define CRYPTO_UTIL_VENDOR_ID_C                     43
#define CRYPTO_UTIL_AR_RELEASE_MAJOR_VERSION_C      4
#define CRYPTO_UTIL_AR_RELEASE_MINOR_VERSION_C      3
#define CRYPTO_UTIL_AR_RELEASE_REVISION_VERSION_C   1
#define CRYPTO_UTIL_SW_MAJOR_VERSION_C              1
#define CRYPTO_UTIL_SW_MINOR_VERSION_C              0
#define CRYPTO_UTIL_SW_PATCH_VERSION_C              0

/*==================================================================================================
*                                     FILE VERSION CHECKS
==================================================================================================*/
/* Check if Crypto_Util source file and Crypto_Util header file are of the same vendor */
#if (CRYPTO_UTIL_VENDOR_ID_C != CRYPTO_UTIL_VENDOR_ID)
    #error "Crypto_Util.c and Crypto_Util.h have different vendor ids"
#endif

/* Check if Crypto_Util source file and Crypto_Util header file are of the same Autosar version */
#if ((CRYPTO_UTIL_AR_RELEASE_MAJOR_VERSION_C    != CRYPTO_UTIL_AR_RELEASE_MAJOR_VERSION) || \
     (CRYPTO_UTIL_AR_RELEASE_MINOR_VERSION_C    != CRYPTO_UTIL_AR_RELEASE_MINOR_VERSION) || \
     (CRYPTO_UTIL_AR_RELEASE_REVISION_VERSION_C != CRYPTO_UTIL_AR_RELEASE_REVISION_VERSION) \
    )
    #error "AutoSar Version Numbers of Crypto_Util.c and Crypto_Util.h are different"
#endif

/* Check if Crypto_Util source file and Crypto_Util header file are of the same Software version */
#if ((CRYPTO_UTIL_SW_MAJOR_VERSION_C != CRYPTO_UTIL_SW_MAJOR_VERSION) || \
     (CRYPTO_UTIL_SW_MINOR_VERSION_C != CRYPTO_UTIL_SW_MINOR_VERSION) || \
     (CRYPTO_UTIL_SW_PATCH_VERSION_C != CRYPTO_UTIL_SW_PATCH_VERSION)    \
    )
    #error "Software Version Numbers of Crypto_Util.c and Crypto_Util.h are different"
#endif

/*==================================================================================================
*                          LOCAL TYPEDEFS (STRUCTURES, UNIONS, ENUMS)
==================================================================================================*/

/*==================================================================================================
*                                       LOCAL MACROS
==================================================================================================*/

/*==================================================================================================
*                                      LOCAL CONSTANTS
==================================================================================================*/

/*==================================================================================================
*                                      LOCAL VARIABLES
==================================================================================================*/

/*==================================================================================================
*                                      GLOBAL CONSTANTS
==================================================================================================*/

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

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


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

/*==================================================================================================
*                                       GLOBAL FUNCTIONS
==================================================================================================*/
#define CRYPTO_START_SEC_CODE
#include "Crypto_MemMap.h"

/**
* @brief        Memset function   
* @details      Memset function   
*
* @param[in]    pSource - Each byte will be set with the value of  u8Value    
* @param[in]    u8Value - Value that will be written into pSource     
* @param[int]   u32Size - Number of bytes that will be written    
* @param[out]   none   
* @returns      none   
*                  
* @api
*
* @pre            
*                   
*/
FUNC(void, CRYPTO_CODE) Crypto_Util_Memset
(
    P2VAR (void,   AUTOMATIC, CRYPTO_APP_DATA) pSource, 
    VAR   (uint8,  AUTOMATIC                 ) u8Value, 
    VAR   (uint32, AUTOMATIC                 ) u32Size
)
{
    P2VAR (uint8, AUTOMATIC, CRYPTO_APP_DATA) pd = (uint8 *)pSource;
    
    while ( 0U < u32Size-- )
    {
        *pd = u8Value;
        pd++;
    }
}

/**
* @brief           Memcopy function
* @details         Memcopy function
*
* @param[in]       pSource - address of the source
* @param[in]       pDest - address of the destination
* @param[in]       u32Size - size to be copied
*
* @returns         void
*                  
* @api
*
* @pre
*
*/
FUNC(void, CRYPTO_CODE) Crypto_Util_Memcpy
(
    P2CONST(uint8,  AUTOMATIC, CRYPTO_APPL_CONST) pSource,
    P2VAR  (uint8,  AUTOMATIC, CRYPTO_APP_DATA  ) pDest,
    VAR    (uint32, AUTOMATIC                   ) u32Size
)
{
    while (0U < u32Size--)
    {
        *pDest = *pSource;
        pDest++;
        pSource++;
    }
}

/**
* @brief           Memcopy function for a specific job
* @details         Memcopy function for a specific job
*
* @param[in]       pSource - address of the source
* @param[in]       pDest - address of the destination
* @param[inout]    none
* @param[out]      none
* @returns         void
*                  
* @api
*
* @pre            
*                   
*/
FUNC(void, CRYPTO_CODE) Crypto_Util_JobTypeMemcpy
(
    P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APP_DATA) pSource,
    P2VAR  (Crypto_JobType, AUTOMATIC, CRYPTO_APP_DATA) pDest
)
{
    VAR    (uint32, AUTOMATIC                 ) u32Size = sizeof(Crypto_JobType);
    P2CONST(uint8,  AUTOMATIC, CRYPTO_APP_DATA) pS      = (const uint8 *)pSource;
    P2VAR  (uint8,  AUTOMATIC, CRYPTO_APP_DATA) pD      = (uint8 *)pDest;
    
    while ( 0U < u32Size--) 
    {
        *pD = *pS;
        pD++;
        pS++;
    }
}

/**
* @brief           Crypto_Util_SwapArrayBytes Swap function for an array input
* @details         Crypto_Util_SwapArrayBytes Swap function will change endianness of input
*
* @param[in]       pSource - address of the source
* @param[in]       u32Len  - size of input in bytes
* @param[inout]    none
* @param[out]      none
* @returns         Std_ReturnType
*                   E_OK: request successful
*                   E_NOT_OK: request unsuccessful              
* @api
*
* @pre            
*                   
*/
FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Util_SwapArrayBytes
(
    P2VAR  (uint8,  AUTOMATIC, CRYPTO_APP_DATA) pSource,
    VAR    (uint32, AUTOMATIC                 ) u32Len
)
{
    VAR    (uint32,         AUTOMATIC) u32Start = (uint32)0U;
    VAR    (uint32,         AUTOMATIC) u32End   = u32Len - 1U ;
    VAR    (uint8,          AUTOMATIC) u8Temp   = (uint8)0U;
    VAR    (Std_ReturnType, AUTOMATIC) RetVal   = E_NOT_OK;
    
    if ( (NULL_PTR != pSource) && ((uint32)0U != u32Len) )
    {
        while ( u32Start < u32End )
        {
            u8Temp            = pSource[u32Start];
            pSource[u32Start] = pSource[u32End];
            pSource[u32End]   = u8Temp;
            u32Start++;
            u32End--;
        }
        RetVal = E_OK;
    }
    
    return RetVal;
}

/**
* @brief           Initialize the CDO job queues
* @details         Each CDO has 2 queues, one with free elements, the second containing jobs sorted by priority. 
*                  For each of the CDO queues, only the first element (head) is stored, as each queue element has a reference to the next one.
*                  This function initializes the queues heads and chains the elements in the free jobs queues
*
* @param[in]       void
* @returns         void
*
* @api
*
* @pre            
*                   
*/
FUNC(void, CRYPTO_CODE) Crypto_Util_InitJobQueues(void)
{
    VAR(uint32, AUTOMATIC) u32ObjectIdIdx;
    VAR(uint32, AUTOMATIC) u32IdxQueueElement;

    for ( u32ObjectIdIdx = 0U; u32ObjectIdIdx < (uint32)CRYPTO_NUMBER_OF_DRIVER_OBJECTS_U32; u32ObjectIdIdx++ )
    {
        /* Initialize the head of queued jobs stating that no job is queued */
        Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfQueuedJobs = CRYPTO_JOB_QUEUE_NONE_U32;

        if(0U == Crypto_aObjectList[u32ObjectIdIdx]->u32CryptoQueueSize)
        {
            /* If CDO queue size is zero, mark the queue of free jobs as being empty. */
            Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfFreeJobs = CRYPTO_JOB_QUEUE_NONE_U32;
        }
        else
        {
            /* Initialize the CDO queue of free jobs by chaining all queue job elements */
            Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfFreeJobs = 0U;
            for (u32IdxQueueElement = 0U; u32IdxQueueElement < (Crypto_aObjectList[u32ObjectIdIdx]->u32CryptoQueueSize - 1U); u32IdxQueueElement++)
            {
                Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[u32IdxQueueElement].u32Next = u32IdxQueueElement + 1U;
            }
            /* Mark the last in the queue as pointing to no other next queue element */
            Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[u32IdxQueueElement].u32Next = CRYPTO_JOB_QUEUE_NONE_U32;
        }
    }
}

/**
* @brief           Adds a job in the jobs queue
* @details         Each CDO has 2 queues, one with free elements, the second containing jobs sorted by priority. 
*                  For each of the CDO queues, only the first element (head) is stored, as each queue element has a reference to the next one.
*                  This function initializes the queues heads and chains the elements in the free jobs queues
*
* @param[in]       u32ObjectIdIdx: identifier of the Crypto Driver Object to be used for queuing the job
* @param[in]       pJob:        pointer to the job to be queued
* @returns         boolean
*                   TRUE  - job has been queued
*                   FALSE - job could not be queued
*
* @api
*
* @pre            
*                   
*/
FUNC(boolean, CRYPTO_CODE) Crypto_Util_QueueJob
(
    VAR    (uint32,         AUTOMATIC                  ) u32ObjectIdIdx,
    P2CONST(Crypto_JobType, AUTOMATIC, CRYPTO_APPL_DATA) pJob
)
{
    P2VAR(uint32,  AUTOMATIC, CRYPTO_APPL_DATA) pQueueSearch = &(Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfQueuedJobs);
    VAR  (boolean, AUTOMATIC                  ) bJobQueued   = (boolean)FALSE;
    VAR  (uint32,  AUTOMATIC                  ) u32IdxQueueElementJob;
    VAR  (uint32,  AUTOMATIC                  ) u32TempIdxQueueElement;

    /* Check if there is at least one element in the free jobs queue */
    if (CRYPTO_JOB_QUEUE_NONE_U32 != Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfFreeJobs)
    {
        /* Enter critical section  */
        SchM_Enter_Crypto_CRYPTO_EXCLUSIVE_AREA_00();
        /* Take the first element from the queue of free jobs */
        u32IdxQueueElementJob = Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfFreeJobs;
        /* Set new value for head of free jobs queue as the next element in the queue */
        Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfFreeJobs = Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[u32IdxQueueElementJob].u32Next;
        /* Put the element taken from free jobs queue in the job queue, in the right position based on priority */
        while (((boolean)FALSE == bJobQueued) && (CRYPTO_JOB_QUEUE_NONE_U32 != *pQueueSearch))
        {
            if (pJob->jobInfo->jobPriority > Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[*pQueueSearch].Job.jobInfo->jobPriority)
            {
                /* Save address of list element we are inserting the new job in front of */
                u32TempIdxQueueElement = *pQueueSearch;
                /* Need to insert the element here */
                *pQueueSearch = u32IdxQueueElementJob;
                Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[u32IdxQueueElementJob].u32Next = u32TempIdxQueueElement;
                /* Mark the job as queued */
                bJobQueued = (boolean)TRUE;
            }
            /* Advance to the next element in the queue */
            pQueueSearch = &(Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[*pQueueSearch].u32Next);
        }
        /* If we looped through all elements containing queued jobs and did not find a place where to put the new element, add it at the end */
        if ((boolean)FALSE == bJobQueued)
        {
            *pQueueSearch = u32IdxQueueElementJob;
            /* Mark the new added queue element as being the last one in the queue */
            Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[u32IdxQueueElementJob].u32Next = CRYPTO_JOB_QUEUE_NONE_U32;
        }
        /* Exit critical section */
        SchM_Exit_Crypto_CRYPTO_EXCLUSIVE_AREA_00();
        /* Copy the job into the free queue element found */
        Crypto_Util_JobTypeMemcpy (pJob, &(Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[u32IdxQueueElementJob].Job));
        /* Mark the job as queued */
        bJobQueued = (boolean)TRUE;
    }
    return bJobQueued;
}

/**
* @brief           Dequeues the first job in the queue
* @details         Removes the first queued job (head) and adds it in the queue of free jobs
*
* @param[in]       u32ObjectIdIdx: identifier of the Crypto Driver Object to be used for dequeuing the job
* @returns         void
*
* @api
*
* @pre            Assumes that at least one element exists in the queued jobs
*                   
*/
FUNC(void, CRYPTO_CODE) Crypto_Util_DequeueHeadJob
(
    VAR  (uint32, AUTOMATIC) u32ObjectIdIdx
)
{
    VAR(uint32, AUTOMATIC) u32HeadOfQueuedJobs;
    VAR(uint32, AUTOMATIC) u32HeadOfFreeJobs;
    
    /* Enter critical section  */
    SchM_Enter_Crypto_CRYPTO_EXCLUSIVE_AREA_01();
    
    u32HeadOfQueuedJobs = Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfQueuedJobs;
    u32HeadOfFreeJobs   = Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfFreeJobs;
    
    /* Move head of queued jobs to the next element in the queue */
    Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfQueuedJobs = Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[u32HeadOfQueuedJobs].u32Next;

    /* Add the removed head of queued jobs as being the head of queue of free jobs */
    Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfFreeJobs                        = u32HeadOfQueuedJobs;
    Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[u32HeadOfQueuedJobs].u32Next = u32HeadOfFreeJobs;
    
    /* Exit critical section */
    SchM_Exit_Crypto_CRYPTO_EXCLUSIVE_AREA_01();
}


/**
* @brief           Dequeues from the list of queued jobs the first found job with the given jobId
* @details         Searches the queued jobs for a job with a given jobId and if found, removes it
*
* @param[in]       u32ObjectIdIdx: identifier of the Crypto Driver Object to be used for dequeuing the job
* @param[in]       pJobInfo:       pointer to jobInfo structure containing the jobId to search for
* @returns         Std_ReturnType
*
* @api
*
* @pre            
*                   
*/
FUNC(Std_ReturnType, CRYPTO_CODE) Crypto_Util_DequeueJob
(
    VAR  (uint32,             AUTOMATIC                  ) u32ObjectIdIdx,
    P2VAR(Crypto_JobInfoType, AUTOMATIC, CRYPTO_APPL_DATA) pJobInfo
)
{
    VAR  (Std_ReturnType, AUTOMATIC                  ) RetVal = (Std_ReturnType)E_NOT_OK;
    P2VAR(uint32,         AUTOMATIC, CRYPTO_APPL_DATA) pQueueSearch;
    VAR  (uint32,         AUTOMATIC                  ) u32TempIdxQueueElement1;
    VAR  (uint32,         AUTOMATIC                  ) u32TempIdxQueueElement2;

    /* Check if there is at least one element in the list of queued jobs */
    if (CRYPTO_JOB_QUEUE_NONE_U32 != Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfQueuedJobs)
    {
        pQueueSearch = &(Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfQueuedJobs);
        /* Loop through entire list of queued jobs */
        while (CRYPTO_JOB_QUEUE_NONE_U32 != *pQueueSearch)
        {
            /* Check if the current looped job from the list of queued jobs has same jobId with the one received as parameter */
            if (pJobInfo->jobId == Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[*pQueueSearch].Job.jobInfo->jobId)
            {
                /* Enter critical section  */
                SchM_Enter_Crypto_CRYPTO_EXCLUSIVE_AREA_02();

                /* Store temporary the value of the address of the element in array Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[] pointed by pQueueSearch */
                u32TempIdxQueueElement1 = *pQueueSearch;

                /* Found a job matching the same jobId. Remove it from the list of queued jobs */
                *pQueueSearch = Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[*pQueueSearch].u32Next;

                /* Add the removed element in in the list of free jobs */
                u32TempIdxQueueElement2 = Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfFreeJobs;
                Crypto_aObjectList[u32ObjectIdIdx]->u32HeadOfFreeJobs = u32TempIdxQueueElement1;
                Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[u32TempIdxQueueElement1].u32Next = u32TempIdxQueueElement2;

                /* Exit critical section */
                SchM_Exit_Crypto_CRYPTO_EXCLUSIVE_AREA_02();
                
                RetVal = (Std_ReturnType)E_OK;
                break;
            }
            /* Advance to the next element in the list of queued jobs, searching for the job to dequeue */
            pQueueSearch = &(Crypto_aObjectList[u32ObjectIdIdx]->pQueuedJobs[*pQueueSearch].u32Next);
        }
    }
    
    return RetVal;
}

#if (STD_ON == CRYPTO_KEYS_EXIST)
/**
* @brief           Checks if a key ID is valid
* @details         Checks if a key ID is valid
*
* @param[in]       u32KeyId -  Id of the key to be checked
* @returns         CRYPTO_INVALID_KEY_IDX_U32: Request failed, keyId not found
*                  All other values:           Request successful, keyId found
* @api
*
* @pre
*
*/
FUNC(uint32, CRYPTO_CODE) Crypto_Util_GetKeyIdIdx
(
    VAR (uint32, AUTOMATIC) u32KeyId
)
{
    VAR(uint32, AUTOMATIC) u32RetVal = CRYPTO_INVALID_KEY_IDX_U32;
    VAR(uint32, AUTOMATIC) u32Counter;
    
    for ( u32Counter = 0U; u32Counter < CRYPTO_NUMBER_OF_KEYS_U32; u32Counter++)
    {
        if (u32KeyId == Crypto_aKeyList[u32Counter]->u32CryptoKeyId)
        {
            u32RetVal = u32Counter;
            break;
        }
    }
    return u32RetVal;
}

/**
* @brief           Checks if a key element ID is valid
* @details         Checks if a key element ID is valid and present inside the required key
*
* @param[in]       u32KeyIndex -  index of the key containing the key element
* @param[in]       u32keyElementId - key element ID
* @returns         Crypto_VerifyKeyElementIdType
*                     
* @api
*
* @pre            
*                   
*/
FUNC(Crypto_VerifyKeyElementIdType, CRYPTO_CODE) Crypto_Util_VerifyKeyElementId
(
    VAR(uint32, AUTOMATIC) u32KeyIndex,
    VAR(uint32, AUTOMATIC) u32keyElementId
)
{
    VAR(Crypto_VerifyKeyElementIdType, AUTOMATIC) VerifyKeyElement;
    VAR(uint32,                        AUTOMATIC) u32Counter;
    
    VerifyKeyElement.RetVal = (Std_ReturnType)E_NOT_OK;
    
    for ( u32Counter = 0U; u32Counter < Crypto_aKeyList[u32KeyIndex]->pCryptoKeyElementList->u32NoCryptoKeyElements; u32Counter++ )
    {
        if ( u32keyElementId == Crypto_aKeyElementList[Crypto_aKeyList[u32KeyIndex]->pCryptoKeyElementList->u32CryptoKeyElements[u32Counter]]->u32CryptoKeyElementId )
        {
            VerifyKeyElement.RetVal     = (Std_ReturnType)E_OK;
            VerifyKeyElement.u32Counter = Crypto_aKeyList[u32KeyIndex]->pCryptoKeyElementList->u32CryptoKeyElements[u32Counter];
            break;
        }
    }
     
    return VerifyKeyElement;
}
#endif

#define CRYPTO_STOP_SEC_CODE
#include "Crypto_MemMap.h"

#ifdef __cplusplus
}
#endif

/** @} */
