//----------------------------------------------------------------------------
//
//
//----------------------------------------------------------------------------


//---Includes-----------------------------------------------------------------

#undef LEPTO_LOG_DEBUG

#include <biwak/can.hpp>
#include <biwak/stm32/can.hpp>
#include <biwak/gpio.hpp>
#include <biwak/rcc.h>           // rccCanClockEnable
#include <biwak/nvic.h>          // setNVInterrupt
#include <biwak/handler.h>       // IRQ
#include <biwak/exception.h>
#include <biwak/timer.hpp>
#include <lepto/log.h>
#include <biwak/biwak.h>
#include <biwak/error_codes.h>
#include <HALWrapper/stm32_can.h>
#include <HALWrapper/stm32_rcc.h>
#include <string.h>              // memset

#include <biwak/heartbeat.hpp>   // lEvent
#include <biwak/dataEvent.h>

#include <HALWrapper/stm32_cortex.h>

#if !defined(LEPTO_CONFIG)
   #define __trouble()
#endif

#undef BIWAK_MEASURE_TRANSMIT_WAIT_LOOPS

/* Not sure yet if this is a good idea
#if ! defined( CONFIG_BIWAK_CAN_TRANSMIT_RETRY )
   #define CONFIG_BIWAK_CAN_TRANSMIT_RETRY               1
#endif
*/


//---Declarations-------------------------------------------------------------


#if defined ( STM32F4 ) || defined( STM32F3 ) || defined( STM32F0 ) || \
    defined ( STM32F1 ) || defined( STM32L4 )
   #define CAN_IMPLEMENTATION_1
#endif

CCan *interruptCan1=nullptr;


/// \brief  Le contructeur
///
///         This constructor does not set anything. you have to call init()
///         afterwards
CCan::CCan( const SCanConfig &_configData, EBitRate bitRate)
   :m_configData(_configData)
   #if 0
   ,m_mailBox(sizeof(SCanMailBox), CAN_MAILBOXES)
   #endif
   ,m_rxBuffer( CAN_BUFFERS )
{
   #if defined ( BIWAK_AUTOMATIC_RCC_ENABLE )
   rccCanClockEnable( m_configData.instance ) ;
   #endif

   #if defined(BIWAK_AUTO_GPIO_INIT)
   CGpio::init( m_configData.eGpioPeripheralTx, m_configData.eGpioPinTx, gpioModeCanTx, m_configData.altFunc );
   CGpio::init( m_configData.eGpioPeripheralRx, m_configData.eGpioPinRx, gpioModeCanRx, m_configData.altFunc );
   #endif

   m_pHandler=new CAN_HandleTypeDef {
      .Instance=m_configData.instance,
      .Init = {
         #if defined ( CAN_IMPLEMENTATION_FDCAN )
            .ClockDivider = FDCAN_CLOCK_DIV1,
            .FrameFormat = FDCAN_FRAME_CLASSIC, // CLASSIC; //FD_BRS;
         #endif
            .Mode = CAN_MODE_NORMAL,      // CAN_MODE_LOOPBACK
         /* CAN cell init */
         #if defined ( CAN_IMPLEMENTATION_1 )
            .TimeTriggeredMode = DISABLE,
            // AutoBusOff DISABLE causes neccessary CAN restarts; highsped sending
            // not possible
            .AutoBusOff = ENABLE,
            .AutoWakeUp = ENABLE,
            .AutoRetransmission = ENABLE,
            .ReceiveFifoLocked = DISABLE,
            .TransmitFifoPriority = DISABLE,
         #elif defined ( CAN_IMPLEMENTATION_2 )
            .TTCM = DISABLE,
            .ABOM = DISABLE,
            .AWUM = DISABLE,
            .NART = DISABLE,
            .RFLM = DISABLE,
            .TXFP = DISABLE,
         #elif defined ( CAN_IMPLEMENTATION_FDCAN )
            .AutoRetransmission = ENABLE,
            .TransmitPause = DISABLE,
            .ProtocolException = DISABLE,
            .NominalPrescaler=0,
            .NominalSyncJumpWidth = 1,
            .NominalTimeSeg1=0,
            .NominalTimeSeg2=0,
            .DataPrescaler = 32,
            .DataSyncJumpWidth = 1,
            .DataTimeSeg1 = 13,
            .DataTimeSeg2 = 2,
            .StdFiltersNbr = 0,
            .ExtFiltersNbr = 0,
            .TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION,
         #else
            #error Unknown CAN implementation
         #endif
      }
   };

   // Assuming we have only one CAN controller
   interruptCan1=this;

   #if ! defined (BIWAK_CAN_DEFER_INIT)
      init(bitRate);
   #endif

   initHeaders();
}

CCan::~CCan()
{
   //delete m_pHandler;
   //m_pHandler=NULL;
}

extern "C"
{
   void CAN1_RX1_IRQHandler();
}


void CCan::init(EBitRate bitRate)
{
   HAL_StatusTypeDef status;

   setNVInterrupt( m_configData.irqnTx, true, NVI_DEFAULT_PRIORITY);
   setNVInterrupt( m_configData.irqnRx0, true, NVI_HIGH_PRIORITY);

   if( m_configData.irqnRx1 )
   {
      setNVInterrupt( m_configData.irqnRx1, true, NVI_HIGH_PRIORITY);
   }

   if( m_configData.irqnMisc )
   {
      setNVInterrupt( m_configData.irqnMisc, true, NVI_DEFAULT_PRIORITY);
   }

   #if defined STM32G4
      m_pHandler->Init.DataSyncJumpWidth=1;
   #else
      m_pHandler->Init.SyncJumpWidth=CAN_SJW_1TQ;
   #endif
   
   #if defined ( CAN_IMPLEMENTATION_1 )
      if ( !calculateRateValues(
                  bitRate,
                  m_pHandler->Init.Prescaler,
                  m_pHandler->Init.TimeSeg1,
                  m_pHandler->Init.TimeSeg2
               ) )
      {
         // Could not calculate CAN bitrate
         exception( );
      }
   #elif defined ( CAN_IMPLEMENTATION_2 )
      // How is speed calculated?
   #elif defined ( CAN_IMPLEMENTATION_FDCAN )
      if ( !calculateRateValues(
                  bitRate,
                  m_pHandler->Init.NominalPrescaler,
                  m_pHandler->Init.NominalTimeSeg1,
                  m_pHandler->Init.NominalTimeSeg2
               ) )
      {
         // Could not calculate CAN bitrate
         exception();
      }
   #else
      #error Unknown CAN implementation
   #endif
      
   #if 0
   lInfo("PS:%X", m_pHandler->Init.Prescaler);
   lInfo("TS1:%X", m_pHandler->Init.TimeSeg1);
   lInfo("TS2:%X", m_pHandler->Init.TimeSeg2);
   #endif
      
   status=HAL_CAN_Init( m_pHandler );
   if(status!=HAL_OK)
   {
      // Could not init CAN
      exception( );
   }
   
   // Needed, otherwise everything is blocked.
   initFilter();

   if( ( status=HAL_CAN_Start(m_pHandler) ) != HAL_OK )
   {
      //exception("Could not startCAN",  m_handler.ErrorCode);
      return;
   }

   #if defined( CAN_IMPLEMENTATION_1 ) || defined( CAN_IMPLEMENTATION_FDCAN )
   if( ( status=HAL_CAN_ActivateNotification(m_pHandler,
               CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_RX_FIFO1_MSG_PENDING
      #if defined( CAN_IMPLEMENTATION_FDCAN )
               ,0
      #endif
   ) ) !=HAL_OK )
   {
      // Could not set up notifications
      exception( );
   }
   #endif

   return;
}


void CCan::initHeaders()
{
   /* Prepare Tx message Header */
   #if defined ( CAN_IMPLEMENTATION_1 )
      m_txHeader.DLC = 4;
      m_txHeader.RTR = 0;
   #elif defined ( CAN_IMPLEMENTATION_FDCAN )
      m_txHeader.Identifier = 0x321;
      m_txHeader.IdType = FDCAN_STANDARD_ID;
      m_txHeader.TxFrameType = FDCAN_DATA_FRAME;
      m_txHeader.DataLength = FDCAN_DLC_BYTES_4;
      m_txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
      m_txHeader.BitRateSwitch = FDCAN_BRS_OFF;
      m_txHeader.FDFormat = FDCAN_CLASSIC_CAN;
      m_txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
      m_txHeader.MessageMarker = 0;
   #else
      #error Unknown CAN implementation
   #endif
   return;
}

void CCan::initFilter()
{
   #if defined( CAN_IMPLEMENTATION_FDCAN )
   CAN_FilterTypeDef sFilterConfig{
   #else
   constexpr const CAN_FilterTypeDef sFilterConfig{
   #endif
      #if ! defined CAN_IMPLEMENTATION_FDCAN
      .FilterIdHigh { 0x0000 },
      .FilterIdLow { 0x0000 },
      .FilterMaskIdHigh { 0x0000 },
      .FilterMaskIdLow { 0x0000 },
      .FilterFIFOAssignment { CAN_RX_FIFO0 },
      #endif

      /* CAN filter init */
      #if defined ( CAN_IMPLEMENTATION_1 )
      .FilterBank { 0 },
      #elif defined ( CAN_IMPLEMENTATION_2 )
      .FilterNumber { 0 },
      .BankNumber { 17 },   // Ignored on STM32F3
      #endif
   
      #if defined ( CAN_IMPLEMENTATION_1 ) || defined ( CAN_IMPLEMENTATION_2 )
      .FilterMode { CAN_FILTERMODE_IDMASK },
      //.FilterMode { CAN_FILTERMODE_IDLIST;
      .FilterScale { CAN_FILTERSCALE_32BIT },
      .FilterActivation { ENABLE }, //CAN_FILTER_ENABLE },
      .SlaveStartFilterBank { 0 },
   
      #elif defined( CAN_IMPLEMENTATION_FDCAN )
      /* Configure reception filter to Rx FIFO 0 on both FDCAN instances */
      .IdType { FDCAN_STANDARD_ID },
      .FilterIndex { 0 },
      .FilterType { FDCAN_FILTER_MASK },
      //.FilterType { FDCAN_FILTER_RANGE },
      .FilterConfig { FDCAN_FILTER_TO_RXFIFO0 },
      .FilterID1 { 0x000 },    // Filter
      .FilterID2 { 0x000 },    // Mask
   #endif
   };
   
   #if defined ( CAN_IMPLEMENTATION_1 ) || defined ( CAN_IMPLEMENTATION_2 )
   
   if (HAL_CAN_ConfigFilter(m_pHandler, &sFilterConfig) != HAL_OK)
   {
      //Could not set CAN filter
      exception( );
   }
   
   #elif defined( CAN_IMPLEMENTATION_FDCAN )
   
   if (HAL_FDCAN_ConfigFilter(m_pHandler, &sFilterConfig) != HAL_OK)
   {
      // Could not set CAN filter
     exception();
   }
   sFilterConfig.IdType = FDCAN_EXTENDED_ID;
   sFilterConfig.FilterIndex = 1;
   if (HAL_FDCAN_ConfigFilter(m_pHandler, &sFilterConfig) != HAL_OK)
   {
     // Could not set CAN filter
     exception();
   }
   #else
      #error Unknown CAN implementation
   #endif

   return;
}

#if defined STM32F042xx

bool CCan::calculateRateValues( EBitRate eBitRate, uint32_t &prescaler, uint32_t &seg1, uint32_t &seg2 )
{
   prescaler=3 * (int)eBitRate;
   seg1=0xC0000;
   seg2=0x100000;

   return(true);
}

#else

bool CCan::calculateRateValues( EBitRate eBitRate, uint32_t &prescaler, uint32_t &seg1, uint32_t &seg2 )
{
   int clock=HAL_RCC_GetPCLK1Freq();
   int bitRate=getBitRate( eBitRate );
   
   // In most cases its 13/2 for sampling point at 87.5%
   seg1=13;
   seg2=2;

   // See http://www.bittiming.can-wiki.info/
   // Because prescaler=clock/speed/divider we need an divider that produces an integer.
   // Don't waste much time; only check these combinations: { {13,2}, {10,1}, {8,1}, {6,1} }, hopefully the first one fits
   while(seg1>=6)
   {
      int divider=seg1+seg2+1;
      if( ( (clock / bitRate ) % divider ) == 0 )
      {
         prescaler = clock / bitRate / divider;

         // Need to encode values to special bitfields via macros?
         #if defined CAN_BS1_1TQ
            switch( seg1 )
            {
               case 13:
                  seg1=CAN_BS1_13TQ;
                  break;
               case 8:
                  seg1=CAN_BS1_8TQ;
                  break;
               default:
                  exception();
                  break;
            }
            switch( seg2 )
            {
               case 1:
                  seg2=CAN_BS2_1TQ;
                  break;
               case 2:
                  seg2=CAN_BS2_2TQ;
                  break;
               default:
                  exception();
                  break;
            }
         #endif
         return(true);
      }
      if(seg2>=2)
         {seg2--; seg1--;}
      seg1-=2;
   }
   return(false);
}
#endif

void CCan::deinit()
{
   HAL_CAN_DeInit(m_pHandler);
   
   #if defined ( CAN_IMPLEMENTATION_1 ) || defined ( CAN_IMPLEMENTATION_FDCAN )
   #else
      if(m_handler.pTxMsg)
      {
         delete m_handler.pTxMsg;
         m_handler.pTxMsg=NULL;
      }

      if(m_handler.pRxMsg)
      {
         delete m_handler.pRxMsg;
         m_handler.pRxMsg=NULL;
      }
   #endif
   return;
}

int CCan::transmit(const SCanMessage &message)
{
   return(transmit (message.getId(), message.getLen(), message.getData()));
};

int CCan::transmit(canId_t id, int len, const void *data)
{
   uint8_t status=0;

   #if defined ( CAN_IMPLEMENTATION_1 )
      m_txHeader.StdId= id;
      m_txHeader.ExtId= id;
      m_txHeader.DLC = len;
      m_txHeader.IDE = id>0x7ff ? CAN_ID_EXT : CAN_ID_STD;

      //if( ! message.isExtended() )
      //   m_txHeader.ExtId=0;

      m_txHeader.RTR = CAN_RTR_DATA;
   #elif defined ( CAN_IMPLEMENTATION_2 )
      m_handler.pTxMsg->StdId= id;
      m_handler.pTxMsg->ExtId= id;
      m_handler.pTxMsg->DLC = len;
      m_handler.pTxMsg->IDE = id>0x7ff ? CAN_ID_EXT : CAN_ID_STD;

      if( ! message.isExtended() )
         m_handler.pTxMsg->ExtId=0;

      m_handler.pTxMsg->RTR = CAN_RTR_DATA;
   #elif defined ( CAN_IMPLEMENTATION_FDCAN )
      m_txHeader.Identifier= id;
      // This seemes to have changed in some HAL revisions
      static_assert( FDCAN_DLC_BYTES_8 == 8, "Size representation changed" );
      m_txHeader.DataLength= len;
      m_txHeader.IdType= (m_txHeader.Identifier >= 0x7FF) ? FDCAN_EXTENDED_ID : FDCAN_STANDARD_ID;
   #else
      #error Unknown CAN implementation
   #endif

   #if defined ( CAN_IMPLEMENTATION_1 ) || defined ( CAN_IMPLEMENTATION_FDCAN )
      int cnt=400;  // 2 Seconds should be fine
      while( (!HAL_CAN_GetTxMailboxesFreeLevel(m_pHandler)) && cnt )
      {
         cnt--;
         usleep( USECS_PER_MSEC * 5 );
         //m_errors++;
      }
      
      if( cnt==0 )
      {
         handleTxError( true );
         // Keep running. Can could actually be disconnected.
         // exception( );
         return( EC_BIWAK_CAN_TX_ERROR );
      }
      else
      {
         handleTxError( false );
      }
   #endif

   #if defined ( CAN_IMPLEMENTATION_1 )
      uint32_t txMailBox;
   #endif
      
   #if IS_ENABLED( CONFIG_BIWAK_CAN_TRANSMIT_RETRY )
   cnt=1024;
   do {
   #endif

         #if defined ( CAN_IMPLEMENTATION_1 )
         status=HAL_CAN_AddTxMessage(m_pHandler, &m_txHeader, (uint8_t *)data, &txMailBox);
         #elif defined ( CAN_IMPLEMENTATION_2 )
         status=HAL_CAN_AddTxMessage(m_pHandler, &m_txHeader, (uint8_t *)data );
         #elif defined( CAN_IMPLEMENTATION_FDCAN )
            status=HAL_FDCAN_AddMessageToTxFifoQ( m_pHandler, &m_txHeader, (uint8_t *)data );
         #else
            #error "Unknown CAN hardware-implementation"
         #endif

   #if IS_ENABLED( CONFIG_BIWAK_CAN_TRANSMIT_RETRY )
   }
   while( status && --cnt );
   #endif
   
   #if IS_ENABLED( CONFIG_BIWAK_CAN_TRANSMIT_ERROR_FATAL )
   if( status )
   {
      exception( );
   }
   #endif

   return(status);
}


void CCan::handleTxError( bool isError )
{
   #if IS_ENABLED( CONFIG_BIWAK_CAN_TX_ERROR_EVENT )
   // The log even may get send via CAN again. To avoid the infinity loop just
   // send the error once when the error occurs.
   if(isError)
   {
         // Only send the CAN message once to have the error code
         if(!m_txError)
         {
            lCriticalE( EC_BIWAK_CAN_TX_ERROR, "CTXE" );
         }
         else
         {
            lCritical( "CTXE" );
         }
         m_txError=true;
   }
   else
   {
         m_txError=false;
   }
   #else
      #if IS_ENABLED( CONFIG_BIWAK_HEARTBEAT )
         if(isError)
         {
            logHeartBeat( ELogCategory::Critical | 0x112233 );
         }
      #endif
   #endif // ? BIWAK_CAN_TX_ERROR_EVENT
}


int CCan::getTransmitStatus(int mailBox) const
{
   return(HAL_CAN_GetState(m_pHandler) );
}


int CCan::getLastError(void) const
{
   return(HAL_CAN_GetError(m_pHandler) );
}

ELogCategory operator+ (ELogCategory c, int i)
{
   return( (ELogCategory)((int)c|i) );
}

void CCan::receive()
{
   HAL_StatusTypeDef sta;
   //SCanMailBox *mailBox = (SCanMailBox *)m_mailBox.getCurrentData();
   SCanMessage *message=m_rxBuffer.topEntry();
   
   // m_newMessagesReceived=true;

   if( !message )
   {
      // No free mailbox available
      // lFatalE( EC_BIWAK_CAN_RX_ERROR, LDS("NFMB", "No free mailbox") );
         
      // This should not happen; you should get one which is not pushible
      exception();
   }
   
   if ( ( sta = HAL_CAN_GetRxMessage(m_pHandler, CAN_RX_FIFO0, &m_rxHeader, message->getData()) ) != HAL_OK)
   {
      /* Reception Error; could not receive can message */
      exception();
   }
   #if defined ( CAN_IMPLEMENTATION_1 )
      message->setId( m_rxHeader.IDE == CAN_ID_EXT ?  m_rxHeader.ExtId : m_rxHeader.StdId );
      message->setLen( m_rxHeader.DLC );
   #else
      message->setId( m_rxHeader.Identifier );
      #if defined(FDCAN_DLC_BYTES_8)
      
         // Older HAL implementation had shifted values in DataLength; it had
         // to be right-shifted "DataLength >> 16" to get real size;
         static_assert( FDCAN_DLC_BYTES_8 == 8, "HAL implementation changed; to be implemented" );
      
         // Caution: Values > 8 are not linear/shiftable; use macros!
         message->setLen( m_rxHeader.DataLength );
         lAssert( m_rxHeader.DataLength <= 8 );
      #else
         message->setLen( m_rxHeader.DataLength );
      #endif
   #endif

   if( ! m_rxBuffer.pushable()  )
   {
      // could not push RX buffer
      lWarningE( EC_BIWAK_CAN_RX_BUFFER_FULL, LDS("CRXF", "Receive buffer full" ) );
   }
   else
   {
      m_rxBuffer.pushEntry();
   }
   lDebug( "CAN ISR RX: ID:0x%X", m_rxHeader.Identifier );
   
   dataAvailable();
   
   return;
}


void CCan::interrupt()
{
   //m_eventCounter++;
   HAL_CAN_IRQHandler( m_pHandler );
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wattributes"

#ifdef STM32F3
IRQ_Funcion_imp( CAN_TX_IRQHandler )
{
   exception( );
}
#endif


#if defined  STM32F3
IRQ_Funcion_imp( CAN_RX0_IRQHandler )
{
   callInterruptFix(interruptCan1);
}
#endif

#ifdef STM32F3
IRQ_Funcion_imp( CAN_RX1_IRQHandler )
{
   exception( );
}
#endif

// STM32F103
IRQ_Funcion_imp( CAN1_TX_IRQHandler )
{
   // Gets called when notification is turned on
   callInterruptFix(interruptCan1);
}

//
IRQ_Funcion_imp( USB_LP_CAN1_RX0_IRQHandler )
{
   callInterruptFix(interruptCan1);
}

// STM32L4
IRQ_Funcion_imp( CAN1_RX0_IRQHandler )
{
   callInterruptFix(interruptCan1);
}

IRQ_Funcion_imp( CAN1_RX1_IRQHandler )
{
   exception();
   callInterruptFix(interruptCan1);
}

IRQ_Funcion_imp( CAN1_SCE_IRQHandler )
{
   exception();
}

#ifdef STM32G4

/**
  * @brief This function handles FDCAN1 interrupt 0.
  */
IRQ_Funcion_imp( FDCAN1_IT0_IRQHandler )
{
   // exception("Exc1");
   if(interruptCan1)
   {
      interruptCan1->interrupt();
   }
   else
   {
      exception();
   }
};

IRQ_Funcion_imp( FDCAN1_IT1_IRQHandler )
{
   // exception("Exc2");
   if(interruptCan1)
   {
      interruptCan1->interrupt();
   }
   else
   {
      exception();
   }
};

#endif

#if defined(STM32F0)
IRQ_Funcion_imp( CEC_CAN_IRQHandler )
{
   callInterrupt(interruptCan1);
}
#endif

#pragma GCC diagnostic pop


/**
  * @brief  Transmission  complete callback in non blocking mode
  * @param  CanHandle: pointer to a CAN_HandleTypeDef structure that contains
  *         the configuration information for the specified CAN.
  * @retval None
  */
/**
  * @brief  Rx Fifo 0 message pending callback
  * @param  hcan: pointer to a CAN_HandleTypeDef structure that contains
  *         the configuration information for the specified CAN.
  * @retval None
  */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
   interruptCan1->receive();
}

#if defined (CAN_IMPLEMENTATION_FDCAN )
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
   HAL_CAN_RxFifo0MsgPendingCallback( hfdcan );
}
#endif

#if 0

void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
   exception();
}

#endif

#if 1

void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan)
{
   // This can happen when holding MCU via SWD
   //exception("Full Callback");
   lWarning("CAN: RX fifo full");
}

void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
   exception( );
}

void HAL_CAN_RxFifo1FullCallback(CAN_HandleTypeDef *hcan)
{
   exception( );
}
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
   //exception( );
}
void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan)
{
   // Gets called when notification is turned on
   // Its OK
   // exception( );
}
void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan)
{
   // Gets called when notification is turned on
   // Its OK
   //exception( );
}
void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan)
{
   // Gets called when notification is turned on
   // Its OK
   //exception( );
}
extern "C" void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan)
{
   exception( );
}
void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan)
{
   exception( );
}
void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan)
{
   exception( );
}
void HAL_CAN_SleepCallback(CAN_HandleTypeDef *hcan)
{
   exception( );
}

void HAL_CAN_WakeUpFromRxMsgCallback(CAN_HandleTypeDef *hcan)
{
   // Its "OK"
   //exception( );
}

#endif

//----------------------------------------------------------------------------
