/**
  ******************************************************************************
  * @file    hal_can.c
  * @brief   CAN module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the Controller Area Network (CAN) peripheral:
  *           + Initialization  functions
  *           + IO operation functions
  *           + Peripheral Control functions
  *           + Peripheral State and Error functions
  * @version V1.0
  * @date    25 Apr 2017
  * @author  AE Team
  *
  * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
  *
  ********************************************************************************
  * @verbatim
  ==============================================================================
                        ##### How to use this driver #####
  ==============================================================================
    [..]
      (#) Enable the CAN controller interface clock.
      (#) CAN pins configuration
        (++) Enable the clock for the CAN GPIOs;
        (++) Connect and configure the involved CAN pins using the
             following function gpio_init();
      (#) Initialise and configure the CAN using can_init() function.
      (#) Transmit the CAN frame using can_send()/can_send_by_it() function.
      (#) Receive a CAN frame using can_recv()/can_recv_by_it function.

     *** Polling mode IO operation ***
     =================================
     [..]
       (+) Start the CAN peripheral transmission and wait the end of this operation
           using can_send(), at this stage user can specify the value of timeout
           according to his end application.
       (+) Start the CAN peripheral reception and wait the end of this operation
           using can_recv(), at this stage user can specify the value of timeout
           according to his end application

     *** Interrupt mode IO operation ***
     ===================================
     [..]
       (+) Start the CAN peripheral transmission using can_send_by_it()
       (+) Start the CAN peripheral reception using can_recv_by_it()
       (+) Use can_irq_handler() called under the used CAN Interrupt subroutine
       (+) At CAN end of transmission pherh->tx_cplt_cbk() function is executed and user can
            add his own code by customization of function pointer pherh->tx_cplt_cbk()
       (+) In case of CAN Error, pherh->rx_cplt_cbk() function is executed and user can
            add his own code by customization of function pointer pherh->rx_cplt_cbk()

     *** CAN HAL driver macros list ***
     =============================================
     [..]
       Below the list of most used macros in CAN driver.

      (+) CAN_RESET_HANDLE_STATE(): Reset CAN handle state.
      (+) CAN_RX_MSG_PENDING(): Return the number of pending received messages.
      (+) CAN_RX_FIFO_RELEASE(): Release the specified receive FIFO.
      (+) CAN_DBG_FREEZE(): Enable or disables the DBG Freeze for CAN.

     [..]
      (@) You can refer to the CAN driver header file for used the macros

  @endverbatim
  ******************************************************************************
  */

#include "hal_can.h"


/** @addtogroup ES32FXXX_HAL
  * @{
  */

/** @defgroup CAN CAN
  * @brief CAN module driver
  * @{
  */
#ifdef HAL_CAN

/** @addtogroup CAN_Private_Functions  CAN Private Functions
  * @{
  */
static hal_status_t __can_send_by_it(can_handle_t *hperh);
static hal_status_t __can_recv_by_it(can_handle_t *hperh, uint8_t num);
/**
  * @}
  */

/** @defgroup CAN_Public_Functions CAN Public Functions
  * @{
  */

/** @defgroup CAN_Public_Functions_Group1 Initialization  functions
  * @brief    Initialization and Configuration functions
  *
  * @verbatim
  ==============================================================================
              ##### Initialization and Configuration functions #####
  ==============================================================================
    [..]  This section provides functions allowing to:
      (+) Initialize and configure the CAN.
      (+) Configures the CAN reception filter.
      (+) Reset the CAN.

    @endverbatim
  * @{
  */

/**
  * @brief  Initializes the CAN peripheral according to the specified
  *         parameters in the CAN_init_t.
  * @param  hperh: pointer to a can_handle_t structure that contains
  *         the configuration information for the specified CAN.
  * @retval Status, see hal_status_t.
  */
hal_status_t can_init(can_handle_t *hperh)
{
	uint32_t tickstart = 0;

	assert_param(IS_CAN_ALL(hperh->perh));
	assert_param(IS_FUNC_STATE(hperh->init.ttcm));
	assert_param(IS_FUNC_STATE(hperh->init.abom));
	assert_param(IS_FUNC_STATE(hperh->init.awum));
	assert_param(IS_FUNC_STATE(hperh->init.nart));
	assert_param(IS_FUNC_STATE(hperh->init.rflm));
	assert_param(IS_FUNC_STATE(hperh->init.txfp));
	assert_param(IS_CAN_MODE(hperh->init.mode));
	assert_param(IS_CAN_SJW(hperh->init.sjw));
	assert_param(IS_CAN_BS1(hperh->init.bs1));
	assert_param(IS_CAN_BS2(hperh->init.bs2));
	assert_param(IS_CAN_PRESCALER(hperh->init.psc));

	if (hperh->state == CAN_STATE_RESET)
		hperh->lock = UNLOCK;

	hperh->state = CAN_STATE_BUSY;
	tickstart    = __get_tick();

	hperh->perh->MCR.SLEEP = 0;
	hperh->perh->MCR.INRQ  = 1;

	while (hperh->perh->MSR.INAK == 0) {
		if ((__get_tick() - tickstart) > CAN_TIMEOUT_VALUE) {
			hperh->state = CAN_STATE_TIMEOUT;
			__UNLOCK(hperh);

			return TIMEOUT;
		}
	}

	hperh->perh->MCR.TTCM = hperh->init.ttcm;
	hperh->perh->MCR.ABOM = hperh->init.abom;
	hperh->perh->MCR.AWUM = hperh->init.awum;
	hperh->perh->MCR.NART = hperh->init.nart;
	hperh->perh->MCR.RFLM = hperh->init.rflm;
	hperh->perh->MCR.TXFP = hperh->init.txfp;
	hperh->perh->BTR.LBKM = hperh->init.mode & 0x1;
	hperh->perh->BTR.SILM = (hperh->init.mode >> 1) & 0x1;

	hperh->perh->BTR.SJW = hperh->init.sjw;
	hperh->perh->BTR.TS1 = hperh->init.bs1;
	hperh->perh->BTR.TS2 = hperh->init.bs2;
	hperh->perh->BTR.BRP = hperh->init.psc - 1;

	hperh->perh->MCR.INRQ = 0;
	tickstart = __get_tick();

	while (hperh->perh->MSR.INAK == 1) {
		if ((__get_tick() - tickstart) > CAN_TIMEOUT_VALUE) {
			hperh->state= CAN_STATE_TIMEOUT;
			__UNLOCK(hperh);

			return TIMEOUT;
		}
	}

	hperh->err   = CAN_ERROR_NONE;
	hperh->state = CAN_STATE_READY;

	return OK;
}

/**
  * @brief  Configures the CAN reception filter according to the specified
  *         parameters in the can_filter_t.
  * @param  hperh: pointer to a can_handle_t structure.
  * @param  config: pointer to a can_filter_t structure that
  *         contains the filter configuration information.
  * @retval Status, see hal_status_t.
  */
hal_status_t can_filter_config(can_handle_t *hperh, can_filter_t *config)
{
	uint32_t pos;

	assert_param(IS_CAN_ALL(hperh->perh));
	assert_param(IS_CAN_FILTER_NUMBER(config->number));
	assert_param(IS_CAN_FILTER_MODE(config->mode));
	assert_param(IS_CAN_FILTER_SCALE(config->scale));
	assert_param(IS_CAN_FILTER_FIFO(config->fifo));
	assert_param(IS_FUNC_STATE(config->active));
	assert_param(IS_CAN_BANKNUMBER(config->bank_number));

	pos = 1 << config->number;

	hperh->perh->FMR.FINIT  = 1;
	hperh->perh->FA1R.FACT &= ~pos;

	if (config->scale == CAN_FILTER_SCALE_16) {
		hperh->perh->FS1R.FSC &= ~pos;
		
		if (config->mode == CAN_FILTER_MODE_MASK) {
			HANDLE_ID(config->id_low);
			HANDLE_ID(config->mask_id_low);
		}
		else {
			HANDLE_ID(config->id_low);
			HANDLE_ID(config->id_high);
			HANDLE_ID(config->mask_id_low);
			HANDLE_ID(config->mask_id_high);
		}

		hperh->perh->Filter[config->number].FR1 =
			((0xFFFF & config->mask_id_low) << 16) |
			 (0xFFFF & config->id_low);

		hperh->perh->Filter[config->number].FR2 =
			((0xFFFF & config->mask_id_high) << 16) |
			 (0xFFFF & config->id_high);
	}

	if (config->scale == CAN_FILTER_SCALE_32) {
		hperh->perh->FS1R.FSC |= pos;
		
		if(config->mode == CAN_FILTER_MODE_MASK) {
			if(!(config->id_low & 0x0004)) {
				config->id_low  &= 0x000B;
				config->id_high &= 0xFF80;
			}			
		}			
		else if(config->mode == CAN_FILTER_MODE_LIST) {
			if(!(config->id_low & 0x0004)) {
				config->id_low  &= 0x000B;
				config->id_high &= 0xFF80;			
			}
			if(!(config->mask_id_low & 0x0004)) {
				config->mask_id_low  &= 0x000B;
				config->mask_id_high &= 0xFF80;				
			}
		}
		
		hperh->perh->Filter[config->number].FR1 =
			((0xFFFF & config->id_high) << 16) |
			 (0xFFFF & config->id_low);
		hperh->perh->Filter[config->number].FR2 =
			((0xFFFF & config->mask_id_high) << 16) |
			 (0xFFFF & config->mask_id_low);
	}

	hperh->perh->FM1R.FBM  |= (config->mode << config->number);
	hperh->perh->FFA1R.FFA |= (config->fifo << config->number);
	hperh->perh->FA1R.FACT |= (config->active << config->number);

	hperh->perh->FMR.FINIT = 0;
	return OK;
}

/**
  * @brief  Reset the CAN peripheral.
  * @param  hperh: pointer to a can_handle_t structure.
  * @retval None
  */
void can_reset(can_handle_t *hperh)
{
	assert_param(IS_CAN_ALL(hperh->perh));

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

	return;
}

/**
  * @}
  */

/** @defgroup CAN_Public_Functions_Group2 IO operation functions
 *  @brief    I/O operation functions
 *
 *  @verbatim
  ==============================================================================
                      ##### IO operation functions #####
  ==============================================================================
    [..]  This section provides functions allowing to:
      (+) Send a CAN frame message.
      (+) Send a CAN frame message using interrupt.
      (+) Receive a CAN frame message.
      (+) Receive a CAN frame message using interrupt.

  *@endverbatim
  * @{
  */

/**
  * @brief  Send a CAN frame message.
  * @param  hperh: pointer to a can_handle_t structure.
  * @param  msg: message which will be snet.
  * @param  timeout: specify Timeout value
  * @retval Status, see hal_status_t.
  */
hal_status_t can_send(can_handle_t *hperh, can_tx_msg_t *msg, uint32_t timeout)
{
	uint32_t tick;
	can_tx_mailbox_t idx;

	assert_param(IS_CAN_ALL(hperh->perh));
	assert_param(IS_CAN_IDTYPE(msg->type));
	assert_param(IS_CAN_RTR(msg->rtr));
	assert_param(IS_CAN_DATA_LEN(msg->len));

	__LOCK(hperh);
	SET_BIT(hperh->state, CAN_STATE_TX_MASK);

	if (hperh->perh->TSR.TME0)
		idx = CAN_TX_MAILBOX_0;
	else if (hperh->perh->TSR.TME1)
		idx = CAN_TX_MAILBOX_1;
	else if (hperh->perh->TSR.TME2)
		idx = CAN_TX_MAILBOX_2;
	else
		idx = CAN_TX_MAILBOX_NONE;

	if (idx == CAN_TX_MAILBOX_NONE) {
		hperh->state = CAN_STATE_ERROR;
		__UNLOCK(hperh);
		return ERROR;
	}

	hperh->perh->TxMailBox[idx].TIR.TXRQ = 0;
	hperh->perh->TxMailBox[idx].TIR.IDE  = msg->type;
	hperh->perh->TxMailBox[idx].TIR.RTR  = msg->rtr;

	if (msg->type == CAN_ID_STD) {
		assert_param(IS_CAN_STDID(msg->std));
		hperh->perh->TxMailBox[idx].TIR.STID = msg->std;
		hperh->perh->TxMailBox[idx].TIR.EXID = 0;
	}
	else {
		assert_param(IS_CAN_EXTID(msg->ext));
		hperh->perh->TxMailBox[idx].TIR.STID = (msg->ext >> 18) & 0x7FF;
		hperh->perh->TxMailBox[idx].TIR.EXID = msg->ext & 0x3FFFF;
	}

	hperh->perh->TxMailBox[idx].TDTR.DLC    = msg->len & 0xF;
	hperh->perh->TxMailBox[idx].TDLR.DATA_0 = msg->data[0];
	hperh->perh->TxMailBox[idx].TDLR.DATA_1 = msg->data[1];
	hperh->perh->TxMailBox[idx].TDLR.DATA_2 = msg->data[2];
	hperh->perh->TxMailBox[idx].TDLR.DATA_3 = msg->data[3];
	hperh->perh->TxMailBox[idx].TDHR.DATA_4 = msg->data[4];
	hperh->perh->TxMailBox[idx].TDHR.DATA_5 = msg->data[5];
	hperh->perh->TxMailBox[idx].TDHR.DATA_6 = msg->data[6];
	hperh->perh->TxMailBox[idx].TDHR.DATA_7 = msg->data[7];

	hperh->perh->TxMailBox[idx].TIR.TXRQ = 1;
	tick = __get_tick();

	while (!(can_get_tx_status(hperh, idx))) {
		if ((timeout == 0) || ((__get_tick() - tick) > timeout)) {
			hperh->state = CAN_STATE_TIMEOUT;
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}

	CLEAR_BIT(hperh->state, CAN_STATE_TX_MASK);
	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Send a CAN frame message using interrupt.
  * @param  hperh: pointer to a can_handle_t structure.
  * @param  msg: message which will be snet.
  * @retval Status, see hal_status_t.
  */
hal_status_t can_send_by_it(can_handle_t *hperh, can_tx_msg_t *msg)
{
	uint8_t idx = CAN_TX_MAILBOX_NONE;

	assert_param(IS_CAN_ALL(hperh->perh));
	assert_param(IS_CAN_IDTYPE(msg->type));
	assert_param(IS_CAN_RTR(msg->rtr));
	assert_param(IS_CAN_DATA_LEN(msg->len));

	if ((hperh->state != CAN_STATE_READY) && (hperh->state != CAN_STATE_BUSY_RX))
		return BUSY;

	if (hperh->perh->TSR.TME0)
		idx = CAN_TX_MAILBOX_0;
	else if (hperh->perh->TSR.TME1)
		idx = CAN_TX_MAILBOX_1;
	else if (hperh->perh->TSR.TME2)
		idx = CAN_TX_MAILBOX_2;
	else
		idx = CAN_TX_MAILBOX_NONE;

	if (idx == CAN_TX_MAILBOX_NONE)
		return BUSY;

	hperh->perh->TxMailBox[idx].TIR.TXRQ = 0;
	hperh->perh->TxMailBox[idx].TIR.IDE  = msg->type;
	hperh->perh->TxMailBox[idx].TIR.RTR  = msg->rtr;

	if (msg->type == CAN_ID_STD) {
		assert_param(IS_CAN_STDID(msg->std));
		hperh->perh->TxMailBox[idx].TIR.STID = msg->std;
		hperh->perh->TxMailBox[idx].TIR.EXID = 0;
	}
	else {
		assert_param(IS_CAN_EXTID(msg->ext));
		hperh->perh->TxMailBox[idx].TIR.STID = (msg->ext >> 18) & 0x7FF;
		hperh->perh->TxMailBox[idx].TIR.EXID = msg->ext & 0x3FFFF;
	}

	hperh->perh->TxMailBox[idx].TDTR.DLC    = msg->len & 0xF;
	hperh->perh->TxMailBox[idx].TDLR.DATA_0 = msg->data[0];
	hperh->perh->TxMailBox[idx].TDLR.DATA_1 = msg->data[1];
	hperh->perh->TxMailBox[idx].TDLR.DATA_2 = msg->data[2];
	hperh->perh->TxMailBox[idx].TDLR.DATA_3 = msg->data[3];
	hperh->perh->TxMailBox[idx].TDHR.DATA_4 = msg->data[4];
	hperh->perh->TxMailBox[idx].TDHR.DATA_5 = msg->data[5];
	hperh->perh->TxMailBox[idx].TDHR.DATA_6 = msg->data[6];
	hperh->perh->TxMailBox[idx].TDHR.DATA_7 = msg->data[7];

	SET_BIT(hperh->state, CAN_STATE_TX_MASK);

	can_interrupt_config(hperh, CAN_IT_EWG, ENABLE);
	can_interrupt_config(hperh, CAN_IT_EPV, ENABLE);
	can_interrupt_config(hperh, CAN_IT_BOF, ENABLE);
	can_interrupt_config(hperh, CAN_IT_LEC, ENABLE);
	can_interrupt_config(hperh, CAN_IT_ERR, ENABLE);
	can_interrupt_config(hperh, CAN_IT_TME, ENABLE);

	hperh->perh->TxMailBox[idx].TIR.TXRQ = 1;
	return OK;
}

/**
  * @brief  Receives a correct CAN frame.
  * @param  hperh: pointer to a can_handle_t structure.
  * @param  num: Receive fifo number, CAN_RX_FIFO0 or CAN_RX_FIFO1
  * @param  msg: Storing message.
  * @param  timeout: Specify timeout value
  * @retval Status, see hal_status_t.
  */
hal_status_t can_recv(can_handle_t *hperh, can_rx_fifo_t num, can_rx_msg_t *msg, uint32_t timeout)
{
	uint32_t tick;

	assert_param(IS_CAN_ALL(hperh->perh));
	assert_param(IS_CAN_FIFO(num));

	__LOCK(hperh);
	SET_BIT(hperh->state, CAN_STATE_RX_MASK);
	tick = __get_tick();

	while (CAN_RX_MSG_PENDING(hperh, num) == 0) {
		if ((timeout == 0) || ((__get_tick() - tick) > timeout)) {
			hperh->state = CAN_STATE_TIMEOUT;
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}

	msg->type = (can_id_type_t)hperh->perh->RxFIFO[num].RIR.IDE;

	if (msg->type == CAN_ID_STD)
		msg->std = hperh->perh->RxFIFO[num].RIR.STID;
	else
		msg->ext = (hperh->perh->RxFIFO[num].RIR.STID << 18) | hperh->perh->RxFIFO[num].RIR.EXID;

	msg->rtr     = (can_remote_req_t)hperh->perh->RxFIFO[num].RIR.RTR;
	msg->len     = hperh->perh->RxFIFO[num].RDTR.DLC;
	msg->fmi     = hperh->perh->RxFIFO[num].RDTR.FMI;
	msg->data[0] = hperh->perh->RxFIFO[num].RDLR.DATA_0;
	msg->data[1] = hperh->perh->RxFIFO[num].RDLR.DATA_1;
	msg->data[2] = hperh->perh->RxFIFO[num].RDLR.DATA_2;
	msg->data[3] = hperh->perh->RxFIFO[num].RDLR.DATA_3;
	msg->data[4] = hperh->perh->RxFIFO[num].RDHR.DATA_4;
	msg->data[5] = hperh->perh->RxFIFO[num].RDHR.DATA_5;
	msg->data[6] = hperh->perh->RxFIFO[num].RDHR.DATA_6;
	msg->data[7] = hperh->perh->RxFIFO[num].RDHR.DATA_7;

	CAN_RX_FIFO_RELEASE(hperh, num);
	CLEAR_BIT(hperh->state, CAN_STATE_RX_MASK);
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Receives a correct CAN frame using interrupt.
  * @param  hperh: pointer to a can_handle_t structure.
  * @param  num: Specify the FIFO number
  * @param  msg: Storing message.
  * @retval Status, see hal_status_t.
  */
hal_status_t can_recv_by_it(can_handle_t *hperh, can_rx_fifo_t num, can_rx_msg_t *msg)
{
	assert_param(IS_CAN_ALL(hperh->perh));
	assert_param(IS_CAN_FIFO(num));

	if ((hperh->state != CAN_STATE_READY) && (hperh->state != CAN_STATE_BUSY_TX))
		return BUSY;

	SET_BIT(hperh->state, CAN_STATE_RX_MASK);
	hperh->rx_msg = msg;

	can_interrupt_config(hperh, CAN_IT_EWG, ENABLE);
	can_interrupt_config(hperh, CAN_IT_EPV, ENABLE);
	can_interrupt_config(hperh, CAN_IT_BOF, ENABLE);
	can_interrupt_config(hperh, CAN_IT_LEC, ENABLE);
	can_interrupt_config(hperh, CAN_IT_ERR, ENABLE);

	if (num == CAN_RX_FIFO0)
		can_interrupt_config(hperh,CAN_IT_FMP0, ENABLE);
	else
		can_interrupt_config(hperh,CAN_IT_FMP1, ENABLE);

	return OK;
}
/**
  * @}
  */

/** @defgroup CAN_Public_Functions_Group3 Peripheral Control functions
  * @brief    Peripheral Control functions
  *
  * @verbatim
  ==============================================================================
            ##### Peripheral Control functions #####
  ==============================================================================
    [..]
    This section provides functions allowing to:
      (+) Configure CAN sleep.
      (+) Configure CAN wakeup.
      (+) CAN cancel send message.
      (+) Handle CAN interrupt.
      (+) Get CAN transmit status.
      (+) Configure CAN interrupt ENABLE/DISABLE.
      (+) Get CAN interrupt source status.
      (+) Get CAN interrupt flag status.
      (+) Clear CAN interrupt flag.

  * @endverbatim
  * @{
  */

/**
  * @brief  Enters the Sleep(low power) mode.
  * @param  hperh: pointer to a can_handle_t.
  * @retval Status, see hal_status_t.
  */
hal_status_t can_sleep(can_handle_t *hperh)
{
	uint32_t tick;

	assert_param(IS_CAN_ALL(hperh->perh));

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

	hperh->perh->MCR.INRQ  = 0;
	hperh->perh->MCR.SLEEP = 1;
	tick = __get_tick();

	while ((hperh->perh->MSR.SLAK == 0) || (hperh->perh->MSR.INAK == 1)) {
		if ((__get_tick() - tick) > CAN_TIMEOUT_VALUE) {
			hperh->state = CAN_STATE_TIMEOUT;
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}

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

	return OK;
}

/**
  * @brief  Wakes up the CAN peripheral from sleep mode, after that the CAN peripheral
  *         is in the normal mode.
  * @param  hperh: pointer to a can_handle_t structure.
  * @retval Status, see hal_status_t.
  */
hal_status_t can_wake_up(can_handle_t *hperh)
{
	uint32_t tick;

	assert_param(IS_CAN_ALL(hperh->perh));

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

	hperh->perh->MCR.SLEEP = 0;
	tick = __get_tick();

	while (hperh->perh->MSR.SLAK) {
		if ((__get_tick() - tick) > CAN_TIMEOUT_VALUE) {
			hperh->state = CAN_STATE_TIMEOUT;
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}

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

	return OK;
}

/**
  * @brief  Handles CAN interrupt request
  * @param  hperh: pointer to a can_handle_t structure.
  * @retval None
  */
void can_irq_handler(can_handle_t* hperh)
{
	if (can_get_it_status(hperh, CAN_IT_TME)) {
		if ((can_get_tx_status(hperh, CAN_TX_MAILBOX_0))
                     || (can_get_tx_status(hperh, CAN_TX_MAILBOX_1))
                     || (can_get_tx_status(hperh, CAN_TX_MAILBOX_2)))
			__can_send_by_it(hperh);
	}

	if ((can_get_it_status(hperh, CAN_IT_FMP0))
                   && (CAN_RX_MSG_PENDING(hperh, CAN_RX_FIFO0) != 0))
		__can_recv_by_it(hperh, CAN_RX_FIFO0);

	if ((can_get_it_status(hperh, CAN_IT_FMP1))
                   && (CAN_RX_MSG_PENDING(hperh, CAN_RX_FIFO1) != 0))
		__can_recv_by_it(hperh, CAN_RX_FIFO1);

	if ((can_get_flag_status(hperh, CAN_FLAG_EWG))
                     && (can_get_it_status(hperh, CAN_IT_EWG))
                     && (can_get_it_status(hperh, CAN_IT_ERR)))
		hperh->err |= CAN_ERROR_EWG;

	if ((can_get_flag_status(hperh, CAN_FLAG_EPV))
                     && (can_get_it_status(hperh, CAN_IT_EPV))
                     && (can_get_it_status(hperh, CAN_IT_ERR)))
		hperh->err |= CAN_ERROR_EPV;

	if ((can_get_flag_status(hperh, CAN_FLAG_BOF))
                     && (can_get_it_status(hperh, CAN_IT_BOF))
                     && (can_get_it_status(hperh, CAN_IT_ERR)))
		hperh->err |= CAN_ERROR_BOF;

	if ((!(hperh->perh->ESR.LEC == 0))
                   && (can_get_it_status(hperh, CAN_IT_LEC))
                   && (can_get_it_status(hperh, CAN_IT_ERR))) {

		switch (hperh->perh->ESR.LEC) {
		case(1):
			hperh->err |= CAN_ERROR_STF;
			break;
		case(2):
			hperh->err |= CAN_ERROR_FOR;
			break;
		case(3):
			hperh->err |= CAN_ERROR_ACK;
			break;
		case(4):
			hperh->err |= CAN_ERROR_BR;
			break;
		case(5):
			hperh->err |= CAN_ERROR_BD;
			break;
		case(6):
			hperh->err |= CAN_ERROR_CRC;
			break;
		default:
			break;
		}

		hperh->perh->ESR.LEC = 0;
	}

	if (hperh->err != CAN_ERROR_NONE) {
		hperh->perh->MSR.ERRI = 1;
		hperh->state = CAN_STATE_READY;

		if (hperh->error_cbk != NULL)
			hperh->error_cbk(hperh);
	}
}

/**
  * @brief  Check the transmission status of a CAN Frame.
  * @param  hperh: pointer to a can_handle_t structure.
  * @param  box: the index of the mailbox that is used for transmission.
  * @retval The new status of transmission(TRUE or FALSE).
  */
type_bool_t can_get_tx_status(can_handle_t *hperh, can_tx_mailbox_t box)
{
	assert_param(IS_CAN_ALL(hperh->perh));
	assert_param(IS_CAN_TX_MAILBOX(box));

	switch (box) {
	case CAN_TX_MAILBOX_0:
		if (hperh->perh->TSR.RQCP0 && hperh->perh->TSR.TXOK0 && hperh->perh->TSR.TME0)
			return TRUE;
		else
			return FALSE;
	case CAN_TX_MAILBOX_1:
		if (hperh->perh->TSR.RQCP1 && hperh->perh->TSR.TXOK1 && hperh->perh->TSR.TME1)
			return TRUE;
		else
			return FALSE;
	case CAN_TX_MAILBOX_2:
		if (hperh->perh->TSR.RQCP2 && hperh->perh->TSR.TXOK2 && hperh->perh->TSR.TME2)
			return TRUE;
		else
			return FALSE;
	default:
		break;
	}

	return FALSE;
}

/**
  * @brief  Cancel transmission.
  * @param  hperh: pointer to a can_handle_t structure.
  * @param  box: the index of the mailbox that is used for transmission.
  * @retval None
  */
void can_cancel_send(can_handle_t *hperh, can_tx_mailbox_t box)
{
	assert_param(IS_CAN_ALL(hperh->perh));
	assert_param(IS_CAN_TX_MAILBOX(box));

	switch (box) {
	case CAN_TX_MAILBOX_0:
		hperh->perh->TSR.ABRQ0 = 1;
		break;
	case CAN_TX_MAILBOX_1:
		hperh->perh->TSR.ABRQ1 = 1;
		break;
	case CAN_TX_MAILBOX_2:
		hperh->perh->TSR.ABRQ2 = 1;
		break;
	default:
		break;
	}

	return;
}

/**
  * @brief  Enable/disable the specified CAN interrupts.
  * @param  hperh: Pointer to a can_handle_t structure.
  * @param  it: Specifies the CAN interrupt sources to be enabled or disabled.
  *         This parameter can be one of the @ref can_it_t.
  * @param  state: New state of the specified CAN interrupts.
  *         This parameter can be:
  *             @arg ENABLE
  *             @arg DISABLE
  * @retval None
  */
void can_interrupt_config(can_handle_t *hperh, can_it_t it, type_func_t state)
{
	assert_param(IS_CAN_ALL(hperh->perh));
	assert_param(IS_CAN_IT(it));
	assert_param(IS_FUNC_STATE(state));

	if (state == ENABLE)
		hperh->perh->IER.Word |= it;
	else
		hperh->perh->IER.Word &= ~it;

	return;
}

/**
  * @brief  Get the status of CAN interrupt source.
  * @param  hperh: Pointer to a can_handle_t structure.
  * @param  it: Specifies the CAN interrupt source.
  *         This parameter can be one of the @ref can_it_t.
  * @retval Status:
  *           - 0: RESET
  *           - 1: SET
  */
it_status_t can_get_it_status(can_handle_t *hperh, can_it_t it)
{
	assert_param(IS_CAN_ALL(hperh->perh));
	assert_param(IS_CAN_IT(it));

	if (hperh->perh->IER.Word & it)
		return SET;

	return RESET;
}

/**
  * @brief  Get the status of CAN interrupt flag.
  * @param  hperh: Pointer to a can_handle_t structure.
  * @param  flag: Specifies the CAN interrupt flag.
  *         This parameter can be one of the @ref can_flag_t.
  * @retval Status:
  *           - 0: RESET
  *           - 1: SET
  */
flag_status_t can_get_flag_status(can_handle_t *hperh, can_flag_t flag)
{
	uint32_t idx   = (flag >> 20) & 0x7;
	uint32_t _flag = flag & 0xFF8FFFFF;

	assert_param(IS_CAN_GET_FLAG(flag));

	switch (idx) {
	case 0:
		if (hperh->perh->MSR.Word & _flag)
			return SET;

		break;
	case 1:
		if (hperh->perh->TSR.Word & _flag)
			return SET;

		break;
	case 2:
		if (hperh->perh->RF0R.Word & _flag)
			return SET;

		break;
	case 3:
		if (hperh->perh->RF1R.Word & _flag)
			return SET;

		break;
	case 4:
		if (hperh->perh->ESR.Word & _flag)
			return SET;

		break;
	default:
		break;
	}

	return RESET;
}

/** @brief  Clear the specified CAN pending flag.
  * @param  hperh: pointer to a can_handle_t structure.
  * @param  flag: specifies the flag to check.
  * @retval None.
  */
void can_clear_flag(can_handle_t *hperh, can_flag_t flag)
{
	uint32_t idx   = (flag >> 20) & 0x7;
	uint32_t _flag = flag & 0xFF8FFFFF;

	assert_param(IS_CAN_CLEAR_FLAG(flag));

	switch (idx) {
	case 0:
		hperh->perh->MSFCR.Word = _flag;
		break;
	case 1:
		hperh->perh->TSFCR.Word = _flag;
		break;
	case 2:
		hperh->perh->RF0CR.Word = _flag;
		break;
	case 3:
		hperh->perh->RF1CR.Word = _flag;
		break;
	default:
		break;
	}

	return;
}
/**
  * @}
  */

/** @defgroup CAN_Public_Functions_Group4 Peripheral State and Error functions
  * @brief    CAN Peripheral State functions
  *
  * @verbatim
  ==============================================================================
            ##### Peripheral State and Error functions #####
  ==============================================================================
    [..]
    This subsection provides functions allowing to:
      (+) Check the CAN state.
      (+) Check CAN Errors detected during interrupt process

  * @endverbatim
  * @{
  */
/**
  * @brief  return the CAN state
  * @param  hperh: pointer to a can_handle_t structure.
  * @retval Status, see can_state_t.
  */
can_state_t can_get_state(can_handle_t *hperh)
{
	return hperh->state;
}

/**
  * @brief  Return the CAN error code
  * @param  hperh: pointer to a can_handle_t structure.
  * @retval CAN Error Code
  */
can_error_t can_get_error(can_handle_t *hperh)
{
	return hperh->err;
}

/**
  * @}
  */

/**
  * @}
  */

/** @defgroup CAN_Private_Functions CAN Private Functions
  * @{
  */
/**
  * @brief  transmits a CAN frame message using interrupt.
  * @param  hperh: pointer to a can_handle_t structure.
  * @retval Status, see hal_status_t.
  */
static hal_status_t __can_send_by_it(can_handle_t *hperh)
{
	can_interrupt_config(hperh, CAN_IT_TME, DISABLE);

	if (hperh->state == CAN_STATE_BUSY_TX) {
		can_interrupt_config(hperh, CAN_IT_EWG, DISABLE);
		can_interrupt_config(hperh, CAN_IT_EPV, DISABLE);
		can_interrupt_config(hperh, CAN_IT_BOF, DISABLE);
		can_interrupt_config(hperh, CAN_IT_LEC, DISABLE);
		can_interrupt_config(hperh, CAN_IT_ERR, DISABLE);
	}

	CLEAR_BIT(hperh->state, CAN_STATE_TX_MASK);

	if (hperh->tx_cplt_cbk)
		hperh->tx_cplt_cbk(hperh);

	return OK;
}

/**
  * @brief  Receives a correct CAN frame using interrupt.
  * @param  hperh:  Pointer to a can_handle_t structure.
  * @param  num: Specify the FIFO number
  * @retval Status, see hal_status_t.
  */
static hal_status_t __can_recv_by_it(can_handle_t *hperh, uint8_t num)
{
	hperh->rx_msg->type = (can_id_type_t)hperh->perh->RxFIFO[num].RIR.IDE;

	if (hperh->rx_msg->type == CAN_ID_STD)
		hperh->rx_msg->std = hperh->perh->RxFIFO[num].RIR.STID;
	else
		hperh->rx_msg->ext = (hperh->perh->RxFIFO[num].RIR.STID << 18) | hperh->perh->RxFIFO[num].RIR.EXID;

	hperh->rx_msg->rtr     = (can_remote_req_t)hperh->perh->RxFIFO[num].RIR.RTR;
	hperh->rx_msg->len     = hperh->perh->RxFIFO[num].RDTR.DLC;
	hperh->rx_msg->fmi     = hperh->perh->RxFIFO[num].RDTR.FMI;
	hperh->rx_msg->data[0] = hperh->perh->RxFIFO[num].RDLR.DATA_0;
	hperh->rx_msg->data[1] = hperh->perh->RxFIFO[num].RDLR.DATA_1;
	hperh->rx_msg->data[2] = hperh->perh->RxFIFO[num].RDLR.DATA_2;
	hperh->rx_msg->data[3] = hperh->perh->RxFIFO[num].RDLR.DATA_3;
	hperh->rx_msg->data[4] = hperh->perh->RxFIFO[num].RDHR.DATA_4;
	hperh->rx_msg->data[5] = hperh->perh->RxFIFO[num].RDHR.DATA_5;
	hperh->rx_msg->data[6] = hperh->perh->RxFIFO[num].RDHR.DATA_6;
	hperh->rx_msg->data[7] = hperh->perh->RxFIFO[num].RDHR.DATA_7;

	if (num == CAN_RX_FIFO0) {
		CAN_RX_FIFO_RELEASE(hperh, CAN_RX_FIFO0);
		can_interrupt_config(hperh, CAN_IT_FMP0, DISABLE);
	}
	else {
		CAN_RX_FIFO_RELEASE(hperh, CAN_RX_FIFO1);
		can_interrupt_config(hperh, CAN_IT_FMP1, DISABLE);
	}

	if (hperh->state == CAN_STATE_BUSY_RX) {
		can_interrupt_config(hperh, CAN_IT_EWG, DISABLE);
		can_interrupt_config(hperh, CAN_IT_EPV, DISABLE);
		can_interrupt_config(hperh, CAN_IT_BOF, DISABLE);
		can_interrupt_config(hperh, CAN_IT_LEC, DISABLE);
		can_interrupt_config(hperh, CAN_IT_ERR, DISABLE);
	}

	CLEAR_BIT(hperh->state, CAN_STATE_RX_MASK);

	if (hperh->rx_cplt_cbk)
		hperh->rx_cplt_cbk(hperh);

	return OK;
}
/**
 * @}
 */

#endif /* HAL_CAN */

/**
  * @}
  */

/**
  * @}
  */
