/*
 * File      : drv_lpccan.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2015, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rt-thread.org/license/LICENSE
 *
 * Change Logs:
 * Date           Author       		Notes
 * 2015-06-30     aubrcool@qq.com 	first version
 */
#include <rthw.h>
#include <rtdevice.h>
#include <board.h>
#include <drv_lpccan.h>
#ifdef RT_USING_COMPONENTS_INIT
#include <components.h>
#endif

#ifdef RT_USING_CAN

#include "lpc_types.h"
#include "lpc_can.h"
#include "lpc_pinsel.h"
#include "lpc_exti.h"
#include "lpc_clkpwr.h"

struct lpccandata
{
    en_CAN_unitId id;
};

static LPC_CAN_TypeDef*  lcpcan_get_reg_base(rt_uint32_t id)
{
    LPC_CAN_TypeDef* pCan;

    switch (id)
    {
    case CAN_ID_1:
        pCan = LPC_CAN1;
        break;

    case CAN_ID_2:
        pCan = LPC_CAN2;
        break;
    default:
        pCan = NULL;
    }
    return pCan;
}

static void lpccan_irqstate_init(rt_uint32_t id)
{
    LPC_CAN_TypeDef* pCan = lcpcan_get_reg_base(id);
    volatile rt_int32_t i;

    pCan->MOD = 1; // Enter Reset Mode
    pCan->IER = 0; // Disable All CAN Interrupts
    pCan->GSR = 0;

    /* Request command to release Rx, Tx buffer and clear data overrun */
    //pCan->CMR = CAN_CMR_AT | CAN_CMR_RRB | CAN_CMR_CDO;
    pCan->CMR = (1 << 1) | (1 << 2) | (1 << 3);

    /* Read to clear interrupt pending in interrupt capture register */
    i = pCan->ICR;
    i = i;

    pCan->MOD = 0;// Return Normal operating
}

static rt_err_t lpccan_baud_set(rt_uint32_t id, rt_uint32_t baud)
{
    uint32_t result = 0;
    uint8_t NT, TSEG1, TSEG2;
    uint32_t CANPclk = 0;
    uint32_t BRP;
    LPC_CAN_TypeDef* pCan = lcpcan_get_reg_base(id);

    CANPclk = CLKPWR_GetCLK(CLKPWR_CLKTYPE_PER);
    result = CANPclk / baud;

    /* Calculate suitable nominal time value
     * NT (nominal time) = (TSEG1 + TSEG2 + 3)
     * NT <= 24
     * TSEG1 >= 2*TSEG2
     */
    for(NT = 24; NT > 0; NT = NT-2)
    {
        if ((result % NT) == 0)
        {
            BRP = result / NT - 1;
            NT--;
            TSEG2 = (NT/3) - 1;
            TSEG1 = NT -(NT/3) - 1;
            break;
        }
    }

    /* Enter reset mode */
    pCan->MOD = 0x01;
    /* Set bit timing
     * Default: SAM = 0x00;
     *          SJW = 0x03;
     */
    pCan->BTR = (TSEG2 << 20) | (TSEG1 << 16) | (3 << 14) | BRP;
    /* Return to normal operating */
    pCan->MOD = 0;

    return RT_EOK;
}

static void lpccan_init_alut_ram(void)
{
    //Reset CANAF value
    LPC_CANAF->AFMR = 0x01;
    //clear ALUT RAM
    rt_memset((void *)LPC_CANAF_RAM->mask, 0, 2048);
    LPC_CANAF->SFF_sa = 0;
    LPC_CANAF->SFF_GRP_sa = 0;
    LPC_CANAF->EFF_sa = 0;
    LPC_CANAF->EFF_GRP_sa = 0;
    LPC_CANAF->ENDofTable = 0;
    LPC_CANAF->AFMR = 0x00;
    // Set AF Mode
    CAN_SetAFMode(CAN_NORMAL);
}

#ifdef RT_USING_LPCCAN1
static void lpccan1_turnon_clk(void)
{
    CLKPWR_ConfigPPWR(CLKPWR_PCONP_PCAN1, ENABLE);
}

static void lpccan1_filter_init(struct rt_can_device *can)
{
}

static void lpccan1_hw_init(uint32_t baud, CAN_MODE_Type mode)
{
    if(mode != CAN_SELFTEST_MODE)
    {
#ifndef LPCCAN1_USEING_GPIO_SECOND
        PINSEL_ConfigPin (0, 0, 1);
        PINSEL_ConfigPin (0, 1, 1);
#else
        PINSEL_ConfigPin (0, 21, 4);
        PINSEL_ConfigPin (0, 22, 4);
#endif
    }
    lpccan1_turnon_clk();
    lpccan_irqstate_init(CAN_1);
    lpccan_init_alut_ram();
    lpccan1_turnon_clk();
    lpccan_baud_set(CAN_1, baud);
    CAN_ModeConfig(CAN_1, mode, ENABLE);
    if(mode == CAN_SELFTEST_MODE)
    {
        //CAN_ModeConfig(CAN_1, CAN_TEST_MODE, ENABLE);
        CAN_SetAFMode(CAN_ACC_BP);
    }
}
#endif /*RT_USING_LPCCAN1*/

#ifdef RT_USING_LPCCAN2
static void lpccan2_turnon_clk(void)
{
    CLKPWR_ConfigPPWR(CLKPWR_PCONP_PCAN2, ENABLE);
}

static void lpccan2_filter_init(struct rt_can_device *can)
{
}

static void lpccan2_hw_init(uint32_t baud,  CAN_MODE_Type mode)
{
    if(mode != CAN_SELFTEST_MODE)
    {
#ifndef LPCCAN2_USEING_GPIO_SECOND
        PINSEL_ConfigPin (0, 4, 2);
        PINSEL_ConfigPin (0, 5, 2);
#else
        PINSEL_ConfigPin (2, 7, 1);
        PINSEL_ConfigPin (2, 8, 1);
#endif
    }
    lpccan2_turnon_clk();
    lpccan_irqstate_init(CAN_2);
#ifndef RT_USING_LPCCAN1
    lpccan_init_alut_ram();
#endif /*RT_USING_LPCCAN1*/
    lpccan_baud_set(CAN_2, baud);
    CAN_ModeConfig(CAN_2, mode, ENABLE);
    if(mode == CAN_SELFTEST_MODE)
    {
        CAN_SetAFMode(CAN_ACC_BP);
    }
}
#endif /*RT_USING_LPCCAN2*/

static rt_err_t configure(struct rt_can_device *can, struct can_configure *cfg)
{
    CAN_MODE_Type mode;
    rt_uint32_t canid;

    switch(cfg->mode)
    {
    case RT_CAN_MODE_NORMAL:
        mode = CAN_OPERATING_MODE;
        break;
    case RT_CAN_MODE_LISEN:
        mode = CAN_LISTENONLY_MODE;
        break;
    case RT_CAN_MODE_LOOPBACKANLISEN:
        mode = CAN_SELFTEST_MODE;
        break;
    default:
        return RT_EIO;
    }

    canid = ((struct lpccandata  *) can->parent.user_data)->id;
#ifdef RT_USING_LPCCAN1
    if(canid == CAN_1)
    {
        lpccan1_hw_init(cfg->baud_rate, mode);
        lpccan1_filter_init(can);
    }
#endif /*RT_USING_LPCCAN1*/
#ifdef RT_USING_LPCCAN2
#ifdef RT_USING_LPCCAN1
    else
#endif /*RT_USING_LPCCAN1*/
    {
        lpccan2_hw_init(cfg->baud_rate, mode);
        lpccan2_filter_init(can);
    }
#endif /*RT_USING_LPCCAN2*/
    return RT_EOK;
}

static CAN_ERROR findfilter(struct lpccandata* plpccan, struct rt_can_filter_item* pitem, rt_int32_t* pos)
{
    extern uint16_t CANAF_FullCAN_cnt;
    extern uint16_t CANAF_std_cnt;
    extern uint16_t CANAF_gstd_cnt;
    extern uint16_t CANAF_ext_cnt;
    extern uint16_t CANAF_gext_cnt;

    rt_uint32_t buf0 = 0, buf1 = 0;
    rt_int16_t cnt1 = 0, cnt2 = 0, bound1 = 0;
    CAN_ID_FORMAT_Type format;
    *pos = -1;
    if(pitem->ide)
    {
        format = EXT_ID_FORMAT;
    }
    else
    {
        format = STD_ID_FORMAT;
    }
    if(pitem->mode)
    {
        rt_uint32_t id = pitem->id;
        if(format == STD_ID_FORMAT)
        {
            id &= 0x07FF;

            id |= plpccan->id << 13;/* Add controller number */

            if (CANAF_std_cnt == 0)
            {
                return CAN_ENTRY_NOT_EXIT_ERROR;
            }
            else if (CANAF_std_cnt == 1)
            {
                cnt2 = (CANAF_FullCAN_cnt + 1) >> 1;
                if(id != LPC_CANAF_RAM->mask[cnt2] >> 16)
                {
                    return CAN_ENTRY_NOT_EXIT_ERROR;
                }
            }
            else
            {
                cnt1 = (CANAF_FullCAN_cnt+1)>>1;

                bound1 = ((CANAF_FullCAN_cnt+1)>>1)+((CANAF_std_cnt+1)>>1);

                while (cnt1 < bound1)
                {
                    /* Loop through standard existing IDs */
                    if (((LPC_CANAF_RAM->mask[cnt1] >> 16) & 0xE7FF) == id)
                    {
                        *pos = cnt1 * 2;
                        return CAN_OK;
                    }

                    if ((LPC_CANAF_RAM->mask[cnt1] & 0x0000E7FF) == id)
                    {
                        *pos = cnt1 * 2 + 1;
                        return CAN_OK;
                    }
                    if (((LPC_CANAF_RAM->mask[cnt1] >> 16) & 0xE7FF) > id)
                    {
                        return CAN_ENTRY_NOT_EXIT_ERROR;
                    }

                    if ((LPC_CANAF_RAM->mask[cnt1] & 0x0000E7FF) > id)
                    {
                        return CAN_ENTRY_NOT_EXIT_ERROR;
                    }
                    cnt1++;
                }
                return CAN_ENTRY_NOT_EXIT_ERROR;
            }
        }
        /*********** Add Explicit Extended Identifier Frame Format entry *********/
        else
        {
            /* Add controller number */
            id |= plpccan->id << 29;

            cnt1 = ((CANAF_FullCAN_cnt+1) >> 1) + (((CANAF_std_cnt + 1) >> 1) + CANAF_gstd_cnt);

            cnt2 = 0;

            while (cnt2 < CANAF_ext_cnt)
            {
                /* Loop through extended existing masks*/
                if (LPC_CANAF_RAM->mask[cnt1] == id)
                {
                    *pos = cnt2;
                    return CAN_OK;
                }
                if (LPC_CANAF_RAM->mask[cnt1] > id)
                {
                    return CAN_ENTRY_NOT_EXIT_ERROR;
                }
                cnt1++;
                cnt2++;
            }
        }
    }
    else
    {
        rt_uint32_t lowerID =  pitem->id;
        rt_uint32_t upperID =  pitem->mask;
        rt_uint32_t  LID,UID;
        if(lowerID > upperID)
            return CAN_CONFLICT_ID_ERROR;
        if(format == STD_ID_FORMAT)
        {
            lowerID &=0x7FF; //mask ID
            upperID &=0x7FF;
            cnt1 = ((CANAF_FullCAN_cnt+1)>>1) + ((CANAF_std_cnt + 1) >> 1);
            if(CANAF_gstd_cnt == 0)
            {
                return CAN_ENTRY_NOT_EXIT_ERROR;
            }
            else
            {
                bound1 = ((CANAF_FullCAN_cnt+1)>>1) + ((CANAF_std_cnt + 1) >> 1) + CANAF_gstd_cnt;
                while(cnt1 < bound1)
                {
                    //compare controller first
                    while((LPC_CANAF_RAM->mask[cnt1] >> 29) < (plpccan->id))//increase until meet greater or equal controller
                        cnt1++;
                    buf0 = LPC_CANAF_RAM->mask[cnt1];
                    if((LPC_CANAF_RAM->mask[cnt1] >> 29) > (plpccan->id)) //meet greater controller
                    {
                        return CAN_ENTRY_NOT_EXIT_ERROR;
                    }
                    else //meet equal controller
                    {
                        LID  = (buf0 >> 16)&0x7FF;
                        UID  = buf0 & 0x7FF;
                        if (upperID == LID && lowerID == UID)
                        {
                            *pos = cnt1;
                            return CAN_OK;
                        }
                        if (upperID < LID)
                        {
                            return CAN_ENTRY_NOT_EXIT_ERROR;
                        }
                        else if (lowerID >= UID)
                        {
                            cnt1 ++;
                        }
                        else
                            return CAN_CONFLICT_ID_ERROR;
                    }
                }
                if(cnt1 >= bound1)
                {
                    return CAN_ENTRY_NOT_EXIT_ERROR;
                }
            }
        }
        /*********Add Group of Extended Identifier Frame Format************/
        else
        {
            lowerID  &= 0x1FFFFFFF; //mask ID
            upperID &= 0x1FFFFFFF;
            cnt1 = ((CANAF_FullCAN_cnt+1)>>1) + ((CANAF_std_cnt + 1) >> 1) + CANAF_gstd_cnt + CANAF_ext_cnt;
            //if this is the first Group standard ID entry
            if(CANAF_gext_cnt == 0)
            {
                return CAN_ENTRY_NOT_EXIT_ERROR;
            }
            else
            {
                bound1 = ((CANAF_FullCAN_cnt+1)>>1) + ((CANAF_std_cnt + 1) >> 1) + CANAF_gstd_cnt \
                         + CANAF_ext_cnt + (CANAF_gext_cnt<<1);
                while(cnt1 < bound1)
                {
                    while((LPC_CANAF_RAM->mask[cnt1] >>29)< plpccan->id ) //increase until meet greater or equal controller
                        cnt1++;
                    buf0 = LPC_CANAF_RAM->mask[cnt1];
                    buf1 = LPC_CANAF_RAM->mask[cnt1+1];
                    if((LPC_CANAF_RAM->mask[cnt1] >> 29) > plpccan->id ) //meet greater controller
                    {
                        return CAN_ENTRY_NOT_EXIT_ERROR;
                    }
                    else //meet equal controller
                    {
                        LID  = buf0 & 0x1FFFFFFF; //mask ID
                        UID  = buf1 & 0x1FFFFFFF;
                        if (upperID == LID && lowerID == UID)
                        {
                            *pos = cnt1;
                            return CAN_OK;
                        }
                        if (upperID < LID)
                        {
                            return CAN_ENTRY_NOT_EXIT_ERROR;
                        }
                        else if (lowerID >= UID)
                        {
                            //load next entry to compare
                            cnt1 +=2;
                        }
                        else
                            return CAN_CONFLICT_ID_ERROR;
                    }
                }
                if(cnt1 >= bound1)
                {
                    return CAN_ENTRY_NOT_EXIT_ERROR;
                }
            }
        }
    }
    return CAN_ENTRY_NOT_EXIT_ERROR;
}

static rt_err_t setfilter(struct lpccandata* plpccan,struct rt_can_filter_config *pconfig)
{
    struct rt_can_filter_item* pitem = pconfig->items;
    rt_uint32_t count = pconfig->count;
    rt_int32_t pos;
    CAN_ID_FORMAT_Type format;
    CAN_ERROR lpccanres;
    while(count)
    {
        if(pitem->ide)
        {
            format = EXT_ID_FORMAT;
        }
        else
        {
            format = STD_ID_FORMAT;
        }
        lpccanres = findfilter(plpccan, pitem, &pos);
        if(pconfig->actived && lpccanres != CAN_OK)
        {
            if(pitem->mode)
            {
                lpccanres = CAN_LoadGroupEntry(plpccan->id, pitem->id, pitem->mask, format);
            }
            else
            {
                lpccanres = CAN_LoadExplicitEntry(plpccan->id, pitem->id, format);
            }
        }
        else if(!pconfig->actived && lpccanres == CAN_OK)
        {
            AFLUT_ENTRY_Type type;
            if(pitem->mode)
            {
                if(format == EXT_ID_FORMAT)
                {
                    type = GROUP_EXTEND_ENTRY;
                }
                else
                {
                    type = GROUP_STANDARD_ENTRY;
                }
            }
            else
            {
                if(format == EXT_ID_FORMAT)
                {
                    type = EXPLICIT_EXTEND_ENTRY;
                }
                else
                {
                    type = EXPLICIT_STANDARD_ENTRY;
                }
            }
            lpccanres = CAN_RemoveEntry(type, (rt_uint16_t)(pos));
        }
        else if(!pconfig->actived && lpccanres != CAN_OK)
        {
            lpccanres = CAN_OK;
        }
        if(lpccanres != CAN_OK)
        {
            return RT_EIO;
        }
        pitem++;
        count--;
    }
    return RT_EOK;
}

static rt_err_t control(struct rt_can_device *can, int cmd, void *arg)
{
    struct lpccandata* plpccan;
    rt_uint32_t argval;
    CAN_MODE_Type mode;

    plpccan = (struct lpccandata* )  can->parent.user_data;
    RT_ASSERT(plpccan != RT_NULL);

    switch (cmd)
    {
    case RT_DEVICE_CTRL_CLR_INT:
        argval = (rt_uint32_t) arg;
        if(argval == RT_DEVICE_FLAG_INT_RX)
        {
            CAN_IRQCmd(plpccan->id, CANINT_RIE, DISABLE);
            CAN_IRQCmd(plpccan->id, CANINT_DOIE, DISABLE);
        }
        else if(argval == RT_DEVICE_FLAG_INT_TX)
        {
            CAN_IRQCmd(plpccan->id, CANINT_TIE1, DISABLE);
            CAN_IRQCmd(plpccan->id, CANINT_TIE2, DISABLE);
            CAN_IRQCmd(plpccan->id, CANINT_TIE3, DISABLE);
        }
        else if(argval == RT_DEVICE_CAN_INT_ERR)
        {
            CAN_IRQCmd(plpccan->id, CANINT_EIE, DISABLE);
        }
        break;

    case RT_DEVICE_CTRL_SET_INT:
        argval = (rt_uint32_t) arg;
        if(argval == RT_DEVICE_FLAG_INT_RX)
        {
            CAN_IRQCmd(plpccan->id, CANINT_RIE, ENABLE);
            CAN_IRQCmd(plpccan->id, CANINT_DOIE, ENABLE);
        }
        else if(argval == RT_DEVICE_FLAG_INT_TX)
        {
            CAN_IRQCmd(plpccan->id, CANINT_TIE1, ENABLE);
            CAN_IRQCmd(plpccan->id, CANINT_TIE2, ENABLE);
            CAN_IRQCmd(plpccan->id, CANINT_TIE3, ENABLE);
        }
        else if(argval == RT_DEVICE_CAN_INT_ERR)
        {
            CAN_IRQCmd(plpccan->id, CANINT_EIE, ENABLE);
        }
        break;

    case RT_CAN_CMD_SET_FILTER:
        return setfilter(plpccan, (struct rt_can_filter_config*) arg);

    case RT_CAN_CMD_SET_MODE:
        argval = (rt_uint32_t) arg;
        if(argval != RT_CAN_MODE_NORMAL ||
                argval != RT_CAN_MODE_LISEN)
        {
            return RT_ERROR;
        }
        if(argval != can->config.mode)
        {
            can->config.mode = argval;
            switch(argval)
            {
            case RT_CAN_MODE_NORMAL:
                mode = CAN_OPERATING_MODE;
                break;
            case RT_CAN_MODE_LISEN:
                mode = CAN_LISTENONLY_MODE;
                break;
            case RT_CAN_MODE_LOOPBACKANLISEN:
                mode = CAN_SELFTEST_MODE;
                break;
            default:
                return RT_EIO;
            }
            CAN_ModeConfig(plpccan->id, mode, ENABLE);
            if(mode == CAN_SELFTEST_MODE)
            {
                //CAN_ModeConfig(CAN_1, CAN_TEST_MODE, ENABLE);
                CAN_SetAFMode(CAN_ACC_BP);
            }
        }
        break;

    case RT_CAN_CMD_SET_BAUD:
        argval = (rt_uint32_t) arg;
        if(argval != can->config.baud_rate)
        {
            can->config.baud_rate = argval;
            return lpccan_baud_set(plpccan->id, (rt_uint32_t) arg);
        }
        break;

    case RT_CAN_CMD_SET_PRIV:
        argval = (rt_uint32_t) arg;
        if(argval != RT_CAN_MODE_PRIV ||
                argval != RT_CAN_MODE_NOPRIV)
        {
            return RT_ERROR;
        }
        if(argval != can->config.privmode)
        {
            can->config.privmode = argval;
            CAN_ModeConfig(plpccan->id, CAN_TXPRIORITY_MODE, ENABLE);
        }
        break;

    case RT_CAN_CMD_GET_STATUS:
    {
        can->status.rcverrcnt = 0;
        can->status.snderrcnt = 0;
        can->status.errcode = 0;
        if(arg != &can->status)
        {
            rt_memcpy(arg,&can->status,sizeof(can->status));
        }
    }
    break;

    }
    return RT_EOK;
}

static int sendmsg(struct rt_can_device *can, const void* buf, rt_uint32_t boxno)
{
    struct lpccandata* plpccan;
    LPC_CAN_TypeDef* pCan;
    struct rt_can_msg* pmsg;
    rt_uint32_t SR_Mask;
    rt_uint32_t CMRMsk;

    plpccan = (struct lpccandata* )  can->parent.user_data;
    RT_ASSERT(plpccan != RT_NULL);

    pCan = lcpcan_get_reg_base(plpccan->id);
    RT_ASSERT(pCan != RT_NULL);

    pmsg = (struct rt_can_msg*) buf;

    if(boxno > 2)
    {
        return RT_ERROR;
    }

    CMRMsk = 0x01 | (0x01 << (boxno + 5));
    SR_Mask = 0x01 <<(boxno * 8 + 2);

    if(pCan->SR & SR_Mask)
    {
    	volatile unsigned int  *pTFI = (&pCan->TFI1 + 0 + 4 * boxno);
    	volatile unsigned int  *pTID = (&pCan->TFI1 + 1 + 4 * boxno);
    	volatile unsigned int  *pTDA = (&pCan->TFI1 + 2 + 4 * boxno);
    	volatile unsigned int  *pTDB = (&pCan->TFI1 + 3 + 4 * boxno);
	    rt_uint32_t data;

        /* Transmit Channel 1 is available */
        /* Write frame informations and frame data into its CANxTFI1,
         * CANxTID1, CANxTDA1, CANxTDB1 register */
        *pTFI &= ~ 0x000F0000;
        *pTFI |= (pmsg->len) << 16;
        if(pmsg->rtr == REMOTE_FRAME)
        {
            *pTFI |= (1 << 30); //set bit RTR
        }
        else
        {
            *pTFI &= ~(1 << 30);
        }

        if(pmsg->ide == EXT_ID_FORMAT)
        {
            *pTFI |= (((uint32_t)1) << 31); //set bit FF
        }
        else
        {
            *pTFI &= ~(((uint32_t)1) << 31);
        }
        if(can->config.privmode)
        {
            *pTFI &= ~0x000000FF;
            *pTFI |= pmsg->priv;
        }
        /* Write CAN ID*/
        *pTID = pmsg->id;
        /*Write first 4 data bytes*/
        data = (pmsg->data[0]) | (((pmsg->data[1]))<< 8) | ((pmsg->data[2]) << 16) | ((pmsg->data[3]) << 24);
        *pTDA = data;
        /*Write second 4 data bytes*/
        data = (pmsg->data[4]) | (((pmsg->data[5])) << 8) | ((pmsg->data[6]) << 16) | ((pmsg->data[7]) << 24);
        *pTDB = data;
        /*Write transmission request*/
        pCan->CMR = CMRMsk;
        return RT_EOK;
    }
    else
    {
        return RT_ERROR;
    }
}

static int recvmsg(struct rt_can_device *can, void* buf, rt_uint32_t boxno)
{
    struct lpccandata* plpccan;
    LPC_CAN_TypeDef* pCan;

    plpccan = (struct lpccandata* )  can->parent.user_data;
    RT_ASSERT(plpccan != RT_NULL);
    pCan = lcpcan_get_reg_base(plpccan->id);
    RT_ASSERT(pCan != RT_NULL);

    //CAN_ReceiveMsg
    //check status of Receive Buffer
    if((pCan->SR &0x00000001))
    {
	    uint32_t data;
    	struct rt_can_msg* pmsg = (struct rt_can_msg*) buf;

        /* Receive message is available */
        /* Read frame informations */
        pmsg->ide = (uint8_t)(((pCan->RFS) & 0x80000000) >> 31);
        pmsg->rtr = (uint8_t)(((pCan->RFS) & 0x40000000) >> 30);
        pmsg->len = (uint8_t)(((pCan->RFS) & 0x000F0000) >> 16);
        /* Read CAN message identifier */
        pmsg->id = pCan->RID;
        /* Read the data if received message was DATA FRAME */
        if (!pmsg->rtr)
        {
            /* Read first 4 data bytes */
            data = pCan->RDA;
            pmsg->data[0] = data & 0x000000FF;
            pmsg->data[1] = (data & 0x0000FF00) >> 8;
            pmsg->data[2] = (data & 0x00FF0000) >> 16;
            pmsg->data[3] = (data & 0xFF000000) >> 24;
            /* Read second 4 data bytes */
            if(pmsg->len > 4)
            {
                data = pCan->RDB;
                pmsg->data[4] = data & 0x000000FF;
                pmsg->data[5] = (data & 0x0000FF00) >> 8;
                pmsg->data[6] = (data & 0x00FF0000) >> 16;
                pmsg->data[7] = (data & 0xFF000000) >> 24;
            }
            pmsg->hdr = 0;
            /*release receive buffer*/
            pCan->CMR = 0x04;
        }
        else
        {
            /* Received Frame is a Remote Frame, not have data, we just receive
             * message information only */
            pCan->CMR = 0x04; /*release receive buffer*/
            return SUCCESS;
        }
    }
    else
    {
        // no receive message available
        return ERROR;
    }
    return RT_EOK;
}

static const struct rt_can_ops canops =
{
    configure,
    control,
    sendmsg,
    recvmsg,
};

#ifdef RT_USING_LPCCAN1
#ifdef RT_CAN_USING_LED
#endif
static struct lpccandata lpccandata1 =
{
    CAN_ID_1,
};
static struct rt_can_device lpccan1;
#endif /*RT_USINGLPCCAN1*/

#ifdef RT_USING_LPCCAN2
#ifdef RT_CAN_USING_LED
#endif
static struct lpccandata lpccandata2 =
{
    CAN_ID_2,
};
static struct rt_can_device lpccan2;
#endif /*RT_USINGLPCCAN2*/

/*----------------- INTERRUPT SERVICE ROUTINES --------------------------*/
/*********************************************************************//**
 * @brief        Event Router IRQ Handler
 * @param[in]    None
 * @return       None
 **********************************************************************/
void CAN_IRQHandler(void)
{
    rt_uint32_t IntStatus;
#ifdef RT_USING_LPCCAN1
    IntStatus = CAN_IntGetStatus(CAN_1);
    //check receive interrupt
    if((IntStatus >> 0) & 0x01)
    {
        rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_RX_IND | 0<<8);
    }
    //check Transmit Interrupt  interrupt1
    if((IntStatus >> 1) & 0x01)
    {
        rt_uint32_t state = 0;
        state = CAN_GetCTRLStatus(CAN_1, CANCTRL_STS);
        if(state & (0x01 << 3))
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_DONE | 0<<8);
        }
        else
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_FAIL | 0<<8);
        }
    }
    //check Error Warning Interrupt
    if((IntStatus >> 2) & 0x01)
    {
        rt_uint32_t errtype;
        rt_uint32_t state;

        errtype = (IntStatus >> 16);
        if(errtype & 0x1F && lpccan1.status.lasterrtype == (errtype & 0x1F))
        {
            switch((errtype & 0x1F))
            {
            case 00011: // Start of Frame
            case 00010: // ID28 ... ID21
            case 00110: //ID20 ... ID18
            case 00100: // SRTR Bit
            case 00101: // IDE bit
            case 00111: // ID17 ... 13
            case 01111: // ID12 ... ID5
            case 01110: // ID4 ... ID0
            case 01100: // RTR Bit
            case 01011: // Data Length Code
            case 01010: // Data Field
                lpccan1.status.formaterrcnt++;
                break;
            case 01101: // Reserved Bit 1
            case 01001: // Reserved Bit 0
                lpccan1.status.bitpaderrcnt++;
                break;
            case 01000: // CRC Sequence
            case 11000: // CRC Delimiter
                lpccan1.status.crcerrcnt++;
                break;
            case 11001: // Acknowledge Slot
            case 11011: // Acknowledge Delimiter
                lpccan1.status.ackerrcnt++;
                break;
            case 11010: // End of Frame
            case 10010: // Intermission
                lpccan1.status.formaterrcnt++;
                break;
            }
            lpccan1.status.lasterrtype = errtype & 0x1F;
        }

        state = CAN_GetCTRLStatus(CAN_1, CANCTRL_GLOBAL_STS);
        lpccan1.status.rcverrcnt = (state >> 16) & 0xFF;
        lpccan1.status.snderrcnt = (state >> 24) & 0xFF;
        lpccan1.status.errcode = (state >> 5) & 0x06;
    }
    //check Data Overrun Interrupt Interrupt
    if((IntStatus >> 3) & 0x01)
    {
        rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_RXOF_IND | 0<<8);
    }
    //check Transmit Interrupt  interrupt2
    if((IntStatus >> 9) & 0x01)
    {
        rt_uint32_t state = 0;
        state = CAN_GetCTRLStatus(CAN_1, CANCTRL_STS);
        if(state & (0x01 << 11))
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_DONE | 1<<8);
        }
        else
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_FAIL | 1<<8);
        }
    }
    //check Transmit Interrupt  interrupt3
    if((IntStatus >> 10) & 0x01)
    {
        rt_uint32_t state = 0;
        state = CAN_GetCTRLStatus(CAN_1, CANCTRL_STS);
        if(state & (0x01 << 19))
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_DONE | 2<<8);
        }
        else
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_FAIL | 2<<8);
        }
    }
#endif /*RT_USING_LPCCAN1*/

#ifdef RT_USING_LPCCAN2
    IntStatus = CAN_IntGetStatus(CAN_2);
    //check receive interrupt
    if((IntStatus >> 0) & 0x01)
    {
        rt_hw_can_isr(&lpccan2,RT_CAN_EVENT_RX_IND | 0<<8);
    }
    //check Transmit Interrupt  interrupt1
    if((IntStatus >> 1) & 0x01)
    {
        rt_uint32_t state = 0;
        state = CAN_GetCTRLStatus(CAN_2, CANCTRL_STS);
        if(state & (0x01 << 3))
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_DONE | 0<<8);
        }
        else
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_FAIL | 0<<8);
        }
    }

    //check Error Warning Interrupt
    if((IntStatus >> 2) & 0x01)
    {
        rt_uint32_t errtype;
        errtype = (IntStatus >> 16);
        if(errtype & 0x1F && lpccan2.status.lasterrtype == (errtype & 0x1F))
        {
            switch((errtype & 0x1F))
            {
            case 00011: // Start of Frame
            case 00010: // ID28 ... ID21
            case 00110: //ID20 ... ID18
            case 00100: // SRTR Bit
            case 00101: // IDE bit
            case 00111: // ID17 ... 13
            case 01111: // ID12 ... ID5
            case 01110: // ID4 ... ID0
            case 01100: // RTR Bit
            case 01011: // Data Length Code
            case 01010: // Data Field
                lpccan2.status.formaterrcnt++;
                break;
            case 01101: // Reserved Bit 1
            case 01001: // Reserved Bit 0
                lpccan2.status.bitpaderrcnt++;
                break;
            case 01000: // CRC Sequence
            case 11000: // CRC Delimiter
                lpccan2.status.crcerrcnt++;
                break;
            case 11001: // Acknowledge Slot
            case 11011: // Acknowledge Delimiter
                lpccan2.status.ackerrcnt++;
                break;
            case 11010: // End of Frame
            case 10010: // Intermission
                lpccan2.status.formaterrcnt++;
                break;
            }
            lpccan2.status.lasterrtype = errtype & 0x1F;
        }
        rt_uint32_t state = 0;
        state = CAN_GetCTRLStatus(CAN_2, CANCTRL_GLOBAL_STS);
        lpccan2.status.rcverrcnt = (state >> 16) & 0xFF;
        lpccan2.status.snderrcnt = (state >> 24) & 0xFF;
        lpccan2.status.errcode = (state >> 5) & 0x06;
    }

    //check Data Overrun Interrupt Interrupt
    if((IntStatus >> 3) & 0x01)
    {
        rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_RXOF_IND | 0<<8);
    }

    //check Transmit Interrupt  interrupt2
    if((IntStatus >> 9) & 0x01)
    {
        rt_uint32_t state = 0;
        state = CAN_GetCTRLStatus(CAN_2, CANCTRL_STS);
        if(state & (0x01 << 11))
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_DONE | 1<<8);
        }
        else
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_FAIL | 1<<8);
        }
    }

    //check Transmit Interrupt  interrupt3
    if((IntStatus >> 10) & 0x01)
    {
        rt_uint32_t state = 0;
        state = CAN_GetCTRLStatus(CAN_2, CANCTRL_STS);
        if(state & (0x01 << 19))
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_DONE | 2<<8);
        }
        else
        {
            rt_hw_can_isr(&lpccan1,RT_CAN_EVENT_TX_FAIL | 2<<8);
        }
    }
#endif /*RT_USING_LPCCAN2*/
}

int lpc_can_init(void)
{
#ifdef RT_USING_LPCCAN1
    lpccan1.config.baud_rate=CAN1MBaud;
    lpccan1.config.msgboxsz=16;
    lpccan1.config.sndboxnumber=3;
    lpccan1.config.mode=RT_CAN_MODE_NORMAL;
    lpccan1.config.privmode=0;

#ifdef RT_CAN_USING_LED
#endif

    lpccan1.config.ticks = 50;

#ifdef RT_CAN_USING_HDR
#endif

    //Enable CAN Interrupt
    NVIC_EnableIRQ(CAN_IRQn);
    rt_hw_can_register(&lpccan1, "lpccan1", &canops, &lpccandata1);
#endif /*RT_USING_LPCCAN1*/

#ifdef RT_USING_LPCCAN2
    lpccan2.config.baud_rate=CAN1MBaud;
    lpccan2.config.msgboxsz=16;
    lpccan2.config.sndboxnumber=3;
    lpccan2.config.mode=RT_CAN_MODE_NORMAL;
    lpccan2.config.privmode=0;

#ifdef RT_CAN_USING_LED
#endif

    lpccan2.config.ticks = 50;

#ifdef RT_CAN_USING_HDR
#endif

    //Enable CAN Interrupt
    NVIC_EnableIRQ(CAN_IRQn);

#ifdef RT_CAN_USING_HDR
#endif

    rt_hw_can_register(&lpccan2, "lpccan2", &canops, &lpccandata2);
#endif /*RT_USING_LPCCAN2*/

    return RT_EOK;
}
INIT_BOARD_EXPORT(lpc_can_init);

#endif /*RT_USING_CAN*/
