/*
 * Copyright 2020-2022 Yuntu Microelectronics co.,ltd
 * All rights reserved.
 *
 * YUNTU Confidential. This software is owned or controlled by YUNTU and may only be
 * used strictly in accordance with the applicable license terms. By expressly
 * accepting such terms or by downloading, installing, activating and/or otherwise
 * using the software, you are agreeing that you have read, and that you agree to
 * comply with and are bound by, such license terms. If you do not agree to be
 * bound by the applicable license terms, then you may not retain, install,
 * activate or otherwise use the software. The production use license in
 * Section 2.3 is expressly granted for this software.
 */
/*!
 * @file hcu_driver.c
 */

#include "hcu_hw_access.h"
#include "interrupt_manager.h"
#include "core_common.h"

/*******************************************************************************
 * Variables
 ******************************************************************************/

/* Pointer to runtime state structure.*/
static hcu_state_t *s_hcuStatePtr = NULL;

/*******************************************************************************
 * Private Functions
 ******************************************************************************/

/* Waits on the synchronization object and updates the internal flags */
static status_t HCU_ConfigAlgorithm(const uint32_t *dataIn,
                                    uint16_t msgLen,
                                    uint16_t exMsgLen,
                                    uint32_t *dataOut,
                                    hcu_engine_sel_t eng,
                                    hcu_alg_aes_mode_t alg,
                                    hcu_mode_sel_t mode);

static status_t HCU_RunOneLoop(void);

/* Callback for DMA complete */
static void HCU_DRV_CompleteDMA(void *parameter, dma_chn_status_t status);

/* DMA configuration */
static status_t HCU_DRV_ConfigDMA(uint8_t ingressDMAChannel, uint8_t egressDMAChannel);

/* When AES-CCM done in last block, call it */
static status_t HCU_DRV_DoneMAC(void);

#if 0
static void HCU_DRV_WaitCommandCompletion(uint32_t timeout);
#endif

/*******************************************************************************
 * Code
 ******************************************************************************/

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_Init
 * Description   : This function initializes the internal state of the driver
 * and enables the HCU interrupt.
 *
 * Implements    : HCU_DRV_Init_Activity
 * END**************************************************************************/
status_t HCU_DRV_Init(const hcu_user_config_t *userConfig, hcu_state_t *state)
{
    /* Check the driver is initialized */
    DEV_ASSERT(state != NULL);
    DEV_ASSERT(userConfig != NULL);

    status_t semaStatus;

    /* Save the driver state structure */
    s_hcuStatePtr = state;

    /* Clear the contents of the state structure */
    s_hcuStatePtr->cmdInProgress = false;
    s_hcuStatePtr->blockingCmd = false;
    s_hcuStatePtr->isLastBlock = false;
    s_hcuStatePtr->callback = NULL;
    s_hcuStatePtr->callbackParam = NULL;
    s_hcuStatePtr->dataInputPtr = NULL;
    s_hcuStatePtr->dataOutputPtr = NULL;
    s_hcuStatePtr->msgLen = 0U;
    s_hcuStatePtr->inputCount = 0U;
    s_hcuStatePtr->outputCount = 0U;
    s_hcuStatePtr->ccmConfig = NULL;
    s_hcuStatePtr->cmacConfig = NULL;
    s_hcuStatePtr->status = STATUS_SUCCESS;

    /* New attribution for hcu state */
    s_hcuStatePtr->carryType = userConfig->carryType;
    s_hcuStatePtr->ingressDMAChannel = userConfig->ingressDMAChannel;
    s_hcuStatePtr->egressDMAChannel = userConfig->egressDMAChannel;

    HCU_DRV_CfgSwapping(userConfig->swap);

    /* Create the synchronization semaphore */
    semaStatus = OSIF_SemaCreate(&s_hcuStatePtr->cmdComplete, 0U);
    if (semaStatus == STATUS_ERROR)
    {
        return STATUS_ERROR;
    }

    return STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_DeInit
 * Description   : This function clears the internal state of the driver and
 * disables the HCU interrupt.
 *
 * Implements    : HCU_DRV_DeInit_Activity
 * END**************************************************************************/
status_t HCU_DRV_DeInit(hcu_state_t *state)
{
    status_t errorCode = STATUS_SUCCESS;
    /* Clear the contents of the state structure */
    s_hcuStatePtr->cmdInProgress = false;
    s_hcuStatePtr->blockingCmd = false;
    s_hcuStatePtr->isLastBlock = false;
    s_hcuStatePtr->callback = NULL;
    s_hcuStatePtr->callbackParam = NULL;
    s_hcuStatePtr->dataInputPtr = NULL;
    s_hcuStatePtr->dataOutputPtr = NULL;
    s_hcuStatePtr->msgLen = 0U;
    s_hcuStatePtr->inputCount = 0U;
    s_hcuStatePtr->outputCount = 0U;
    s_hcuStatePtr->ccmConfig = NULL;
    s_hcuStatePtr->cmacConfig = NULL;
    s_hcuStatePtr->status = STATUS_SUCCESS;
    /* Disable the interrupt */
    INT_SYS_DisableIRQ(HCU_IRQn);
    /* Clear the state pointer. */
    s_hcuStatePtr = NULL;
    HCU->CR = 0;
    HCU->INTE = 0;

    /* Destroy the semaphore */
    errorCode = OSIF_SemaDestroy(&(state->cmdComplete));
    DEV_ASSERT(errorCode == STATUS_SUCCESS);
    return errorCode;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_CfgSwapping
 * Description   : This function cfg data swapping.
 *
 * Implements    : HCU_DRV_CfgSwapping_Activity
 * END**************************************************************************/
void HCU_DRV_CfgSwapping(hcu_swapping_t cfg)
{
    /* 2-bit cfg value check*/
    DEV_ASSERT(cfg < 4);

    uint32_t temp;
    temp = HCU->CR;
    temp &= ~HCU_CR_DATSWP_MASK;
    temp |= HCU_CR_DATSWP(cfg);
    HCU->CR = temp;
}

void HCU_DRV_ClearODFlag(void)
{
    /* clear OD flag in W1C method */
    HCU->SR = HCU_SR_OD_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_InstallCallback
 * Description   : This function installs a user callback for the command
 * complete event.
 *
 * Implements    : HCU_DRV_InstallCallback_Activity
 * END**************************************************************************/
security_callback_t HCU_DRV_InstallCallback(security_callback_t callbackFunction, void *callbackParam)
{
    /* Check the driver is initialized */
    DEV_ASSERT(s_hcuStatePtr != NULL);

    security_callback_t currentCallback = s_hcuStatePtr->callback;
    s_hcuStatePtr->callback = callbackFunction;
    s_hcuStatePtr->callbackParam = callbackParam;

    return currentCallback;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_LoadUserKey
 * Description   : This function loads the user key into the HCU.
 *
 * Implements    : HCU_DRV_LoadUserKey_Activity
 * END**************************************************************************/

status_t HCU_DRV_LoadUserKey(const void *key, hcu_key_size_t keySize)
{
    /* Check the key addresses are 32 bit aligned */
    DEV_ASSERT((((uint32_t)key) & HCU_BUFF_ADDR_CHECK_MASK) == 0U);
    /* Check the driver is initialized */
    DEV_ASSERT(s_hcuStatePtr != NULL);
    uint8_t keySizeInWords;
    uint32_t *keyPtr = (uint32_t *)key;
    uint8_t keyLoopIndex;
    switch (keySize)
    {
        case KEY_SIZE_128_BITS:
            keySizeInWords = 4U;
            break;
        case KEY_SIZE_192_BITS:
            keySizeInWords = 6U;
            break;
        case KEY_SIZE_256_BITS:
            keySizeInWords = 8U;
            break;
        default:
            return STATUS_HCU_KEY_SIZE_NOT_SUPPORTED;
    }
    /* Set key should always update when no command is in progress */
    if (s_hcuStatePtr->cmdInProgress)
    {
        return STATUS_HCU_LOAD_KEY_WHEN_BUSY;
    }
    /* Write user key to HCU */
    for (keyLoopIndex = 0; keyLoopIndex < keySizeInWords; keyLoopIndex++)
    {
        HCU_SetUserKey(keyPtr[keyLoopIndex], keyLoopIndex);
    }
    /* Update Key Size */
    HCU_SetKeySize(keySize);
    return STATUS_SUCCESS;
}

#if FEATURE_HCU_HMAC_ENGINE
/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_LoadHMACKey
 * Description   : This function loads the user key into the HCU for HMAC.
 *
 * Implements    : HCU_DRV_LoadHMACKey_Activity
 * END**************************************************************************/
status_t HCU_DRV_LoadHMACKey(const void *key, hcu_hmac_key_size_t keySize)
{
    /* Check the key addresses are 32 bit aligned */
    DEV_ASSERT((((uint32_t)key) & HCU_BUFF_ADDR_CHECK_MASK) == 0U);
    /* Check the driver is initialized */
    DEV_ASSERT(s_hcuStatePtr != NULL);
    uint8_t keySizeInWords;
    uint32_t *keyPtr = (uint32_t *)key;
    uint8_t keyLoopIndex;

    keySizeInWords = ((2 << keySize) >> 2);
    keySizeInWords = (keySizeInWords == 0) ? 1 : keySizeInWords;

    /* Set key should always update when no command is in progress */
    if (s_hcuStatePtr->cmdInProgress)
    {
        return STATUS_HCU_LOAD_KEY_WHEN_BUSY;
    }
    /* Enable HMAC engine */
    HCU_EnableHMAC(true);
    /* Write user key to HCU */
    for (keyLoopIndex = 0; keyLoopIndex < keySizeInWords; keyLoopIndex++)
    {
        HCU_SetUserKey(keyPtr[keyLoopIndex], keyLoopIndex);
    }
    /* Update Key Size */
    HCU_SetHMACKeySize(keySize);
    return STATUS_SUCCESS;
}
#endif /* FEATURE_HCU_HMAC_ENGINE */

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_ConfigAlgorithm
 * Description   : This function configures algorithm with given information
 *
 * Implements    : HCU_ConfigAlgorithm_Activity
 * END**************************************************************************/
static status_t HCU_ConfigAlgorithm(const uint32_t *dataIn,
                                    uint16_t msgLen,
                                    uint16_t exMsgLen,
                                    uint32_t *dataOut,
                                    hcu_engine_sel_t eng,
                                    hcu_alg_aes_mode_t alg,
                                    hcu_mode_sel_t mode)
{
    /* Check the buffer addresses are valid */
    DEV_ASSERT(dataIn != NULL);
    /* Check the buffers addresses are 32 bit aligned */
    DEV_ASSERT((((uint32_t)dataIn) & HCU_BUFF_ADDR_CHECK_MASK) == 0U);
    DEV_ASSERT((((uint32_t)dataOut) & HCU_BUFF_ADDR_CHECK_MASK) == 0U);

    /* Check there is no other command in execution */
    if (HCU_IsBusy() || s_hcuStatePtr->cmdInProgress)
    {
        return STATUS_BUSY;
    }

    /* Update the internal flags */
    s_hcuStatePtr->cmdInProgress = true;
    /* Update engine and algorithm settings */
    HCU_SetEngineAlgorithm(eng, alg, mode);
    /* Update message length */
    HCU_SetMsgLength(msgLen, exMsgLen);
    /* Save data information */
    s_hcuStatePtr->mode = mode;
    s_hcuStatePtr->msgLen = msgLen;
    s_hcuStatePtr->inputCount = msgLen;
    s_hcuStatePtr->outputCount = msgLen;
    s_hcuStatePtr->dataInputPtr = dataIn;
    s_hcuStatePtr->dataOutputPtr = dataOut;
    s_hcuStatePtr->algorithm = (hcu_alg_mode_t)((alg + 1) << ((eng - 1) << 2));
#if FEATURE_HCU_SHA_ENGINE
    /* SHA output count is fixed */
    if (ENG_SHA == eng)
    {
        s_hcuStatePtr->outputCount = (HCU_SHA_256 == (hcu_sha_type_t)alg) ? HCU_SHA_256_LENGTH : HCU_SHA_384_LENGTH;
    }
#endif /* FEATURE_HCU_SHA_ENGINE */
    /* Config input and output FIFOs */
    HCU_SetFIFOWatermark(FEATURE_HCU_ONE_LOOP_INPUT_WATERMARK, FEATURE_HCU_ONE_LOOP_DATA_SIZE);
    return STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_CompleteDMA
 * Description   : This function is called back for DMA done
 *
 * Implements    : HCU_DRV_CompleteDMA_Activity
 * END**************************************************************************/
static void HCU_DRV_CompleteDMA(void *parameter, dma_chn_status_t status)
{
    DEV_ASSERT(s_hcuStatePtr != NULL);
    uint32_t channel = (uint32_t)parameter;
    if (status == DMA_CHN_ERROR)
    {
        HCU_SetInputDMA(false);
        HCU_SetInputDMA(false);
    }
    else
    {
        if (channel == s_hcuStatePtr->ingressDMAChannel)
        {
            s_hcuStatePtr->inputCount = 0;
#if FEATURE_HCU_HAS_FIXED_DMA
            HCU_SetInputDMA(false);
#endif
        }
        else
        {
            HCU_SetInputDMA(false);
            HCU_SetOutputDMA(false);
            s_hcuStatePtr->outputCount = 0;
            s_hcuStatePtr->cmdInProgress = false;
            if ((SHA_256_MODE != s_hcuStatePtr->algorithm) && (SHA_384_MODE != s_hcuStatePtr->algorithm))
            {
                HCU_ClearStatusFlag(OPERATION_DONE_FLAG);
            }
            /* AES-CCM should check status in decrypt and copy MAC in encrypt */
            if ((AES_CCM_MODE == s_hcuStatePtr->algorithm) && (s_hcuStatePtr->isLastBlock))
            {
                (void)HCU_DRV_DoneMAC();
            }
        }
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_ConfigDMA
 * Description   : This function config DMA for data carrying.
 *
 * Implements    : HCU_DRV_ConfigDMA_Activity
 * END**************************************************************************/
static status_t HCU_DRV_ConfigDMA(uint8_t ingressDMAChannel, uint8_t egressDMAChannel)
{
    DEV_ASSERT(s_hcuStatePtr != NULL);
    dma_transfer_size_t dmaTransferSize = DMA_TRANSFER_SIZE_4B;
    s_hcuStatePtr->ingressDMAChannel = ingressDMAChannel;
    s_hcuStatePtr->egressDMAChannel = egressDMAChannel;

    if (s_hcuStatePtr->dataOutputPtr != NULL)
    {
        /* DMA block size config by watermark, now is 4 words, 16 bytes */
        (void)DMA_DRV_ConfigMultiBlockTransfer(s_hcuStatePtr->egressDMAChannel,
                                               DMA_TRANSFER_PERIPH2MEM,
                                               (uint32_t)(&(HCU->OFDAT)),
                                               (uint32_t)s_hcuStatePtr->dataOutputPtr,
                                               dmaTransferSize,
                                               16U,
                                               (uint32_t)s_hcuStatePtr->outputCount / 16,
                                               true);

        /* Combine ingress to completed event */
        (void)DMA_DRV_InstallCallback(s_hcuStatePtr->egressDMAChannel,
                                      (HCU_DRV_CompleteDMA),
                                      (void *)(uint32_t)(s_hcuStatePtr->egressDMAChannel));
        (void)DMA_DRV_StartChannel(s_hcuStatePtr->egressDMAChannel);
        HCU_SetOutputDMA(true);
    }

    if (s_hcuStatePtr->dataInputPtr != NULL)
    {
#if FEATURE_HCU_HAS_FIXED_DMA
        {
            /* DMA block size config by watermark, now is 4 words, 16 bytes */
            (void)DMA_DRV_ConfigMultiBlockTransfer(s_hcuStatePtr->ingressDMAChannel,
                                                   DMA_TRANSFER_MEM2PERIPH,
                                                   (uint32_t)s_hcuStatePtr->dataInputPtr,
                                                   (uint32_t)(&(HCU->IFDAT)),
                                                   dmaTransferSize,
                                                   16U,
                                                   (uint32_t)s_hcuStatePtr->inputCount / 16,
                                                   true);
        }
#else
        {
            /* DMA block size config by watermark, now is 4 words, 16 bytes */
            (void)DMA_DRV_ConfigMultiBlockTransfer(s_hcuStatePtr->ingressDMAChannel,
                                                   DMA_TRANSFER_MEM2PERIPH,
                                                   (uint32_t)s_hcuStatePtr->dataInputPtr,
                                                   (uint32_t)(&(HCU->IFDAT)),
                                                   dmaTransferSize,
                                                   16U,
                                                   (uint32_t)s_hcuStatePtr->inputCount / 16,
                                                   false);
        }
        /* Control address for out of range */
        DMA_DRV_SetSrcLastAddrAdjustment(s_hcuStatePtr->ingressDMAChannel, -s_hcuStatePtr->msgLen);
#endif
        /* Combine ingress to completed event */
        (void)DMA_DRV_InstallCallback(s_hcuStatePtr->ingressDMAChannel,
                                      (HCU_DRV_CompleteDMA),
                                      (void *)(uint32_t)(s_hcuStatePtr->ingressDMAChannel));
        (void)DMA_DRV_StartChannel(s_hcuStatePtr->ingressDMAChannel);
        HCU_SetInputDMA(true);
    }

    return STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_DoneMAC
 * Description   : This function is called back when need MAC at last block.
 *
 * Implements    : HCU_DRV_DoneMAC_Activity
 * END**************************************************************************/
static status_t HCU_DRV_DoneMAC(void)
{
    DEV_ASSERT(s_hcuStatePtr != NULL);
    status_t status = STATUS_SUCCESS;
    if (MODE_ENC == s_hcuStatePtr->mode)
    {
        uint8_t i;
        uint8_t *macPtr = (uint8_t *)HCU->AESMAC;
        if (AES_CCM_MODE == s_hcuStatePtr->algorithm)
        {
            for (i = 0; (NULL != s_hcuStatePtr->ccmConfig) && (i < s_hcuStatePtr->ccmConfig->tagSize); i++)
            {
                s_hcuStatePtr->ccmConfig->tag[i] = macPtr[i];
            }
        }
        else if (AES_CMAC_MODE == s_hcuStatePtr->algorithm)
        {
            for (i = 0; (NULL != s_hcuStatePtr->cmacConfig) && (i < s_hcuStatePtr->cmacConfig->macLen); i++)
            {
                s_hcuStatePtr->cmacConfig->macPtr[i] = macPtr[i];
            }
        }
    }
    else
    {
        if (false == HCU_GetStatusFlag(AES_MAC_VALID_FLAG))
        {
            status = STATUS_HCU_MAC_CHECK_ERROR;
            s_hcuStatePtr->status = STATUS_HCU_MAC_CHECK_ERROR;
        }
        else
        {
            s_hcuStatePtr->status = STATUS_SUCCESS;
        }
    }
    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_EncryptECB
 * Description   : This function performs the AES-128 encryption in ECB mode of
 * the input plain text buffer.
 *
 * Implements    : HCU_DRV_EncryptECB_Activity
 * END**************************************************************************/
status_t HCU_DRV_EncryptECB(const void *plainText, uint16_t length, void *cipherText)
{
    status_t status;
    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(plainText, length, 0, cipherText, ENG_AES, ALG_AES_ECB, MODE_ENC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }

    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark, output fifo watermark and operate done */
        HCU_SetDefaultInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }

    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        s_hcuStatePtr->cmdInProgress = false;
    }

    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_DecryptECB
 * Description   : This function performs the AES-128 decryption in ECB mode of
 * the input cipher text buffer.
 *
 * Implements    : HCU_DRV_DecryptECB_Activity
 * END**************************************************************************/
status_t HCU_DRV_DecryptECB(const void *cipherText, uint32_t length, void *plainText)
{
    status_t status;
    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(cipherText, length, 0, plainText, ENG_AES, ALG_AES_ECB, MODE_DEC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }

    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark, output fifo watermark and operate done */
        HCU_SetDefaultInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }

    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        s_hcuStatePtr->cmdInProgress = false;
    }

    return status;
}

#if FEATURE_HCU_AES_CTR_ENGINE
/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_EncryptCTR
 * Description   : This function performs the AES-128 encryption in CTR mode of
 * the input cipher text buffer.
 *
 * Implements    : HCU_DRV_EncryptCTR_Activity
 * END**************************************************************************/
status_t HCU_DRV_EncryptCTR(const void *plainText, uint32_t length, const void *cv, void *cipherText)
{
    status_t status;
    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(plainText, length, 0, cipherText, ENG_AES, ALG_AES_CTR, MODE_ENC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    /* Configure the CV */
    if (NULL != cv)
    {
        /* Only update when the CV is not NULL */
        HCU_SetCV(cv);
    }

    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark, output fifo watermark and operate done */
        HCU_SetDefaultInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }

    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        s_hcuStatePtr->cmdInProgress = false;
    }

    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_DecryptCTR
 * Description   : This function performs the AES-128 decryption in CTR mode of
 * the input cipher text buffer.
 *
 * Implements    : HCU_DRV_DecryptCTR_Activity
 * END**************************************************************************/
status_t HCU_DRV_DecryptCTR(const void *cipherText, uint32_t length, const void *cv, void *plainText)
{
    status_t status;
    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(cipherText, length, 0, plainText, ENG_AES, ALG_AES_CTR, MODE_DEC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    /* Configure the CV */
    if (NULL != cv)
    {
        /* Only update when the CV is not NULL */
        HCU_SetCV(cv);
    }

    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark, output fifo watermark and operate done */
        HCU_SetDefaultInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }

    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        s_hcuStatePtr->cmdInProgress = false;
    }

    return status;
}
#endif /* FEATURE_HCU_AES_CTR_ENGINE */

#if FEATURE_HCU_SM4_ENGINE
/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_EncryptSM4ECB
 * Description   : This function performs the SM4-128 encryption in ECB mode of
 * the input plain text buffer.
 *
 * Implements    : HCU_DRV_EncryptSM4ECB_Activity
 * END**************************************************************************/
status_t HCU_DRV_EncryptSM4ECB(const void *plainText, uint16_t length, void *cipherText)
{
    /* Check the driver is initialized */
    DEV_ASSERT(s_hcuStatePtr != NULL);

    status_t status;
    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(plainText, length, 0, cipherText, ENG_SM4, ALG_AES_ECB, MODE_ENC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark, output fifo watermark and operate done */
        HCU_SetDefaultInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }
    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        s_hcuStatePtr->cmdInProgress = false;
    }

    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_DecryptSM4ECB
 * Description   : This function performs the SM4-128 decryption in ECB mode of
 * the input cipher text buffer.
 *
 * Implements    : HCU_DRV_DecryptSM4ECB_Activity
 * END**************************************************************************/
status_t HCU_DRV_DecryptSM4ECB(const void *cipherText, uint32_t length, void *plainText)
{
    status_t status;
    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(cipherText, length, 0, plainText, ENG_SM4, ALG_AES_ECB, MODE_DEC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark, output fifo watermark and operate done */
        HCU_SetDefaultInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }
    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        s_hcuStatePtr->cmdInProgress = false;
    }
    return status;
}
#endif /* FEATURE_HCU_SM4_ENGINE */

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_EncryptCBC
 * Description   : This function performs the AES-128 encryption in CBC mode of
 * the input plain text buffer.
 *
 * Implements    : HCU_DRV_EncryptCBC_Activity
 * END**************************************************************************/
status_t HCU_DRV_EncryptCBC(const void *plainText, uint32_t length, const void *iv, void *cipherText)
{
    status_t status;

    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(plainText, length, 0, cipherText, ENG_AES, ALG_AES_CBC, MODE_ENC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    /* Configure the IV */
    if (NULL != iv)
    {
        /* Only update when the IV is not NULL */
        HCU_SetIV(iv);
    }

    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark, output fifo watermark and operate done */
        HCU_SetDefaultInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }

    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        s_hcuStatePtr->cmdInProgress = false;
    }

    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_DecryptCBC
 * Description   : This function performs the AES-128 decryption in CBC mode of
 * the input cipher text buffer.
 *
 * Implements    : HCU_DRV_DecryptCBC_Activity
 * END**************************************************************************/
status_t HCU_DRV_DecryptCBC(const void *cipherText, uint32_t length, const void *iv, void *plainText)
{
    status_t status;

    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(cipherText, length, 0, plainText, ENG_AES, ALG_AES_CBC, MODE_DEC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    /* Configure the IV */
    if (NULL != iv)
    {
        /* Only update when the IV is not NULL */
        HCU_SetIV(iv);
    }

    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark, output fifo watermark and operate done */
        HCU_SetDefaultInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }

    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        s_hcuStatePtr->cmdInProgress = false;
    }

    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_GenerateMAC
 * Description   : This function calculates the MAC of a given message using CMAC
 * with AES-128.
 *
 * Implements    : HCU_DRV_GenerateMAC_Activity
 * END**************************************************************************/
status_t HCU_DRV_GenerateMAC(const void *msg, uint16_t msgLen, hcu_msg_type_t msgType, hcu_cmac_config_t *cmacConfig)
{
    status_t status;
    uint8_t *macPtr = (uint8_t *)HCU->AESMAC;
    uint8_t i;
    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(msg, msgLen, 0, NULL, ENG_AES, ALG_AES_CMAC, MODE_ENC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    /* Copy mac pointer and length */
    if (NULL != cmacConfig->macPtr)
    {
        s_hcuStatePtr->cmacConfig = cmacConfig;
    }
    /* Set isLastBlock at the end block */
    if (msgType & MSG_END)
    {
        s_hcuStatePtr->isLastBlock = true;
    }
    else
    {
        s_hcuStatePtr->isLastBlock = false;
    }

    /* Configure the MAC length */
    HCU_SetMacLength(cmacConfig->macLen);
    /* Configure the message type */
    HCU_SetMsgType(msgType);
    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark and operate done */
        HCU_SetDoneInterrupt(true);
        HCU_SetInputInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        /* Enable operate done */
        HCU_SetDoneInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }
    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        /* Copy CMAC result to output buffer */
        for (i = 0; (NULL != cmacConfig->macPtr) && (i < cmacConfig->macLen); ++i)
        {
            cmacConfig->macPtr[i] = macPtr[i];
        }
        /* Copy CMAC result at the end */
        if (msgType & MSG_END)
        {
            status = HCU_DRV_DoneMAC();
        }
        s_hcuStatePtr->cmdInProgress = false;
    }

    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_AuthorizeMAC
 * Description   : This function authorize the MAC of a given message using CMAC
 * with AES-128.
 *
 * Implements    : HCU_DRV_AuthorizeMAC_Activity
 * END**************************************************************************/
status_t HCU_DRV_AuthorizeMAC(const void *msg, uint16_t msgLen, hcu_msg_type_t msgType, hcu_cmac_config_t *cmacConfig)
{
    status_t status;
    uint8_t i;

    union
    {
        uint32_t words[4];
        uint8_t bytes[16];
    } tagBuffer;

    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(msg, msgLen, 0, NULL, ENG_AES, ALG_AES_CMAC, MODE_DEC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    if (NULL != cmacConfig->macPtr)
    {
        /* Copy mac pointer and length */
        s_hcuStatePtr->cmacConfig = cmacConfig;
        /* Set MAC value */
        for (i = 0; (NULL != cmacConfig->macPtr) && (i < cmacConfig->macLen); i++)
        {
            tagBuffer.bytes[i] = cmacConfig->macPtr[i];
        }
        HCU->AESMAC[0] = tagBuffer.words[0];
        HCU->AESMAC[1] = tagBuffer.words[1];
        HCU->AESMAC[2] = tagBuffer.words[2];
        HCU->AESMAC[3] = tagBuffer.words[3];
    }
    /* Set isLastBlock at the end block */
    if (msgType & MSG_END)
    {
        s_hcuStatePtr->isLastBlock = true;
    }
    else
    {
        s_hcuStatePtr->isLastBlock = false;
    }
    /* Configure the MAC length */
    HCU_SetMacLength(cmacConfig->macLen);
    /* Configure the message type */
    HCU_SetMsgType(msgType);
    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark and operate done */
        HCU_SetDoneInterrupt(true);
        HCU_SetInputInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        /* Enable operate done */
        HCU_SetDoneInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }
    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        /* Check CMAC result at the end */
        if (msgType & MSG_END)
        {
            status = HCU_DRV_DoneMAC();
        }
        s_hcuStatePtr->cmdInProgress = false;
    }

    return status;
}
#if FEATURE_HCU_SHA_ENGINE
/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_GenerateSHA
 * Description   : This function calculates the MAC of a given message using SHA
 *
 * Implements    : HCU_DRV_GenerateSHA_Activity
 * END**************************************************************************/
status_t HCU_DRV_GenerateSHA(const void *msg,
                             uint16_t msgLen,
                             uint32_t totalLen,
                             hcu_sha_type_t shaType,
                             hcu_msg_type_t msgType,
                             void *result)
{
    status_t status;
    if (!(msgType & MSG_END))
    {
        /* If divide in several blocks, each block must at least 64 bytes */
        if (HCU_SHA_256 == shaType)
        {
            if ((msgLen % HCU_SHA_256_BLOCK_SIZE != 0) || (msgLen == 0))
            {
                return STATUS_ERROR;
            }
        }
        /* If divide in several blocks, each block must at least 128 bytes */
        if (HCU_SHA_384 == shaType)
        {
            if ((msgLen % HCU_SHA_384_BLOCK_SIZE != 0) || (msgLen == 0))
            {
                return STATUS_ERROR;
            }
        }
    }
    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(msg, msgLen, msgLen, result, ENG_SHA, (hcu_alg_aes_mode_t)shaType, MODE_ENC);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    /* Configure the message type */
    HCU_SetMsgType(msgType);
    /* Configure the total length of message at the beginning and disable sha-verify*/
    if (msgType & MSG_START)
    {
        HCU_SetSHAVerification(false);
        HCU_SetMsgTotalLength(totalLen);
        /* When change sha-verify, OD will immediately set */
        HCU_ClearStatusFlag(OPERATION_DONE_FLAG);
    }
    /* Set if last block */
    s_hcuStatePtr->isLastBlock = (msgType & MSG_END) ? true : false;
    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark and operate done */
        HCU_SetInputInterrupt(true);
        HCU_SetDoneInterrupt(true);
        if (NULL != result)
        {
            HCU_SetOutputInterrupt(true);
        }
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
#if FEATURE_HCU_HAS_FIXED_DMA
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        /* Check by operate done flag */
        HCU_SetDoneInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }
#endif
    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        s_hcuStatePtr->cmdInProgress = false;
    }
    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_AuthorizeSHA
 * Description   : This function verify the MAC of a given message using SHA
 *
 * Implements    : HCU_DRV_AuthorizeSHA_Activity
 * END**************************************************************************/
#if FEATURE_HCU_HAS_SHA_AUTHORIZE
status_t HCU_DRV_AuthorizeSHA(const void *msg,
                              uint16_t msgLen,
                              uint32_t totalLen,
                              hcu_sha_type_t shaType,
                              hcu_msg_type_t msgType,
                              void *result,
                              void *trueResult)
{
    status_t status;
    if (!(msgType & MSG_END))
    {
        /* If divide in several blocks, each block must at least 64 bytes */
        if (HCU_SHA_256 == shaType)
        {
            if ((msgLen % HCU_SHA_256_BLOCK_SIZE != 0) || (msgLen == 0))
            {
                return STATUS_ERROR;
            }
        }
        /* If divide in several blocks, each block must at least 128 bytes */
        if (HCU_SHA_384 == shaType)
        {
            if ((msgLen % HCU_SHA_384_BLOCK_SIZE != 0) || (msgLen == 0))
            {
                return STATUS_ERROR;
            }
        }
    }
    /* SHA authorize need reset HCU at first */
    if (msgType & MSG_START)
    {
        /* Write hash value to SHAICV, load at first */
        if (HCU_SHA_256 == shaType)
        {
            HCU_SetICV(result, HCU_SHA_256_LENGTH >> 2);
        }
        else
        {
            HCU_SetICV(result, HCU_SHA_384_LENGTH >> 2);
        }
    }
    /* Configure the algorithm */
    status = HCU_ConfigAlgorithm(msg, msgLen, 0, trueResult, ENG_SHA, (hcu_alg_aes_mode_t)shaType, MODE_ENC);
    s_hcuStatePtr->mode = MODE_DEC;
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    /* Configure the message type */
    HCU_SetMsgType(msgType);
    /* Configure the total length of message at the beginning*/
    if (msgType & MSG_START)
    {
        HCU_SetMsgTotalLength(totalLen);
    }
    /* Start SHA at ending block */
    if (msgType & MSG_END)
    {
        HCU_SetSHAVerification(true);
    }
    /* Set if last block */
    s_hcuStatePtr->isLastBlock = (msgType & MSG_END) ? true : false;
    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark and operate done */
        HCU_SetInputInterrupt(true);
        HCU_SetDoneInterrupt(true);
        if (NULL != trueResult)
        {
            HCU_SetOutputInterrupt(true);
        }
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
#if FEATURE_HCU_HAS_FIXED_DMA
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        /* Check by operate done flag */
        HCU_SetDoneInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }
#endif
    /* Start the command */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        if (true == HCU_GetStatusFlag(SHA_HASH_INVALID_FLAG))
        {
            status = STATUS_HCU_HASH_CHECK_ERROR;
            s_hcuStatePtr->status = STATUS_HCU_HASH_CHECK_ERROR;
        }
        else
        {
            s_hcuStatePtr->status = STATUS_SUCCESS;
        }
        s_hcuStatePtr->cmdInProgress = false;
    }
    return status;
}
#endif /* FEATURE_HCU_HAS_SHA_AUTHORIZE */
#endif /* FEATURE_HCU_SHA_ENGINE */

#if FEATURE_HCU_HMAC_ENGINE
/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_GenerateHMAC
 * Description   : This function calculates the MAC of a given message using HMAC
 *
 * Implements    : HCU_DRV_GenerateHMAC_Activity
 * END**************************************************************************/
status_t HCU_DRV_GenerateHMAC(const void *msg,
                              uint16_t msgLen,
                              uint32_t totalLen,
                              hcu_sha_type_t shaType,
                              hcu_msg_type_t msgType,
                              void *result)
{
    return HCU_DRV_GenerateSHA(msg, msgLen, totalLen, shaType, msgType, result);
}

#if FEATURE_HCU_HAS_SHA_AUTHORIZE
/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_AuthorizeHMAC
 * Description   : This function verify the MAC of a given message using HMAC
 *
 * Implements    : HCU_DRV_AuthorizeHMAC_Activity
 * END**************************************************************************/
status_t HCU_DRV_AuthorizeHMAC(const void *msg,
                               uint16_t msgLen,
                               uint32_t totalLen,
                               hcu_sha_type_t shaType,
                               hcu_msg_type_t msgType,
                               void *result,
                               void *trueResult)
{
    return HCU_DRV_AuthorizeSHA(msg, msgLen, totalLen, shaType, msgType, result, trueResult);
}
#endif /* FEATURE_HCU_HAS_SHA_AUTHORIZE */
#endif /* FEATURE_HCU_HMAC_ENGINE */

#if FEATURE_HCU_AES_CCM_ENGINE
static uint8_t HCU_AESGetL(uint64_t PlainLen, uint8_t NonceLen)
{
    uint8_t L;
    L = 0;
    while (PlainLen)
    {
        PlainLen >>= 8;
        ++L;
    }
    L = (L < 2) ? 2 : L;
    /* Increase L to match the nonce len */
    NonceLen = (NonceLen > 13) ? 13 : NonceLen;
    L = ((15 - NonceLen) > L) ? (15 - NonceLen) : L;
    return L;
}

static status_t HCU_PushAdditionData(const uint8_t *Nonce,
                                     uint8_t NonceLen,
                                     uint8_t *AddData,
                                     uint32_t AddDataLen,
                                     uint64_t PlainLen,
                                     uint8_t MacLen)
{
    union
    {
        uint32_t words[4];
        uint8_t bytes[16];
    } B;

    uint8_t L;
    uint32_t i;
    uint16_t BLen;
    uint8_t x;
    /* initialize B */
    for (i = 0; i < 4; i++)
    {
        B.words[i] = 0;
    }
    /* Calculate Add data length */
    BLen = (NonceLen / 16 + 1) * 16;
    if (AddDataLen > 0)
    {
        if (AddDataLen < ((1UL << 16) - (1UL << 8)))
        {
            if ((AddDataLen + 2) % 16 == 0)
                BLen += (AddDataLen + 2);
            else
                BLen += ((AddDataLen + 2) / 16 + 1) * 16;
        }
        else
        {
            if ((AddDataLen + 6) % 16 == 0)
                BLen += (AddDataLen + 6);
            else
                BLen += ((AddDataLen + 6) / 16 + 1) * 16;
        }
    }
    HCU_SetMsgType(MSG_START);
    /* Set the MAC length */
    HCU_SetMacLength(MacLen);
    /* Set message length */
    HCU_SetMsgLength(BLen, BLen);
    /*B0*/
    L = HCU_AESGetL(PlainLen, NonceLen);
    if (L + NonceLen != 15)
        return STATUS_HCU_CCM_NONCE_DATA_SIZE_ERROR;
    /* Update Data size configuration */
    /* Count value start at 1 */
    B.bytes[0] = L - 1;
    /*B1-B15: Nonce & Q*/
    for (i = 0; i < NonceLen; i++)
    {
        B.bytes[i + 1] = Nonce[i];
    }
    /* Update Nonce */
    HCU_SetCV(B.words);
    /* Start engine */
    HCU_StartEngine();
    /* Update additional data flag */
    B.bytes[0] = AddDataLen ? 0x40 : 0x00;
    /* Update Mac length */
    if (MacLen == 0)
    {
        B.bytes[0] |= (0 << 3) | (L - 1);
    }
    else
    {
        B.bytes[0] |= (((MacLen - 2) / 2) << 3) | (L - 1);
    }
    // L = (L > 4) ? 4 : L;
    for (i = 0; i < L; i++)
    {
        B.bytes[15 - i] = PlainLen & 0xff;
        PlainLen >>= 8;
    }
    /* Push data to FIFO */
    HCU_WriteInputFifo((uint32_t *)B.words, 4);
    /* reinitialize B */
    for (i = 0; i < 4; i++)
    {
        B.words[i] = 0;
    }
    /*aad length and data*/
    if (AddDataLen > 0)
    {
        /* store length */
        if (AddDataLen < ((1UL << 16) - (1UL << 8)))
        {
            B.bytes[0] = (AddDataLen >> 8) & 255;
            B.bytes[1] = AddDataLen & 255;
            x = 2;
        }
        else
        {
            B.bytes[0] = 0xFF;
            B.bytes[1] = 0xFE;
            B.bytes[2] = (AddDataLen >> 24) & 255;
            B.bytes[3] = (AddDataLen >> 16) & 255;
            B.bytes[4] = (AddDataLen >> 8) & 255;
            B.bytes[5] = AddDataLen & 255;
            x = 6;
        }
        /* x bytes length of addData_ori */
        for (i = x; i < AddDataLen + x; i++)
        {
            B.bytes[i & 0xF] = *AddData++;
            /* Check if reaches one block */
            if ((i & 0xF) == 0xF)
            {
                /* Push data to FIFO */
                while (false == HCU_IsInputFifoEmpty())
                {
                    /* Wait for input fifo */
                }
                /* Push data to FIFO */
                HCU_WriteInputFifo((uint32_t *)B.words, 4);
                /* reinitialize B */
                B.words[0] = 0;
                B.words[1] = 0;
                B.words[2] = 0;
                B.words[3] = 0;
            }
        }
        /* Check if we need to push the last block */
        if ((i & 0xF) != 0)
        {
            /* Push data to FIFO */
            while (false == HCU_IsInputFifoEmpty())
            {
                /* Wait for input fifo */
            }
            /* Push data to FIFO */
            HCU_WriteInputFifo((uint32_t *)B.words, 4);
        }
    }
    /* Wait for engine to finish */
    while (HCU_IsBusy())
    {
        /* Wait for engine to finish */
    }
    HCU_ClearStatusFlag(OPERATION_DONE_FLAG);
    return STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_CCMConfig
 * Description   : This function prepares the AES-128 CCM configuration.
 * with AES-128.
 *
 * Implements    : HCU_DRV_CCMConfig_Activity
 * END**************************************************************************/
status_t HCU_DRV_CCMConfig(hcu_ccm_config_t *ccm, hcu_mode_sel_t mode)
{
    status_t status;
    uint8_t i;

    union
    {
        uint32_t words[4];
        uint8_t bytes[16];
    } tagBuffer;

    /* Check the input parameters */
    if (ccm->tagSize > 16 || (ccm->tagSize & 0x01))
    {
        return STATUS_HCU_CCM_TAG_SIZE_ERROR;
    }
    /* Update the internal flags */
    s_hcuStatePtr->cmdInProgress = true;
    /* Update engine and algorithm settings */
    HCU_SetEngineAlgorithm(ENG_AES, ALG_AES_CCM, mode);
    /* Set total message length */
    HCU_SetMsgTotalLength(ccm->msgLen);
    /* Reset fifo before set watermark */
    HCU_ResetFifo();
    /* Config input and output FIFOs */
    HCU_SetFIFOWatermark(FEATURE_HCU_ONE_LOOP_INPUT_WATERMARK, FEATURE_HCU_ONE_LOOP_DATA_SIZE);
    /* Update tag information if mode is decrypt */
    if (MODE_DEC == mode)
    {
        for (i = 0; i < ccm->tagSize; i++)
        {
            tagBuffer.bytes[i] = ccm->tag[i];
        }
        HCU->AESMAC[0] = tagBuffer.words[0];
        HCU->AESMAC[1] = tagBuffer.words[1];
        HCU->AESMAC[2] = tagBuffer.words[2];
        HCU->AESMAC[3] = tagBuffer.words[3];
    }
    status =
        HCU_PushAdditionData(ccm->nonce, ccm->nonceSize, ccm->addData, ccm->addDataSize, ccm->msgLen, ccm->tagSize);
    if (STATUS_SUCCESS != status)
    {
        return status;
    }
    /* Update ccm configuration */
    s_hcuStatePtr->ccmConfig = ccm;
    return STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_EncryptCCM
 * Description   : This function performs the AES-128 encryption in CCM mode of
 * the input cipher text buffer.
 *
 * Implements    : HCU_DRV_EncryptCCM_Activity
 * END**************************************************************************/
status_t HCU_DRV_EncryptCCM(const void *plainText, uint16_t length, void *cipherText, bool isLast)
{
    status_t status = STATUS_ERROR;
    /* Check the buffer addresses are valid */
    DEV_ASSERT(plainText != NULL);
    DEV_ASSERT(cipherText != NULL);
    /* Check the buffers addresses are 32 bit aligned */
    DEV_ASSERT((((uint32_t)plainText) & HCU_BUFF_ADDR_CHECK_MASK) == 0U);
    DEV_ASSERT((((uint32_t)cipherText) & HCU_BUFF_ADDR_CHECK_MASK) == 0U);
    /* Check the buffer length is multiple of 16 bytes */
    DEV_ASSERT((length & HCU_BUFF_LEN_CHECK_MASK) == 0U);

    /* Check there is no other command in execution */
    if (HCU_IsBusy())
    {
        return STATUS_BUSY;
    }
    if (s_hcuStatePtr->ccmConfig == NULL)
    {
        return STATUS_HCU_CCM_NOT_CONFIGURED_ERROR;
    }

    /* Update the internal flags */
    s_hcuStatePtr->cmdInProgress = true;
    /* Update message length */
    HCU_SetMsgLength(length, 0);
    /* Save data information */
    s_hcuStatePtr->mode = MODE_ENC;
    s_hcuStatePtr->msgLen = length;
    s_hcuStatePtr->inputCount = length;
    s_hcuStatePtr->outputCount = length;
    s_hcuStatePtr->dataInputPtr = plainText;
    s_hcuStatePtr->dataOutputPtr = cipherText;
    s_hcuStatePtr->algorithm = AES_CCM_MODE;
    /* Reset fifo before set watermark */
    HCU_ResetFifo();
    /* Config input and output FIFOs */
    HCU_SetFIFOWatermark(FEATURE_HCU_ONE_LOOP_INPUT_WATERMARK, FEATURE_HCU_ONE_LOOP_DATA_SIZE);
    /* Configure the message type */
    if (isLast)
    {
        HCU_SetMsgType(MSG_END);
        s_hcuStatePtr->isLastBlock = true;
    }
    else
    {
        HCU_SetMsgType(MSG_MIDDLE);
        s_hcuStatePtr->isLastBlock = false;
    }

    /* Check carry type */
    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark, output fifo watermark and operate done */
        HCU_SetDefaultInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }

    /* Start the engine */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        /* Start to run loops */
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        /* Update the internal flags */
        s_hcuStatePtr->cmdInProgress = false;
        if (s_hcuStatePtr->isLastBlock)
        {
            status = HCU_DRV_DoneMAC();
        }
    }

    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_DecryptCCM
 * Description   : This function performs the AES-128 decryption in CCM mode of
 * the input cipher text buffer.
 *
 * Implements    : HCU_DRV_DecryptCCM_Activity
 * END**************************************************************************/
status_t HCU_DRV_DecryptCCM(const void *cipherText, uint16_t length, void *plainText, bool isLast)
{
    status_t status = STATUS_ERROR;
    /* Check the buffer addresses are valid */
    DEV_ASSERT(plainText != NULL);
    DEV_ASSERT(cipherText != NULL);
    /* Check the buffers addresses are 32 bit aligned */
    DEV_ASSERT((((uint32_t)plainText) & HCU_BUFF_ADDR_CHECK_MASK) == 0U);
    DEV_ASSERT((((uint32_t)cipherText) & HCU_BUFF_ADDR_CHECK_MASK) == 0U);
    /* Check the buffer length is multiple of 16 bytes */
    DEV_ASSERT((length & HCU_BUFF_LEN_CHECK_MASK) == 0U);

    /* Check there is no other command in execution */
    if (HCU_IsBusy())
    {
        return STATUS_BUSY;
    }
    if (s_hcuStatePtr->ccmConfig == NULL)
    {
        return STATUS_HCU_CCM_NOT_CONFIGURED_ERROR;
    }

    /* Update the internal flags */
    s_hcuStatePtr->cmdInProgress = true;
    /* Update message length */
    HCU_SetMsgLength(length, 0);
    /* Save data information */
    s_hcuStatePtr->mode = MODE_DEC;
    s_hcuStatePtr->msgLen = length;
    s_hcuStatePtr->inputCount = length;
    s_hcuStatePtr->outputCount = length;
    s_hcuStatePtr->dataInputPtr = cipherText;
    s_hcuStatePtr->dataOutputPtr = plainText;
    s_hcuStatePtr->algorithm = AES_CCM_MODE;
    /* Reset fifo before set watermark */
    HCU_ResetFifo();
    /* Config input and output FIFOs */
    HCU_SetFIFOWatermark(FEATURE_HCU_ONE_LOOP_INPUT_WATERMARK, FEATURE_HCU_ONE_LOOP_DATA_SIZE);
    /* Configure the message type */
    if (isLast)
    {
        HCU_SetMsgType(MSG_END);
        s_hcuStatePtr->isLastBlock = true;
    }
    else
    {
        HCU_SetMsgType(MSG_MIDDLE);
        s_hcuStatePtr->isLastBlock = false;
    }

    /* Check carry type */
    if (HCU_USING_INTERRUPT == s_hcuStatePtr->carryType)
    {
        /* Enable input fifo watermark, output fifo watermark and operate done */
        HCU_SetDefaultInterrupt(true);
        INT_SYS_EnableIRQ(HCU_IRQ_NUMBER);
    }
    else if (HCU_USING_DMA == s_hcuStatePtr->carryType)
    {
        HCU_DRV_ConfigDMA(s_hcuStatePtr->ingressDMAChannel, s_hcuStatePtr->egressDMAChannel);
    }

    /* Start the engine */
    HCU_StartEngine();
    /* Polling is blocking */
    if (HCU_USING_POLLING == s_hcuStatePtr->carryType)
    {
        /* Start to run loops */
        do
        {
            status = HCU_RunOneLoop();
        } while (STATUS_BUSY == status);
        /* Update the internal flags */
        s_hcuStatePtr->cmdInProgress = false;
        if (s_hcuStatePtr->isLastBlock)
        {
            status = HCU_DRV_DoneMAC();
        }
    }

    return status;
}
#endif /* FEATURE_HCU_AES_CCM_ENGINE */

static status_t HCU_RunOneLoop(void)
{
    /* Check if hcu need to read data */
    while (HCU_IsOutputFifoFull() && (NULL != s_hcuStatePtr->dataOutputPtr))
    {
        HCU_ReadOutputFifo((uint32_t *)s_hcuStatePtr->dataOutputPtr, FEATURE_HCU_ONE_LOOP_DATA_SIZE);
        s_hcuStatePtr->dataOutputPtr += FEATURE_HCU_ONE_LOOP_DATA_SIZE;
        s_hcuStatePtr->outputCount -= FEATURE_HCU_ONE_LOOP_DATA_SIZE << 2;
    }
    /* Check if Operation is already done */
    if (HCU_IsDone())
    {
        while (HCU_IsOutputFifoFull() && (NULL != s_hcuStatePtr->dataOutputPtr))
        {
            HCU_ReadOutputFifo((uint32_t *)s_hcuStatePtr->dataOutputPtr, FEATURE_HCU_ONE_LOOP_DATA_SIZE);
            s_hcuStatePtr->dataOutputPtr += FEATURE_HCU_ONE_LOOP_DATA_SIZE;
            s_hcuStatePtr->outputCount -= FEATURE_HCU_ONE_LOOP_DATA_SIZE << 2;
        }
        HCU_ClearStatusFlag(OPERATION_DONE_FLAG);
        /* No more data to process */
        return STATUS_SUCCESS;
    }
    /* Check if hcu need to write data */
    if (HCU_IsInputFifoEmpty() && (s_hcuStatePtr->inputCount != 0U))
    {
        HCU_WriteInputFifo((uint32_t *)s_hcuStatePtr->dataInputPtr, FEATURE_HCU_ONE_LOOP_DATA_SIZE);
        s_hcuStatePtr->dataInputPtr += FEATURE_HCU_ONE_LOOP_DATA_SIZE;
        /* Convert the message length to be processed */
        s_hcuStatePtr->inputCount -= FEATURE_HCU_ONE_LOOP_DATA_SIZE << 2;
    }
    return STATUS_BUSY;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : HCU_DRV_IRQHandler
 * Description   : Implementation of the HCU interrupt handler. Handles completed
 * command events.
 *
 * END**************************************************************************/
void HCU_DRV_IRQHandler(void)
{
    /* Input fifo watermark handler */
    if ((HCU_GetStatusFlag(INPUT_FIFO_WATERMARK_FLAG)) && (HCU_GetIntMode(INPUT_FIFO_WATERMARK_FLAG)))
    {
        if (s_hcuStatePtr->inputCount != 0U)
        {
            HCU_WriteInputFifo((uint32_t *)s_hcuStatePtr->dataInputPtr, FEATURE_HCU_ONE_LOOP_DATA_SIZE);
            s_hcuStatePtr->dataInputPtr += FEATURE_HCU_ONE_LOOP_DATA_SIZE;
            /* Convert the message length to be processed */
            s_hcuStatePtr->inputCount -= FEATURE_HCU_ONE_LOOP_DATA_SIZE << 2;
        }
        if (s_hcuStatePtr->inputCount == 0U)
        {
            /* When HCU busy, INTE can not be changed */
            HCU_SetIntMode(INPUT_FIFO_WATERMARK_FLAG, false);
#if (FEATURE_HCU_HAS_FIXED_DMA < 1)
            /* Write empty data to avoid reentry interrupt */
            HCU_WriteInputFifoPatch(FEATURE_HCU_ONE_LOOP_DATA_SIZE);
#endif
        }
    }

    /* Output fifo watermark handler */
    if ((HCU_GetStatusFlag(OUTPUT_FIFO_WATERMARK_FLAG)) && (HCU_GetIntMode(OUTPUT_FIFO_WATERMARK_FLAG)))
    {
        if ((s_hcuStatePtr->outputCount != 0U) && (NULL != s_hcuStatePtr->dataOutputPtr))
        {
            HCU_ReadOutputFifo((uint32_t *)s_hcuStatePtr->dataOutputPtr, FEATURE_HCU_ONE_LOOP_DATA_SIZE);
            s_hcuStatePtr->dataOutputPtr += FEATURE_HCU_ONE_LOOP_DATA_SIZE;
            /* Convert the message length to be processed */
            s_hcuStatePtr->outputCount -= FEATURE_HCU_ONE_LOOP_DATA_SIZE << 2;
        }
        if (s_hcuStatePtr->outputCount == 0U)
        {
            s_hcuStatePtr->cmdInProgress = false;
            HCU_SetIntMode(OUTPUT_FIFO_WATERMARK_FLAG, false);
            if (s_hcuStatePtr->callback != NULL)
            {
                s_hcuStatePtr->callback(STATUS_SUCCESS, s_hcuStatePtr->callbackParam);
            }
        }
    }

    /* Operate done handler */
    if ((HCU_GetStatusFlag(OPERATION_DONE_FLAG)) && (HCU_GetIntMode(OPERATION_DONE_FLAG)))
    {
        /* Clear operate done flag */
        HCU_ClearStatusFlag(OPERATION_DONE_FLAG);
        HCU_SetIntMode(OPERATION_DONE_FLAG, false);
        /* Disable input watermark interrupt */
        HCU_SetIntMode(INPUT_FIFO_WATERMARK_FLAG, false);
        /* CMAC has no output to indicate ending */
        if (AES_CMAC_MODE == s_hcuStatePtr->algorithm)
        {
            HCU_SetInputDMA(false);
            if (s_hcuStatePtr->isLastBlock)
            {
                (void)HCU_DRV_DoneMAC();
                if (s_hcuStatePtr->callback != NULL)
                {
                    s_hcuStatePtr->callback(s_hcuStatePtr->status, s_hcuStatePtr->callbackParam);
                }
            }
            s_hcuStatePtr->cmdInProgress = false;
        }
        /* AES-CCM should check status in decrypt and copy MAC in encrypt */
        else if (AES_CCM_MODE == s_hcuStatePtr->algorithm)
        {
            if (s_hcuStatePtr->isLastBlock)
            {
                (void)HCU_DRV_DoneMAC();
            }
        }
        /* SHA may have no output to indicate ending */
        else if ((SHA_256_MODE == s_hcuStatePtr->algorithm) || (SHA_384_MODE == s_hcuStatePtr->algorithm))
        {
            HCU_SetInputDMA(false);
            if (s_hcuStatePtr->isLastBlock)
            {
                /* When in authorize mode, check hash-valid */
                if (MODE_DEC == s_hcuStatePtr->mode)
                {
                    if (true == HCU_GetStatusFlag(SHA_HASH_INVALID_FLAG))
                    {
                        s_hcuStatePtr->status = STATUS_HCU_HASH_CHECK_ERROR;
                    }
                    else
                    {
                        s_hcuStatePtr->status = STATUS_SUCCESS;
                    }
                }
                if (s_hcuStatePtr->callback != NULL)
                {
                    s_hcuStatePtr->callback(s_hcuStatePtr->status, s_hcuStatePtr->callbackParam);
                }
            }
            /* Only output enable, wait until data carry done */
            if (NULL == s_hcuStatePtr->dataOutputPtr)
            {
                s_hcuStatePtr->cmdInProgress = false;
            }
        }
    }
}

/******************************************************************************
 * EOF
 *****************************************************************************/
