/**
 *******************************************************************************
 * @file  hc32_ll_aes.c
 * @brief This file provides firmware functions to manage the Advanced Encryption
 *        Standard(AES).
 @verbatim
   Change Logs:
   Date             Author          Notes
   2022-03-31       CDT             First version
   2023-06-30       CDT             Add API AES_DeInit()
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2022-2023, Xiaohua Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by XHSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 *
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "hc32_ll_aes.h"
#include "hc32_ll_utility.h"

/**
 * @addtogroup LL_Driver
 * @{
 */

/**
 * @defgroup LL_AES AES
 * @brief AES Driver Library
 * @{
 */

#if (LL_AES_ENABLE == DDL_ON)

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
/**
 * @defgroup AES_Local_Macros AES Local Macros
 * @{
 */
/* Delay count for timeout */
#define AES_TIMEOUT                     (30000UL)

/* AES block size */
#define AES_BLOCK_SIZE                  (16U)

/**
 * @defgroup AES_Check_Parameters_Validity AES Check Parameters Validity
 * @{
 */
#define IS_AES_KEY_SIZE(x)              ((x) == AES_KEY_SIZE_16BYTE)

/**
 * @}
 */
/**
 * @}
 */

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @defgroup AES_Local_Functions AES Local Functions
 * @{
 */
/**
 * @brief  Write the input buffer in data register.
 * @param  [in] pu8SrcData              Point to the source data buffer.
 * @retval None
 */
static void AES_WriteData(const uint8_t *pu8SrcData)
{
    uint8_t i;
    __IO uint32_t *regDR = &CM_AES->DR0;
    const uint32_t *pu32Data = (const uint32_t *)((uint32_t)pu8SrcData);

    for (i = 0U; i < 4U; i++) {
        regDR[i] = pu32Data[i];
    }
}

/**
 * @brief  Read the from data register.
 * @param  [out] pu8Result              Point to the result buffer.
 * @retval None
 */
static void AES_ReadData(uint8_t *pu8Result)
{
    uint8_t i;
    __IO uint32_t *regDR = &CM_AES->DR0;
    uint32_t *pu32Result = (uint32_t *)((uint32_t)pu8Result);

    for (i = 0U; i < 4U; i++) {
        pu32Result[i] = regDR[i];
    }
}

/**
 * @brief  Write the input buffer in key register.
 * @param  [in]  pu8Key                 Pointer to the key buffer.
 * @param  [in]  u8KeySize              AES key size. This parameter can be a value of @ref AES_Key_Size
 * @retval None
 */
static void AES_WriteKey(const uint8_t *pu8Key, uint8_t u8KeySize)
{
    uint8_t i;
    uint8_t u8KeyWordSize = u8KeySize / 4U;
    __IO uint32_t *regKR = &CM_AES->KR0;
    const uint32_t *pu32Key = (const uint32_t *)((uint32_t)pu8Key);

    for (i = 0U; i < u8KeyWordSize; i++) {
        regKR[i] = pu32Key[i];
    }
}

/**
 * @brief  Wait AES operation done.
 * @param  None
 * @retval None
 */
static int32_t AES_WaitDone(void)
{
    __IO uint32_t u32TimeCount = 0UL;
    int32_t i32Ret = LL_OK;

    while (bCM_AES->CR_b.START != 0UL) {
        if (u32TimeCount++ >= AES_TIMEOUT) {
            i32Ret = LL_ERR_TIMEOUT;
            break;
        }
    }

    return i32Ret;
}
/**
 * @}
 */

/**
 * @defgroup AES_Global_Functions AES Global Functions
 * @{
 */

/**
 * @brief  AES encryption.
 * @param  [in]  pu8Plaintext           Buffer of the plaintext(the source data which will be encrypted).
 * @param  [in]  u32PlaintextSize       Length of plaintext in bytes.
 * @param  [in]  pu8Key                 Pointer to the AES key.
 * @param  [in]  u8KeySize              AES key size. This parameter can be a value of @ref AES_Key_Size
 * @param  [out] pu8Ciphertext          Buffer of the ciphertext.
 * @retval int32_t:
 *           - LL_OK:                   Encrypt successfully.
 *           - LL_ERR_INVD_PARAM:       Invalid parameter.
 *           - LL_TIMEOUT:              Encrypt timeout.
 */
int32_t AES_Encrypt(const uint8_t *pu8Plaintext, uint32_t u32PlaintextSize,
                    const uint8_t *pu8Key, uint8_t u8KeySize,
                    uint8_t *pu8Ciphertext)
{
    int32_t i32Ret = LL_ERR_INVD_PARAM;
    uint32_t u32Index = 0UL;

    DDL_ASSERT(IS_AES_KEY_SIZE(u8KeySize));
    DDL_ASSERT((u32PlaintextSize % AES_BLOCK_SIZE) == 0U);

    if ((pu8Plaintext != NULL) && (u32PlaintextSize > 0UL) && \
        (pu8Key != NULL) && (pu8Ciphertext != NULL)) {
        AES_WriteKey(pu8Key, u8KeySize);
        /* Set AES encrypt. */
        WRITE_REG32(bCM_AES->CR_b.MODE, 0UL);
        while (u32Index < u32PlaintextSize) {
            AES_WriteData(&pu8Plaintext[u32Index]);
            /* Start AES calculating. */
            WRITE_REG32(bCM_AES->CR_b.START, 1UL);
            /* Wait for AES to stop */
            i32Ret = AES_WaitDone();
            if (i32Ret != LL_OK) {
                break;
            }
            AES_ReadData(&pu8Ciphertext[u32Index]);
            u32Index += AES_BLOCK_SIZE;
        }
    }

    return i32Ret;
}

/**
 * @brief  AES decryption.
 * @param  [in]  pu8Ciphertext          Buffer of the Ciphertext(the source data which will be decrypted).
 * @param  [in]  u32CiphertextSize      Length of ciphertext in bytes.
 * @param  [in]  pu8Key                 Pointer to the AES key.
 * @param  [in]  u8KeySize              AES key size. This parameter can be a value of @ref AES_Key_Size
 * @param  [out] pu8Plaintext           Buffer of the plaintext.
 * @retval int32_t:
 *           - LL_OK:                   Decrypt successfully.
 *           - LL_ERR_INVD_PARAM:       Invalid parameter.
 *           - LL_TIMEOUT:              Decrypt timeout.
 */
int32_t AES_Decrypt(const uint8_t *pu8Ciphertext, uint32_t u32CiphertextSize,
                    const uint8_t *pu8Key, uint8_t u8KeySize,
                    uint8_t *pu8Plaintext)
{
    int32_t i32Ret = LL_ERR_INVD_PARAM;
    uint32_t u32Index = 0UL;

    DDL_ASSERT(IS_AES_KEY_SIZE(u8KeySize));
    DDL_ASSERT((u32CiphertextSize % AES_BLOCK_SIZE) == 0U);

    if ((pu8Plaintext != NULL) && (u32CiphertextSize > 0UL) && \
        (pu8Key != NULL) && (pu8Ciphertext != NULL)) {
        AES_WriteKey(pu8Key, u8KeySize);
        /* Set AES decrypt. */
        WRITE_REG32(bCM_AES->CR_b.MODE, 1UL);
        while (u32Index < u32CiphertextSize) {
            AES_WriteData(&pu8Ciphertext[u32Index]);
            /* Start AES calculating. */
            WRITE_REG32(bCM_AES->CR_b.START, 1UL);
            /* Wait for AES to stop */
            i32Ret = AES_WaitDone();
            if (i32Ret != LL_OK) {
                break;
            }
            AES_ReadData(&pu8Plaintext[u32Index]);
            u32Index += AES_BLOCK_SIZE;
        }
    }

    return i32Ret;
}

/**
 * @brief  De-Initialize AES function.
 * @param  None
 * @retval int32_t:
 *           - LL_OK:                   De-Initialize success.
 *           - LL_ERR_TIMEOUT:          Timeout.
 */
int32_t AES_DeInit(void)
{
    int32_t i32Ret = LL_OK;
    __IO uint32_t u32TimeOut = 0U;
    uint8_t i;
    __IO uint32_t *regDR = &CM_AES->DR0;
    __IO uint32_t *regKR = &CM_AES->KR0;
    /* Wait generating done */
    while (0UL != READ_REG32(bCM_AES->CR_b.START)) {
        u32TimeOut++;
        if (u32TimeOut > AES_TIMEOUT) {
            i32Ret = LL_ERR_TIMEOUT;
            break;
        }
    }
    if (LL_OK == i32Ret) {
        /* Configures the registers to reset value. */
        WRITE_REG32(CM_AES->CR, 0x00000000UL);
        for (i = 0U; i < 4U; i++) {
            regDR[i] = 0x00000000UL;
        }
        for (i = 0U; i < 8U; i++) {
            regKR[i] = 0x00000000UL;
        }
    }
    return i32Ret;
}
/**
 * @}
 */

#endif /* LL_AES_ENABLE */

/**
 * @}
 */

/**
 * @}
 */
/*******************************************************************************
 * EOF (not truncated)
 ******************************************************************************/

