#include "Driver_CAN.h"

#define ARM_CAN_DRV_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(1,0) // CAN driver version

// Driver Version
static const ARM_DRIVER_VERSION can_driver_version = { ARM_CAN_API_VERSION, ARM_CAN_DRV_VERSION };

// Driver Capabilities
static const ARM_CAN_CAPABILITIES can_driver_capabilities = {
  32U,  // Number of CAN Objects available
  1U,   // Supports reentrant calls to ARM_CAN_MessageSend, ARM_CAN_MessageRead, ARM_CAN_ObjectConfigure and abort message sending used by ARM_CAN_Control.
  0U,   // Does not support CAN with Flexible Data-rate mode (CAN_FD)
  0U,   // Does not support restricted operation mode
  1U,   // Supports bus monitoring mode
  1U,   // Supports internal loopback mode
  1U,   // Supports external loopback mode
};

// Object Capabilities
static const ARM_CAN_OBJ_CAPABILITIES can_object_capabilities = {
  1U,   // Object supports transmission
  1U,   // Object supports reception
  0U,   // Object does not support RTR reception and automatic Data transmission
  0U,   // Object does not support RTR transmission and automatic Data reception
  1U,   // Object allows assignment of multiple filters to it
  1U,   // Object supports exact identifier filtering
  0U,   // Object does not support range identifier filtering
  1U,   // Object supports mask identifier filtering
  3U    // Object can buffer 3 messages
};

static uint8_t                     can_driver_powered     = 0U;
static uint8_t                     can_driver_initialized = 0U;
static ARM_CAN_SignalUnitEvent_t   CAN_SignalUnitEvent    = NULL;
static ARM_CAN_SignalObjectEvent_t CAN_SignalObjectEvent  = NULL;

//
//   Functions
//

static ARM_DRIVER_VERSION CAN_GetVersion (void) {
  // Return driver version
  return can_driver_version;
}

static ARM_CAN_CAPABILITIES CAN_GetCapabilities (void) {
  // Return driver capabilities
  return can_driver_capabilities;
}

static int32_t CAN_Initialize (ARM_CAN_SignalUnitEvent_t   cb_unit_event,
                               ARM_CAN_SignalObjectEvent_t cb_object_event) {

  if (can_driver_initialized != 0U) { return ARM_DRIVER_OK; }

  CAN_SignalUnitEvent   = cb_unit_event;
  CAN_SignalObjectEvent = cb_object_event;

  // Add code for pin, memory, RTX objects initialization
  // ..

  can_driver_initialized = 1U;

  return ARM_DRIVER_OK;
}

static int32_t CAN_Uninitialize (void) {

  // Add code for pin, memory, RTX objects de-initialization
  // ..

  can_driver_initialized = 0U;

  return ARM_DRIVER_OK;
}

static int32_t CAN_PowerControl (ARM_POWER_STATE state) {
  switch (state) {
    case ARM_POWER_OFF:
      can_driver_powered = 0U;
      // Add code to disable interrupts and put peripheral into reset mode,
      // and if possible disable clock
      // ..

    case ARM_POWER_FULL:
      if (can_driver_initialized == 0U) { return ARM_DRIVER_ERROR; }
      if (can_driver_powered     != 0U) { return ARM_DRIVER_OK;    }

      // Add code to enable clocks, reset variables enable interrupts
      // and put peripheral into operational
      // ..

      can_driver_powered = 1U;
      break;

    default:
      // Other states are not supported
      return ARM_DRIVER_ERROR_UNSUPPORTED;
  }

  return ARM_DRIVER_OK;
}

uint32_t CAN_GetClock (void) {

  // Add code to return peripheral clock frequency
  // ..
}

static int32_t CAN_SetBitrate (ARM_CAN_BITRATE_SELECT select, uint32_t bitrate, uint32_t bit_segments) {

  if (can_driver_powered == 0U) { return ARM_DRIVER_ERROR; }

  // Add code to setup peripheral parameters to generate specified bitrate
  // with specified bit segments
  // ..

  return ARM_DRIVER_OK;
}

static int32_t CAN_SetMode (ARM_CAN_MODE mode) {

  if (can_driver_powered == 0U) { return ARM_DRIVER_ERROR; }

  switch (mode) {
    case ARM_CAN_MODE_INITIALIZATION:
      // Add code to put peripheral into initialization mode
      // ..
      break;
    case ARM_CAN_MODE_NORMAL:
      // Add code to put peripheral into normal operation mode
      // ..
      break;
    case ARM_CAN_MODE_RESTRICTED:
      // Add code to put peripheral into restricted operation mode
      // ..
      break;
    case ARM_CAN_MODE_MONITOR:
      // Add code to put peripheral into bus monitoring mode
      // ..
      break;
    case ARM_CAN_MODE_LOOPBACK_INTERNAL:
      // Add code to put peripheral into internal loopback mode
      // ..
      break;
    case ARM_CAN_MODE_LOOPBACK_EXTERNAL:
      // Add code to put peripheral into external loopback mode
      // ..
      break;
    default:
      // Handle unknown mode code
      return ARM_DRIVER_ERROR_UNSUPPORTED;
  }

  return ARM_DRIVER_OK;
}

ARM_CAN_OBJ_CAPABILITIES CAN_ObjectGetCapabilities (uint32_t obj_idx) {
  // Return object capabilities
  return can_object_capabilities;
}

static int32_t CAN_ObjectSetFilter (uint32_t obj_idx, ARM_CAN_FILTER_OPERATION operation, uint32_t id, uint32_t arg) {

  if (can_driver_powered == 0U) { return ARM_DRIVER_ERROR; }

  switch (operation) {
    case ARM_CAN_FILTER_ID_EXACT_ADD:
      // Add code to setup peripheral to receive messages with specified exact ID
      break;
    case ARM_CAN_FILTER_ID_MASKABLE_ADD:
      // Add code to setup peripheral to receive messages with specified maskable ID
      break;
    case ARM_CAN_FILTER_ID_RANGE_ADD:
      // Add code to setup peripheral to receive messages within specified range of IDs
      break;
    case ARM_CAN_FILTER_ID_EXACT_REMOVE:
      // Add code to remove specified exact ID from being received by peripheral
      break;
    case ARM_CAN_FILTER_ID_MASKABLE_REMOVE:
      // Add code to remove specified maskable ID from being received by peripheral
      break;
    case ARM_CAN_FILTER_ID_RANGE_REMOVE:
      // Add code to remove specified range of IDs from being received by peripheral
      break;
    default:
      // Handle unknown operation code
      return ARM_DRIVER_ERROR_UNSUPPORTED;
  }

  return ARM_DRIVER_OK;
}

static int32_t CAN_ObjectConfigure (uint32_t obj_idx, ARM_CAN_OBJ_CONFIG obj_cfg) {

  if (can_driver_powered == 0U) { return ARM_DRIVER_ERROR; }

  switch (obj_cfg) {
    case ARM_CAN_OBJ_INACTIVE:
      // Deactivate object
      // ..
      break;
    case ARM_CAN_OBJ_RX_RTR_TX_DATA:
      // Setup object to automatically return data when RTR with it's ID is received
      // ..
      break;
    case ARM_CAN_OBJ_TX_RTR_RX_DATA:
      // Setup object to send RTR and receive data response
      // ..
      break;
    case ARM_CAN_OBJ_TX:
      // Setup object to be used for sending messages
      // ..
      break;
    case ARM_CAN_OBJ_RX:
      // Setup object to be used for receiving messages
      // ..
      break;
    default:
      // Handle unknown object configuration code
      return ARM_DRIVER_ERROR_UNSUPPORTED;
  }

  return ARM_DRIVER_OK;
}

static int32_t CAN_MessageSend (uint32_t obj_idx, ARM_CAN_MSG_INFO *msg_info, const uint8_t *data, uint8_t size) {

  if (can_driver_powered == 0U) { return ARM_DRIVER_ERROR; }

  // Add code to send requested message
  // ..

  return ((int32_t)size);
}

static int32_t CAN_MessageRead (uint32_t obj_idx, ARM_CAN_MSG_INFO *msg_info, uint8_t *data, uint8_t size) {

  if (can_driver_powered == 0U) { return ARM_DRIVER_ERROR;  }

  // Add code to read previously received message
  // (reception was started when object was configured for reception)
  // ..

  return ((int32_t)size);
}

static int32_t CAN_Control (uint32_t control, uint32_t arg) {

  if (can_driver_powered == 0U) { return ARM_DRIVER_ERROR; }

  switch (control & ARM_CAN_CONTROL_Msk) {
    case ARM_CAN_ABORT_MESSAGE_SEND:
      // Add code to abort message pending to be sent
      // ..
      break;
    case ARM_CAN_SET_FD_MODE:
      // Add code to enable Flexible Data-rate mode
      // ..
      break;
    case ARM_CAN_SET_TRANSCEIVER_DELAY:
      // Add code to set transceiver delay
      // ..
      break;
    default:
      // Handle unknown control code
      return ARM_DRIVER_ERROR_UNSUPPORTED;
  }

  return ARM_DRIVER_OK;
}

static ARM_CAN_STATUS CAN_GetStatus (void) {

  // Add code to return device bus and error status
  // ..
}


// IRQ handlers
// Add interrupt routines to handle transmission, reception, error and status interrupts
// ..

// CAN driver functions structure

ARM_DRIVER_CAN Driver_CAN = {
  CAN_GetVersion,
  CAN_GetCapabilities,
  CAN_Initialize,
  CAN_Uninitialize,
  CAN_PowerControl,
  CAN_GetClock,
  CAN_SetBitrate,
  CAN_SetMode,
  CAN_ObjectGetCapabilities,
  CAN_ObjectSetFilter,
  CAN_ObjectConfigure,
  CAN_MessageSend,
  CAN_MessageRead,
  CAN_Control,
  CAN_GetStatus
};