view packages/devs/can/arm/lpc2xxx/current/src/can_lpc2xxx.c @ 2509:a52d470bc691

* cdl/can_lpc2xxx.cdl: Added CYGOPT_DEVS_CAN_LPC2XXX_ALIE to make arbitration lost interrupt optional. Added option CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY to configure the interrupt priority for global CAN interrupt in LPC24xx variants. * include/can_lpc2xxx_baudrates.h: Replaced CYGNUM_CAN_LPC2XXX_VPB_CLK by CYGNUM_HAL_ARM_LPC2XXX_CAN_CLK because newer variants like LPC24xx do not have a global VPB_CLK. * src/can_accfilt_lpc2xxx.c: Adjusted LPC2XXX_CAN_FIRST_IN_LUT to be 0 for LPC24xxx variants. Added macro CAN_CHAN_NO_LUT(_info_) to abstract channel numbering from acceptance filter code. * src/can_lpc2xxx.c: Removed icr data field from lpc2xxx_can_info_st structure because it is not required any longer. A lot of small modifications to make the driver usable with newer LPC2xxx variants like LPC24xx. ISR and DSR code changed - instead of disabling interrupts in IER register they are disabled in VIC by using cyg_drv_interrupt_mask() function calls. Added global CAN ISR and DSR for LPC24xx variants (they do not support individual interrupt vectors for RX and TX interrupts). Moved LUT error checking code from ISR into DSR to keep ISR as short as possible and made LUT error checking code optional. * cdl/can_lpc2xxx.cdl: add CAN interrupt priorities * src/can_lpc2xxx.c: add CAN interrupt priorities, repair "chan" definition missing in rx_ISR * src/can_accfilt_lpc2xxx.c: add various types of CAN controllers numbering (depends on LPC2XXX version)
author asl
date Tue, 22 Jul 2008 19:40:02 +0000
parents ba2923d2586f
children 74dbf4c3f2e1
line wrap: on
line source

//==========================================================================
//
//      devs/can/arm/lpc2xxx/current/src/can_lpc2xxx.c
//
//      CAN driver for LPC2xxx microcontrollers
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2003 Gary Thomas
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    Uwe Kindler
// Contributors: Uwe Kindler
// Date:         2007-04-09
// Purpose:      Support LPC2xxx on-chip CAN moduls
// Description: 
//
//####DESCRIPTIONEND####
//
//==========================================================================


//==========================================================================
//                              INCLUDES
//==========================================================================
#include <pkgconf/system.h>
#include <pkgconf/io_can.h>
#include <pkgconf/io.h>
#include <pkgconf/devs_can_lpc2xxx.h>

#include <cyg/infra/diag.h>

#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_io.h>

#include <cyg/hal/hal_diag.h>
#include <cyg/infra/cyg_ass.h>

#include <cyg/io/io.h>
#include <cyg/io/devtab.h>
#include <cyg/io/can.h>
#include <cyg/io/can_lpc2xxx.h>
#include <cyg/io/can_lpc2xxx_baudrates.h>




//===========================================================================
//                                DEFINES  
//===========================================================================
//
// Check if the macro HAL_LPC2XXX_GET_CAN_BR is provided
//
#ifndef HAL_LPC2XXX_GET_CAN_BR
#error "Macro HAL_LPC2XXX_GET_CAN_BR() missing"
#endif

//
// Support debug output if this option is enabled in CDL file
//
#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
#define LPC2XXX_DBG_PRINT diag_printf
#else
#define LPC2XXX_DBG_PRINT( fmt, ... )
#endif


//---------------------------------------------------------------------------
// we define our own set of register bits in order to be independent from
// platform specific names
//

//---------------------------------------------------------------------------
// Memory map of CAN block
//
#define CAN_ACCFILT_RAM_BASE  0xE0038000
#define CAN_ACCFILT_REG_BASE  0xE003C000
#define CAN_CENTRAL_REG_BASE  0xE0040000
#define CAN_CTRL_1_REG_BASE   0xE0044000
#define CAN_CTRL_2_REG_BASE   0xE0048000
#define CAN_CTRL_3_REG_BASE   0xE004C000
#define CAN_CTRL_4_REG_BASE   0xE0050000


//---------------------------------------------------------------------------
// CAN Acceptance Filter register layout
//
#define CAN_ACCFILT_AFMR            (CAN_ACCFILT_REG_BASE  + 0x0000)
#define CAN_ACCFILT_SFF_SA          (CAN_ACCFILT_REG_BASE  + 0x0004)
#define CAN_ACCFILT_SFF_GRP_SA      (CAN_ACCFILT_REG_BASE  + 0x0008)
#define CAN_ACCFILT_EFF_SA          (CAN_ACCFILT_REG_BASE  + 0x000C)
#define CAN_ACCFILT_EFF_GRP_SA      (CAN_ACCFILT_REG_BASE  + 0x0010)
#define CAN_ACCFILT_ENDOFTABLE      (CAN_ACCFILT_REG_BASE  + 0x0014)
#define CAN_ACCFILT_LUT_ERR_ADDR    (CAN_ACCFILT_REG_BASE  + 0x0018)
#define CAN_ACCFILT_LUT_ERR         (CAN_ACCFILT_REG_BASE  + 0x001C)

//---------------------------------------------------------------------------
// CAN_ACCFILT_AFMR Bits
//
#define AFMR_OFF       0x00000001 // 1 = Acceptance filter is not operational
#define AFMR_BYPASS    0x00000002 // 1 = all Rx messages are accepted on enabled CAN controllers.
#define AFMR_FULLCAN   0x00000004 // 1 = FullCAN mode
#define AFMR_ON        0x00000000 // Acceptance filter on
#define ACCFILT_RAM_SIZE 2048     // size of acceptance filter ram


//---------------------------------------------------------------------------
// Acceptance filter tool macros
//
#define ACCFILT_STD_ID_MASK                    0x7FF
#define ACCFILT_EXT_ID_MASK                    0x1FFFFFFF
#define ACCFILT_STD_DIS                        0x1000
#define ACCFILT_STD_CTRL_MASK                  0xE000
#define ACCFILT_EXT_CTRL_MASK                  0xE0000000
#define ACCFILT_STD_GET_CTRL(_entry_)          (((_entry_) >> 13) & 0x7)
#define ACCFILT_STD_GET_CTRL_LOWER(_entry_)    (((_entry_) >> 29) & 0x7)
#define ACCFILT_STD_GET_CTRL_UPPER(_entry_)    (((_entry_) >> 13) & 0x7)
#define ACCFILT_STD_GET_ID(_entry_)            ((_entry_) & ACCFILT_STD_ID_MASK)
#define ACCFILT_EXT_GET_ID(_entry_)            ((_entry_) & ACCFILT_EXT_ID_MASK)
#define ACCFILT_EXT_GET_CTRL(_entry_)          (((_entry_) >> 29) & 0x7)
#define ACCFILT_EXT_SET_CTRL(_entry_, _ctrl_)  ((_entry_ & 0xE0000000) | ((_ctrl_) << 29)) 


//---------------------------------------------------------------------------
// CAN Central CAN Registers register layout
//
#define CAN_CENTRAL_TXSR            (CAN_CENTRAL_REG_BASE  + 0x0000)
#define CAN_CENTRAL_RXSR            (CAN_CENTRAL_REG_BASE  + 0x0004)
#define CAN_CENTRAL_MSR             (CAN_CENTRAL_REG_BASE  + 0x0008)


//---------------------------------------------------------------------------
// CAN Controller register offsets
// Registers are offsets from base CAN module control register
//
#define CANREG_MOD   0x0000
#define CANREG_CMR   0x0004
#define CANREG_GSR   0x0008
#define CANREG_ICR   0x000C
#define CANREG_IER   0x0010
#define CANREG_BTR   0x0014
#define CANREG_EWL   0x0018
#define CANREG_SR    0x001C
#define CANREG_RFS   0x0020
#define CANREG_RID   0x0024
#define CANREG_RDA   0x0028
#define CANREG_RDB   0x002C
#define CANREG_TFI1  0x0030
#define CANREG_TID1  0x0034
#define CANREG_TDA1  0x0038
#define CANREG_TDB1  0x003C
#define CANREG_TFI2  0x0040
#define CANREG_TID2  0x0044
#define CANREG_TDA2  0x0048
#define CANREG_TDB2  0x004C
#define CANREG_TFI3  0x0050
#define CANREG_TID3  0x0054
#define CANREG_TDA3  0x0058
#define CANREG_TDB3  0x005C


//---------------------------------------------------------------------------
// CAN Controller register layout
//
#define CAN_CTRL_MOD(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_MOD)
#define CAN_CTRL_CMR(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_CMR)
#define CAN_CTRL_GSR(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_GSR)
#define CAN_CTRL_ICR(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_ICR)
#define CAN_CTRL_IER(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_IER)
#define CAN_CTRL_BTR(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_BTR)
#define CAN_CTRL_EWL(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_EWL)
#define CAN_CTRL_SR(_extra_)    (CAN_CTRL_BASE(_extra_) + CANREG_SR)
#define CAN_CTRL_RFS(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_RFS)
#define CAN_CTRL_RID(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_RID)
#define CAN_CTRL_RDA(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_RDA)
#define CAN_CTRL_RDB(_extra_)   (CAN_CTRL_BASE(_extra_) + CANREG_RDB)
#define CAN_CTRL_TFI1(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TFI1)
#define CAN_CTRL_TID1(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TID1)
#define CAN_CTRL_TDA1(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TDA1)
#define CAN_CTRL_TDB1(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TDB1)
#define CAN_CTRL_TFI2(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TFI2)
#define CAN_CTRL_TID2(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TID2)
#define CAN_CTRL_TDA2(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TDA2)
#define CAN_CTRL_TDB2(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TDB2)
#define CAN_CTRL_TFI3(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TFI3)
#define CAN_CTRL_TID3(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TID3)
#define CAN_CTRL_TDA3(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TDA3)
#define CAN_CTRL_TDB3(_extra_)  (CAN_CTRL_BASE(_extra_) + CANREG_TDB3)


//---------------------------------------------------------------------------
// CAN_CTRL_ICR register bits
//
#define ICR_RX                  0x00000001
#define ICR_TX1                 0x00000002
#define ICR_ERR_WARN            0x00000004
#define ICR_DATA_OVR            0x00000008
#define ICR_WAKE_UP             0x00000010
#define ICR_ERR_PASSIVE         0x00000020
#define ICR_ARBITR_LOST         0x00000040
#define ICR_BUS_ERR             0x00000080
#define ICR_ID_READY            0x00000100
#define ICR_TX2                 0x00000200
#define ICR_TX3                 0x00000400
#define ICR_LUT_ERR             0x00000800
#define ICR_GET_ERRBIT(_icr_)   (((_icr_) >> 16) & 0x1F)
#define ICR_ERR_DIRECTION       0x00200000
#define ICR_GET_ERRCODE(_icr_)  (((_icr_) >> 22) & 0x03)
#define ICR_GET_ALCBIT(_icr_)   (((_icr_) >> 24) & 0x1F)

#ifdef CYGOPT_DEVS_CAN_LPC2XXX_ALIE
#define CAN_ALL_ERR_INT (ICR_ERR_PASSIVE | ICR_ARBITR_LOST | ICR_BUS_ERR | ICR_ERR_WARN)
#else
#define CAN_ALL_ERR_INT (ICR_ERR_PASSIVE | ICR_BUS_ERR | ICR_ERR_WARN)
#endif
#define CAN_MISC_INT    (CAN_ALL_ERR_INT | ICR_WAKE_UP)


//---------------------------------------------------------------------------
// CAN_CTRL_ICR register bits
//
#define ICR_ERRCODE_BIT_ERR   0x00
#define ICR_ERRCODE_FORM_ERR  0x01
#define ICR_ERRCODE_STUFF_ERR 0x02
#define ICR_ERRCODE_OTHER_ERR 0x03


//---------------------------------------------------------------------------
// CAN_CTRL_RFS register bits
//
#define RFS_ACCFILT_INDEX_MASK       0x000003FF
#define RFS_RECEIVED_IN_BYPASS_MODE  0x00000400
#define RFS_DLC_MASK                 0x000F0000
#define RFS_RTR                      0x40000000
#define RFS_EXT                      0x80000000
#define RFS_GET_DLC(_regval_)        (((_regval_) >> 16) & 0xF)

//---------------------------------------------------------------------------
// CAN_CTRL_CMR register bits
//
#define CMR_TX_REQ          0x00000001
#define CMR_TX_ABORT        0x00000002
#define CMR_RX_RELEASE_BUF  0x00000004
#define CMR_CLEAR_DATA_OVR  0x00000008
#define CMR_SELF_RX_REQ     0x00000010
#define CMR_SEND_TX_BUF1    0x00000020
#define CMR_SEND_TX_BUF2    0x00000040
#define CMR_SEND_TX_BUF3    0x00000080


//---------------------------------------------------------------------------
// CAN_CTRL_TFI register bits
//
#define TFI_PRIO_MASK 0x000000FF
#define TFI_DLC_MASK  0x000F0000
#define TFI_DLC_RTR   0x40000000
#define TFI_DLC_EXT   0x80000000


//---------------------------------------------------------------------------
// CAN_CTRL_MOD register bits
//
#define CANMOD_OPERATIONAL    0x00000000
#define CANMOD_RESET          0x00000001
#define CANMOD_LISTEN_ONLY    0x00000002
#define CANMOD_SELF_TEST      0x00000004
#define CANMOD_TX_BUF_CFG     0x00000008
#define CANMOD_SLEEP          0x00000010
#define CANMOD_REV_POLARITY   0x00000020
#define CANMOD_TEST           0x00000040


//---------------------------------------------------------------------------
// CAN_CTRL_IER register bits
//
#define IER_RX                0x00000001
#define IER_TX1               0x00000002
#define IER_ERR_WARN          0x00000004
#define IER_DATA_OVR          0x00000008
#define IER_WAKE_UP           0x00000010
#define IER_ERR_PASSIVE       0x00000020
#define IER_ARBITR_LOST       0x00000040
#define IER_BUS_ERR           0x00000080
#define IER_ID_READY          0x00000100
#define IER_TX2               0x00000200
#define IER_TX3               0x00000400


//---------------------------------------------------------------------------
// CAN_CTRL_GSR register bits
//
#define GSR_RX_MSG_AVAILABLE  0x00000001
#define GSR_DATA_OVR          0x00000002
#define GSR_TX_NOT_PENDING    0x00000004
#define GSR_ALL_TX_COMPLETE   0x00000008
#define GSR_RECEIVING_ACTIVE  0x00000010
#define GSR_SENDING_ACTIVE    0x00000020
#define GSR_ERR               0x00000040
#define GSR_BUS_OFF           0x00000080
#define GSR_RXERR_CNT(_reg_)  (((_reg_) >> 16) & 0xFF)
#define GSR_TXERR_CNT(_reg_)  (((_reg_) >> 24) & 0xFF)


//---------------------------------------------------------------------------
// CAN_CTRL_SR register bits
//
#define SR_RX_MSG_AVAILABLE  0x01 
#define SR_DATA_OVR          0x02
#define SR_TX_BUF_WRITE_OK   0x04 // TBS1, TBS2, TBS3 (Bit 2, 10, 18)
#define SR_TX_COMPLETE       0x08 // TCS1, TCS2, TCS3 (Bit 3, 11, 19)
#define SR_RECEIVING_ACTIVE  0x10
#define SR_SENDING_ACTIVE    0x20 // TS1, TS2, TS3 (5, 13, 21)
#define SR_ERR               0x40
#define SR_BUS_OFF           0x80


//---------------------------------------------------------------------------
// Optimize for the case of a single CAN channel, while still allowing
// multiple channels.
//
#if CYGINT_IO_CAN_CHANNELS == 1
#define CAN_CTRL_BASE(_extra_)   CAN_CTRL_SINGLETON_BASE
#define CAN_ISRVEC(_extra_)      CAN_SINGLETON_ISRVEC
#define CAN_CHAN_NO(_extra_)     CAN_SINGLETON_CHAN_NO
#define CAN_DECLARE_INFO(_chan_)
#define CAN_DECLARE_CHAN(_data_)
#else
#define CAN_CTRL_BASE(_extra_)   ((_extra_)->base)
#define CAN_ISRVEC(_extra_)      ((_extra_)->isrvec)
#define CAN_CHAN_NO(_extra_)     ((_extra_)->chan_no)
#define CAN_DECLARE_INFO(_chan_) lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv;
#define CAN_DECLARE_CHAN(_data_) can_channel  *chan = (can_channel *)data;
#endif // CYGINT_IO_CAN_CHANNELS == 1 


#ifdef CYGOPT_DEVS_CAN_LPC2XXX_CAN0_ACCFILT_STARTUP_CFG_RX_ALL
#define CAN0_FLAG_STARTUP_ACCFILT_SETUP INFO_FLAG_STARTUP_RX_ALL
#else
#define CAN0_FLAG_STARTUP_ACCFILT_SETUP 0x00
#endif

#ifdef CYGOPT_DEVS_CAN_LPC2XXX_CAN1_ACCFILT_STARTUP_CFG_RX_ALL
#define CAN1_FLAG_STARTUP_ACCFILT_SETUP INFO_FLAG_STARTUP_RX_ALL
#else
#define CAN1_FLAG_STARTUP_ACCFILT_SETUP 0x00
#endif

#ifdef CYGOPT_DEVS_CAN_LPC2XXX_CAN2_ACCFILT_STARTUP_CFG_RX_ALL
#define CAN2_FLAG_STARTUP_ACCFILT_SETUP INFO_FLAG_STARTUP_RX_ALL
#else
#define CAN2_FLAG_STARTUP_ACCFILT_SETUP 0x00
#endif

#ifdef CYGOPT_DEVS_CAN_LPC2XXX_CAN3_ACCFILT_STARTUP_CFG_RX_ALL
#define CAN3_FLAG_STARTUP_ACCFILT_SETUP INFO_FLAG_STARTUP_RX_ALL
#else
#define CAN3_FLAG_STARTUP_ACCFILT_SETUP 0x00
#endif


//===========================================================================
//                              DATA TYPES
//===========================================================================
//
// Structure stores LPC2xxx CAN channel related stuff
//

// If we use Self Reception Request command instead of the Transmission Request
// we must add last transmit message id in order to reject it in rx_ISR
// There are two last_tx_id because tx interrupt (and so transmission of next 
// message) happens before rx interrupt (which uses last_tx_id for rejecting)) 

// Format of last_tx_id:
//  (bits: 28:0-ID, 29-Validation, 30-RTR, 31-EXT)
//  if last_tx_id == 0xFFFFFFFF (Validation == 1) then last id is not valid
#ifdef CYGOPT_DEVS_CAN_LPC2XXX_USE_SELF_RECEPTION
 #define LPC2XXX_CAN_INFO_LAST_TX_ID_IDMASK   0x1FFFFFFF
 #define LPC2XXX_CAN_INFO_LAST_TX_ID_FLMASK   0xC0000000
 #define LPC2XXX_CAN_INFO_LAST_TX_ID_NOVALID  0xFFFFFFFF

 #define LPC2XXX_CAN_INFO_LAST_TX_ID_DECL     cyg_uint8    last_tx_index;                 \
                                              cyg_uint32   last_tx_id[2];
 #define LPC2XXX_CAN_INFO_LAST_TX_ID_INIT     last_tx_index : 0,                          \
                                              last_tx_id    : {LPC2XXX_CAN_INFO_LAST_TX_ID_NOVALID, LPC2XXX_CAN_INFO_LAST_TX_ID_NOVALID},
#else
 #define LPC2XXX_CAN_INFO_LAST_TX_ID_DECL
 #define LPC2XXX_CAN_INFO_LAST_TX_ID_INIT
#endif

typedef struct lpc2xxx_can_info_st
{
//
// Newer LPC2xxx variants like the LPC2468 do not support per channel 
// interrupts. They provide only one single interrupt vector for all
// CAN interrupts
//
#ifndef CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
    cyg_interrupt      tx_interrupt;
    cyg_handle_t       tx_interrupt_handle;     
    cyg_uint8          tx_interrupt_priority;     
    cyg_interrupt      rx_interrupt;
    cyg_handle_t       rx_interrupt_handle; 
    cyg_uint8          rx_interrupt_priority;
#endif // CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
    cyg_can_state      state;            // state of CAN controller 
    cyg_uint8          flags;            // flags indicating several states       
    LPC2XXX_CAN_INFO_LAST_TX_ID_DECL     // last transmitted messages ids   
#if CYGINT_IO_CAN_CHANNELS > 1
    cyg_uint32         base;             // Per-bus h/w details
#ifndef CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
    cyg_uint8          isrvec;           // ISR vector (peripheral id)
#endif // CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
    cyg_uint8          chan_no;          // number of CAN channel
#endif // CYGINT_IO_CAN_CHANNELS > 1
} lpc2xxx_can_info_t;


#define INFO_FLAG_RX_ALL           0x01 // this bit indicates that channel receives all CAN messages - no filtering active
#define INFO_FLAG_STARTUP_RX_ALL   0x02 // this bit indicates filter state at startup


//
// lpc2xxx info initialisation
//
#define LPC2XXX_CTRL_NOT_INITIALIZED 0xFF

#ifndef CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
#if CYGINT_IO_CAN_CHANNELS > 1
#define LPC2XXX_CAN_INFO(_l, _base, _isrvec, _chan_no_, _tx_priority, _rx_priority, _flags) \
lpc2xxx_can_info_t _l  = {                                                                  \
    state             :  LPC2XXX_CTRL_NOT_INITIALIZED,                                      \
    base              : (_base),                                                            \
    isrvec            : (_isrvec),                                                          \
    chan_no           : (_chan_no_),                                                        \
    tx_interrupt_priority : (_tx_priority),                                                 \
    rx_interrupt_priority : (_rx_priority),                                                 \
    flags             : (_flags),                                                           \
    LPC2XXX_CAN_INFO_LAST_TX_ID_INIT                                                        \
};
#else // CYGINT_IO_CAN_CHANNELS == 1
#define LPC2XXX_CAN_INFO(_l, _tx_priority, _rx_priority, _flags) \
lpc2xxx_can_info_t _l = {                          \
    state      : CYGNUM_CAN_STATE_STOPPED,         \
    tx_interrupt_priority : (_tx_priority),        \
    rx_interrupt_priority : (_rx_priority),        \
    flags      : (_flags),                         \
    LPC2XXX_CAN_INFO_LAST_TX_ID_INIT               \
};
#endif // CYGINT_IO_CAN_CHANNELS == 1
#else // CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
//
// Newer devices support only one global CAN interrupt. We do not need
// per channel interrupt data an ignore the values during initialisation
//
#if CYGINT_IO_CAN_CHANNELS > 1
#define LPC2XXX_CAN_INFO(_l, _base, _isrvec, _chan_no_, _tx_priority, _rx_priority, _flags) \
lpc2xxx_can_info_t _l  = {                                                       \
    state             :  LPC2XXX_CTRL_NOT_INITIALIZED,                           \
    base              : (_base),                                                 \
    chan_no           : (_chan_no_),                                             \
    flags             : (_flags),                                                \
    LPC2XXX_CAN_INFO_LAST_TX_ID_INIT                                             \
};
#else // CYGINT_IO_CAN_CHANNELS == 1
#define LPC2XXX_CAN_INFO(_l, _tx_priority, _rx_priority, _flags) \
lpc2xxx_can_info_t _l = {                          \
    state      : CYGNUM_CAN_STATE_STOPPED,         \
    flags      : (_flags),                         \
    LPC2XXX_CAN_INFO_LAST_TX_ID_INIT               \
};
#endif // CYGINT_IO_CAN_CHANNELS == 1

//
// The following defines are only dummies required for proper 
// initialisation of can channel data structures
//
#define CYGNUM_HAL_INTERRUPT_CAN1_TX
#define CYGNUM_DEVS_CAN_LPC2XXX_CAN0_TX_INT_PRIORITY
#define CYGNUM_DEVS_CAN_LPC2XXX_CAN0_RX_INT_PRIORITY
#define CYGNUM_HAL_INTERRUPT_CAN2_TX
#define CYGNUM_DEVS_CAN_LPC2XXX_CAN1_TX_INT_PRIORITY
#define CYGNUM_DEVS_CAN_LPC2XXX_CAN1_RX_INT_PRIORITY
#define CYGNUM_HAL_INTERRUPT_CAN3_TX
#define CYGNUM_DEVS_CAN_LPC2XXX_CAN2_TX_INT_PRIORITY
#define CYGNUM_DEVS_CAN_LPC2XXX_CAN2_RX_INT_PRIORITY
#define CYGNUM_HAL_INTERRUPT_CAN4_TX
#define CYGNUM_DEVS_CAN_LPC2XXX_CAN3_TX_INT_PRIORITY
#define CYGNUM_DEVS_CAN_LPC2XXX_CAN3_RX_INT_PRIORITY
#endif // CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY


//
// Acceptance filter data
//
typedef struct lpc2xxx_global_can_info_st
{
    cyg_interrupt       interrupt;          // common CAN interrupt
    cyg_handle_t        interrupt_handle;   // common CAN interrupt handle 
    cyg_uint16          free_filters;       // number of free message filter
#if CYGINT_IO_CAN_CHANNELS > 1              // optimize for single channel
    cyg_uint8           init_cnt;           // counts number of initialized channels
    can_channel*        active_channels[5]; // stores pointers to active channels - the last entry is just a delimiter
#else // CYGINT_IO_CAN_CHANNELS > 1
    can_channel*        active_channels[1]; // optimize for one single channel
#endif // CYGINT_IO_CAN_CHANNELS > 1
} lpc2xxx_global_can_info_t;


#if CYGINT_IO_CAN_CHANNELS > 1
#define LPC2XXX_GET_CAN_CHANNEL(_can_info_, _chan_no_) ((can_channel*)(_can_info_).active_channels[_chan_no_])
#else
#define LPC2XXX_GET_CAN_CHANNEL(_can_info_, _chan_no_) ((can_channel*)(_can_info_).active_channels[0])
#endif

//
// The number of available message filters depends on the size of the
// acceptance filter RAM and on the size of one entry. The size of
// one entry is 4 byte (standard ID only 2 byte, extended groups 8 byte)
//
#define ACCFILT_COMMON_ENTRY_SIZE 4
#define LPC2XXX_CAN_MSG_FILTERS_MAX (ACCFILT_RAM_SIZE / ACCFILT_COMMON_ENTRY_SIZE)
lpc2xxx_global_can_info_t lpc2xxx_global_can_info =
{
    .free_filters     = LPC2XXX_CAN_MSG_FILTERS_MAX,
#if CYGINT_IO_CAN_CHANNELS > 1 // optimize for single channel
    .init_cnt         = 0,
    .active_channels  = {0, 0, 0, 0, 0},
#endif // #if CYGINT_IO_CAN_CHANNELS > 1
};



//
// Data type for access of single bytes/words of an dword value
//
typedef union lsc_buf_u
{
    cyg_uint8  bytes[4];
    struct 
    {
        cyg_uint16 low;
        cyg_uint16 high;
    } words;
    
    struct
    {
        cyg_uint16 upper; // uppper column of acceptance filter ram
        cyg_uint16 lower; // lower column of acceptance filter ram
    } column;
    
    cyg_uint32 dword;
} lsc_buf_t;


//===========================================================================
//                          GLOBAL DATA
//===========================================================================
#if CYGINT_IO_CAN_CHANNELS > 1
#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN0
LPC2XXX_CAN_INFO(lpc2xxx_can0_info,
                 CAN_CTRL_1_REG_BASE,
                 CYGNUM_HAL_INTERRUPT_CAN1_TX,
                 0,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN0_TX_INT_PRIORITY,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN0_RX_INT_PRIORITY,
                 CAN0_FLAG_STARTUP_ACCFILT_SETUP);
#endif

#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN1
LPC2XXX_CAN_INFO(lpc2xxx_can1_info, 
                 CAN_CTRL_2_REG_BASE, 
                 CYGNUM_HAL_INTERRUPT_CAN2_TX,
                 1,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN1_TX_INT_PRIORITY,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN1_RX_INT_PRIORITY,
                 CAN1_FLAG_STARTUP_ACCFILT_SETUP);
#endif

#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN2
LPC2XXX_CAN_INFO(lpc2xxx_can2_info, 
                 CAN_CTRL_3_REG_BASE, 
                 CYGNUM_HAL_INTERRUPT_CAN3_TX,
                 2,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN2_TX_INT_PRIORITY,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN2_RX_INT_PRIORITY,
                 CAN2_FLAG_STARTUP_ACCFILT_SETUP);
#endif

#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN3
LPC2XXX_CAN_INFO(lpc2xxx_can3_info, 
                 CAN_CTRL_4_REG_BASE, 
                 CYGNUM_HAL_INTERRUPT_CAN4_TX,
                 3,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN3_TX_INT_PRIORITY,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN3_RX_INT_PRIORITY,
                 CAN3_FLAG_STARTUP_ACCFILT_SETUP);
#endif
#else // CYGINT_IO_CAN_CHANNELS == 1
#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN0
LPC2XXX_CAN_INFO(lpc2xxx_can0_info, 
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN0_TX_INT_PRIORITY, 
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN0_RX_INT_PRIORITY, 
                 CAN0_FLAG_STARTUP_ACCFILT_SETUP);
#define CAN_CTRL_SINGLETON_BASE   CAN_CTRL_1_REG_BASE
#define CAN_SINGLETON_ISRVEC      CYGNUM_HAL_INTERRUPT_CAN1_TX
#define CAN_SINGLETON_CHAN_NO     0
#endif

#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN1
LPC2XXX_CAN_INFO(lpc2xxx_can1_info,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN1_TX_INT_PRIORITY,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN1_RX_INT_PRIORITY,
                 CAN1_FLAG_STARTUP_ACCFILT_SETUP);
#define CAN_CTRL_SINGLETON_BASE   CAN_CTRL_2_REG_BASE
#define CAN_SINGLETON_ISRVEC      CYGNUM_HAL_INTERRUPT_CAN2_TX
#define CAN_SINGLETON_CHAN_NO     1
#endif

#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN2
LPC2XXX_CAN_INFO(lpc2xxx_can2_info,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN2_TX_INT_PRIORITY,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN2_RX_INT_PRIORITY,
                 CAN2_FLAG_STARTUP_ACCFILT_SETUP);
#define CAN_CTRL_SINGLETON_BASE   CAN_CTRL_3_REG_BASE
#define CAN_SINGLETON_ISRVEC      CYGNUM_HAL_INTERRUPT_CAN3_TX
#define CAN_SINGLETON_CHAN_NO     2
#endif

#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN3
LPC2XXX_CAN_INFO(lpc2xxx_can3_info,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN3_TX_INT_PRIORITY,
                 CYGNUM_DEVS_CAN_LPC2XXX_CAN3_RX_INT_PRIORITY,
                 CAN3_FLAG_STARTUP_ACCFILT_SETUP);
#define CAN_CTRL_SINGLETON_BASE   CAN_CTRL_4_REG_BASE
#define CAN_SINGLETON_ISRVEC      CYGNUM_HAL_INTERRUPT_CAN4_TX
#define CAN_SINGLETON_CHAN_NO     3
#endif

#endif // #if CYGINT_IO_CAN_CHANNELS > 1


//===========================================================================
//                              PROTOTYPES
//===========================================================================

//--------------------------------------------------------------------------
// Device driver interface functions
//
static bool        lpc2xxx_can_init(struct cyg_devtab_entry* devtab_entry);
static Cyg_ErrNo   lpc2xxx_can_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry* sub_tab, const char* name);
static Cyg_ErrNo   lpc2xxx_can_set_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len);
static Cyg_ErrNo   lpc2xxx_can_get_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len);
static bool        lpc2xxx_can_putmsg(can_channel *priv, CYG_CAN_MSG_T *pmsg, void *pdata);
static bool        lpc2xxx_can_getevent(can_channel *priv, CYG_CAN_EVENT_T *pevent, void *pdata);
static void        lpc2xxx_can_start_xmit(can_channel* chan);
static void        lpc2xxx_can_stop_xmit(can_channel* chan);


//--------------------------------------------------------------------------
// ISRs and DSRs
//
#ifndef CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
static cyg_uint32 lpc2xxx_can_tx_ISR(cyg_vector_t vector, cyg_addrword_t data);
static void       lpc2xxx_can_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
static cyg_uint32 lpc2xxx_can_rx_ISR(cyg_vector_t vector, cyg_addrword_t data);
static void       lpc2xxx_can_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
#endif // CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
static cyg_uint32 lpc2xxx_can_ISR(cyg_vector_t vector, cyg_addrword_t data);
static void       lpc2xxx_can_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);


//--------------------------------------------------------------------------
// Private utility functions
//
static bool lpc2xxx_can_config_channel(can_channel* chan, cyg_can_info_t* config, cyg_bool init);
static bool lpc2xxx_can_set_baud(can_channel *chan, cyg_can_baud_rate_t *baudrate);
static Cyg_ErrNo lpc2xxx_enter_lowpower_mode(can_channel *chan);
static void lpc2xxx_start_module(can_channel *chan);
static cyg_can_state lpc2xxx_get_state(lpc2xxx_can_info_t *info);
static void lpc2xxx_set_state(lpc2xxx_can_info_t *info, cyg_can_state state);


//--------------------------------------------------------------------------
// Message box configuration
//
static void lpc2xxx_can_config_rx_all(can_channel *chan);
#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
static void lpc2xxx_can_config_rx_none(can_channel *chan);
static bool lpc2xxx_can_add_rx_filter(lpc2xxx_can_info_t *info, cyg_can_filter *filter);
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG


#include "can_accfilt_lpc2xxx.c"

//===========================================================================
//                   GENERIC CAN IO DATA INITIALISATION
//===========================================================================
CAN_LOWLEVEL_FUNS(lpc2xxx_can_lowlevel_funs,
                  lpc2xxx_can_putmsg,
                  lpc2xxx_can_getevent,
                  lpc2xxx_can_get_config,
                  lpc2xxx_can_set_config,
                  lpc2xxx_can_start_xmit,
                  lpc2xxx_can_stop_xmit
     );


//---------------------------------------------------------------------------
// CAN channel 0
//
#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN0
CYG_CAN_EVENT_T  lpc2xxx_can0_rxbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN0_QUEUESIZE_RX]; // buffer for RX can events
CYG_CAN_MSG_T    lpc2xxx_can0_txbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN0_QUEUESIZE_TX]; // buffer for TX can messages


CAN_CHANNEL_USING_INTERRUPTS(lpc2xxx_can0_chan,
                             lpc2xxx_can_lowlevel_funs,
                             lpc2xxx_can0_info,
                             CYG_CAN_BAUD_RATE(CYGNUM_DEVS_CAN_LPC2XXX_CAN0_KBAUD),
                             lpc2xxx_can0_txbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN0_QUEUESIZE_TX,
                             lpc2xxx_can0_rxbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN0_QUEUESIZE_RX
    );


DEVTAB_ENTRY(lpc2xxx_can0_devtab, 
             CYGPKG_DEVS_CAN_LPC2XXX_CAN0_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_can_devio, 
             lpc2xxx_can_init, 
             lpc2xxx_can_lookup,    // CAN driver may need initializing
             &lpc2xxx_can0_chan
    );
#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN0


//---------------------------------------------------------------------------
// CAN channel 1
//
#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN1
CYG_CAN_EVENT_T  lpc2xxx_can1_rxbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN1_QUEUESIZE_RX]; // buffer for RX can events
CYG_CAN_MSG_T    lpc2xxx_can1_txbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN1_QUEUESIZE_TX]; // buffer for TX can messages


CAN_CHANNEL_USING_INTERRUPTS(lpc2xxx_can1_chan,
                             lpc2xxx_can_lowlevel_funs,
                             lpc2xxx_can1_info,
                             CYG_CAN_BAUD_RATE(CYGNUM_DEVS_CAN_LPC2XXX_CAN1_KBAUD),
                             lpc2xxx_can1_txbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN1_QUEUESIZE_TX,
                             lpc2xxx_can1_rxbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN1_QUEUESIZE_RX
    );


DEVTAB_ENTRY(lpc2xxx_can1_devtab, 
             CYGPKG_DEVS_CAN_LPC2XXX_CAN1_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_can_devio, 
             lpc2xxx_can_init, 
             lpc2xxx_can_lookup,    // CAN driver may need initializing
             &lpc2xxx_can1_chan
    );
#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN1


//---------------------------------------------------------------------------
// CAN channel 2
//
#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN2
CYG_CAN_EVENT_T  lpc2xxx_can2_rxbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN2_QUEUESIZE_RX]; // buffer for RX can events
CYG_CAN_MSG_T    lpc2xxx_can2_txbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN2_QUEUESIZE_TX]; // buffer for TX can messages


CAN_CHANNEL_USING_INTERRUPTS(lpc2xxx_can2_chan,
                             lpc2xxx_can_lowlevel_funs,
                             lpc2xxx_can2_info,
                             CYG_CAN_BAUD_RATE(CYGNUM_DEVS_CAN_LPC2XXX_CAN2_KBAUD),
                             lpc2xxx_can2_txbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN2_QUEUESIZE_TX,
                             lpc2xxx_can2_rxbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN2_QUEUESIZE_RX
    );


DEVTAB_ENTRY(lpc2xxx_can2_devtab, 
             CYGPKG_DEVS_CAN_LPC2XXX_CAN2_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_can_devio, 
             lpc2xxx_can_init, 
             lpc2xxx_can_lookup,    // CAN driver may need initializing
             &lpc2xxx_can2_chan
    );
#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN2


//---------------------------------------------------------------------------
// CAN channel 3
//
#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN3
CYG_CAN_EVENT_T  lpc2xxx_can3_rxbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN3_QUEUESIZE_RX]; // buffer for RX can events
CYG_CAN_MSG_T    lpc2xxx_can3_txbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN3_QUEUESIZE_TX]; // buffer for TX can messages


CAN_CHANNEL_USING_INTERRUPTS(lpc2xxx_can3_chan,
                             lpc2xxx_can_lowlevel_funs,
                             lpc2xxx_can3_info,
                             CYG_CAN_BAUD_RATE(CYGNUM_DEVS_CAN_LPC2XXX_CAN3_KBAUD),
                             lpc2xxx_can3_txbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN3_QUEUESIZE_TX,
                             lpc2xxx_can3_rxbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN3_QUEUESIZE_RX
    );


DEVTAB_ENTRY(lpc2xxx_can3_devtab, 
             CYGPKG_DEVS_CAN_LPC2XXX_CAN3_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_can_devio, 
             lpc2xxx_can_init, 
             lpc2xxx_can_lookup,    // CAN driver may need initializing
             &lpc2xxx_can3_chan
    );
#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN3


//===========================================================================
//                            IMPLEMENTATION
//===========================================================================



//===========================================================================
/// First initialisation and reset of CAN modul.
//===========================================================================
static bool lpc2xxx_can_init(struct cyg_devtab_entry* devtab_entry)
{
    can_channel          *chan    = (can_channel*)devtab_entry->priv;
    bool                  res;

#ifdef CYGDBG_IO_INIT
    diag_printf("LPC2XXX CAN init\n");
#endif  

    //
    // Newer LPC2xxx variants do not support individual interrupt
    // sources for CAN on chip peripherals
    //
#ifndef CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY  
    lpc2xxx_can_info_t   *info    = (lpc2xxx_can_info_t *)chan->dev_priv;  
    //
    // Create TX interrupt
    //
    cyg_drv_interrupt_create(CAN_ISRVEC(info),
                             info->tx_interrupt_priority,
                             (cyg_addrword_t)chan,     // Data item passed to interrupt handler
                             lpc2xxx_can_tx_ISR,
                             lpc2xxx_can_tx_DSR,
                             &info->tx_interrupt_handle,
                             &info->tx_interrupt);
    cyg_drv_interrupt_attach(info->tx_interrupt_handle);
    cyg_drv_interrupt_unmask(CAN_ISRVEC(info));
    
    //
    // Create RX interrupt
    //
    cyg_drv_interrupt_create(CAN_ISRVEC(info) + 6,
                             info->rx_interrupt_priority,
                             (cyg_addrword_t)chan,     // Data item passed to interrupt handler
                             lpc2xxx_can_rx_ISR,
                             lpc2xxx_can_rx_DSR,
                             &info->rx_interrupt_handle,
                             &info->rx_interrupt);
    cyg_drv_interrupt_attach(info->rx_interrupt_handle);
    cyg_drv_interrupt_unmask(CAN_ISRVEC(info) + 6);
#endif // CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
    
    //
    // Now create and enable global CAN interrupt. This interrupt is
    // global for all channels and so we need to call it only one times -
    // when the first channel is initialized
    //
#if CYGINT_IO_CAN_CHANNELS > 1
    if (!lpc2xxx_global_can_info.init_cnt)
#endif // #if CYGINT_IO_CAN_CHANNELS > 1
    {
        //
        // Create err interrupt
        //
        cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_CAN,
#ifndef CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
                                 CYGNUM_DEVS_CAN_LPC2XXX_ERR_INT_PRIORITY,
#else // CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
                                 CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY,
#endif // CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
                                 0,                        // Data item passed to interrupt handler
                                 lpc2xxx_can_ISR,
                                 lpc2xxx_can_DSR,
                                 &lpc2xxx_global_can_info.interrupt_handle,
                                 &lpc2xxx_global_can_info.interrupt);
        cyg_drv_interrupt_attach(lpc2xxx_global_can_info.interrupt_handle);
        cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_CAN);     
    }   
   
    res = lpc2xxx_can_config_channel(chan, &chan->config, true);
#if CYGINT_IO_CAN_CHANNELS > 1
    lpc2xxx_global_can_info.active_channels[lpc2xxx_global_can_info.init_cnt++] = chan;
#else // CYGINT_IO_CAN_CHANNELS > 1
    lpc2xxx_global_can_info.active_channels[0] = chan;
#endif
    return res; 
}


//===========================================================================
// Configure can channel
//===========================================================================
static bool lpc2xxx_can_config_channel(can_channel* chan, cyg_can_info_t* config, cyg_bool init)
{
    CAN_DECLARE_INFO(chan);
    bool       res = true;
    
    if (init)
    {
        //
        // In case platform needs extra initialization (i.e. setup of
        // CAN transceivers) it should implement this macro
        //
#ifdef CYGPRI_IO_CAN_LPC2XXX_PLF_INIT_HOOK
        CYGPRI_IO_CAN_LPC2XXX_PLF_INIT_HOOK(chan, config);
#endif
        
        HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_OFF);       // Acceptance Filter Mode Register = off
        HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_RESET); // Go into reset mode
        HAL_WRITE_UINT32(CAN_CTRL_IER(info), 0);            // disable all interrupts
        HAL_WRITE_UINT32(CAN_CTRL_GSR(info), 0);            // Clear Status register - clears error counters  
        
        //
        // Perform platform/variant specific initialisation here. 
        // The variant/ platform should setup the pin configuration to support 
        // CAN here
        //
        HAL_LPC2XXX_INIT_CAN(CAN_CHAN_NO(info)); 
         
        //
        // If this is the first channel to initialize then we reset the CAN 
        // registers and setup the CAN I/O pins
        //
#if CYGINT_IO_CAN_CHANNELS > 1
        if (!lpc2xxx_global_can_info.init_cnt)
#endif // #if CYGINT_IO_CAN_CHANNELS > 1
        {
            lpc2xxx_can_accfilt_reset();           
        }   
    } // if (init)
    
    res = lpc2xxx_can_set_baud(chan, &config->baud);           // set baudrate
    // $$$$ enable receive interrupt?
    HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_OPERATIONAL);  // enter normal operating mode
            
    //
    // store new config values
    //
    if (config != &chan->config) 
    {
        chan->config = *config;
    }   
    
    return res;
}


//===========================================================================
// Set baudrate of certain can channel
//===========================================================================
static bool lpc2xxx_can_set_baud(can_channel *chan, cyg_can_baud_rate_t *baudrate)
{
    bool                  res = true;
    cyg_uint32            canbtr;
    cyg_uint32            canmod;
    CAN_DECLARE_INFO(chan);
    
    //
    // Get bit timings from HAL because bit timings depend on sysclock
    // If the macro fills the canbtr value with 0 then the baudrate
    // is not supported and the function returns false
    //
    HAL_LPC2XXX_GET_CAN_BR(*baudrate, canbtr);   
    if (0 == canbtr)
    {
        return false;
    }
    
    //
    // Any modificatons to the baudrate register must be done while CAN
    // module is in reset mode. So we first set the CAN module in reset
    // mode, then we set baudrate and then we restore content of CANMOD 
    // register
    //
    HAL_READ_UINT32(CAN_CTRL_MOD(info), canmod);        // backup canmod register
    HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_RESET); // Go into reset mode
    HAL_WRITE_UINT32(CAN_CTRL_BTR(info), canbtr);       // write baudrate value
    HAL_WRITE_UINT32(CAN_CTRL_MOD(info), canmod);       // restore previous value
       
    return res;
}


//===========================================================================
//  Lookup the device and return its handle
//===========================================================================
static Cyg_ErrNo lpc2xxx_can_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry* sub_tab, const char* name)
{
    can_channel* chan    = (can_channel*) (*tab)->priv;
    lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv;
    cyg_uint32   regval;

    chan->callbacks->can_init(chan); 
    
    //
    // If runtime acceptance filter configuration is supported then we only
    // configure RX ALL if the user selected the RX ALL setup in config utility
    //
#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
    if (info->flags & INFO_FLAG_STARTUP_RX_ALL)
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
    {
       lpc2xxx_can_config_rx_all(chan); 
    }
    
    HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_ON);       // Activate acceptance filter
    HAL_READ_UINT32(CAN_CTRL_IER(info), regval);
    regval = regval | IER_RX | CAN_MISC_INT;           // enable all interrupts     
    HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval);  
      
    return ENOERR;
}


#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
//===========================================================================
// Setup LPC2XXX CAN module in a state where all message boxes are disabled
// After this call it is possible to add single message buffers and filters
//===========================================================================
static void lpc2xxx_can_config_rx_none(can_channel *chan)
{
    lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv;
      
    //
    // Remove all acceptance filters
    // $$$$ maybe we should also abort any pending transfers and
    // disable receive interrupts ?
    //
    lpc2xxx_can_accfilt_remove_all_ctrl_entries(info);
    info->flags  &= ~INFO_FLAG_RX_ALL;
#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
    lpc2xxx_can_accfilt_dbg_dump();
#endif
}


//===========================================================================
// Add one single message filter to acceptance filter
//===========================================================================
static bool lpc2xxx_can_add_rx_filter(lpc2xxx_can_info_t *info, cyg_can_filter *filter)
{
    bool res;
    
    res = lpc2xxx_can_accfilt_add(info, filter->msg.id, 0, filter->msg.ext); 
    if (!res)
    {
        filter->handle = CYGNUM_CAN_MSGBUF_NA;
    }
#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
    lpc2xxx_can_accfilt_dbg_dump();
#endif    
    return res;
}
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG



#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
//===========================================================================
// Configure message buffers
//===========================================================================
static Cyg_ErrNo lpc2xxx_can_config_msgbuf(can_channel *chan, const void* buf, cyg_uint32* len)
{
    Cyg_ErrNo             res  = ENOERR;
    lpc2xxx_can_info_t   *info = (lpc2xxx_can_info_t *)chan->dev_priv;   
    cyg_can_msgbuf_cfg   *msg_buf = (cyg_can_msgbuf_cfg *)buf;

    if (*len != sizeof(cyg_can_msgbuf_cfg))
    {
        return -EINVAL;
    }

    switch (msg_buf->cfg_id)
    {
        //
        // clear all message filters and remote buffers - prepare for message buffer
        // configuration
        //
        case CYGNUM_CAN_MSGBUF_RESET_ALL :
             {
                 lpc2xxx_can_config_rx_none(chan);
             }
             break;

        //
        // setup driver for reception of all standard and extended messages
        //
        case CYGNUM_CAN_MSGBUF_RX_FILTER_ALL :
             {
                 if (!(info->flags & INFO_FLAG_RX_ALL)) // if rx_all is enabled we do not need to do anything
                 {
                    lpc2xxx_can_config_rx_all(chan);  // setup RX all state
                 }
             }
             break;
        
        //
        // add single message filter, message with filter ID will be received
        //     
        case CYGNUM_CAN_MSGBUF_RX_FILTER_ADD :
             {
                 cyg_can_filter *filter   = (cyg_can_filter*) buf;
                 
                 //
                 // if the acceptance filter is configured to receive all messages then 
                 // it is not allowed to add single message filters because then more 
                 // than one acceptance filter would receive the same CAN id
                 //
                 if (info->flags & INFO_FLAG_RX_ALL)
                 {
                    return -EPERM;
                 }
                 
                 //
                 // try to allocate a free acceptance filter entry - if we have a free one
                 // then we can prepare the acceptance filter table for reception of
                 // this message
                 //
                 if (!lpc2xxx_can_add_rx_filter(info, filter))
                 {
                     return -EPERM;
                 }
             }
             break; //CYGNUM_CAN_MSGBUF_RX_FILTER_ADD
             

#ifdef CYGOPT_IO_CAN_REMOTE_BUF
        //
        // Try to add a new RTR response message buffer for automatic
        // transmission of data frame on reception of a remote frame
        //
        case CYGNUM_CAN_MSGBUF_REMOTE_BUF_ADD :
             {
                 // $$$$ TODO implement remote response buffers in software
                 return -ENOSUPP;
             }
             break;
                     
        //
        // write data into remote response buffer
        //
        case CYGNUM_CAN_MSGBUF_REMOTE_BUF_WRITE :
             {
                 // $$$$ TODO implement remote response buffers in software
                 return -ENOSUPP;
             }
             break;
#endif // #ifdef CYGOPT_IO_CAN_REMOTE_BUF
        default:
            return -EINVAL;
    } // switch (buf->cfg_id)
    
    return res;
}
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG


//===========================================================================
// Read state of CAN controller
// The CAN state variable for each channel is modified by DSR so if we 
// read the state we need to lock DSRs to protect the data access
//===========================================================================
static cyg_can_state lpc2xxx_get_state(lpc2xxx_can_info_t *info)
{
    cyg_can_state result;
    
    cyg_drv_dsr_lock();
    result = info->state;
    cyg_drv_dsr_unlock();
    
    return result;
}


//===========================================================================
// Set state of CAN controller
// The CAN state variable for each channel is modified by DSR so if we 
// write the state we need to lock DSRs to protect the data access
//===========================================================================
static void lpc2xxx_set_state(lpc2xxx_can_info_t *info, cyg_can_state state)
{   
    cyg_drv_dsr_lock();
    info->state = state;
    cyg_drv_dsr_unlock();
}


//===========================================================================
// Enter low power mode
//===========================================================================
static Cyg_ErrNo lpc2xxx_enter_lowpower_mode(can_channel *chan)
{
    cyg_uint32          regval;
    lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv;
    
    //
    // Before we enter low power mode, we have to enable wake up interrupt
    // Normally this interrupt is always enabled so we do not need to do
    // anything here
    //
    HAL_READ_UINT32(CAN_CTRL_MOD(info), regval); 
    
    //
    // Software can only set SM when RM in the CAN Mode register is 0
    //
    if (regval & CANMOD_RESET)
    {
        return -EPERM;
    }
    
    //regval &= CANMOD_SLEEP;
    lpc2xxx_set_state(info, CYGNUM_CAN_STATE_STANDBY);
    HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_SLEEP); 
    return ENOERR;
}


//===========================================================================
// Start CAN module - set CANMOD operational and enable all interrupts
//===========================================================================
static void lpc2xxx_start_module(can_channel *chan)
{
    cyg_uint32          regval;
    lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv;
    
    HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_OPERATIONAL);  
    //
    // The interrupt enable register is also modified by ISR and DSR so
    // we need to protect acces here
    //
    cyg_drv_isr_lock();
    HAL_READ_UINT32(CAN_CTRL_IER(info), regval);
    regval = regval | IER_RX | CAN_MISC_INT;           // enable all interrupts     
    HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); 
    info->state = CYGNUM_CAN_STATE_ACTIVE; 
    cyg_drv_isr_unlock();
}


//===========================================================================
// Enter reset mode
//===========================================================================
static void lpc2xxx_enter_reset_mode(can_channel *chan)
{
    lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv;  
    
    info->state = CYGNUM_CAN_STATE_STOPPED;
    HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_RESET);
}


#ifdef CYGOPT_DEVS_CAN_LPC2XXX_EXTENDED_CFG_KEYS
//===========================================================================
// Add message filter group
//===========================================================================
static Cyg_ErrNo lpc2xxx_can_config_accfilt_group(can_channel *chan, const void* buf, cyg_uint32* len)
{
    bool                     res;
    cyg_can_filtergroup_cfg *filter_grp = (cyg_can_filtergroup_cfg *)buf;
    lpc2xxx_can_info_t      *info = (lpc2xxx_can_info_t *)chan->dev_priv; 
    
    
    if (*len != sizeof(cyg_can_filtergroup_cfg))
    {
        return -EINVAL;
    }
    
    if (filter_grp->lower_id_bound >= filter_grp->upper_id_bound)
    {
        return -EINVAL;
    }
    
    //
    // if the acceptance filter is configured to receive all messages then 
    // it is not allowed to add single message filter groups because then more 
    // than one acceptance filter would receive the same CAN id
    //
    if (info->flags & INFO_FLAG_RX_ALL)
    {
        return -EPERM;
    }
    
    res = lpc2xxx_can_accfilt_add(info, 
                                  filter_grp->lower_id_bound, 
                                  filter_grp->upper_id_bound, 
                                  filter_grp->ext);
    
#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
    lpc2xxx_can_accfilt_dbg_dump();
#endif        
    return res ? ENOERR : -EPERM;
}
#endif // CYGOPT_DEVS_CAN_LPC2XXX_EXTENDED_CFG_KEYS


//===========================================================================
// Change device configuration
//===========================================================================
static Cyg_ErrNo lpc2xxx_can_set_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len)
{
    Cyg_ErrNo  res = ENOERR;
    
    switch (key)
    {   
        //
        // Setup a new CAN configuration. This will i.e. setup a new baud rate
        //
        case CYG_IO_SET_CONFIG_CAN_INFO:
             {
                 cyg_can_info_t*  config = (cyg_can_info_t*) buf;
                 if (*len < sizeof(cyg_can_info_t))
                 {
                     return -EINVAL;
                 }
                 *len = sizeof(cyg_can_info_t);
                 if (!lpc2xxx_can_config_channel(chan, config, false))
                 {
                     return -EINVAL;
                 }
             }
             break;

#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG            
        //
        // configure message buffers
        //
        case CYG_IO_SET_CONFIG_CAN_MSGBUF :
             {               
                res = lpc2xxx_can_config_msgbuf(chan, buf, len);
             }
             break;
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
         
#ifdef CYGOPT_DEVS_CAN_LPC2XXX_EXTENDED_CFG_KEYS
        //
        // Add message filter group to acceptance filter
        //
        case CYG_IO_SET_CONFIG_LPC2XXX_ACCFILT_GROUP :
             {
                 return lpc2xxx_can_config_accfilt_group(chan, buf, len);
             }
             break;
#endif // CYGOPT_DEVS_CAN_LPC2XXX_EXTENDED_CFG_KEYS 
                        
        //
        // Change CAN state of CAN module
        //    
        case CYG_IO_SET_CONFIG_CAN_MODE :
             {
                cyg_can_mode   *can_mode  = (cyg_can_mode*) buf;
                
                if (*len != sizeof(cyg_can_mode)) 
                {
                    return -EINVAL;
                }
                *len = sizeof(cyg_can_mode);
                
                //
                // decide what to do according to mode
                //
                switch (*can_mode)
                {
                    //
                    // The controller does not support a stopped and standby state so we
                    // simply enter the low power state here. This state is also safe for
                    // message buffer configuration
                    //
                    case CYGNUM_CAN_MODE_STOP :    lpc2xxx_enter_reset_mode(chan);    break; 
                    case CYGNUM_CAN_MODE_START :   lpc2xxx_start_module(chan);        break;                       
                    case CYGNUM_CAN_MODE_STANDBY : lpc2xxx_enter_lowpower_mode(chan); break;
                    case CYGNUM_CAN_MODE_CONFIG :  lpc2xxx_enter_reset_mode(chan);    break;
                }
             }
             break; // case CYG_IO_SET_CONFIG_CAN_MODE :
        //
        // Unknown config key - indicate this by returning -EINVAL
        //
        default:
                    return -EINVAL;
    } // switch (key)
    
    return res;
}


//===========================================================================
// Query device configuration
//===========================================================================
static Cyg_ErrNo lpc2xxx_can_get_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len)
{
    Cyg_ErrNo            res  = ENOERR;
    lpc2xxx_can_info_t  *info = (lpc2xxx_can_info_t *)chan->dev_priv;
    
    switch(key)
    {
        //
        // query state of CAN controller
        //
        case CYG_IO_GET_CONFIG_CAN_STATE :
             {
                cyg_can_state *can_state  = (cyg_can_state*) buf;
                
                if (*len != sizeof(cyg_can_state)) 
                {
                    return -EINVAL;
                }
                *len = sizeof(cyg_can_state);
                *can_state = lpc2xxx_get_state(info);
             }
             break;

#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG       
        //
        // Query message box information - returns available and free message
        // boxes
        //     
        case CYG_IO_GET_CONFIG_CAN_MSGBUF_INFO :
             {
                 cyg_can_msgbuf_info *mbox_info  = (cyg_can_msgbuf_info*) buf;
                
                 if (*len != sizeof(cyg_can_msgbuf_info)) 
                 {
                     return -EINVAL;
                 }
                 cyg_uint32 end_of_table;
                *len = sizeof(cyg_can_msgbuf_info);
              
                 HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table);
                 mbox_info->count = LPC2XXX_CAN_MSG_FILTERS_MAX;
                 mbox_info->free  = (ACCFILT_RAM_SIZE - end_of_table) / ACCFILT_COMMON_ENTRY_SIZE;
             }
             break;
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG

        
        //
        // Query hardware description of FlexCAN device driver
        //     
        case CYG_IO_GET_CONFIG_CAN_HDI :
             {
                cyg_can_hdi *hdi = (cyg_can_hdi *)buf;
                //
                // comes from high level driver so we do not need to
                // check buffer size here
                //             
                hdi->support_flags = CYGNUM_CAN_HDI_FRAMETYPE_EXT_ACTIVE
                                   | CYGNUM_CAN_HDI_FULLCAN;
             }
             break;
             
        default :
            res = -EINVAL;
    }// switch(key)
    
    return res;
}


//===========================================================================
// Send single message
//===========================================================================
static bool lpc2xxx_can_putmsg(can_channel *chan, CYG_CAN_MSG_T *pmsg, void *pdata)
{
    cyg_uint32 regval;
#ifdef CYGOPT_DEVS_CAN_LPC2XXX_USE_SELF_RECEPTION
    lpc2xxx_can_info_t  *info = (lpc2xxx_can_info_t *) chan->dev_priv;
#else
    CAN_DECLARE_INFO(info);
#endif
    
    //
    // We use only one single transmit buffer of the three available buffers
    // We use buffer 1 (buffer 2 and 3 are unused)
    //
    // The errata sheet tells the following about the transmit buffers:
    // Problem: The Triple Transmit Buffer function cannot be used.
    // Work-around: Use any one Transmit buffer only (Use either Transmit Buffer 1, 
    // Transmit Buffer 2 or Transmit Buffer 3 exclusively). The buffer you decided 
    // to use should be loaded only when there is no pending transmission.
    //
    HAL_READ_UINT32(CAN_CTRL_SR(info), regval);
    if (!(regval & SR_TX_BUF_WRITE_OK))
    {
        return false;    
    }
    
    regval = pmsg->dlc << 16;
    if (pmsg->rtr)
    {
        regval |= TFI_DLC_RTR;
    }
 
#ifdef CYGOPT_IO_CAN_EXT_CAN_ID
    if (pmsg->ext)
    {
        regval |= TFI_DLC_EXT;
    }
#endif // #define CYGOPT_IO_CAN_EXT_CAN_ID
    HAL_WRITE_UINT32(CAN_CTRL_TFI1(info), regval);                       // write DLC
    HAL_WRITE_UINT32(CAN_CTRL_TID1(info), pmsg->id);                     // write ID
    HAL_WRITE_UINT32(CAN_CTRL_TDA1(info), pmsg->data.dwords[0]);         // write first 4 data bytes
    HAL_WRITE_UINT32(CAN_CTRL_TDB1(info), pmsg->data.dwords[1]);         // write second 4 data bytes
    
    //
    // Request transmission of message
    // The errata sheet tells the following about tx request:
    // Introduction: The CAN module can lose arbitration to another CAN node during an 
    // attempt to transmit a CAN message. The message of the CAN node the arbitration was 
    // lost to is supposed to be received correctly by the CAN module.
    // Problem: Messages might not be received correctly if during a CAN Transmission the 
    // CAN bus arbitration is lost to another CAN node.
    // Work-around: Use the Self Reception Request command instead of the Transmission 
    // Request command. However, it has to be taken into account that now all transmitted
    // messages may be received if not prevented by appropriate Acceptance Filter settings. 
    // (Don't set up Acceptance Filter Message Identifiers for the messages you are
    // transmitting yourself.)
    //
#ifdef CYGOPT_DEVS_CAN_LPC2XXX_USE_SELF_RECEPTION
    // Calc last_tx_id
    regval = pmsg->id | (regval & LPC2XXX_CAN_INFO_LAST_TX_ID_FLMASK);
    
    // Save last message id to next last_tx_id
    info->last_tx_index = info->last_tx_index == 0 ? 1 : 0;
    info->last_tx_id[info->last_tx_index] = regval;
    
    // Write self transmission request
    HAL_WRITE_UINT32(CAN_CTRL_CMR(info), CMR_SELF_RX_REQ | CMR_SEND_TX_BUF1);
#else
    // Write transmission request
    HAL_WRITE_UINT32(CAN_CTRL_CMR(info), CMR_TX_REQ | CMR_SEND_TX_BUF1);
#endif
   
    return true;
}


//===========================================================================
// Read event from device driver
//===========================================================================
static bool lpc2xxx_can_getevent(can_channel *chan, CYG_CAN_EVENT_T *pevent, void *pdata)
{
    lpc2xxx_can_info_t   *info       = (lpc2xxx_can_info_t *)chan->dev_priv;
    bool                  res        = true;
    cyg_uint32            regval;
    cyg_uint32            event      = *((cyg_uint32*)pdata);
    lsc_buf_t             data;
      
    //
    // Handle RX event
    //
    if (event & ICR_RX)
    {
        cyg_uint32            id;
        
        pevent->flags |= CYGNUM_CAN_EVENT_RX;
        HAL_READ_UINT32(CAN_CTRL_RFS(info), regval); 
        HAL_READ_UINT32(CAN_CTRL_RID(info), id);

#ifdef CYGOPT_IO_CAN_EXT_CAN_ID
        if (regval & RFS_EXT)
        {
            pevent->msg.ext = CYGNUM_CAN_ID_EXT;
            pevent->msg.id = id & 0x1FFFFFFF;
        }
        else
#endif // #define CYGOPT_IO_CAN_EXT_CAN_ID
        {
#ifdef CYGOPT_IO_CAN_STD_CAN_ID
            pevent->msg.ext = CYGNUM_CAN_ID_STD;
            pevent->msg.id = id & 0x7FF;
#endif // #ifdef CYGOPT_IO_CAN_STD_CAN_ID
        } // if (regval & RFS_EXT)
        
        if (regval & RFS_RTR)
        {
            pevent->msg.rtr = CYGNUM_CAN_FRAME_RTR;
        }
        else
        {
            pevent->msg.rtr = CYGNUM_CAN_FRAME_DATA;  
            HAL_READ_UINT32(CAN_CTRL_RDA(info), pevent->msg.data.dwords[0]);
            HAL_READ_UINT32(CAN_CTRL_RDB(info), pevent->msg.data.dwords[1]);
        } //if (regval & RFS_RTR)
        pevent->msg.dlc = RFS_GET_DLC(regval);
        //
        // Release the message buffer. Now this buffer can receive the next message
        //
        HAL_WRITE_UINT32(CAN_CTRL_CMR(info), CMR_RX_RELEASE_BUF);
    
        //
        // Now check if an data overrun occurred - a message was lost
        // because the preceding message to this CAN controller was not read 
        // and released quickly enough. After reading the status we clear 
        // the overrun bit
        // 
        HAL_READ_UINT32(CAN_CTRL_GSR(info), regval);
        if (regval & GSR_DATA_OVR)
        {
            pevent->flags |= CYGNUM_CAN_EVENT_OVERRUN_RX;
            HAL_WRITE_UINT32(CAN_CTRL_CMR(info), CMR_CLEAR_DATA_OVR);
        }
    }
    
    //
    // Handle TX events
    //
#ifndef CYGOPT_IO_CAN_TX_EVENT_SUPPORT 
    if (event & ICR_TX1)
    {
        pevent->flags |= CYGNUM_CAN_EVENT_TX;
    }
#endif
    
    //
    // Handle all other events
    //
    if (event & (CAN_MISC_INT | ICR_LUT_ERR))
    {
        HAL_READ_UINT32(CAN_CTRL_GSR(info), data.dword);
        
        //
        // 1: Error Warning Interrupt -- this bit is set on every change (set or clear) of the Error
        // Status or Bus Status bit in CANSR, if the EIE bit in CAN is 1 at the time of the
        // change.
        //
        if (event & ICR_ERR_WARN)
        {
            //
            // If one of the warning counters is above 96 then the controller is in bus warning
            // state. If both counters are below 96 the this interrupt indicates that the
            // controller has left the bus warning state and is error active again
            //
            if (data.bytes[2] >= 96)
            {
                pevent->flags |= CYGNUM_CAN_EVENT_WARNING_RX;  
                info->state = CYGNUM_CAN_STATE_BUS_WARN;
            }
            else if (data.bytes[3] >= 96)
            {
                pevent->flags |= CYGNUM_CAN_EVENT_WARNING_TX;
                info->state = CYGNUM_CAN_STATE_BUS_WARN;
            }
            else
            {
                info->state = CYGNUM_CAN_STATE_ACTIVE;
            }
            LPC2XXX_DBG_PRINT("ICR_ERR_WARN (%p)\n", (void*) chan);
        }
        
        //
        // 1: Wake-Up Interrupt: this bit is set if the CAN controller is sleeping and bus activity
        // is detected, if the WUIE bit in CANIE is 1.
        //
        if (event & ICR_WAKE_UP)
        {
            pevent->flags |= CYGNUM_CAN_EVENT_LEAVING_STANDBY; 
            info->state = CYGNUM_CAN_STATE_ACTIVE;
            LPC2XXX_DBG_PRINT("ICR_WAKE_UP (%p)\n", (void*) chan);
        }
        
        //
        // Error Passive Interrupt -- this bit is set if the EPIE bit in CANIE is 1, and the CAN
        // controller switches between Error Passive and Error Active mode in either
        // direction. We have to check if the ERR bit is set in global status register.
        // If it is set, then it is a switch to error passive else it is a switch to
        // error active state
        //
        if (event & ICR_ERR_PASSIVE)
        {
            if (data.dword & GSR_ERR)
            {
                pevent->flags |= CYGNUM_CAN_EVENT_ERR_PASSIVE;    
                info->state = CYGNUM_CAN_STATE_ERR_PASSIVE;
            }
            else
            {
                info->state = CYGNUM_CAN_STATE_ACTIVE;    
            }
            LPC2XXX_DBG_PRINT("ICR_ERR_PASSIVE (%p)\n", (void*) chan);
        }

#ifdef CYGOPT_DEVS_CAN_LPC2XXX_ALIE       
        //
        // Arbitration Lost Interrupt -- this bit is set if the ALIE bit in CANIE is 1, and the
        // CAN controller loses arbitration while attempting to transmit.
        //
        if (event & ICR_ARBITR_LOST)
        {
            pevent->flags |= CYGNUM_CAN_EVENT_ARBITRATION_LOST;   
            LPC2XXX_DBG_PRINT("ICR_ARBITR_LOST (%p)\n", (void*) chan);
        }
#endif // CYGOPT_DEVS_CAN_LPC2XXX_ALIE
        
        //
        // 1: Bus Error Interrupt -- this bit is set if the BEIE bit in CANIE is 1, and the CAN
        // controller detects an error on the bus.
        //
        if (event & ICR_BUS_ERR)
        {
            pevent->flags |= CYGNUM_CAN_EVENT_BUS_OFF; 
            LPC2XXX_DBG_PRINT("ICR_BUS_ERR (%p)\n", (void*) chan);
        }
        
#ifdef CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP
        //
        // LUT error interrupt -- this bit is set if bit 0 in LUTerr is 1 and LUTerrAd
        // points to entry in filter table for this CAN controller
        //
        if(event & ICR_LUT_ERR)
        {
            pevent->flags |= CYGNUM_CAN_EVENT_FILTER_ERR;
            LPC2XXX_DBG_PRINT("ICR_LUT_ERR (%p)\n", (void*) chan);
        }
#endif // CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP

    } // if (event & (CAN_MISC_INT | ICR_LUT_ERR))
          
    return res;
}


//===========================================================================
// Kick transmitter
//===========================================================================
static void lpc2xxx_can_start_xmit(can_channel* chan)
{
    cyg_uint32 regval;
    CAN_DECLARE_INFO(chan);
    
    LPC2XXX_DBG_PRINT("start_xmit (%p)\n", (void*) chan);

    cyg_drv_dsr_lock();
    HAL_READ_UINT32(CAN_CTRL_IER(info), regval);
    regval |= IER_TX1;                           // enable tx interrupt for tx buf 1
    HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); 
    cyg_drv_dsr_unlock();
    
    //
    // kick transmitter
    //
    chan->callbacks->xmt_msg(chan, 0);  // Kick transmitter (if necessary)
}


//===========================================================================
// Stop transmitter
//===========================================================================
static void lpc2xxx_can_stop_xmit(can_channel* chan)
{
    cyg_uint32 regval; 
    CAN_DECLARE_INFO(chan);
    
    LPC2XXX_DBG_PRINT("stop_xmit (%p)\n", (void*) chan);
     
    cyg_drv_dsr_lock();
    HAL_READ_UINT32(CAN_CTRL_IER(info), regval);
    regval &= ~IER_TX1;                           // disable tx interrupt for tx buf 1
    HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); 
    cyg_drv_dsr_unlock();
}


#ifndef CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY
//===========================================================================
// Low level transmit interrupt handler
//===========================================================================
static cyg_uint32 lpc2xxx_can_tx_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
    //
    // Now read input capture register - this clears all interrupt bits in this
    // register and also acknowledges the interrupt - any further processing is done
    // by the DSR
    //
    cyg_drv_interrupt_mask(vector);
    cyg_drv_interrupt_acknowledge(vector);
    LPC2XXX_DBG_PRINT("tx_ISR (%p)\n", (void*) data);
    return CYG_ISR_CALL_DSR;
}


//===========================================================================
// High level transmit interrupt handler
//===========================================================================
static void lpc2xxx_can_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    can_channel              *chan = (can_channel *)data;
    cyg_uint32                regval;
    lpc2xxx_can_info_t       *info = (lpc2xxx_can_info_t *)chan->dev_priv;
    
    //
    // First read the ICR register to acknowledge all interrupts and
    // get all captured interrupts
    //
    HAL_READ_UINT32(CAN_CTRL_ICR(info), regval);
    
    //
    // If TX events are supported then only call the rcv_event() callback
    // if any other event occurred - pass the event field to the getevent function
    // to indicate the events
    //
#ifndef CYGOPT_IO_CAN_TX_EVENT_SUPPORT
    if (regval & ~ICR_TX1) 
#endif
    {
        chan->callbacks->rcv_event(chan, &regval); 
    }
    
    //
    // Now transmit next message and reenable interrupts
    //
    chan->callbacks->xmt_msg(chan, 0); // send next message 
    LPC2XXX_DBG_PRINT("tx_DSR (%p)\n", (void*) data);
    cyg_drv_interrupt_unmask(vector);    
}


//===========================================================================
// Low level receive interrupt handler
//===========================================================================
static cyg_uint32 lpc2xxx_can_rx_ISR(cyg_vector_t vector, cyg_addrword_t data)
{   
#ifdef CYGOPT_DEVS_CAN_LPC2XXX_USE_SELF_RECEPTION
    cyg_uint32   regval;
    can_channel* chan = (can_channel*)data;
    lpc2xxx_can_info_t  *info = (lpc2xxx_can_info_t *) chan->dev_priv;
    cyg_uint32 id;
    cyg_uint32 index;
    
    // We have to reject self tx message, so read message id
    HAL_READ_UINT32(CAN_CTRL_RID(info), id);
    HAL_READ_UINT32(CAN_CTRL_RFS(info), regval);
    id |= (regval & LPC2XXX_CAN_INFO_LAST_TX_ID_FLMASK);
    
    // Test message id
    for(index = 0; index < 2; index++)
    {
        if(id == info->last_tx_id[index])
        {
            // Clear last_tx_id
            info->last_tx_id[index] = LPC2XXX_CAN_INFO_LAST_TX_ID_NOVALID;
            
            // Clear receive buffer
            HAL_WRITE_UINT32(CAN_CTRL_CMR(info), CMR_RX_RELEASE_BUF);
            
            // Acknowledge a vector
            cyg_drv_interrupt_acknowledge(vector);
            
            // Exit without calling DSR
            LPC2XXX_DBG_PRINT("self_rx_ISR (%p)\n", (void*) data);
            return CYG_ISR_HANDLED;
        }
    }
#endif
        
    //
    // The ISR only disables and acknowledges the RX interrupt
    // any further processing is done by DSR. We also need to mask the
    // global CAN status interrupt here because the interrupt flag
    // in ICR is not cleared yet and may still cause a status 
    // interrupt
    //
    cyg_drv_interrupt_mask(vector);
    cyg_drv_interrupt_acknowledge(vector);
    LPC2XXX_DBG_PRINT("rx_ISR (%p)\n", (void*) data);

    return CYG_ISR_CALL_DSR;
}


//===========================================================================
// High level receive interrupt handler
//===========================================================================
static void lpc2xxx_can_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    can_channel  *chan = (can_channel *)data;
    cyg_uint32    icr = ICR_RX;
      
    //
    // Read the event, the receive buffer will be released by the
    // get_event() function
    //
    chan->callbacks->rcv_event(chan, &icr); 
    LPC2XXX_DBG_PRINT("rx_DSR (%p)\n", (void*) data);
    cyg_drv_interrupt_unmask(vector);
}



//===========================================================================
// status ISR handler
//===========================================================================
static cyg_uint32 lpc2xxx_can_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
    //
    // Acknowledge and disable the interrupt - any further processing is
    // done by the DSR
    //    
    cyg_drv_interrupt_mask(vector);    
    cyg_drv_interrupt_acknowledge(vector);
    LPC2XXX_DBG_PRINT("err_ISR\n");
    return CYG_ISR_CALL_DSR;    
}


//===========================================================================
// status ISR handler
//===========================================================================
static void lpc2xxx_can_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{     
#ifdef CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP
    // If we use acceptance filter we can get LUT error
    cyg_uint32 luterr;
    cyg_uint8  luterr_chan0 = 0; // Init to avoid warnings
    cyg_uint8  luterr_chan1 = 0; // Init to avoid warnings
    
    // Read LUT error flag
    HAL_READ_UINT32(CAN_ACCFILT_LUT_ERR, luterr);
    
    if (luterr & 1)
    {
        cyg_uint32 lutaddr;
        cyg_uint32 eff_sa;
        lsc_buf_t  errentry;
        
        // Read address of failed entry (it clears interrupt flag)
        HAL_READ_UINT32(CAN_ACCFILT_LUT_ERR_ADDR, lutaddr);
        
        // Read address of extended id individual table
        HAL_READ_UINT32(CAN_ACCFILT_EFF_SA, eff_sa); 
        
        // Read error entry
        HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + lutaddr, errentry.dword);
        
        // If err entry from standard id tables then read two
        // controllers numbers
        if(lutaddr < eff_sa)
        {
            // Calc CAN controllers numbers
            luterr_chan0 = (cyg_uint8) ACCFILT_STD_GET_CTRL_UPPER(errentry.dword);
            
            if(errentry.column.lower & ACCFILT_STD_DIS)
            {
            	luterr_chan1 = luterr_chan0;
            }
            else 
            {
            	luterr_chan1 = (cyg_uint8) ACCFILT_STD_GET_CTRL_LOWER(errentry.dword);
            }
        }
        else
        {
            // Calc CAN controller number 
            luterr_chan0 = luterr_chan1 = (cyg_uint8) ACCFILT_EXT_GET_CTRL(errentry.dword);
        }
    }
#endif // CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP

    //
    // Loop through all channels - we need to do this only if we have more
    // than one channel so we can optimize here for single channel
    //
#if CYGINT_IO_CAN_CHANNELS > 1 
    cyg_uint8 i = 0;     
    while (lpc2xxx_global_can_info.active_channels[i])
#endif
    {
        cyg_uint32 regval;
        can_channel *chan = LPC2XXX_GET_CAN_CHANNEL(lpc2xxx_global_can_info, i++);
        CAN_DECLARE_INFO(chan);

        HAL_READ_UINT32(CAN_CTRL_ICR(info), regval);      
#ifdef CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP
        // Set ICR_LUT_ERR flag only for controller which cause LUT error
        if ((luterr & 1) && ((luterr_chan0 == i) || (luterr_chan1 == i)))
        {
            regval |= ICR_LUT_ERR;
        } 
#endif // CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP
        regval &= CAN_MISC_INT; // don't care about RX and TX events here
        if (regval)
        {
            chan->callbacks->rcv_event(chan, &regval);
        }
    } // while (lpc2xxx_global_can_info.active_channels[i])
    
    LPC2XXX_DBG_PRINT("err_DSR\n");
    cyg_drv_interrupt_unmask(vector);
}
#else // #ifndef CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY


//===========================================================================
// Global CAN interrupt handler
//===========================================================================
static cyg_uint32 lpc2xxx_can_ISR(cyg_vector_t vector, cyg_addrword_t data)
{   
    //
    // Disable interrupts, the DSR will enable it as soon as it processed
    // the current interrupt
    //
    cyg_drv_interrupt_mask(vector);    
    cyg_drv_interrupt_acknowledge(vector);
    LPC2XXX_DBG_PRINT("CAN_ISR\n");
    return CYG_ISR_CALL_DSR;   
}

//===========================================================================
// Global CAN DSR
//===========================================================================
static void lpc2xxx_can_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{ 
#ifdef CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP
    // If we use acceptance filter we can get LUT error
    cyg_uint32 luterr;
    cyg_uint8  luterr_chan0 = 0xFF; // Init to avoid warnings
    cyg_uint8  luterr_chan1 = 0xFF; // Init to avoid warnings
    
    // Read LUT error flag
    HAL_READ_UINT32(CAN_ACCFILT_LUT_ERR, luterr);
    
    if (luterr & 1)
    {
        cyg_uint32 lutaddr;
        cyg_uint32 eff_sa;
        lsc_buf_t  errentry;
        
        // Read address of failed entry (it clears interrupt flag)
        HAL_READ_UINT32(CAN_ACCFILT_LUT_ERR_ADDR, lutaddr);
        
        // Read address of extended id individual table
        HAL_READ_UINT32(CAN_ACCFILT_EFF_SA, eff_sa); 
        
        // Read error entry
        HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + lutaddr, errentry.dword);
        
        // If errentry from standard id tables then read two controllers numbers
        if(lutaddr < eff_sa)
        {
            // Calc CAN controllers numbers
            luterr_chan0 = (cyg_uint8) ACCFILT_STD_GET_CTRL_UPPER(errentry.dword);
            
            if(errentry.column.lower & ACCFILT_STD_DIS)
            {
            	luterr_chan1 = luterr_chan0;
            }
            else 
            {
            	luterr_chan1 = (cyg_uint8) ACCFILT_STD_GET_CTRL_LOWER(errentry.dword);
            }
        }
        else
        {
            // Calc CAN controller number 
            luterr_chan0 = luterr_chan1 = (cyg_uint8) ACCFILT_EXT_GET_CTRL(errentry.dword);
        }
    }
#endif // CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP
    
    //
    // Walk through list of active CAN channels and process interrupts
    // of all channels - we need to loop only if we have more than one CAN channel so
    // we can optimize for single CAN channel here
    //
#if CYGINT_IO_CAN_CHANNELS > 1
    cyg_uint8 i = 0; 
    while (lpc2xxx_global_can_info.active_channels[i])
#endif // CYGINT_IO_CAN_CHANNELS > 1
    {
        cyg_uint32   icr;
        can_channel *chan = LPC2XXX_GET_CAN_CHANNEL(lpc2xxx_global_can_info, i++);
        CAN_DECLARE_INFO(chan);
        
        HAL_READ_UINT32(CAN_CTRL_ICR(info), icr);      // this read clears ICR     
#ifdef CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP
        // Set ICR_LUT_ERR flag only for controller which cause LUT error
        if ((luterr_chan0 == i) || (luterr_chan1 == i))
        {
            icr |= ICR_LUT_ERR;
        }
#endif // CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP
        //
        // If TX events are supported then we simply call the rcv_event()
        // callback to store the event. If TX events are not supported then
        // we only call the rcv_event() function if any other interrupt than
        // the TX interrupt was captured
        //
#ifndef CYGOPT_IO_CAN_TX_EVENT_SUPPORT  
        if (icr & ~ICR_TX1)
#endif // CYGOPT_IO_CAN_TX_EVENT_SUPPORT 
        {
            chan->callbacks->rcv_event(chan, &icr); 
        }   
        //
        // If this was an TX interrupt then transmit next message now
        //
        if (icr & ICR_TX1)
        { 
            chan->callbacks->xmt_msg(chan, 0); // send next message 
        }
    } // while (lpc2xxx_global_can_info.active_channels[i])   
    LPC2XXX_DBG_PRINT("CAN_DSR\n"); 
    cyg_drv_interrupt_unmask(vector);
}
#endif // #ifndef CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY


#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
//===========================================================================
// Configure message boxes for reception of any CAN message
//===========================================================================
static void lpc2xxx_can_config_rx_all(can_channel *chan)
{   
    lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv;
    //
    // First clear all acceptance filter entries and then insert the
    // two RX all groups
    //
    lpc2xxx_can_config_rx_none(chan); 
#ifdef CYGOPT_IO_CAN_STD_CAN_ID
    lpc2xxx_can_accfilt_add(info, 0x000, 0x7FF, CYGNUM_CAN_ID_STD);
#endif //  CYGOPT_IO_CAN_STD_CAN_ID
#ifdef CYGOPT_IO_CAN_EXT_CAN_ID
    lpc2xxx_can_accfilt_add(info, 0x000, 0x1FFFFFFF, CYGNUM_CAN_ID_EXT);
#endif // CYGOPT_IO_CAN_EXT_CAN_ID   
  
    info->flags  |= INFO_FLAG_RX_ALL;
#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
    lpc2xxx_can_accfilt_dbg_dump();
#endif // CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
}
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG


#ifndef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
//===========================================================================
// Configure message boxes for reception of any CAN message
//===========================================================================
static void lpc2xxx_can_config_rx_all(can_channel *chan)
{   
    lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv;

    lpc2xxx_can_accfilt_simple_rx_all();
    info->flags  |= INFO_FLAG_RX_ALL;

#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
    lpc2xxx_can_accfilt_dbg_dump();
#endif // CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
}
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG


//---------------------------------------------------------------------------
// EOF can_lpc2xxx.c