/**
  *********************************************************************************
  *
  * @file    hal_crypt.c
  * @brief   CRYPT module driver.
  *	     This is the common part of the CRYPT initialization
  *
  * @version V1.0
  * @date    7 Dec 2017
  * @author  AE Team
  * @note
  *
  * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
  *
  *********************************************************************************
  */


#include "hal_crypt.h"


/** @addtogroup ES32FXXX_HAL
  * @{
  */

/** @defgroup CRYPT CRYPT
  * @brief CRYPT module driver
  * @{
  */
#ifdef HAL_CRYPT

/** @addtogroup CRYPT_Private_Functions CRYPT Private Functions
  * @{
  */
void crypt_reset(crypt_handle_t *hperh);
#ifdef HAL_DMA
static void crypt_dma_crypt_cplt(void *arg);
static void crypt_dma_error(void *arg);
#endif
/**
  * @}
  */


/** @defgroup CRYPT_Public_Functions CRYPT Public Functions
  * @{
  */

/** @defgroup CRYPT_Public_Functions_Group1 Initialization functions
  * @brief Initialization and Configuration functions
  * @{
  */

/**
  * @brief  Initializes the CRYPT mode according to the specified parameters in
  *         the crypt_init_t and create the associated handle.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t crypt_init(crypt_handle_t *hperh)
{
	if (hperh == NULL)
		return ERROR;

	assert_param(IS_CRYPT(hperh->perh));
	assert_param(IS_CRYPT_MODE(hperh->init.mode));
	assert_param(IS_CRYPT_KS(hperh->init.key_select));

	__LOCK(hperh);
	crypt_reset(hperh);

	if (hperh->state == CRYPT_STATE_RESET)
		__UNLOCK(hperh);

	switch (hperh->init.key_select) {
	case CRYPT_KS_AES_BITS_128:
		hperh->perh->CR.CRYSEL = CRYPT_CRYSEL_AES;
		hperh->perh->CR.AESKS  = CRYPT_AESKS_BITS_128;
		crypt_write_key(hperh,hperh->key,KEY_4_LEN);
		hperh->step = 4;
		break;

	case CRYPT_KS_AES_BITS_192:
		hperh->perh->CR.CRYSEL = CRYPT_CRYSEL_AES;
		hperh->perh->CR.AESKS = CRYPT_AESKS_BITS_192;
		crypt_write_key(hperh,hperh->key,KEY_6_LEN);
		hperh->step = 4;
		break;

	case CRYPT_KS_AES_BITS_256:
		hperh->perh->CR.CRYSEL = CRYPT_CRYSEL_AES;
		hperh->perh->CR.AESKS  = CRYPT_AESKS_BITS_256;
		crypt_write_key(hperh,hperh->key,KEY_8_LEN);
		hperh->step = 4;
		break;

	case CRYPT_KS_DES_KEYS_1:
		hperh->perh->CR.CRYSEL = CRYPT_CRYSEL_DES;
		hperh->perh->CR.TDES   = 0;
		crypt_write_key(hperh,hperh->key,KEY_2_LEN);
		hperh->step = 2;
		break;

	case CRYPT_KS_DES_KEYS_2:
		hperh->perh->CR.CRYSEL = CRYPT_CRYSEL_DES;
		hperh->perh->CR.TDES   = 1;
		hperh->perh->CR.DESKS  = CRYPT_DESKS_KEYS_2;
		crypt_write_key(hperh,hperh->key,KEY_4_LEN);
		hperh->step = 2;
		break;

	case CRYPT_KS_DES_KEYS_3:
		hperh->perh->CR.CRYSEL = CRYPT_CRYSEL_DES;
		hperh->perh->CR.TDES   = 1;
		hperh->perh->CR.DESKS  = CRYPT_DESKS_KEYS_3;
		crypt_write_key(hperh,hperh->key,KEY_6_LEN);
		hperh->step = 2;
		break;

	default:
		break;
	}

	hperh->perh->CR.FIFOODR = 1;
	hperh->perh->CR.MODE    = hperh->init.mode;
	hperh->perh->CR.TYPE    = hperh->init.type;
	hperh->perh->CR.ENCS    = 0;
	hperh->perh->CR.FIFOEN  = 1;

	hperh->state = CRYPT_STATE_READY;
	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Write the Content of KEY.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  key: Pointer to key data buffer
  * @param  len: The length of key(32 bits)
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t crypt_write_key(crypt_handle_t *hperh, uint32_t * key, crypt_key_len_t len)
{
	uint32_t *temp   = key;
	uint32_t keysize = len;

	if (hperh->state == CRYPT_STATE_BUSY)
		return BUSY;

	if ((hperh == NULL) ||(key == NULL))
		return ERROR;

	assert_param(IS_CRYPT(hperh->perh));
	assert_param(IS_CRYPT_KEY_LEN(len));

	switch (keysize) {
	case KEY_8_LEN:
		hperh->perh->KEY7.KEY = *temp;
		++temp;
		hperh->perh->KEY6.KEY = *temp;
		++temp;
		keysize -= 2;

	case KEY_6_LEN:
		hperh->perh->KEY5.KEY = *temp;
		++temp;
		hperh->perh->KEY4.KEY = *temp;
		++temp;
		keysize -= 2;

	case KEY_4_LEN:
		hperh->perh->KEY3.KEY = *temp;
		++temp;
		hperh->perh->KEY2.KEY = *temp;
		++temp;
		keysize -= 2;

	case KEY_2_LEN:
		hperh->perh->KEY1.KEY = *temp;
		++temp;
		hperh->perh->KEY0.KEY = *temp;
		break;

	default:
		break;
	}

	return OK;
}

/**
  * @brief  Read the Content of KEY.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  key: Pointer to key data buffer
  * @param  len: The length of key(32 bits)
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t crypt_read_key(crypt_handle_t *hperh, uint32_t * key, crypt_key_len_t len)
{
	uint32_t *temp = key;
	uint32_t keysize = len;

	if (hperh->state == CRYPT_STATE_BUSY)
		return BUSY;

	if ((hperh == NULL) ||(key == NULL))
		return ERROR;

	assert_param(IS_CRYPT(hperh->perh));
	assert_param(IS_CRYPT_KEY_LEN(len));

	switch (keysize) {
	case KEY_8_LEN:
		*temp = hperh->perh->KEY7.KEY;
		++temp;
		*temp = hperh->perh->KEY6.KEY;
		++temp;
		keysize -= 2;

	case KEY_6_LEN:
		*temp = hperh->perh->KEY5.KEY;
		++temp;
		*temp = hperh->perh->KEY4.KEY;
		++temp;
		keysize -= 2;

	case KEY_4_LEN:
		*temp = hperh->perh->KEY3.KEY;
		++temp;
		*temp = hperh->perh->KEY2.KEY;
		++temp;
		keysize -= 2;

	case KEY_2_LEN:
		*temp = hperh->perh->KEY1.KEY;
		++temp;
		*temp = hperh->perh->KEY0.KEY;
		break;

	default:
		break;
	}

	return OK;
}

/**
  * @brief  Write the Content of IVR if you use  CBC mode
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  ivr: Pointer to ivr data buffer
  * @param  len: the length of ivr(32 bits)
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t crypt_write_ivr(crypt_handle_t *hperh,uint32_t * ivr,crypt_ivr_len_t len)
{
	uint32_t *temp = ivr;
	uint32_t ivrsize = len;

	if (hperh->state == CRYPT_STATE_BUSY)
		return BUSY;

	if ((hperh == NULL) || (ivr == NULL))
		return ERROR;

	assert_param(IS_CRYPT(hperh->perh));
	assert_param(IS_CRYPT_IVR_LEN(len));

	switch (ivrsize) {
	case IVR_4_LEN:
		hperh->perh->IVR3.IVR = *temp;
		++temp;
		hperh->perh->IVR2.IVR = *temp;
		++temp;
		ivrsize -= 2;

	case IVR_2_LEN:
		hperh->perh->IVR1.IVR = *temp;
		++temp;
		hperh->perh->IVR0.IVR = *temp;
		break;

	default:
		break;
	}

	CRYPT_IVREN_ENABLE(hperh);
	return OK;
}

/**
  * @brief  Read the Content of IVR.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  ivr: Pointer to ivr data buffer
  * @param  len: the length of ivr(32 bits)
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t crypt_read_ivr(crypt_handle_t *hperh, uint32_t *ivr, crypt_ivr_len_t len)
{
	uint32_t *temp   = ivr;
	uint32_t ivrsize = len;

	if(hperh->state == CRYPT_STATE_BUSY)
		return BUSY;

	if ((hperh == NULL) || (ivr == NULL))
		return ERROR;

	assert_param(IS_CRYPT(hperh->perh));
	assert_param(IS_CRYPT_IVR_LEN(len));

	switch(ivrsize) {
	case IVR_4_LEN:
		*temp = hperh->perh->IVR3.IVR;
		++temp;
		*temp = hperh->perh->IVR2.IVR;
		++temp;
		ivrsize -= 2;

	case IVR_2_LEN:
		*temp = hperh->perh->IVR1.IVR;
		++temp;
		*temp = hperh->perh->IVR0.IVR;
		break;

	default:
		break;
	}

	return OK;
}

/**
  * @}
  */

/** @defgroup CRYPT_Public_Functions_Group2 Encrypt or Decrypt functions
  * @brief Encrypt or Decrypt functions
  * @{
  */

/**
  * @brief  Encrypt an amount of data in blocking mode.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  plain_text: Pointer to plain data buffer
  * @param  cipher_text: Pointer to cipher data buffer
  * @param  size: Amount of plain data
  * @retval Status, see @ref hal_status_t.
  * @note   the size is multiple of 8(des) or 16(ase)
  */
hal_status_t crypt_encrypt(crypt_handle_t *hperh, uint8_t * plain_text, uint8_t * cipher_text, uint32_t size)
{
	uint32_t count = 0;
	uint32_t i;
	uint32_t *plain_buf  = (uint32_t *)plain_text;
	uint32_t *cipher_buf = (uint32_t *)cipher_text;

	if (hperh->state != CRYPT_STATE_READY)
		return ERROR;

	if ((plain_buf == NULL) || (cipher_buf == NULL) || (size == 0))
		return ERROR;

	assert_param(IS_CRYPT(hperh->perh));

	__LOCK(hperh);
	hperh->state = CRYPT_STATE_BUSY;
	CRYPT_SETDIR(hperh,CRYPT_ENCS_ENCRYPT);
	count = size / (4 * hperh->step);

	while (count--) {
		for (i = 0; i < hperh->step; i++) {
			CRYPT_WRITE_FIFO(hperh,*plain_buf);
			plain_buf++;
		}

		while (crypt_get_flag_status(hperh,CRYPT_FLAG_DONE) == SET);

		for (i = 0; i < hperh->step; i++) {
			*cipher_buf = CRYPT_READ_FIFO(hperh);
			cipher_buf++;
		}
	}

	hperh->state = CRYPT_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Decrypt an amount of data in blocking mode.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  cipher_text: Pointer to cipher data buffer
  * @param  plain_text: Pointer to plain data buffer
  * @param  size: Amount of cipher data
  * @retval Status, see @ref hal_status_t.
  * @note   the size is multiple of 8(des) or 16(ase)
  */
hal_status_t crypt_decrypt(crypt_handle_t *hperh,uint8_t *cipher_text, uint8_t *plain_text, uint32_t size)
{
	uint32_t count = 0;
	uint32_t i;
	uint32_t *plain_buf  = (uint32_t*)plain_text;
	uint32_t *cipher_buf = (uint32_t*)cipher_text;

	if (hperh->state != CRYPT_STATE_READY)
		return ERROR;

	if ((plain_buf == NULL) || (cipher_buf == NULL) || (size == 0))
		return ERROR;

	assert_param(IS_CRYPT(hperh->perh));

	__LOCK(hperh);
	hperh->state = CRYPT_STATE_BUSY;
	CRYPT_SETDIR(hperh,CRYPT_ENCS_DECRYPT);
	count = size / (4 * hperh->step);

	while (count--) {
		for (i = 0; i < hperh->step; i++) {
			CRYPT_WRITE_FIFO(hperh,*cipher_buf);
			cipher_buf++;
		}

		while (crypt_get_flag_status(hperh,CRYPT_FLAG_DONE) == SET);

		for (i = 0; i < hperh->step; i++) {
			*plain_buf = CRYPT_READ_FIFO(hperh);
			plain_buf++;
		}
	}

	hperh->state = CRYPT_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Encrypt an amount of data in non-blocking mode.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  plain_text: Pointer to plain data buffer
  * @param  cipher_text: Pointer to cipher data buffer
  * @param  size: Amount of plain data
  * @retval Status, see @ref hal_status_t.
  * @note   the size is multiple of 8(des) or 16(ase)
  */
hal_status_t crypt_encrypt_by_it(crypt_handle_t *hperh, uint8_t * plain_text, uint8_t *cipher_text, uint32_t size)
{
	uint32_t i;
	uint32_t *plain_buf = (uint32_t *)plain_text;

	if (hperh->state != CRYPT_STATE_READY)
		return ERROR;

	if ((plain_text == NULL) || (cipher_text == NULL) || (size == 0))
		return ERROR;

	assert_param(IS_CRYPT(hperh->perh));

	__LOCK(hperh);
	hperh->state = CRYPT_STATE_BUSY;
	CRYPT_SETDIR(hperh,CRYPT_ENCS_ENCRYPT);
	hperh->count       = 4;
	hperh->plain_text  = plain_text;
	hperh->cipher_text = cipher_text;
	hperh->size        = size;
	crypt_interrupt_config(hperh,CRYPT_IT_IT,ENABLE);

	for (i = 0; i < hperh->step; i++) {
		CRYPT_WRITE_FIFO(hperh, *plain_buf);
		++plain_buf;
	}

	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Decrypt an amount of data in non-blocking mode.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  plain_text: Pointer to plain data buffer
  * @param  cipher_text: Pointer to cipher data buffer
  * @param  size: Amount of cipher data
  * @retval Status, see @ref hal_status_t.
  * @note   the size is multiple of 8(des) or 16(ase)
  */
hal_status_t crypt_decrypt_by_it(crypt_handle_t *hperh, uint8_t * plain_text, uint8_t *cipher_text, uint32_t size)
{
	uint32_t i;
	uint32_t *cipher_buf = (uint32_t*)cipher_text;

	if (hperh->state != CRYPT_STATE_READY)
		return ERROR;

	if ((plain_text == NULL) || (cipher_text == NULL) || (size == 0))
		return ERROR;

	assert_param(IS_CRYPT(hperh->perh));

	__LOCK(hperh);
	hperh->state = CRYPT_STATE_BUSY;
	CRYPT_SETDIR(hperh,CRYPT_ENCS_DECRYPT);
	hperh->count       = 4;
	hperh->plain_text  = plain_text;
	hperh->cipher_text = cipher_text;
	hperh->size        = size;
	crypt_interrupt_config(hperh,CRYPT_IT_IT,ENABLE);

	for (i = 0; i < hperh->step; i++) {
		CRYPT_WRITE_FIFO(hperh, *cipher_buf);
		cipher_buf ++;
	}

	__UNLOCK(hperh);
	return OK;
}

#ifdef HAL_DMA
/**
  * @brief  Encrypt an amount of data in non-blocking mode.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  plain_text: Pointer to plain data buffer
  * @param  cipher_text: Pointer to cipher data buffer
  * @param  size: Amount of plain data
  * @param  channel_m2p: Memory to Crypt module DMA channel
  * @param  channel_p2m: Crypt module to Memory DMA channel
  * @retval Status, see @ref hal_status_t.
  * @note   the size is multiple of 8(des) or 16(ase)
  */
hal_status_t crypt_encrypt_by_dma(crypt_handle_t *hperh, uint8_t * plain_text,
             uint8_t *cipher_text, uint32_t size, uint8_t channel_m2p, uint8_t channel_p2m)
{
	if(hperh->state != CRYPT_STATE_READY)
		return ERROR;

	if(plain_text == NULL || cipher_text == NULL || size == 0)
		return ERROR;

	assert_param(IS_CRYPT(hperh->perh));

	__LOCK(hperh);
	hperh->state = CRYPT_STATE_BUSY;

	hperh->plain_text  = plain_text;
	hperh->cipher_text = cipher_text;
	hperh->size        = size;
	hperh->count       = size;

	if (hperh->hdma_m2p.perh == NULL)
		hperh->hdma_m2p.perh = DMA0;
	if (hperh->hdma_p2m.perh == NULL)
		hperh->hdma_p2m.perh = DMA0;

	hperh->hdma_m2p.cplt_arg = NULL;
	hperh->hdma_m2p.cplt_cbk = NULL;
	hperh->hdma_m2p.err_arg  = NULL;
	hperh->hdma_m2p.err_cbk  = NULL;

	hperh->hdma_p2m.cplt_arg = (void *)hperh;
	hperh->hdma_p2m.cplt_cbk = &crypt_dma_crypt_cplt;
	hperh->hdma_p2m.err_arg  = (void *)hperh;
	hperh->hdma_p2m.err_cbk  = &crypt_dma_error;

	CRYPT_SETDIR(hperh,CRYPT_ENCS_ENCRYPT);

	dma_config_struct(&hperh->hdma_p2m.config);
	hperh->hdma_m2p.config.data_width = DMA_DATA_SIZE_WORD;
	hperh->hdma_m2p.config.src     = (void *)hperh->plain_text;
	hperh->hdma_m2p.config.dst     = (void *)&hperh->perh->FIFO.Word;
	hperh->hdma_m2p.config.size    = size / 4;
	hperh->hdma_m2p.config.src_inc = DMA_DATA_INC_WORD;
	hperh->hdma_m2p.config.dst_inc = DMA_DATA_INC_NONE;
	hperh->hdma_m2p.config.msel    = DMA_MSEL_CRYPT;
	hperh->hdma_m2p.config.msigsel = DMA_MSIGSEL_CRYPT_WRITE;
	hperh->hdma_m2p.config.channel = channel_m2p;
	dma_config_basic(&(hperh->hdma_m2p));

	dma_config_struct(&hperh->hdma_p2m.config);
	hperh->hdma_p2m.config.data_width = DMA_DATA_SIZE_WORD;
	hperh->hdma_p2m.config.src     = (void *)&hperh->perh->FIFO.Word;
	hperh->hdma_p2m.config.dst     = (void *)hperh->cipher_text;
	hperh->hdma_p2m.config.size    = size / 4;
	hperh->hdma_p2m.config.src_inc = DMA_DATA_INC_NONE;
	hperh->hdma_p2m.config.dst_inc = DMA_DATA_INC_WORD;
	hperh->hdma_p2m.config.msel    = DMA_MSEL_CRYPT;
	hperh->hdma_p2m.config.msigsel = DMA_MSIGSEL_CRYPT_READ;
	hperh->hdma_p2m.config.channel = channel_p2m;
	dma_config_basic(&(hperh->hdma_p2m));

	CRYPT_DMA_ENABLE(hperh);
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Decrypt an amount of data in non-blocking mode.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  plain_text: Pointer to plain data buffer
  * @param  cipher_text: Pointer to cipher data buffer
  * @param  size: Amount of cipher data
  * @param  channel_m2p: Memory to Crypt module DMA channel
  * @param  channel_p2m: Crypt module to Memory DMA channel
  * @retval Status, see @ref hal_status_t.
  * @note   the size is multiple of 8(des) or 16(ase)
  */
hal_status_t crypt_decrypt_by_dma(crypt_handle_t *hperh, uint8_t * cipher_text,
               uint8_t *plain_text, uint32_t size, uint8_t channel_m2p, uint8_t channel_p2m)
{
	if(hperh->state != CRYPT_STATE_READY)
		return ERROR;

	if(plain_text == NULL || cipher_text == NULL || size == 0)
		return ERROR;

	__LOCK(hperh);
	hperh->state = CRYPT_STATE_BUSY;

	hperh->plain_text  = plain_text;
	hperh->cipher_text = cipher_text;
	hperh->size        = size;
	hperh->count       = size;

	if (hperh->hdma_m2p.perh == NULL)
		hperh->hdma_m2p.perh = DMA0;
	if (hperh->hdma_p2m.perh == NULL)
		hperh->hdma_p2m.perh = DMA0;


	hperh->hdma_m2p.cplt_arg = NULL;
	hperh->hdma_m2p.cplt_cbk = NULL;
	hperh->hdma_m2p.err_arg  = NULL;
	hperh->hdma_m2p.err_cbk  = NULL;

	hperh->hdma_p2m.cplt_arg = (void *)hperh;
	hperh->hdma_p2m.cplt_cbk = &crypt_dma_crypt_cplt;
	hperh->hdma_p2m.err_arg  = (void *)hperh;
	hperh->hdma_p2m.err_cbk  = &crypt_dma_error;

	CRYPT_SETDIR(hperh,CRYPT_ENCS_DECRYPT);

	dma_config_struct(&hperh->hdma_p2m.config);
	hperh->hdma_m2p.config.data_width = DMA_DATA_SIZE_WORD;
	hperh->hdma_m2p.config.src     = (void *)hperh->cipher_text;
	hperh->hdma_m2p.config.dst     = (void *)&hperh->perh->FIFO.Word;
	hperh->hdma_m2p.config.size    = size / 4;
	hperh->hdma_m2p.config.src_inc = DMA_DATA_INC_WORD;
	hperh->hdma_m2p.config.dst_inc = DMA_DATA_INC_NONE;
	hperh->hdma_m2p.config.msel    = DMA_MSEL_CRYPT;
	hperh->hdma_m2p.config.msigsel = DMA_MSIGSEL_CRYPT_WRITE;
	hperh->hdma_m2p.config.channel = channel_m2p;
	dma_config_basic(&(hperh->hdma_m2p));

	dma_config_struct(&hperh->hdma_p2m.config);
	hperh->hdma_p2m.config.data_width = DMA_DATA_SIZE_WORD;
	hperh->hdma_p2m.config.src     = (void *)&hperh->perh->FIFO.Word;
	hperh->hdma_p2m.config.dst     = (void *)hperh->plain_text;
	hperh->hdma_p2m.config.size    = size / 4;
	hperh->hdma_p2m.config.src_inc = DMA_DATA_INC_NONE;
	hperh->hdma_p2m.config.dst_inc = DMA_DATA_INC_WORD;
	hperh->hdma_p2m.config.msel    = DMA_MSEL_CRYPT;
	hperh->hdma_p2m.config.msigsel = DMA_MSIGSEL_CRYPT_READ;
	hperh->hdma_p2m.config.channel = channel_p2m;
	dma_config_basic(&(hperh->hdma_p2m));

	CRYPT_DMA_ENABLE(hperh);
	__UNLOCK(hperh);

	return OK;
}

/**
  * @}
  */

/** @defgroup CRYPT_Public_Functions_Group3 DMA operation functions
  * @brief DMA operation functions
  * @{
  */

/**
  * @brief  Pauses the DMA Transfer.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t crypt_dma_pause(crypt_handle_t *hperh)
{
	__LOCK(hperh);
	CRYPT_DMA_DISABLE(hperh);
	__UNLOCK(hperh);

	return OK;

}

/**
  * @brief  Resumes the DMA Transfer.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t crypt_dma_resume(crypt_handle_t *hperh)
{
	__LOCK(hperh);
	CRYPT_DMA_ENABLE(hperh);
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Stops the DMA Transfer.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t crypt_dma_stop(crypt_handle_t *hperh)
{
	__LOCK(hperh);
	CRYPT_DMA_DISABLE(hperh);
	__UNLOCK(hperh);

	hperh->state = CRYPT_STATE_READY;
	return OK;
}
#endif

/**
  * @brief  This function handles CRYPT interrupt request.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @retval None
  */
void crypt_irq_handle(crypt_handle_t *hperh)
{
	uint32_t i;
	uint32_t *in_buf  = (uint32_t *)hperh->cipher_text;
	uint32_t *out_buf = (uint32_t *)hperh->plain_text;

	if (hperh->perh->CR.ENCS == CRYPT_ENCS_DECRYPT) {
		in_buf  = (uint32_t *)hperh->cipher_text;
		out_buf = (uint32_t *)hperh->plain_text;
	}
	else {
		in_buf  = (uint32_t *)hperh->plain_text;
		out_buf = (uint32_t *)hperh->cipher_text;
	}

	if (crypt_get_flag_status(hperh, CRYPT_FLAG_AESIF) == SET) {
		crypt_clear_flag_status(hperh, CRYPT_FLAG_AESIF);
	}

	if (crypt_get_flag_status(hperh, CRYPT_FLAG_DESIF) == SET) {
		crypt_clear_flag_status(hperh, CRYPT_FLAG_DESIF);
	}


	for (i = 0; i < hperh->step; i++) {
		*out_buf = CRYPT_READ_FIFO(hperh);
		++out_buf;
	}

	in_buf  = in_buf + hperh->count;
 	out_buf = out_buf + hperh->count;

	hperh->count = hperh->count + 4;
	if (hperh->count > hperh->size) {
		hperh->count = 0;
		hperh->state = CRYPT_STATE_READY;
		hperh->crypt_cplt_cbk(hperh);
	}
	else {
		for (i = 0; i < hperh->step; i++) {
			CRYPT_WRITE_FIFO(hperh,*in_buf);
			++in_buf;
		}
	}
}
/**
  * @}
  */

/** @defgroup CRYPT_Public_Functions_Group4 Peripheral Control functions
  *  @brief   CRYPT control functions
  * @{
  */

/**
  * @brief  Enables or disables the specified CRYPT interrupts.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  it: Specifies the CRYPT interrupt sources to be enabled or disabled.
  *           This parameter can be one of the following values:
  *           @arg crypt_it_t:  CRYPT interrupt
  * @param  state: New status
  *           - ENABLE
  *           - DISABLE
  * @retval None
  */
void crypt_interrupt_config(crypt_handle_t *hperh, crypt_it_t it, type_func_t state)
{
	assert_param(IS_CRYPT(hperh->perh));

	if (it == CRYPT_IT_IT)
		CRYPT->CR.IE = state;

	return;
}

/** @brief  Check whether the specified CRYPT flag is set or not.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  flag: specifies the flag to check.
  *         This parameter can be one of the @ref crypt_flag_t.
  * @retval Status
  *           - SET
  *           - RESET
  */
flag_status_t crypt_get_flag_status(crypt_handle_t *hperh, crypt_flag_t flag)
{
	assert_param(IS_CRYPT(hperh->perh));
	assert_param(IS_CRYPT_FLAG(flag));

	if (CRYPT->SR.Word & flag)
		return SET;

	return RESET;
}

/** @brief  Clear the specified CRYPT pending flags.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  flag: specifies the flag to check.
  *          This parameter can be any combination of the following values:
  *            @arg CRYPT_FLAG_AESIF: AES encrypt or decrypt Complete flag.
  *            @arg CRYPT_FLAG_DESIF: AES encrypt or decrypt Complete flag.
  *            @arg CRYPT_FLAG_DONE: encrypt or decrypt Complete flag.
  * @retval None
  */
void crypt_clear_flag_status(crypt_handle_t *hperh, crypt_flag_t flag)
{
	assert_param(IS_CRYPT(hperh->perh));
	assert_param(IS_CRYPT_FLAG(flag));

	CRYPT->ICFR.Word = (uint32_t)flag;
	return;
}

/**
  * @brief  Checks whether the specified CRYPT interrupt has occurred or not.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @param  it: Specifies the CRYPT interrupt source to check.
  *	       This parameter can be one of the following values:
  *	       @arg crypt_it_t:  CRYPT interrupt
  * @retval Status
  *           - SET
  *           - RESET
  */
it_status_t crypt_get_it_status(crypt_handle_t *hperh, crypt_it_t it)
{
	assert_param(IS_CRYPT_IT(it));

	if (CRYPT->CR.IE == 1)
		return SET;

	return RESET;
}


/**
  * @}
  */

/** @defgroup CRYPT_Public_Functions_Group5 Peripheral State and Errors functions
  * @brief    State and Errors functions
  * @{
  */

/**
  * @brief  Returns the CRYPT state.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @retval CRYPT state
  */
crypt_state_t crypt_get_state(crypt_handle_t *hperh)
{
	assert_param(IS_CRYPT(hperh->perh));

	return hperh->state;
}

/**
  * @}
  */

/**
  * @}
  */

/** @defgroup CRYPT_Private_Functions   CRYPT Private Functions
  *  @brief   CRYPT Private functions
  * @{
  */

/**
  * @brief  Reset the CRYPT peripheral.
  * @param  hperh: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @retval None
  */
void crypt_reset(crypt_handle_t *hperh)
{
	hperh->perh->DATA0.Word = 0x0;
	hperh->perh->DATA1.Word = 0x0;
	hperh->perh->DATA2.Word = 0x0;
	hperh->perh->DATA3.Word = 0x0;

	hperh->perh->KEY0.Word = 0x0;
	hperh->perh->KEY1.Word = 0x0;
	hperh->perh->KEY2.Word = 0x0;
	hperh->perh->KEY3.Word = 0x0;
	hperh->perh->KEY4.Word = 0x0;
	hperh->perh->KEY5.Word = 0x0;
	hperh->perh->KEY6.Word = 0x0;
	hperh->perh->KEY7.Word = 0x0;

	hperh->perh->IVR0.Word = 0x0;
	hperh->perh->IVR1.Word = 0x0;
	hperh->perh->IVR2.Word = 0x0;
	hperh->perh->IVR3.Word = 0x0;

	hperh->perh->RES0.Word = 0x0;
	hperh->perh->RES1.Word = 0x0;
	hperh->perh->RES2.Word = 0x0;
	hperh->perh->RES3.Word = 0x0;

	hperh->perh->CR.Word = 0x0;
	hperh->perh->SR.Word = 0x0;

	hperh->state = CRYPT_STATE_READY;
	__UNLOCK(hperh);
}

#ifdef HAL_DMA
/**
  * @brief  DMA CRYPT encrypt or decrypt process complete callback.
  * @param  arg: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @retval None
  */
static void crypt_dma_crypt_cplt(void *arg)
{
	crypt_handle_t *hperh = (crypt_handle_t *)arg;

	CRYPT_DMA_DISABLE(hperh);
	hperh->count       = 0;
	hperh->plain_text  = NULL;
	hperh->cipher_text = NULL;
	hperh->size        = 0;

	hperh->state = CRYPT_STATE_READY;

	if (hperh->crypt_cplt_cbk)
		hperh->crypt_cplt_cbk(hperh);
}

/**
  * @brief  DMA CRYPT communication error callback.
  * @param  arg: Pointer to a crypt_handle_t structure that contains
  *         the configuration information for the specified CRYPT module.
  * @retval None
  */
static void crypt_dma_error(void *arg)
{
	crypt_handle_t *hperh = (crypt_handle_t *)arg;
	CRYPT_DMA_DISABLE(hperh);

	hperh->count       = 0;
	hperh->plain_text  = NULL;
	hperh->cipher_text = NULL;
	hperh->size        = 0;

	hperh->state = CRYPT_STATE_READY;

	if (hperh->err_cplt_cbk)
		hperh->err_cplt_cbk(hperh);
}
#endif
/**
  * @}
  */

/**
  * @}
  */
#endif /* HAL_CRYPT */

/**
  * @}
  */
