/***************************************************************************//**
 * @file
 * @brief Controller Area Network API
 *******************************************************************************
 * # License
 * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
 *******************************************************************************
 *
 * SPDX-License-Identifier: Zlib
 *
 * The licensor of this software is Silicon Laboratories Inc.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 ******************************************************************************/

#ifndef EM_CAN_H
#define EM_CAN_H

#include "em_bus.h"
#include "em_device.h"
#include <stdbool.h>

#if defined(CAN_COUNT) && (CAN_COUNT > 0)

#ifdef __cplusplus
extern "C" {
#endif

/***************************************************************************//**
 * @addtogroup emlib
 * @{
 ******************************************************************************/

/***************************************************************************//**
 * @addtogroup CAN
 * @{
 ******************************************************************************/

/*******************************************************************************
 *******************************   DEFINES   ***********************************
 ******************************************************************************/

/*******************************************************************************
 ********************************   ENUMS   ************************************
 ******************************************************************************/

/** CAN Status codes. */
typedef enum {
  /** No error occurred during the last CAN bus event. */
  canErrorNoError  = CAN_STATUS_LEC_NONE,

  /**
   * More than 5 equal bits in a sequence have occurred in a part of a received
   * message where this is not allowed.
   */
  canErrorStuff    = CAN_STATUS_LEC_STUFF,

  /** A fixed format part of a received frame has the wrong format. */
  canErrorForm     = CAN_STATUS_LEC_FORM,

  /** The message this CAN Core transmitted was not acknowledged by another node. */
  canErrorAck      = CAN_STATUS_LEC_ACK,

  /** A wrong monitored bus value : dominant when the module wants to send a recessive. */
  canErrorBit1     = CAN_STATUS_LEC_BIT1,

  /** A wrong monitored bus value : recessive when the module intends to send a dominant. */
  canErrorBit0     = CAN_STATUS_LEC_BIT0,

  /** CRC check sum incorrect. */
  canErrorCrc      = CAN_STATUS_LEC_CRC,

  /** Unused. No new error since the CPU wrote this value. */
  canErrorUnused   = CAN_STATUS_LEC_UNUSED
} CAN_ErrorCode_TypeDef;

/** CAN peripheral mode. */
typedef enum {
  /** CAN peripheral in Normal mode : ready to send and receive messages. */
  canModeNormal,

  /** CAN peripheral in Basic mode : no use of the RAM. */
  canModeBasic,

  /**
   * CAN peripheral in Loopback mode : input from the CAN bus is disregarded
   * and comes from TX instead.
   */
  canModeLoopBack,

  /**
   * CAN peripheral in SilentLoopback mode : input from the CAN bus is
   * disregarded and comes from TX instead ; no output on the CAN bus.
   */
  canModeSilentLoopBack,

  /** CAN peripheral in Silent mode : no output on the CAN bus. If required to
   * send a dominant bit, it's rerouted internally so that the CAN module
   * monitors it but the CAN bus stays recessive.
   */
  canModeSilent
} CAN_Mode_TypeDef;

/*******************************************************************************
 *******************************   STRUCTS   ***********************************
 ******************************************************************************/

/** CAN Message Object TypeDef structure. LSBs is used. */
typedef struct {
  /** A message number of this Message Object, [1 - 32]. */
  uint8_t   msgNum;

  /** ID extended if true, standard if false.  */
  bool      extended;

  /**
   * ID of the message with 11 bits (standard) or 28 bits (extended).
   * LSBs are used for both.
   */
  uint32_t  id;

  /** Data Length Code [0 - 8].  */
  uint8_t   dlc;

  /** A pointer to data, [0 - 8] bytes.  */
  uint8_t   data[8];

  /** A mask for ID filtering. */
  uint32_t  mask;

  /** Enable the use of 'extended' value for filtering. */
  bool      extendedMask;

  /** Enable the use of 'direction' value for filtering. */
  bool      directionMask;
} CAN_MessageObject_TypeDef;

/** CAN initialization structure. */
typedef struct {
  /** True to set the CAN Device in normal mode after initialization. */
  bool      enable;

  /** True to reset messages during initialization. */
  bool      resetMessages;

  /** Default bitrate. */
  uint32_t  bitrate;

  /** Default Propagation Time Segment. */
  uint8_t   propagationTimeSegment;

  /** Default Phase Buffer Segment 1. */
  uint8_t   phaseBufferSegment1;

  /** Default Phase Buffer Segment 2. */
  uint8_t   phaseBufferSegment2;

  /** Default Synchronisation Jump Width. */
  uint8_t   synchronisationJumpWidth;
} CAN_Init_TypeDef;

/**
 * Default initialization of CAN_Init_TypeDef. The total duration of a bit with
 * these default parameters is 10 tq (time quantum : tq = brp/fsys, brp being
 * the baudrate prescaler and being set according to the wanted bitrate, fsys
 * beeing the CAN device frequency).
 */
#define CAN_INIT_DEFAULT                                                      \
  {                                                                           \
    true,     /** Set the CAN Device in normal mode after initialization.  */ \
    true,     /** Reset messages during initialization.         */            \
    100000,   /** Set bitrate to 100 000                        */            \
    1,        /** Set the Propagation Time Segment to 1.         */           \
    4,        /** Set the Phase Buffer Segment 1 to 4.           */           \
    4,        /** Set the Phase Buffer Segment 2 to 4.           */           \
    1         /** Set the Synchronization Jump Width to 1.       */           \
  }

/*******************************************************************************
 *****************************   PROTOTYPES   **********************************
 ******************************************************************************/

void CAN_Init(CAN_TypeDef *can, const CAN_Init_TypeDef *init);

uint32_t CAN_GetClockFrequency(CAN_TypeDef *can);

bool CAN_MessageLost(CAN_TypeDef *can, uint8_t interface, uint8_t msgNum);

void CAN_SetRoute(CAN_TypeDef *can,
                  bool active,
                  uint16_t pinRxLoc,
                  uint16_t pinTxLoc);

void CAN_SetBitTiming(CAN_TypeDef *can,
                      uint32_t bitrate,
                      uint16_t propagationTimeSegment,
                      uint16_t phaseBufferSegment1,
                      uint16_t phaseBufferSegment2,
                      uint16_t synchronisationJumpWidth);

void CAN_SetMode(CAN_TypeDef *can, CAN_Mode_TypeDef mode);

void CAN_SetIdAndFilter(CAN_TypeDef *can,
                        uint8_t interface,
                        bool useMask,
                        const CAN_MessageObject_TypeDef *message,
                        bool wait);

void CAN_ConfigureMessageObject(CAN_TypeDef *can,
                                uint8_t interface,
                                uint8_t msgNum,
                                bool valid,
                                bool tx,
                                bool remoteTransfer,
                                bool endOfBuffer,
                                bool wait);

void CAN_SendMessage(CAN_TypeDef *can,
                     uint8_t interface,
                     const CAN_MessageObject_TypeDef *message,
                     bool wait);

bool CAN_ReadMessage(CAN_TypeDef *can,
                     uint8_t interface,
                     CAN_MessageObject_TypeDef *message);

void CAN_AbortSendMessage(CAN_TypeDef *can,
                          uint8_t interface,
                          uint8_t msgNum,
                          bool wait);

void CAN_ResetMessages(CAN_TypeDef *can, uint8_t interface);

void CAN_Reset(CAN_TypeDef *can);

void CAN_WriteData(CAN_TypeDef *can,
                   uint8_t interface,
                   const CAN_MessageObject_TypeDef *message);

void CAN_SendRequest(CAN_TypeDef *can,
                     uint8_t interface,
                     uint8_t msgNum,
                     bool wait);

/***************************************************************************//**
 * @brief
 *   Enable the Host Controller to send messages.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] enable
 *   True to enable CAN device, false to disable it. If the CAN device is
 *   enabled, it goes to normal mode (the default working mode).
 ******************************************************************************/
__STATIC_INLINE void CAN_Enable(CAN_TypeDef *can, bool enable)
{
  BUS_RegBitWrite(&can->CTRL, _CAN_CTRL_INIT_SHIFT, (enable ? 0 : 1));
}

/***************************************************************************//**
 * @brief
 *   Give the communication capabilities state.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @return
 *   True if the Host Controller can send messages, false otherwise.
 ******************************************************************************/
__STATIC_INLINE bool CAN_IsEnabled(CAN_TypeDef *can)
{
  return (can->CTRL & _CAN_CTRL_INIT_MASK) == 0;
}

/***************************************************************************//**
 * @brief
 *   Waiting function.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] interface
 *   Indicate which Message Interface Register to use.
 *
 ******************************************************************************/
__STATIC_INLINE void CAN_ReadyWait(CAN_TypeDef *can,
                                   uint8_t interface)
{
  while ((_CAN_MIR_CMDREQ_BUSY_MASK & can->MIR[interface].CMDREQ) != 0) {
  }
}

/***************************************************************************//**
 * @brief
 *   Get the last error code and clear its register.
 *
 * @param[in] can
 *   Pointer to CAN peripheral register block.
 *
 * @return
 *   return Last error code.
 ******************************************************************************/
__STATIC_INLINE CAN_ErrorCode_TypeDef CAN_GetLastErrorCode(CAN_TypeDef *can)
{
  CAN_ErrorCode_TypeDef errorCode = (CAN_ErrorCode_TypeDef)
                                    (can->STATUS & _CAN_STATUS_LEC_MASK);
  can->STATUS |= ~_CAN_STATUS_LEC_MASK;
  return errorCode;
}

/***************************************************************************//**
 * @brief
 *   Indicates which message objects have received new data.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @return
 *   State of MESSAGEDATA register indicating which message objects have received
 *   new data.
 ******************************************************************************/
__STATIC_INLINE uint32_t CAN_HasNewdata(CAN_TypeDef *can)
{
  return can->MESSAGEDATA;
}

/***************************************************************************//**
 * @brief
 *   Clear one or more pending CAN status interrupts.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] flags
 *   Pending CAN status interrupt source(s) to clear.
 ******************************************************************************/
__STATIC_INLINE void CAN_StatusIntClear(CAN_TypeDef *can, uint32_t flags)
{
  can->IF1IFC = flags;
}

/***************************************************************************//**
 * @brief
 *   Disable CAN status interrupts.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] flags
 *   CAN status interrupt source(s) to disable.
 ******************************************************************************/
__STATIC_INLINE void CAN_StatusIntDisable(CAN_TypeDef *can, uint32_t flags)
{
  can->IF1IEN &= ~flags;
}

/***************************************************************************//**
 * @brief
 *   Enable CAN status interrupts.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] flags
 *   CAN status interrupt source(s) to enable.
 ******************************************************************************/
__STATIC_INLINE void CAN_StatusIntEnable(CAN_TypeDef *can, uint32_t flags)
{
  can->IF1IEN |= flags;
}

/***************************************************************************//**
 * @brief
 *   Get pending CAN status interrupt flags.
 *
 * @note
 *   This function does not clear event bits.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @return
 *   CAN interrupt source(s) pending.
 ******************************************************************************/
__STATIC_INLINE uint32_t CAN_StatusIntGet(CAN_TypeDef *can)
{
  return can->IF1IF;
}

/***************************************************************************//**
 * @brief
 *   Get pending and enabled CAN status interrupt flags.
 *
 * @note
 *   This function does not clear event bits.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @return
 *   CAN interrupt source(s) pending and enabled.
 ******************************************************************************/
__STATIC_INLINE uint32_t CAN_StatusIntGetEnabled(CAN_TypeDef *can)
{
  uint32_t ien;

  ien = can->IF1IEN;
  return can->IF1IF & ien;
}

/***************************************************************************//**
 * @brief
 *   Set one or more CAN status interrupts.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] flags
 *   CAN status interrupt source(s) to set to pending.
 ******************************************************************************/
__STATIC_INLINE void CAN_StatusIntSet(CAN_TypeDef *can, uint32_t flags)
{
  can->IF1IFS = flags;
}

/***************************************************************************//**
 * @brief
 *   Get CAN status.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @return
 *   A value of CAN register STATUS.
 ******************************************************************************/
__STATIC_INLINE uint32_t CAN_StatusGet(CAN_TypeDef *can)
{
  return can->STATUS & ~_CAN_STATUS_LEC_MASK;
}

/***************************************************************************//**
 * @brief
 *   Clear CAN status.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] flags
 *   CAN status bits to clear.
 ******************************************************************************/
__STATIC_INLINE void CAN_StatusClear(CAN_TypeDef *can, uint32_t flags)
{
  can->STATUS &= ~flags;
}

/***************************************************************************//**
 * @brief
 *   Get the error count.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @return
 *   Error count.
 ******************************************************************************/
__STATIC_INLINE uint32_t CAN_GetErrorCount(CAN_TypeDef *can)
{
  return can->ERRCNT;
}

/***************************************************************************//**
 * @brief
 *   Clear one or more pending CAN message interrupts.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] flags
 *   Pending CAN message interrupt source(s) to clear.
 ******************************************************************************/
__STATIC_INLINE void CAN_MessageIntClear(CAN_TypeDef *can, uint32_t flags)
{
  can->IF0IFC = flags;
}

/***************************************************************************//**
 * @brief
 *   Disable CAN message interrupts.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] flags
 *   CAN message interrupt source(s) to disable.
 ******************************************************************************/
__STATIC_INLINE void CAN_MessageIntDisable(CAN_TypeDef *can, uint32_t flags)
{
  can->IF0IEN &= ~flags;
}

/***************************************************************************//**
 * @brief
 *   Enable CAN message interrupts.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] flags
 *   CAN message interrupt source(s) to enable.
 ******************************************************************************/
__STATIC_INLINE void CAN_MessageIntEnable(CAN_TypeDef *can, uint32_t flags)
{
  can->IF0IEN |= flags;
}

/***************************************************************************//**
 * @brief
 *   Get pending CAN message interrupt flags.
 *
 * @note
 *   This function does not clear event bits.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @return
 *   CAN message interrupt source(s) pending.
 ******************************************************************************/
__STATIC_INLINE uint32_t CAN_MessageIntGet(CAN_TypeDef *can)
{
  return can->IF0IF;
}

/***************************************************************************//**
 * @brief
 *   Get CAN message interrupt flags that are pending and enabled.
 *
 * @note
 *   This function does not clear event bits.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @return
 *   CAN message interrupt source(s) pending and enabled.
 ******************************************************************************/
__STATIC_INLINE uint32_t CAN_MessageIntGetEnabled(CAN_TypeDef *can)
{
  uint32_t ien;

  ien = can->IF0IEN;
  return can->IF0IF & ien;
}

/***************************************************************************//**
 * @brief
 *   Set one or more CAN message interrupts.
 *
 * @param[in] can
 *   A pointer to the CAN peripheral register block.
 *
 * @param[in] flags
 *   CAN message interrupt source(s) to set as pending.
 ******************************************************************************/
__STATIC_INLINE void CAN_MessageIntSet(CAN_TypeDef *can, uint32_t flags)
{
  can->IF0IFS = flags;
}

/** @} (end addtogroup CAN) */
/** @} (end addtogroup emlib) */

#ifdef __cplusplus
}
#endif

#endif /* defined(CAN_COUNT) && (CAN_COUNT > 0) */
#endif /* EM_CAN_H */
