#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
};

