/*****************************************************************************
* Copyright (c) 2007 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/

#include <string.h>
#include "Npttypes.h"
#include "nastatus.h"
#include "iop_api.h"
#include "gpio.h"
#include "tx_api.h"
#include "sysClockApi.h"
#include "bsp_api.h"
#include "nainvalidate.h"
#include "iop_reg.h"
#include "scm_reg.h"
#include "iohub_reg.h"
#include "canTable.h"
#include "nacbusPrivate.h"
#include "nacbus.h"


#define NACBUS_FLAGS_POSITION_IN_RECV_FRAME     (4)
#define NACBUS_DATA_START_IN_RECV_FRAME         (5)
#define NACBUS_MINIMUM_FRAME_LENGTH             (5)

#define DEFAULT_BAUD_RATE   (1000000)

#define CAN_MAX_INTERFACES      (2)

#define CAN_SHUTDOWN_MSG        (1)

#define NA_CBUS_COMMAND_INDEX           (0)

/*
 * This constant should be set to the interrupt latency of the
 * PIC ISR routine during transmit operations.  This includes the
 * H/W latency (2 cycles), plus the number of instructions the
 * ISR executes before it actually puts the data onto the bus.
 */
#define PIC_TRANSMIT_INTERRUPT_LATENCY      (22)

/*
 * This constant should be set to the interrupt latency of the
 * PIC ISR routine during receive operations.  This includes the
 * H/W latency (2 cycles), plus the number of instructions the
 * ISR executes before it actually samples the bus.
 */
#define PIC_RECEIVE_INTERRUPT_LATENCY       (24)
/*#define PIC_RECEIVE_INTERRUPT_LATENCY       (0)*/

/*
 * Size is set up so that the buffer ends at the end of a cache line boundary.
 * This prevents us from invalidating fields in NACanFrameBuffer_t when the
 * buffer is invalidated.  Note that we have to bit stuff transmit frames, and there
 * are two extra header bytes in a transmit frame.  That's why the buffer size is
 * set to be so large.
 */
#define NA_CAN_FRAME_BUFFER_SIZE            ((32 + NA_CACHE_LINE_SIZE - 1) & ~(NA_CACHE_LINE_SIZE - 1))

typedef struct NACanFrameBuffer_t
{
    BYTE buffer[NA_CAN_FRAME_BUFFER_SIZE];
    struct NACanFrameBuffer_t *next;
    unsigned crc;
    unsigned position;
    unsigned bytePos;
    unsigned bitCount;
    unsigned bitStuff;
    unsigned lastFiveBits;
    unsigned stuffedBitCount;
    unsigned length;
    unsigned result;
} NACanFrameBuffer_t;



/*
 * This union is used to help align buffers on cache page boundaries.
 * The array member is there to guarentee the unions are an even number
 * of cache lines in size.
 */
#define TRANSMIT_BUFFER_SIZE_IN_LINES   ((sizeof(NACanFrameBuffer_t) + NA_CACHE_LINE_SIZE - 1) / NA_CACHE_LINE_SIZE)
typedef union
{
    NACanFrameBuffer_t bufferStruct;
    BYTE array[TRANSMIT_BUFFER_SIZE_IN_LINES * NA_CACHE_LINE_SIZE];
} NACanFrameBufferUnion_t;


typedef struct NACanRtrInfo_t
{
    unsigned isActive;
    unsigned id;
    unsigned length;
    BYTE data[NA_CBUS_MAX_DATA_LENGTH];
} NACanRtrInfo_t;


/*
 * Number of bytes in 1 filter.
 */
#define NA_CBUS_FILTER_SIZE                     (8)

/*
 * Total number of bytes in filter table.  Includes all filters.
 */
#define NA_CBUS_DEFAULT_FILTER_TABLE_SIZE       (NA_CBUS_MAX_FILTERS * NA_CBUS_FILTER_SIZE)


/*
 * Information stored about each CAN channel.
 */
typedef struct NACanInfo_t
{
    BYTE defaultFilterTable[NA_CBUS_DEFAULT_FILTER_TABLE_SIZE];
    unsigned sentRtrResponse;
    NACanRtrInfo_t *rtr;
    unsigned rtrCount;
    unsigned long volatile *receiveErrorCount;
    unsigned long volatile *transmitErrorCount;
    unsigned long volatile *fatalErrorRegister;
    unsigned long volatile *busState;
    unsigned expectingResetInterrupt;
    unsigned fatalErrorCode;
    unsigned bitrate;
    NACanDriverState_t state;
    NaIopProcessor_t processor;
    NACBusDmaReceiverFn_t receiveFn;
    unsigned sjw;
    unsigned samplePoint;
    unsigned syncPeriod;
    unsigned isOpen;
    NACanFrameBuffer_t *freeTransmitBufferQ;
    BYTE *mallocedTransmitBuffer;
    BYTE *mallocedReceiveBuffer;
    BYTE *stack;
    NACBusDmaReceiverFn_t receiveThreadFn;
    BYTE *pendingReceiveQBuffer;
    unsigned txPendingCount;
    unsigned long volatile *fifoStatusRegister;
} NACanInfo_t;


/*
 * Default values for information about each CAN channel.
 */
#define DEFAULT_SJW_TIME        (40)
#define DEFAULT_SAMPLE_POINT    (88)
#define DEFAULT_SYNC_PERIOD     (10)
static NACanInfo_t naCanInfo[CAN_MAX_INTERFACES];


typedef struct NACanThreadXInfo_t
{
    TX_MUTEX mutex;
    TX_QUEUE pendingReceiveQ;
    TX_THREAD receiveThread;
} NACanThreadXInfo_t;


/*
 * This is seperate from naCanInfo because the compiler won't
 * let me use a default initializer for these fields.
 */
static NACanThreadXInfo_t naCanThreadXInfo[CAN_MAX_INTERFACES];


#define GET_MUTEX(x)    NA_INSIST(tx_mutex_get(&naCanThreadXInfo[x].mutex, TX_WAIT_FOREVER) == TX_SUCCESS)
#define PUT_MUTEX(x)    NA_INSIST(tx_mutex_put(&naCanThreadXInfo[x].mutex) == TX_SUCCESS)

/*
 * This macro validates that interface is in range, that the controller has been
 * opened, and gets the mutex.  This implementation assumes there is only one CAN 
 * controller and will have to be updated if a 2nd controller is ever supported.
 */
#define FUNCTION_ENTRY(interface)   if ((interface >= naCanTableSize) || (interface >= CAN_MAX_INTERFACES)) return NASTATUS_CBUS_BAD_INTERFACE; \
                                    if (naCanInfo[interface].isOpen == FALSE) return NASTATUS_CBUS_NOT_OPEN; \
                                    GET_MUTEX(interface)

#define FUNCTION_EXIT(interface)    PUT_MUTEX(interface)



static void naCBusReceiveBufferReturn(unsigned interface, void * buffer);
static NaStatus naCBusTransmitQInsert(unsigned interface, NaCanId_t id, unsigned flags, BYTE *buffer, unsigned length);
    



/*
 * This routine returns an empty transmit buffer from the pool of
 * transmit buffers for this interface.  NULL is returned if the pool
 * is empty.
 */
static BYTE *naCBusTransmitBufferGet(unsigned interface)
{
    BYTE *result = NULL;
    TX_INTERRUPT_SAVE_AREA;
    
    if (interface >= CAN_MAX_INTERFACES)
    {
        panic_num("naCBusTransmitBufferGet called with bad interface.", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }

    TX_DISABLE;
    if (naCanInfo[interface].freeTransmitBufferQ != NULL)
    {
        result = (BYTE *) naCanInfo[interface].freeTransmitBufferQ;
        naCanInfo[interface].freeTransmitBufferQ = naCanInfo[interface].freeTransmitBufferQ->next;
    }
    TX_RESTORE;
    
    return result;
}


/*
 * This function puts a buffer into the transmit pool.
 */
static void naCBusTransmitBufferReturn(unsigned interface, void * buffer)
{
    NACanFrameBuffer_t *bufferPointer = (NACanFrameBuffer_t *) buffer;
    TX_INTERRUPT_SAVE_AREA;

    if ((interface >= CAN_MAX_INTERFACES) || (buffer == NULL))
    {
        panic_num("naCBusTransmitBufferReturn called with bad arguments.", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }

    /*
     * Need to disable interrupts because this function can be called from
     * an ISR as well as from threads.
     */
    TX_DISABLE;
    
    bufferPointer->next = naCanInfo[interface].freeTransmitBufferQ;
    naCanInfo[interface].freeTransmitBufferQ = bufferPointer;
    TX_RESTORE;
}


/*
 * This routine is used internally to receive CAN frames from the IOP DMA
 * routines.  It is responsible for handling automatic RTR replies, and for
 * passing the frames onto the receiver thread via a queue.  It is called 
 * from the IOP ISR.
 */
static void naCbusReceiveCallback(NaIopProcessor_t processor, void * buffer, unsigned length, unsigned result, void * context)
{
    /*
     * Context is a generic variable that the IOP API lets us pass to ISR
     * routines.  Convert it now into an interface index.
     */
    unsigned interface = (unsigned) context;

    /*
     * The buffer argument will never be NULL since the IOP only calls
     * this routine to pass it a receive buffer.
     */
    NACanFrameBuffer_t *frame = (NACanFrameBuffer_t *) buffer;
    
    NA_UNUSED_PARAM(processor);
    
    frame->length = length;
    frame->result = result;

#define LENGTH_OF_REQUEST_FRAME     (6)
    if (   (naCanInfo[interface].rtr != NULL) 
        && (length == LENGTH_OF_REQUEST_FRAME) 
        && (frame->buffer[0] & NA_CBUS_CANID_BYTE0_IS_RTR))
    {
        NaCanId_t id;
        unsigned idx;
        
        memcpy(&id, buffer, sizeof(id));
        id &= ~NA_CBUS_CANID_IS_REQUEST;
        
        for (idx = 0; idx < naCanInfo[interface].rtrCount; idx++)
        {
            if (naCanInfo[interface].rtr[idx].isActive)
            {
                if (naCanInfo[interface].rtr[idx].id == id)
                {
                    if (naCBusTransmitQInsert(interface, id, 
                                            0, 
                                            naCanInfo[interface].rtr[idx].data, 
                                            naCanInfo[interface].rtr[idx].length) == NASTATUS_SUCCESS)
                    {
                        frame->buffer[NACBUS_FLAGS_POSITION_IN_RECV_FRAME] |= NA_CBUS_SENT_AUTO_RESPONSE;
                    }
                    break;
                }
            }
        }
    }

    NA_INSIST(tx_queue_send(&naCanThreadXInfo[interface].pendingReceiveQ, &frame, TX_NO_WAIT) == TX_SUCCESS);
}
    


/*
 * This function puts a buffer into the receive queue.
 */
static void naCBusReceiveBufferReturn(unsigned interface, void * buffer)
{
    if ((interface >= CAN_MAX_INTERFACES) || (buffer == NULL))
    {
        panic_num("naCBusReceiveBufferReturn called with bad arguments.", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }

    if (naIopDmaReceive(naCanInfo[interface].processor, buffer, NA_CAN_FRAME_BUFFER_SIZE, naCbusReceiveCallback, (void *) interface) != NASTATUS_SUCCESS)
    {
        panic_num("naCBusReceiveBufferReturn: naIopDmaReceive failed.", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }
}


/*
 * This function is called by naCBusStart to allocate a block of memory to
 * be used as CAN transmit buffers.  The buffers are set up so they are on
 * cache line boundaries so that the cache invalidate functions will not 
 * screw up other memory.
 */
static NaStatus naCBusAllocateTransmitBuffers(unsigned interface)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    
    NA_INSIST(interface < naCanTableSize);
    
    if (naCanInfo[interface].mallocedTransmitBuffer == NULL)
    {
        unsigned bufferAddress;
        unsigned idx;
        NACanFrameBufferUnion_t *pool;
        
        naCanInfo[interface].freeTransmitBufferQ = NULL;
        
        naCanInfo[interface].mallocedTransmitBuffer = malloc(IOP_MAX_DMA_DESCRIPTORS * sizeof (NACanFrameBufferUnion_t) 
                                                             + NA_CACHE_LINE_SIZE);
        if (naCanInfo[interface].mallocedTransmitBuffer == NULL)
        {
            ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
            goto naCBusAllocateTransmitBuffersExit;
        }
        bufferAddress = (unsigned) naCanInfo[interface].mallocedTransmitBuffer;
        
        /*
         * These buffers will be used for DMA, so we want to make sure they are
         * aligned on a cache line boundary.  We allocated enough extra buffer space
         * so that we could shift the start of the buffer by up to NA_CACHE_LINE_SIZE
         * bytes.  Shift the buffer pointer now (if necessary) so that it is properly
         * aligned.
         */
        bufferAddress = ((bufferAddress + (NA_CACHE_LINE_SIZE - 1)) & ~(NA_CACHE_LINE_SIZE - 1));
        pool = (NACanFrameBufferUnion_t *) bufferAddress;
        
        for (idx = 0; idx < IOP_MAX_DMA_DESCRIPTORS; idx++)        
        {
            /*
             * Note that NACanFrameBufferUnion_t objects are sized so that
             * they are an even number of cache lines long.  So, once the first
             * one is aligned, they all are.
             */
            naCBusTransmitBufferReturn(interface, pool[idx].bufferStruct.buffer);
        }
    }

naCBusAllocateTransmitBuffersExit:
    return ccode;
}



/*
 * This function is called by naCBusStart to allocate a block of memory to
 * be used as CAN receive buffers.  The buffers are set up so they are on
 * cache line boundaries so that the cache invalidate functions will not 
 * screw up other memory.
 */
static NaStatus naCBusAllocateReceiveBuffers(unsigned interface)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    
    NA_INSIST(interface < naCanTableSize);
    
    if (naCanInfo[interface].mallocedReceiveBuffer == NULL)
    {
        unsigned bufferAddress;
        unsigned idx;
        NACanFrameBufferUnion_t *pool;
        
        naCanInfo[interface].mallocedReceiveBuffer = malloc(IOP_MAX_DMA_DESCRIPTORS * sizeof (NACanFrameBufferUnion_t) 
                                                             + NA_CACHE_LINE_SIZE);
        if (naCanInfo[interface].mallocedReceiveBuffer == NULL)
        {
            ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
            goto naCBusAllocateReceiveBuffersExit;
        }
        bufferAddress = (unsigned) naCanInfo[interface].mallocedReceiveBuffer;
        
        /*
         * These buffers will be used for DMA, so we want to make sure they are
         * aligned on a cache line boundary.  We allocated enough extra buffer space
         * so that we could shift the start of the buffer by up to NA_CACHE_LINE_SIZE
         * bytes.  Shift the buffer pointer now (if necessary) so that it is properly
         * aligned.
         */
        bufferAddress = ((bufferAddress + (NA_CACHE_LINE_SIZE - 1)) & ~(NA_CACHE_LINE_SIZE - 1));
        pool = (NACanFrameBufferUnion_t *) bufferAddress;
        
        for (idx = 0; idx < IOP_MAX_DMA_DESCRIPTORS; idx++)        
        {
            /*
             * Note that NACanFrameBufferUnion_t objects are sized so that
             * they are an even number of cache lines long.  So, once the first
             * one is aligned, they all are.
             */
            naCBusReceiveBufferReturn(interface, pool[idx].bufferStruct.buffer);
        }
    }

naCBusAllocateReceiveBuffersExit:
    return ccode;
}



/*ad*
 * Set the bit rate for the CAN bus.
 *
 * The bit rate can only be set when after 
 * @link naCBusOpen completes, but before 
 * @link naCBusStart is called.
 * The bit rate must match the bit rate of the CAN bus.  The
 * controller and driver support bit rates between 1 MHz and
 * 10 KHz.
 *
 * @since 7.4
 * @external 
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     indicates which interface
 * @param bitrate       desired bit rate
 *
 * @return NA_SUCCESS                   Bit rate has been set
 * @return NASTATUS_CBUS_BAD_INTERFACE  Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT   bitrate is out of range
 * @return NASTATUS_CBUS_NOT_STOPPED    CAN driver is running
*ad*/
NaStatus naCBusBpsSet(unsigned interface, unsigned bitrate)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    
    FUNCTION_ENTRY(interface);
    
    if (   (bitrate > NA_CBUS_MAX_BIT_RATE) 
        || (bitrate < NA_CBUS_MIN_BIT_RATE))
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusBpsSetExit;
    }
    
    if (naCanInfo[interface].state != CAN_STATE_STOPPED)
    {
        ccode = NASTATUS_CBUS_NOT_STOPPED;
        goto naCBusBpsSetExit;
    }        

    naCanInfo[interface].bitrate = bitrate;

naCBusBpsSetExit:
    FUNCTION_EXIT(interface);
    return ccode;
}


/*ad*
 * Set the maximum reSynchronization Jump Width (SJW).
 *
 * This function can only be called after 
 * @link naCBusOpen completes, but before 
 * @link naCBusStart is called.
 *
 * This routine sets the SJW as a percentage of the total
 * bit time.  A value of 20 indicates the SJW should be no more
 * than 20% of the total bit time.  The value must be between 10
 * and 40.
 *
 * @since 7.4
 * @external    
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     indicates which CAN bus interface
 * @param sjw           set to a value between 10 and 40.
 *
 * @return NA_SUCCESS                   maximum SJW has been set
 * @return NASTATUS_CBUS_BAD_INTERFACE  Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT   SJW is out of range
 * @return NASTATUS_CBUS_NOT_STOPPED    CAN driver is running
*ad*/
NaStatus naCBusSjwSet(unsigned interface, unsigned sjw)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    
    FUNCTION_ENTRY(interface);

    if (   (sjw > NA_CBUS_MAX_SJW)
        || (sjw < NA_CBUS_MIN_SJW))
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusSjwSetExit;
    }
    
    if (naCanInfo[interface].state != CAN_STATE_STOPPED)
    {
        ccode = NASTATUS_CBUS_NOT_STOPPED;
        goto naCBusSjwSetExit;
    }        

    naCanInfo[interface].sjw = sjw;
    
naCBusSjwSetExit:
    FUNCTION_EXIT(interface);
    return ccode;
}



/*ad*
 * Set the sample point for CAN frames.
 *
 * This function can only be called after 
 * @link naCBusOpen completes, but before 
 * @link naCBusStart is called.
 *
 * This routine sets the sample point for CAN frames.  This is the point
 * of time in a bit when the controller will sample the data line to
 * determine the current bit value.  The sample point is set as a
 * percentage of the total bit time.
 *
 * The samplePoint parameter must be set as a percentage value
 * between 60 and 90.  For example, to sample data 80% into the
 * bit, you would set samplePoint to 80.  The default value is 87.  
 *
 * @since 7.4
 * @external    
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     indicates which CAN bus interface
 * @param samplePoint   point where frame should be sampled
 *
 * @return NA_SUCCESS                   sample point has been set
 * @return NASTATUS_CBUS_BAD_INTERFACE  Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT   sample point is out of range
 * @return NASTATUS_CBUS_NOT_STOPPED    CAN driver is running
*ad*/
NaStatus naCBusSamplePointSet(unsigned interface, unsigned samplePoint)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    
    FUNCTION_ENTRY(interface);

    if (   (samplePoint > NA_CBUS_MAX_SAMPLE_POINT)
        || (samplePoint < NA_CBUS_MIN_SAMPLE_POINT))
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusSamplePointSetExit;
    }
    
    if (naCanInfo[interface].state != CAN_STATE_STOPPED)
    {
        ccode = NASTATUS_CBUS_NOT_STOPPED;
        goto naCBusSamplePointSetExit;
    }        

    naCanInfo[interface].samplePoint = samplePoint;
    
naCBusSamplePointSetExit:
    FUNCTION_EXIT(interface);
    return ccode;
}



/*ad*
 * Use this routine to set the length of the synchronization period.
 * 
 * This function can only be called after 
 * @link naCBusOpen completes, but before 
 * @link naCBusStart is called.
 *
 * The syncPeriod argument should be set to a value between 5 and 25,
 * and will be length of the synchronization period as a percentage of 
 * the total bit time.  
 *
 * The default value is 10.
 *
 * @since 7.4
 * @external    
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     indicates which CAN bus interface
 * @param syncPeriod    length of sync period as a percentage of the 
 *                      total bit time
 *
 * @return NA_SUCCESS                   sample point has been set
 * @return NASTATUS_CBUS_BAD_INTERFACE  Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT   sample point is out of range
 * @return NASTATUS_CBUS_NOT_STOPPED    CAN driver is running
*ad*/
NaStatus naCBusSyncPeriodSet(unsigned interface, unsigned syncPeriod)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    
    FUNCTION_ENTRY(interface);

    if (   (syncPeriod < NA_CBUS_MIN_SYNC_PERIOD)
        || (syncPeriod > NA_CBUS_MAX_SYNC_PERIOD))
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusSyncPeriodSetExit;
    }
    
    if (naCanInfo[interface].state != CAN_STATE_STOPPED)
    {
        ccode = NASTATUS_CBUS_NOT_STOPPED;
        goto naCBusSyncPeriodSetExit;
    }        

    naCanInfo[interface].syncPeriod = syncPeriod;
    
naCBusSyncPeriodSetExit:
    FUNCTION_EXIT(interface);
    return ccode;
}


/*
 * This function is the entry point for the thread that
 * handles receive frames.  The ISR callback will place received
 * frames onto ThreadX queue.  This will wake up this thread
 * which will pass the frame onto the application's call back
 * function.
 */
void naCbusCanRecvThread(ULONG interface)
{
    NACanFrameBuffer_t *frame;
    UINT qCcode;
    
    NA_INSIST(interface < naCanTableSize);
    
    do {
        qCcode = tx_queue_receive(&naCanThreadXInfo[interface].pendingReceiveQ,
                        &frame, TX_WAIT_FOREVER);
        if (qCcode == TX_SUCCESS)
        {
            NaCanId_t id;
            
            if (NA_IOP_DMA_SUCCESS == frame->result)
            {
                if (frame->length >= NACBUS_MINIMUM_FRAME_LENGTH)
                {
                    unsigned flags = frame->buffer[NACBUS_FLAGS_POSITION_IN_RECV_FRAME];
                    unsigned length = flags & NA_CBUS_FLAGS_LENGTH_MASK;
                    
                    if (length > NA_CBUS_MAX_DATA_LENGTH)
                    {
                        length = NA_CBUS_MAX_DATA_LENGTH;
                    }
                    memcpy(&id, frame->buffer, sizeof(id));
                    if (id & NA_CBUS_CANID_IS_REQUEST)
                    {
                        length = 0;
                    }
                
                    naCanInfo[interface].receiveThreadFn(interface, id, flags, &frame->buffer[NACBUS_DATA_START_IN_RECV_FRAME], length);
                }
            }
            if (naCanInfo[interface].state != CAN_STATE_SHUTTING_DOWN)
            {
                naCBusReceiveBufferReturn(interface, (BYTE *) frame);
            }
        }
        else 
        {
            NA_INSIST(qCcode == TX_DELETED);
        }
    } while (qCcode != TX_DELETED);
}


/*ad*
 * Call this function to register a call back function to receive frames.
 *
 * This function must be called after 
 * @link naCBusOpen completes, but before 
 * @link naCBusStart is called.
 *
 * Once 
 * @link naCBusStart completes, the driver will call the call back function you register
 * to pass on frames received on the CAN bus.  The function will be called from
 * within a thread created for this purpose.  You must specify the stack size
 * and priority for this thread.
 *
 * The stack size required by the thread will depend upon what other functions
 * your receiver thread will call.  The CAN driver will use about 200 bytes of
 * stack in this thread.  We recommend a minimum stack size of 4K.
 *
 * The priority level of the thread determines which other threads it can preempt,
 * and what threads can preempt it.  If your application must respond to messages
 * in a timely fashion, then you should set a high priority level.  Set the
 * priority argument to smaller values to increase the priority level of the thread.
 *
 * @since 7.4
 * @external   
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     must be set to 0
 * @param receiverFn    pointer to your call back function
 * @param stackSize     stack size for the receiver thread
 * @param priority      priority level for the receiver thread
 * @param threshold     preemption threshold for the receiver thread
 *
 * @return NA_SUCCESS   Receiver thread has been installed
 * @return NASTATUS_CBUS_RECEIVER_ALREADY_INSTALLED A receiver was already installed
 * @return NASTATUS_CBUS_BAD_ARGUMENT   stack size or priority is invalid
 * @return NASTATUS_CBUS_OUT_OF_RESOURCES   Unable to allocate resources needed by the receiver thread
*ad*/
NaStatus naCBusReceiverThreadInstall(unsigned interface, NACBusDmaReceiverFn_t receiverFn, unsigned stackSize, unsigned priority, unsigned threshold)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    
    FUNCTION_ENTRY(interface);
    
    /*
     * See if a receiver is alread installed.
     */
    if (naCanInfo[interface].stack != NULL)
    {
        ccode = NASTATUS_CBUS_RECEIVER_ALREADY_INSTALLED;
        goto naCBusReceiverThreadInstallExitNoFree;
    }

    /*
     * Edit check parameters.
     */
    if ((stackSize < NACBUS_MIN_RECV_STACK_SIZE) || (priority >= TX_MAX_PRIORITIES) || (receiverFn == NULL))
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusReceiverThreadInstallExitNoFree;
    }

    /*
     * Mark pendingReceiveQBuffer as NULL so we don't pass free() a bad
     * pointer if we have to terminate early.  
     */
    naCanInfo[interface].pendingReceiveQBuffer = NULL;

    /*
     * Allocate the thread's stack.
     */
    naCanInfo[interface].stack = malloc(stackSize);
    if (naCanInfo[interface].stack == NULL)
    {
        ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
        goto naCBusReceiverThreadInstallExit;
    }

    /*
     * Allocate the buffer for the thread's receive queue.
     */
#define NACBUS_RECV_QUEUE_SIZE      (IOP_MAX_DMA_DESCRIPTORS * sizeof (NACanFrameBuffer_t *))
    naCanInfo[interface].pendingReceiveQBuffer = malloc(NACBUS_RECV_QUEUE_SIZE);
    if (naCanInfo[interface].pendingReceiveQBuffer == NULL)
    {
        ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
        goto naCBusReceiverThreadInstallExit;
    }

    /*
     * Create the queue.
     */
    if (tx_queue_create(&naCanThreadXInfo[interface].pendingReceiveQ, "CAN Q", 
        TX_1_ULONG, naCanInfo[interface].pendingReceiveQBuffer, 
        NACBUS_RECV_QUEUE_SIZE) != TX_SUCCESS)
    {
        ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
        goto naCBusReceiverThreadInstallExit;
    }
    
    /*
     * Save the receiver function.
     */
    naCanInfo[interface].receiveThreadFn = receiverFn;

    /*
     * So far, so good.  Now create the thread.
     */
    if (tx_thread_create(&naCanThreadXInfo[interface].receiveThread, "CAN Rx Thread", 
        naCbusCanRecvThread, (ULONG) interface, naCanInfo[interface].stack,
        stackSize, priority, threshold, 1, TX_AUTO_START) != TX_SUCCESS)
    {
        ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
        goto naCBusReceiverThreadInstallExit;
    }
    else
    {   /* Don't free allocated memory if we succeed. */
        goto naCBusReceiverThreadInstallExitNoFree;
    }
    
naCBusReceiverThreadInstallExit:
    /*
     * Oops.  Something went wrong.  Free any buffers we allocated 
     * and clean up.
     */
    if (naCanInfo[interface].stack != NULL)
    {
        free(naCanInfo[interface].stack);
        naCanInfo[interface].stack = NULL;
    }
    if (naCanInfo[interface].pendingReceiveQBuffer != NULL)
    {
        NA_INSIST(tx_queue_delete(&naCanThreadXInfo[interface].pendingReceiveQ) == TX_SUCCESS);
        memset(&naCanThreadXInfo[interface].pendingReceiveQ, 0, sizeof(naCanThreadXInfo[interface].pendingReceiveQ));
        free(naCanInfo[interface].pendingReceiveQBuffer);
        naCanInfo[interface].pendingReceiveQBuffer = NULL;
    }
    naCanInfo[interface].receiveThreadFn = NULL;
    
naCBusReceiverThreadInstallExitNoFree:
    FUNCTION_EXIT(interface);
    return ccode;
}




/*
 * Used by naCBusConfigureController to receive transmit buffers after they
 * have been DMA'ed to the PIC.
 */
static void receiveTransmitMsgBuffer(NaIopProcessor_t processor, void * buffer, unsigned length, unsigned result, void * context)
{
    NA_UNUSED_PARAM(processor);
    NA_UNUSED_PARAM(length);
    if (result != NA_IOP_DMA_SUCCESS)
    {
        panic_num("receiveConfigMsgBuffer: error sending configuration", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }
    naCanInfo[(unsigned) context].txPendingCount--;
    naCBusTransmitBufferReturn((unsigned) context, buffer);
}


/*
 * This is just a wrapper for naIopDmaTransmit.  It increments a count of TX buffers
 * in the TX queue.  We use the counter to make sure the transmit buffers are done
 * before we shut down.
 */
static NaStatus sendTxDmaBuffer(unsigned interface, void * buffer, unsigned length, NaIopDmaCallback_t transmitCallback, void * context)
{
    /*
     * Parameters will be edit checked by naIopDmaTransmit.
     */
    NaStatus ccode = naIopDmaTransmit(naCanInfo[interface].processor, buffer, 
                                          length, transmitCallback, 
                                          context);
    if (ccode == NASTATUS_SUCCESS)
    {
        naCanInfo[interface].txPendingCount++;
    }
    return ccode;
}


/*
 * This function sends the configuration message to the PIC.  The message sets 
 * timing parameters used by the PIC firmware and CAN hardware assist modules.
 *
 * CAN Messages are defined as:
 *
 *      --------------------------------------------------------
 *      | Sync  | Prop  |   Phase 1         |    Phase 2        |
 *      | 1q    | 1q    |   4q              |    4q             |
 *      ---------------------------------------------------------
 *
 * where q is a quanta, and there are 10 quantum in a single message.  Refer to
 * the Bit Timing Requirements chapter of the CAN spec for more details.
 *
 * However, no one actually follows these rules.  Instead, the sample point,
 * which is the start of phase2, is fine tuned.  The typical value for 
 * the sample point is 87% of the total bit time.
 *
 * The code below builds the configuration message for the PIC based on values
 * the user has already set for the sample point, SJW, and length of the sync
 * period.  We base the calculations on 10% of the bit time instead of 1% (which
 * would be easier to follow) in order to minimize rounding errors.
 *
 * CSPL = sample point, which is Sync + Prop + Phase 1
 * CP2PL = Phase 2
 * CSP = Sync Period
 * CSI = Time to generate interrupt before sample point
 * CP2I = Time to generate interrupt before end of bit time
 * SJW = reSynchronization Jump Width Limit
 *
 * The CSI and CP2I values attempt to compensate for the PIC latency, so
 * they indicate the point in time where an interrupt should be generated,
 * but that point in time occurs before the actual event so that the PIC
 * ISR has time to set up for the operation.
 *
 * The default filter table follows the configuration information above.  The
 * filter table consists of 8 filters, each which is 4 pairs of bytes where
 * each byte pair is an address byte and its mask.
 */
static NaStatus naCBusConfigureController(unsigned interface)
{
    unsigned picClock = NAgetPllOutputClkFreq();        /* PIC runs at PLL clock frequency */
    /*
     * Figure out how many clock cycles in 10% of the bit time.  Use 10% 
     * instead of 1% to reduce rounding errors.  All of the calculations below
     * will divide down by 10 to get the correct value.
     */
    unsigned tenPercent = (picClock + (5 * naCanInfo[interface].bitrate)) / (10 * naCanInfo[interface].bitrate);

    /*
     * Compute start of sample point.
     */
    unsigned cspl = ((naCanInfo[interface].samplePoint * tenPercent) + 5)/10;                        
    
    /* 
     * Compute number of cycles remaining in bit time.
     */
    unsigned cp2pl = (((100 - naCanInfo[interface].samplePoint) * tenPercent) + 5)/10;                      

    /*
     * Compute number of cycles in Sync Period.
     */
    unsigned csp = ((naCanInfo[interface].syncPeriod * tenPercent) + 5)/10;                            

    /*
     * Interrupt compensation time for receive interrupt.
     */
    unsigned csi = PIC_RECEIVE_INTERRUPT_LATENCY; 

    /*
     * Interrupt compensation time for transmit interrupt.
     */
    unsigned cp2i = PIC_TRANSMIT_INTERRUPT_LATENCY;  

    /*
     * Compute SJW time.
     */
    unsigned sjw = ((naCanInfo[interface].sjw * tenPercent) + 5)/10;  
    BYTE *transmitBuffer = naCBusTransmitBufferGet(interface);
    NaStatus ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
    
    NA_INSIST(interface < naCanTableSize);

    if (transmitBuffer != NULL)
    {
        /*
         * This structure describes the configuration message that must be sent
         * to the PIC firmware after the CAN firmware is loaded and started.
         * Unfortunately, the message is in little endian format.  Note that 
         * the default filter table immediately follows, but is not inside of
         * this structure because our transmit buffers are not big enough to
         * hold it.
         */
        typedef struct NaCBusConfigMessageType
        {
            BYTE commandCode;
            BYTE sjwLsb;
            BYTE sjwMsb;
            BYTE csplLsb;
            BYTE csplMsb;
            BYTE cp2plLsb;
            BYTE cp2plMsb;
            BYTE csiLsb;
            BYTE csiMsb;
            BYTE cp2iLsb;
            BYTE cp2iMsb;
            BYTE cspLsb;
            BYTE cspMsb;
        } NACBusConfigMessageType;
        NACBusConfigMessageType *config = (NACBusConfigMessageType *) transmitBuffer;

#define NA_CBUS_CONFIG_COMMAND_CODE     (0x41)        
        config->commandCode = NA_CBUS_CONFIG_COMMAND_CODE;
        config->sjwLsb = sjw & 0xff;
        config->sjwMsb = sjw >> 8;
        config->csplLsb = cspl & 0xff;
        config->csplMsb = cspl >> 8;
        config->cp2plLsb = cp2pl & 0xff;
        config->cp2plMsb = cp2pl >> 8;
        config->csiLsb = csi & 0xff;
        config->csiMsb = csi >> 8;
        config->cp2iLsb = cp2i & 0xff;
        config->cp2iMsb = cp2i >> 8;
        config->cspLsb = csp & 0xff;
        config->cspMsb = csp >> 8;

/*
 * Do not use sizeof(NACBusConfigMessageType) for the command length since
 * C will pad the structure with extra bytes.
 */
#define NA_CBUS_CONFIG_COMMAND_LENGTH       (13)
        ccode = sendTxDmaBuffer(interface, transmitBuffer, 
                                  NA_CBUS_CONFIG_COMMAND_LENGTH, receiveTransmitMsgBuffer, 
                                  (void *) interface);
    }
    /*
     * Now send the default filter table.  This is all one message, but our transmit
     * buffers are not big enough to hold the entire configuration message, so we
     * send the default filters in seperate buffers.  The CAN controller will see them
     * all as one big message.
     */
    if (ccode == NASTATUS_SUCCESS)
    {
        unsigned filterIndex = 0;
        
        do
        {
            transmitBuffer = naCBusTransmitBufferGet(interface);
            if (transmitBuffer != NULL)
            {
                BYTE *filter = &naCanInfo[interface].defaultFilterTable[filterIndex * NA_CBUS_FILTER_SIZE];
                unsigned idx;
                
                for (idx = 0; idx < NA_CBUS_FILTER_SIZE; idx++)
                {
                    transmitBuffer[idx] = filter[idx];
                }
                ccode = sendTxDmaBuffer(interface, transmitBuffer, 
                                          NA_CBUS_FILTER_SIZE, receiveTransmitMsgBuffer, 
                                          (void *) interface);
                filterIndex++;
            }
        } while ((ccode == NASTATUS_SUCCESS) && (transmitBuffer != NULL) && (filterIndex < NA_CBUS_MAX_FILTERS));
    }
    return ccode;
}

    

/*ad*
 * Use this function to set a CAN ID filter.
 *
 * The CAN controller filters received CAN frames.  Only those frames which have
 * IDs that match one of the filters are passed onto the ARM9.  Use this
 * function to set one of the filters.  The CAN controller supports 8 filters.
 *
 * A CAN filter consists of a CAN ID and a mask, both of which are 
 * @link NaCanId_t type.  The controller compares the ID of each frame it receives
 * against its filters.  Only those bits which are set in the filter mask are 
 * significant.  This means that any bits in the filter mask that are set to zero will
 * be ignored for filtering purposes.  However, if a bit in the filter mask is set,
 * then the corresponding bits in the ID of the received frame and the filter ID must
 * match exactly for the frame to be received.
 *
 * The NaCanId_t type includes bits that represent the RTR and IDE bits in CAN frames.
 * These bits can be used in filters to select remote or data frames, or to select 
 * frames that use 11 or 29 bit IDs.  
 *
 * Use the 
 * @link naCBusFilterSetRejectAll function to set a filter which rejects all frames.
 *
 * For example, a filter with the mask set to 0x0 will match all frames.  A filter with
 * an ID of 0x4500000 and mask of 0x1fffffff will match all frames with the ID of 0x4500000. 
 * A filter with an ID of 0x84500000 and mask 0x9fffffff will match all request frames 
 * with the ID 0x04500000.
 *
 * This function may be called anytime after 
 * @link naCBusOpen completes.  By default, filter 0 allows all CAN frames to be 
 * received, and the other filters reject all frames.  The processor may be overwhelmed
 * with frames if the CAN bus is heavily loaded and 
 * @link naCBusStart is called while filter 0 is set up to receive all frames.  You
 * should use this function to set up the filters your application will use before 
 * calling naCBusStart.  Filters can be changed on the fly.
 *
 * @since 7.4
 * @external
 * @category BSP:Device_Drivers:CAN
 *
 * @note The first filter in the default filter table accepts all frames, and the
 *       other default filters reject all frames.
 *
 * @param interface     indicates which interface
 * @param filterNumber  set between 0 and 7 to determine which filter to set
 * @param id            contains the filter ID
 * @param mask          contains the filter mask
 *
 * @return NA_SUCCESS                       the filter has been set
 * @return NASTATUS_CBUS_BAD_INTERFACE      Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT       the filter number is out of range
 * @return NASTATUS_CBUS_NOT_RUNNING        CAN driver is not running
 * @return NASTATUS_CBUS_OUT_OF_RESOURCES   CAN driver was not able to allocate a message buffer
*ad*/
NaStatus naCBusFilterSet(unsigned interface, unsigned filterNumber, NaCanId_t id, NaCanId_t mask)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    BYTE *transmitBuffer = NULL; 
    
    FUNCTION_ENTRY(interface);
    
    if (filterNumber >= NA_CBUS_MAX_FILTERS)
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusSetFilterExit;
    }

    if (naCanInfo[interface].state == CAN_STATE_STOPPED)
    {
        /*
         * If we haven't sent the configuration message yet, then 
         * load the filter into the default filter table.
         */
        BYTE *filter = &naCanInfo[interface].defaultFilterTable[filterNumber * NA_CBUS_FILTER_SIZE];
        
        filter[0] = (BYTE) ((id >> 24) & 0xff);
        filter[1] = (BYTE) ((mask >> 24) & 0xff);
        filter[2] = (BYTE) ((id >> 16) & 0xff);
        filter[3] = (BYTE) ((mask >> 16) & 0xff);
        filter[4] = (BYTE) ((id >> 8) & 0xff);
        filter[5] = (BYTE) ((mask >> 8) & 0xff);
        filter[6] = (BYTE) (id & 0xff);
        filter[7] = (BYTE) (mask & 0xff);
        goto naCBusSetFilterExit;
    }
    else if (naCanInfo[interface].state != CAN_STATE_RUNNING)
    {
        ccode = NASTATUS_CBUS_NOT_RUNNING;
        goto naCBusSetFilterExit;
    }

    transmitBuffer = naCBusTransmitBufferGet(interface);

    if (transmitBuffer == NULL)
    {
        ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
        goto naCBusSetFilterExit;
    }
    
#define NA_CBUS_SET_FILTER_COMMAND_BIT      (0x80)
    transmitBuffer[0] = NA_CBUS_SET_FILTER_COMMAND_BIT | filterNumber;
    transmitBuffer[1] = (BYTE) ((id >> 24) & 0xff);
    transmitBuffer[2] = (BYTE) ((mask >> 24) & 0xff);
    transmitBuffer[3] = (BYTE) ((id >> 16) & 0xff);
    transmitBuffer[4] = (BYTE) ((mask >> 16) & 0xff);
    transmitBuffer[5] = (BYTE) ((id >> 8) & 0xff);
    transmitBuffer[6] = (BYTE) ((mask >> 8) & 0xff);
    transmitBuffer[7] = (BYTE) (id & 0xff);
    transmitBuffer[8] = (BYTE) (mask & 0xff);

#define NA_CBUS_SET_FILTER_COMMAND_LENGTH   (9)
    ccode = sendTxDmaBuffer(interface, transmitBuffer, 
                              NA_CBUS_SET_FILTER_COMMAND_LENGTH, receiveTransmitMsgBuffer, 
                              (void *) interface);

    transmitBuffer = NULL;                  /* buffer is freed by receiveTransmitMsgBuffer */

naCBusSetFilterExit:
    if (transmitBuffer != NULL)
    {
        naCBusTransmitBufferReturn(interface, transmitBuffer);
    }
    FUNCTION_EXIT(interface);
    return ccode;
}


/*ad*
 * Use this function to set a CAN ID filter that rejects all frames.
 *
 * The CAN controller filters received CAN frames.  Only those frames which have
 * IDs that match one of the filters are passed onto the ARM9.  Use this
 * function to set a filter to reject all frames.  This means that a frame will
 * have to match some other filter to be accepted.  Filters which are not in use
 * should be set to reject all frames.
 *
 * This function may be called anytime after 
 * @link naCBusOpen completes.  By default, filter 0 allows all CAN frames to be 
 * received, and the other filters reject all frames.  The processor may be overwhelmed
 * with frames if the CAN bus is heavily loaded and 
 * @link naCBusStart is called while filter 0 is set up to receive all frames. 
 *
 * @since 7.4
 * @external
 * @category BSP:Device_Drivers:CAN
 *
 * @note The first filter in the default filter table accepts all frames, and the
 *       other default filters reject all frames.
 *
 * @param interface     indicates which interface
 * @param filterNumber  set between 0 and 7 to determine which filter to set
 *
 * @return NA_SUCCESS                       the filter has been set
 * @return NASTATUS_CBUS_BAD_INTERFACE      Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT       the filter number is out of range
 * @return NASTATUS_CBUS_NOT_RUNNING        CAN driver is not running
 * @return NASTATUS_CBUS_OUT_OF_RESOURCES   CAN driver was not able to allocate a message buffer
*ad*/
NaStatus naCBusFilterSetRejectAll(unsigned interface, unsigned filterNumber)
{
    /*
     * Don't want to document this bit, so we provide this function to set a 
     * filter that rejects all frames.  This way, we can reassign the bit later
     * if we find that we need it.
     */
    return naCBusFilterSet(interface, filterNumber, NA_CBUS_CANID_ALWAYS_ZERO_BIT, NA_CBUS_CANID_ALWAYS_ZERO_BIT);
}


/*
 * This routine receives interrupts from the controller.  We expect to get an interrupt 
 * when we start the controller indicating that it has been reset.  Anything other than
 * that is an error indication.
 */
static void naCBusControllerIsr(NaIopProcessor_t processor, void * context, unsigned interruptCode)
{
    unsigned interface = (unsigned) context;
    
    NA_UNUSED_PARAM(processor);
    
    if ((naCanInfo[interface].expectingResetInterrupt) && (interruptCode == NA_CBUS_CONTROLLER_RESET))
    {
        naCanInfo[interface].expectingResetInterrupt = FALSE;
    }
    else
    {
        naCanInfo[interface].fatalErrorCode = interruptCode;
    }
}



/*ad*
 * Use this function to retrieve the current status of the CAN controller
 * and the driver. 
 *
 * @since 7.4
 * @external
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     set to 0
 * @param status        pointer to NACBusStatus_t structure the driver will write
 *                      status information into
 *
 * @return NA_SUCCESS                   the current status has been loaded into the memory
 *                                      pointed to by status
 * @return NASTATUS_CBUS_BAD_INTERFACE  Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT   status  is NULL
*ad*/
NaStatus naCBusGetStatus(unsigned interface, NACBusStatus_t * status)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    
    FUNCTION_ENTRY(interface);
    
    if (status == NULL)
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusGetStatusExit;
    }

    memset(status, 0, sizeof(*status));
    
    status->receiveErrorCount = *naCanInfo[interface].receiveErrorCount;
    status->transmitErrorCount = *naCanInfo[interface].transmitErrorCount;
    if (*naCanInfo[interface].fatalErrorRegister == NA_CBUS_NO_ERROR)
    {
        if (naCanInfo[interface].state != CAN_STATE_RUNNING)
        {
            status->state = NA_CBUS_STATE_NOT_RUNNING;
        }
        else if (*naCanInfo[interface].busState & NA_CBUS_BUS_OFF_FLAG)
        {
            status->state = NA_CBUS_STATE_BUS_OFF;
        }
        else if (*naCanInfo[interface].busState & NA_CBUS_ERROR_PASSIVE_FLAG)
        {
            status->state = NA_CBUS_STATE_ERROR_PASSIVE;
        }
        else
        {
            status->state = NA_CBUS_STATE_OPERATIONAL;
        }
    }
    else
    {
        status->state = NA_CBUS_STATE_FATAL_ERROR;
    }
    
naCBusGetStatusExit:
    FUNCTION_EXIT(interface);
    
    return ccode;
}


/*ad*
 * Start the CAN bus driver.
 *
 * Call this function to start the CAN bus driver.  You must call 
 * @link naCBusReceiverThreadInstall to register a callback function to receive packets
 * before you call this function.  Use the 
 * @link naCBusBpsSet function to set the bit rate of the CAN bus before 
 * calling this function.  
 *
 * You can use the 
 * @link naCBusSamplePointSet
 * @link naCBusSjwSet and
 * @link naCBusSyncPeriodSet functions to fine tune the CAN bus timing parameters 
 * before you call this function.
 *
 * Use the 
 * @link naCBusFilterSet function to set up a filter table before calling this
 * function to start the controller.  This is especially important if the CAN
 * bus will be heavily loaded as the default filter table accepts all frames.
 *
 * This function will start the CAN controller, configure it, and then start it.  
 * The receive function registered with naCBusReceiverThreadInstall 
 * may be called before this function completes.
 *
 * @since 7.4
 * @external
 * @category BSP:Device_Drivers:CAN
 *
 * @note This function will panic if the GPIO multiplexer has not be set up for
 *       CAN, or if there is some failure in downloading the firmware to the FIM
 *       and starting it.
 *
 * @param interface     indicates which interface
 *
 * @return NA_SUCCESS                   receiver function has been set
 * @return NASTATUS_CBUS_BAD_INTERFACE  Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT   receive function is NULL
 * @return NASTATUS_CBUS_NOT_STOPPED    CAN driver is running
 * @return NASTATUS_CBUS_NO_RECEIVER    CAN receiver callback function has not been set
*ad*/
NaStatus naCBusStart(unsigned interface)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    NaIopProcessor_t processor;
    NaIopProgram_t *firmware;
    
    FUNCTION_ENTRY(interface);
    
    processor = naCanTable[interface].processor;
    firmware = naCanTable[interface].program;

    if (firmware == NULL) 
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusStartExit;
    }
    
    if (firmware->ns9215Header.numberOfInstructions == 0)
    {
        panic_num("Please register at http://www.digiembedded.com/requestcan to receive the required CAN bus support files.", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }
    
    if (naCanInfo[interface].receiveThreadFn == NULL)
    {
        ccode = NASTATUS_CBUS_NO_RECEIVER;
        goto naCBusStartExit;
    }        

    if (naCanInfo[interface].state != CAN_STATE_STOPPED)
    {
        ccode = NASTATUS_CBUS_NOT_STOPPED;
        goto naCBusStartExit;
    }
    
    if (processor == NA_IOP_PROCESSOR_PIC0)
    {
        if (   (BSP_GPIO_MUX_IOP_0_CAN_RXD == BSP_GPIO_MUX_INTERNAL_USE_ONLY)
            || (BSP_GPIO_MUX_IOP_0_CAN_TXD == BSP_GPIO_MUX_INTERNAL_USE_ONLY))
        {
            panic_num("GPIO has not been configured for CAN.", ERROR_CAN, ERROR_CAN_INITIALIZATION);
        }
        naCanInfo[interface].processor = processor;
        narm_write_reg(NA_SCM_CCR_REG, NA_SCM_CCR_ADDRESS, iop0, SCM_CCR_ENABLE_CLOCK);
        naCanInfo[interface].fifoStatusRegister = NA_IOHUB_IFS_getAddress(0);
    }
    else if (processor == NA_IOP_PROCESSOR_PIC1)
    {
        
        if (   (BSP_GPIO_MUX_IOP_1_CAN_RXD == BSP_GPIO_MUX_INTERNAL_USE_ONLY)
            || (BSP_GPIO_MUX_IOP_1_CAN_TXD == BSP_GPIO_MUX_INTERNAL_USE_ONLY))
        {
            panic_num("GPIO has not been configured for CAN.", ERROR_CAN, ERROR_CAN_INITIALIZATION);
        }
        naCanInfo[interface].processor = processor;
        narm_write_reg(NA_SCM_CCR_REG, NA_SCM_CCR_ADDRESS, iop1, SCM_CCR_ENABLE_CLOCK);
        naCanInfo[interface].fifoStatusRegister = NA_IOHUB_IFS_getAddress(1);
    }
    else
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusStartExit;
    }

    /*
     * Set up pointers to the controller status registers.
     */
    naCanInfo[interface].receiveErrorCount = NA_IOP_STATUS_REG_getAddress(processor, NA_CBUS_RECEIVE_ERROR_COUNT_REGISTER);
    naCanInfo[interface].transmitErrorCount = NA_IOP_STATUS_REG_getAddress(processor, NA_CBUS_TRANSMIT_ERROR_COUNT_REGISTER);
    naCanInfo[interface].fatalErrorRegister = NA_IOP_STATUS_REG_getAddress(processor, NA_CBUS_FATAL_ERROR_REGISTER);
    naCanInfo[interface].busState = NA_IOP_STATUS_REG_getAddress(processor, NA_CBUS_BUS_STATE_REGISTER);

    /*
     * Stop the processor so we can download its program.
     */
    ccode = naIopProcessorStopAndReset(naCanInfo[interface].processor);
    if (ccode != NASTATUS_SUCCESS)
    {
        panic_num("naIopProcessorStopAndReset failed", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }
    
    /*
     * Download the code to the I/O processor.  Must call naIopProcessorDownloadPicProgram
     * before calling naIopDmaFlushQueue because naIopProcessorDownloadPicProgram does
     * some initialization of the DMA descriptors.
     */
    ccode = naIopProcessorDownloadPicProgram(naCanInfo[interface].processor, firmware);
    if (ccode != NASTATUS_SUCCESS)
    {
        panic_num("naIopProcessorDownloadProgram failed", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }
    
    /*
     * Flush the IOP API's DMA queues.
     */
    ccode = naIopDmaFlushQueue(naCanInfo[interface].processor, TRUE);
    if (ccode != NASTATUS_SUCCESS)
    {
        panic_num("naIopDmaFlushQueue failed", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }
    ccode = naIopDmaFlushQueue(naCanInfo[interface].processor, FALSE);
    if (ccode != NASTATUS_SUCCESS)
    {
        panic_num("naIopDmaFlushQueue failed", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }

    if (naCanInfo[interface].mallocedTransmitBuffer == NULL)
    {
        naCBusAllocateTransmitBuffers(interface);
    }
    
    if (naCanInfo[interface].mallocedReceiveBuffer == NULL)
    {
        naCBusAllocateReceiveBuffers(interface);
    }

    /*
     * Install interrupt handler.
     */
    naCanInfo[interface].fatalErrorCode = NA_CBUS_NO_ERROR;
    naCanInfo[interface].expectingResetInterrupt = TRUE;
    ccode = naIopRegisterIsrCallback(naCanInfo[interface].processor, naCBusControllerIsr, (void *) interface);
    if (ccode != NASTATUS_SUCCESS)
    {
        panic_num("naIopRegisterIsrCallback failed", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }
    /*
     * Let her rip.
     */
    ccode = naIopProcessorStartAtZero(naCanInfo[interface].processor);
    if (ccode != NASTATUS_SUCCESS)
    {
        panic_num("naIopProcessorDownloadProgram failed", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }
    
    naCBusConfigureController(interface);
    
    naCanInfo[interface].state = CAN_STATE_RUNNING;
    
naCBusStartExit:
    FUNCTION_EXIT(interface);
    return ccode;
}


    

/*
 * This function is used to shut down the CAN controller and driver after naCBusStart
 * has been called.
 *
 *      1. Sends a shutdown command to the controller.  This tells it to turn its
 *         transmitter off so that we do not jam the CAN bus.
 *      2. Places the FIM module into reset.
 *      3. Flushes the DMA queues.
 *      4. Deletes the message buffers allocated by the driver.
 *      5. Deletes the buffer used to hold RTR information.
 */
 static NaStatus naCBusStop(unsigned interface)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    unsigned timeout;
    BYTE *command;
    
    FUNCTION_ENTRY(interface);
    
    if (naCanInfo[interface].state != CAN_STATE_RUNNING)
    {
        ccode = NASTATUS_CBUS_NOT_RUNNING;
        goto naCBusStopExit;
    }
    
    /*
     * Send a message to the PIC to shut down.  We can be either asserting
     * Tx (dominant) or not (recessive) on the CAN bus.  Tell the FIM firmware
     * to place our transmitter into the recessive state so that we don't 
     * stop at a bad point and jam the bus.
     */
    command = naCBusTransmitBufferGet(interface);
    if (command != NULL)
    {
#define NA_CBUS_SHUTDOWN_COMMAND    (0x40)
        command[NA_CBUS_COMMAND_INDEX] = NA_CBUS_SHUTDOWN_COMMAND;
        sendTxDmaBuffer(interface, command, 1,
                         receiveTransmitMsgBuffer, (void *) interface);
    }

/*
 * This timeout is arbitrary.
 *
 * The code below waits for any FIM transmit buffers to drain from the
 * DMA queue and then for the FIM transmitter FIFO to empty.  
 */
#define SHUTDOWN_TIMEOUT        (1 * NABspTicksPerSecond)
    timeout = SHUTDOWN_TIMEOUT;
    do
    {   /* loop to wait for DMA queue to drain */
        if (naCanInfo[interface].txPendingCount == 0)
        {
            break;
        }
        tx_thread_sleep(1);
        timeout--;
    } while (timeout != 0);
    
    while (timeout != 0)
    {   /* loop to wait for FIM transmit FIFO to drain */
        if ((*naCanInfo[interface].fifoStatusRegister & NA_IOHUB_IFS_TX_FIFO_EMPTY) != 0)
        {
            break;
        }
        tx_thread_sleep(1);
        timeout--;
    } 
    
    /*
     * Stop the processor 
     */
    ccode = naIopProcessorStopAndReset(naCanInfo[interface].processor);
    if (ccode != NASTATUS_SUCCESS)
    {
        panic_num("naIopProcessorStopAndReset failed", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }
    
    naCanInfo[interface].state = CAN_STATE_SHUTTING_DOWN;
    
    /*
     * The DMA channel can get hung up if the PIC stops responding for some 
     * reason.  In this case, the timeout will be zero. Recover by resetting
     * the IOP module.
     */
    if (timeout == 0)
    {
        if (naCanTable[interface].processor == NA_IOP_PROCESSOR_PIC0)
        {
            narm_write_reg(NA_SCM_RSR_REG, NA_SCM_RSR_ADDRESS, iop0, SCM_RSR_RESET_MODULE); 
            narm_write_reg(NA_SCM_RSR_REG, NA_SCM_RSR_ADDRESS, iop0, SCM_RSR_ENABLE_MODULE); 
        }
        else
        {
            narm_write_reg(NA_SCM_RSR_REG, NA_SCM_RSR_ADDRESS, iop1, SCM_RSR_RESET_MODULE); 
            narm_write_reg(NA_SCM_RSR_REG, NA_SCM_RSR_ADDRESS, iop1, SCM_RSR_ENABLE_MODULE); 
        }
    }
    
    /*
     * Flush the IOP API's DMA queues.
     */
    ccode = naIopDmaFlushQueue(naCanInfo[interface].processor, TRUE);
    if (ccode != NASTATUS_SUCCESS)
    {
        panic_num("naIopDmaFlushQueue failed", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }
    ccode = naIopDmaFlushQueue(naCanInfo[interface].processor, FALSE);
    if (ccode != NASTATUS_SUCCESS)
    {
        panic_num("naIopDmaFlushQueue failed", ERROR_CAN, ERROR_CAN_INITIALIZATION);
    }

    NA_INSIST(naiopUninstallIsr(naCanInfo[interface].processor) == NASTATUS_SUCCESS);
    
    /*
     * Free transmit and receive buffers.
     */
    free(naCanInfo[interface].mallocedTransmitBuffer);
    naCanInfo[interface].mallocedTransmitBuffer = NULL;
    naCanInfo[interface].freeTransmitBufferQ = NULL;
    free(naCanInfo[interface].mallocedReceiveBuffer);
    naCanInfo[interface].mallocedReceiveBuffer = NULL;
    naCanInfo[interface].state = CAN_STATE_STOPPED;

    if (naCanInfo[interface].rtr != NULL)
    {
        free(naCanInfo[interface].rtr);
        naCanInfo[interface].rtr = NULL;
        naCanInfo[interface].rtrCount = 0;
    }

naCBusStopExit:
    FUNCTION_EXIT(interface);
    return ccode;
}




/*
 * Return the calculated CRC.
 *
 */
static unsigned naCBusCrcGet(NACanFrameBuffer_t *frame)
{
    return frame->crc;
}

/*
 * Update the stored CRC value with the latest data bit.
 *
 * The CRC algorithm from the CAN spec is:
 *
 *       CRCNXT = NXTBIT XOR CRC(14)
 *       CRC = CRC << 1
 *       if CRCNXT then
 *           CRC = CRC XOR 0x4599
 *
 * TODO:  See if there is a faster CRC algorithm we can use.  I
 *        suspect there isn't because the data is not an even number 
 *        of bytes long and must therefore be operated on bit by bit.
 */
static void naCBusFrameCrcUpdate(NACanFrameBuffer_t *frame, unsigned bit)
{
/*
 * Real CAN polynomial is 0xc599, but this value used used
 * do to the way the algorithm works.  The high bit is
 * xor'ed in the first line.
 */
#define CAN_CRC_POLYNOMIAL_LESS_HIGH_BIT    (0x4599)

    int crcnxt;
    
    frame->crc <<= 1;
    crcnxt = ((frame->crc & 0x8000) != 0) ^ (bit);
    frame->crc &= 0x7ffe;
    if (crcnxt)
    {
        frame->crc ^= CAN_CRC_POLYNOMIAL_LESS_HIGH_BIT;
    }
}


/*
 * Stop bit stuffing.
 */
static void naCBusStuffStop(NACanFrameBuffer_t *frame)
{
    frame->bitStuff = FALSE;
}
    
/*
 * Write one bit to the frame, update the CRC, and bit stuff if we have
 * 5 consecutive bits all of the same value.
 */
static void naCBusFrameWriteOneBitStuffed(NACanFrameBuffer_t *frame, unsigned bit)
{
#define LOWEST_FIVE_BITS_MASK       (0x1f)
    bit &= 1;
    frame->buffer[frame->bytePos] = (frame->buffer[frame->bytePos] << 1) | bit;
    frame->lastFiveBits = ((frame->lastFiveBits << 1) | bit) & LOWEST_FIVE_BITS_MASK;
    frame->bitCount++;
    frame->stuffedBitCount++;
    if (frame->bitCount == 8)
    {
        frame->bytePos += 1;
        frame->bitCount = 0;
    }
    
    if ((frame->bitStuff) && ((frame->lastFiveBits == LOWEST_FIVE_BITS_MASK) || (frame->lastFiveBits == 0)))
    {
        naCBusFrameWriteOneBitStuffed(frame, ~frame->lastFiveBits);
    }
}

/*
 * Write a bit into the CAN frame.  Stuff it if necessary and update the CRC value.
 */
static void naCBusFrameWriteOneBit(NACanFrameBuffer_t *frame, unsigned bit)
{
    naCBusFrameCrcUpdate(frame, bit);
    naCBusFrameWriteOneBitStuffed(frame, bit);
}

/*
 * Initialize fields in frame used for writing and 
 * write the Start Of Frame bit, which is dominant.
 */
static void naCBusFrameWriteStart(NACanFrameBuffer_t *frame)
{
#define FIRST_DATA_BYTE_IN_MESSAGE  (3)
    frame->crc = 0;
    frame->bytePos = FIRST_DATA_BYTE_IN_MESSAGE;
    /*
     * The SOF bit is dominant, so put a recessive bit in to make it easier
     * to figure out how many bits have been sent so far for bit stuffing.
     */
    frame->lastFiveBits = NA_CBUS_RECESSIVE;
    frame->bitCount = 0;
    frame->bitStuff = TRUE;
    frame->stuffedBitCount = 0;
    naCBusFrameWriteOneBit(frame, NA_CBUS_DOMINANT);
}


/*
 * Write one or more bits to the frame.
 */
static void naCBusFrameWrite(NACanFrameBuffer_t *frame, unsigned data, unsigned bitCount)
{
    int idx;
    
    for (idx = bitCount - 1; idx >= 0; idx--)
    {
        naCBusFrameWriteOneBit(frame, (data & (1 << idx)) != 0);
    }
}


/*
 * Last bit has been written, so finalize the last byte of
 * the frame buffer, which contains the last byte of data and
 * the number of valid bits in that byte.
 */
static void naCBusFrameWriteStop(NACanFrameBuffer_t *frame)
{
    frame->buffer[frame->bytePos] <<= (8 - frame->bitCount);
    frame->buffer[frame->bytePos] |= (BYTE) (0xff >> frame->bitCount);
}




/*
 * Take the user's data and make it into a CAN frame that the PIC
 * firmware will understand.  CAN frames have the following format:
 *
 *      Byte 0:     Transmit command
 *      Byte 1:     Length of contention field in bits
 *      Byte 2:     Total number of bits in message
 *      n bytes:    bit stuffed frame
 */
NaStatus naCBusTransmitQInsert(unsigned interface, NaCanId_t id, unsigned flags, BYTE *buffer, unsigned length)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    NACanFrameBuffer_t *frame;
    unsigned dataLength = length;
    unsigned idx;
    
    if (((id & NA_CBUS_CANID_IS_REQUEST) == 0) && (length != 0) && (buffer == NULL))
    {
        /*
         * Caller must provide data if this is a data frame with nonzero length.
         */
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusTransmitQInsertExit;
    }
    /*
     * Don't accept more transmit frames if the controller is currently
     * off the bus.
     */
    if (*naCanInfo[interface].busState & NA_CBUS_BUS_OFF_FLAG)
    {
        ccode = NASTATUS_CBUS_OFF_BUS;
        goto naCBusTransmitQInsertExit;
    }
/*
 * The CAN specification does not disallow frames with the length field set to
 * values larger than 8.  This is probably an oversight.  However, an addendum 
 * to the spec, written later, says that frames with the length field set larger
 * than 8 must be accepted by receivers, but only 8 data bytes are to be received.
 * So, the code above sets the length field in the frame to whatever the user says,
 * but we only send a maximum of 8 data bytes.
 */
    if (dataLength > 8)                         /* CAN allows length field to be set to*/
    {                                           /* more than 8, but allows a max of */
        dataLength = 8;                         /* 8 bytes of data in the frame */
    }
    if (id & NA_CBUS_CANID_IS_REQUEST)              /* CAN allows length field to be set to*/
    {                                           /* non zero value for Request frames,*/
        dataLength = 0;                         /* but there is no actual data in them */
    } 
    
    if (naCanInfo[interface].state != CAN_STATE_RUNNING)
    {
        ccode = NASTATUS_CBUS_NOT_RUNNING;
        goto naCBusTransmitQInsertExit;
    }

    /*
     * Get a message buffer for the transmit frame.
     */
    frame = (NACanFrameBuffer_t *) naCBusTransmitBufferGet(interface);

    if (frame == NULL)
    {
        ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
        goto naCBusTransmitQInsertExit;
    }

    /*
     * Initialize the frame and start writing into it.
     */
    naCBusFrameWriteStart(frame);               /* init frame fields and write SOF */
                                                /* then write base ID */
    naCBusFrameWrite(frame, id >> NA_CBUS_CANID_STANDARD_ID_SHIFT, NA_CBUS_CANID_STANDARD_ID_BIT_COUNT);

    if ((id & NA_CBUS_CANID_IS_EXTENDED_FRAME) == 0)    /* if standard format frame */
    {                                           /* write RTR bit */
        naCBusFrameWriteOneBit(frame, (id & NA_CBUS_CANID_IS_REQUEST) != 0);
                                                /* number bits in contention field stored here*/
#define NA_CBUS_ARBITRATION_FIELD_INDEX (1)
#define LENGTH_OF_RTR_BIT               (1)
        frame->buffer[NA_CBUS_ARBITRATION_FIELD_INDEX] = NA_CBUS_CANID_STANDARD_ID_BIT_COUNT + LENGTH_OF_RTR_BIT;  
                                                /* write IDE bit, which is dominant in standard frame */
        naCBusFrameWriteOneBit(frame, NA_CBUS_DOMINANT); /* IDE bit set to 0 for standard frame */
                                                /* write R0 bit */
        naCBusFrameWriteOneBit(frame, (flags & NA_CBUS_FLAGS_R0_BIT) != 0);
    }
    else                                        /* if extended frame format */
    {                                           /* write IDE and SSR, which are both recessive for extended frame*/
        naCBusFrameWrite(frame, NA_CBUS_RECESSIVE, 2);
        naCBusFrameWrite(frame, id, NA_CBUS_CANID_EXTENDED_ID_BIT_COUNT);        /* write extended portion of ID*/
                                                /* write RTR bit */
        naCBusFrameWriteOneBit(frame, (id & NA_CBUS_CANID_IS_REQUEST) != 0);
        naCBusFrameWriteOneBit(frame, (flags & NA_CBUS_FLAGS_R1_BIT) != 0);
        naCBusFrameWriteOneBit(frame, (flags & NA_CBUS_FLAGS_R0_BIT) != 0);

#define LENGTH_OF_IDE_RTR_AND_SSR_BITS  (3)
                                                /* number bits in contention field stored here*/
        frame->buffer[NA_CBUS_ARBITRATION_FIELD_INDEX] = NA_CBUS_CANID_STANDARD_ID_BIT_COUNT + NA_CBUS_CANID_EXTENDED_ID_BIT_COUNT + LENGTH_OF_IDE_RTR_AND_SSR_BITS;  
    }
    
                                                
    naCBusFrameWrite(frame, length, 4);         /* write length field */

    for (idx = 0; idx < dataLength; idx++)
    {                                           /* write the data bytes */
        naCBusFrameWrite(frame, buffer[idx], 8);
    }
    
                                                /* write the CRC */
    naCBusFrameWrite(frame, naCBusCrcGet(frame), 15);
    naCBusStuffStop(frame);                     /* Done bit stuffing */
                                                /* Write the CRC delimiter, CRC ACK, and ACK delimiter */
    naCBusFrameWrite(frame, NA_CBUS_RECESSIVE, 3);
    naCBusFrameWriteStop(frame);
    
#define NA_CBUS_TRANSMIT_COMMAND        (0)
    frame->buffer[NA_CBUS_COMMAND_INDEX] = NA_CBUS_TRANSMIT_COMMAND;
#define NA_CBUS_STUFFED_BIT_COUNT_INDEX (2)
    frame->buffer[NA_CBUS_STUFFED_BIT_COUNT_INDEX] = frame->stuffedBitCount;  /* write total number of bits in frame*/
    /*
     * Send the transmit message to the CAN controller.
     */
    ccode = sendTxDmaBuffer(interface, frame, 
                              3 + ((frame->stuffedBitCount + 7)/8), receiveTransmitMsgBuffer, 
                              (void *) interface);
     
naCBusTransmitQInsertExit:
    return ccode;
}



/*ad*
 * Use this function to transmit a CAN frame.
 *
 * Load bits 28-0 of the id argument with the CAN ID of the message.  Set the 
 * @link NA_CBUS_CANID_IS_REQUEST bit in the ID if this is a request frame,
 * or leave the bit clear if it is a data frame.  Set the 
 * @link NA_CBUS_CANID_IS_EXTENDED_FRAME bit in the ID if the ID uses
 * the 29-bit format, or leave the bit clear if the ID uses the 11-bit
 * format.
 *
 * If the frame is a data frame, then buffer should point to memory with
 * the frame's data, and length should be set to the number of bytes of 
 * data in the frame.
 *
 * The CAN specification defines the length field of frames ambiguously.  It allows
 * the length field of data frames to be greater than 8, even though CAN frames can 
 * only have a
 * maximum of 8 bytes, and allows request frames to have a nonzero length even 
 * though they have no data.  The length field of the transmitted frame will be
 * set to the value you pass in the length argument to this function.  However,
 * only a maximum of 8 bytes will be sent in a data frame, and no data will be
 * sent in a request frame.
 *
 *
 * @since 7.4
 * @external   
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     must be set to 0
 * @param id            ID of the frame to transmit
 * @param buffer        pointer to data bytes for frame
 * @param length        number of data bytes
 *
 * @return NA_SUCCESS                       frame has been placed on the transmit queue
 * @return NASTATUS_CBUS_BAD_INTERFACE      Invalid interface specified
 * @return NASTATUS_CBUS_OUT_OF_RESOURCES   Unable to allocate resources needed to complete this function
 * @return NASTATUS_CBUS_NOT_RUNNING        The CAN driver is not running
 * @return NASTATUS_CBUS_OFF_BUS            The CAN controller is currently off the bus
*ad*/
NaStatus naCBusTransmit(unsigned interface, NaCanId_t id, BYTE * buffer, unsigned length)
{
    NaStatus ccode;

    FUNCTION_ENTRY(interface);

    ccode = naCBusTransmitQInsert(interface, id, 0, buffer, length);
    
    FUNCTION_EXIT(interface);
    
    return ccode;
}



/*ad*
 * Call this function to open the CAN driver.  You must call this function
 * before calling any other CAN controller API.  
 *
 * This function will set default configuration values for the CAN bus bit rate
 * and timing values.  You must use the 
 * @link naCBusReceiverThreadInstall to install a call back function to receive
 * frames, and use the 
 * @link naCBusBpsSet,
 * @link naCBusSamplePointSet,
 * @link naCBusFilterSet,
 * @link naCBusSjwSet, and
 * @link naCBusSyncPeriodSet functions to set configuration values appropriate 
 * for your application.
 *
 * Use the 
 * @link naCBusStart function to start the CAN controller after you have installed
 * a receiver call back function and set your configuration.
 *
 * @since 7.4
 * @external   
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     must be set to 0
 *
 * @return NA_SUCCESS                       CAN driver has been opened
 * @return NASTATUS_CBUS_BAD_INTERFACE      Invalid interface specified
 * @return NASTATUS_CBUS_ALREADY_OPEN       CAN driver is already open
 * @return NASTATUS_CBUS_OUT_OF_RESOURCES   Unable to allocate resources needed by the CAN driver
*ad*/
NaStatus naCBusOpen(unsigned interface)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    const NACanInfo_t defaultValues = 
    {
        {
            /*
             * This is the default filter table.  The first filter accepts all
             * packets.  The others reject all packets.  We use bit 29 to reject
             * packets since we know that it will always be received as zero.
             */
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        },
        FALSE,
        NULL,
        0,
        NULL,
        NULL,
        NULL,
        NULL,
        TRUE,
        NA_CBUS_NO_ERROR,
        DEFAULT_BAUD_RATE,              
        CAN_STATE_STOPPED,              
        NA_IOP_PROCESSOR_PIC0,          
        NULL,                           
        DEFAULT_SJW_TIME,               
        DEFAULT_SAMPLE_POINT,           
        DEFAULT_SYNC_PERIOD,            
        FALSE,                          
        NULL,                           
        NULL,                           
        NULL,                           
        NULL,                           
        NULL,                           
        NULL,
        0,
        NULL
    };

    if ((interface >= CAN_MAX_INTERFACES) || (interface >= naCanTableSize))
    {
        ccode = NASTATUS_CBUS_BAD_INTERFACE;
        goto naCbusOpenExit;
    }
    
    if (naCanInfo[interface].isOpen)
    {
        ccode = NASTATUS_CBUS_ALREADY_OPEN;
        goto naCbusOpenExit;
    }
    
    if (tx_mutex_create(&naCanThreadXInfo[interface].mutex, "CAN", TX_INHERIT) != TX_SUCCESS)
    {
        ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
        goto naCbusOpenExit;
    }

    memcpy(&naCanInfo[interface], &defaultValues, sizeof(naCanInfo[interface]));
    naCanInfo[interface].isOpen = TRUE;
    
naCbusOpenExit:
    return ccode;
}



/*ad*
 * Call this function to shut down the CAN controller and close the driver.  Once
 * this function completes, you will have to call
 * @link naCBusOpen to reinitialize the controller and driver.
 *
 * @since 7.4
 * @external   
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     must be set to 0
 *
 * @return NA_SUCCESS                   CAN driver has been shut down
 * @return NASTATUS_CBUS_BAD_INTERFACE  Invalid interface specified
*ad*/
NaStatus naCBusClose(unsigned interface)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    UINT threadState;
    
    FUNCTION_ENTRY(interface);
    
    if (naCanInfo[interface].state != CAN_STATE_STOPPED)
    {
        ccode = naCBusStop(interface);
        if (ccode != NA_SUCCESS)
        {
            goto naCBusCloseExit;
        }
    }
    
    /*
     * Shut down receiver thread.  Receiver thread will terminate when it
     * senses that the queue has been deleted.  This is done here rather than
     * in naCBusStop because it is possible to create the receiver thread
     * without actually starting the CAN driver.
     */
    if (naCanInfo[interface].stack != NULL)
    {
        NA_INSIST(tx_queue_delete(&naCanThreadXInfo[interface].pendingReceiveQ) == TX_SUCCESS);
        do
        {
            CHAR *charDontCare;
            UINT uintDontCare;
            ULONG longDontCare;
            TX_THREAD *tx_threadDontCare;
            
            tx_thread_sleep(1);
            NA_INSIST(tx_thread_info_get(&naCanThreadXInfo[interface].receiveThread, &charDontCare, 
                               &threadState, &longDontCare, &uintDontCare, &uintDontCare,
                               &longDontCare, &tx_threadDontCare, &tx_threadDontCare) == TX_SUCCESS);
        } while ((threadState != TX_COMPLETED) && (threadState != TX_TERMINATED));
        
        NA_INSIST(tx_thread_delete(&naCanThreadXInfo[interface].receiveThread) == TX_SUCCESS);
        memset(&naCanThreadXInfo[interface].receiveThread, 0, sizeof(naCanThreadXInfo[interface].receiveThread));
        free(naCanInfo[interface].stack);
        naCanInfo[interface].stack = NULL;
        free(naCanInfo[interface].pendingReceiveQBuffer);
        naCanInfo[interface].pendingReceiveQBuffer = NULL;
    }

    NA_INSIST(tx_mutex_delete(&naCanThreadXInfo[interface].mutex) == TX_SUCCESS);
    memset(&naCanThreadXInfo[interface].mutex, 0, sizeof(naCanThreadXInfo[interface].mutex));
    naCanInfo[interface].isOpen = FALSE;
    
naCBusCloseExit:
/*
 * We are deliberately not using FUNTION_EXIT() here because we just destroyed
 * the mutex.
 */
    return ccode;
}



/*ad*
 * Call this function to allocate the resources needed to support automatic
 * replies to request messages.  This call must be made after
 * @link naCBusOpen completes, but before 
 * @link naCBusStart is called.
 *
 * Do not allocate any more automatic reply control blocks than your application needs.  The control blocks
 * are scanned by a ISR every time a CAN frame is received.  System interrupt
 * latency will be adversely impacted if many automatic reply control blocks have been allocated.
 *
 * @since 7.4
 * @external   
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     must be set to 0
 * @param count         the maximum number of automatic request replies to support
 *
 * @return NA_SUCCESS                   The automatic reply control blocks have been allocated
 * @return NASTATUS_CBUS_BAD_INTERFACE  Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT   count is larger than the maximum number of 
 *                                      control blocks the driver supports
*ad*/
NaStatus naCBusAutoReplyAllocate(unsigned interface, unsigned count)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    
    FUNCTION_ENTRY(interface);
    
    if ((count > NA_CBUS_MAX_RTRS) || (count == 0))
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusAutoReplyAllocateExit;
    }
    
    if (naCanInfo[interface].state != CAN_STATE_STOPPED)
    {
        ccode = NASTATUS_CBUS_NOT_STOPPED;
        goto naCBusAutoReplyAllocateExit;
    }        

    if (naCanInfo[interface].rtr != NULL)
    {
        ccode = NASTATUS_CBUS_RTRS_ALREADY_ALLOCATED;
        goto naCBusAutoReplyAllocateExit;
    }
    
    naCanInfo[interface].rtr = malloc(count * sizeof(NACanRtrInfo_t));
    if (naCanInfo[interface].rtr == NULL)
    {
        ccode = NASTATUS_CBUS_OUT_OF_RESOURCES;
        goto naCBusAutoReplyAllocateExit;
    }
    
    memset(naCanInfo[interface].rtr, 0, count * sizeof(NACanRtrInfo_t));
    naCanInfo[interface].rtrCount = count;

naCBusAutoReplyAllocateExit:
    FUNCTION_EXIT(interface);
    return ccode;
}

 
/*ad*
 * Set the reply message for an request message.
 *
 * Use this function to set up an automatic reply for a request message.  The 
 * reply will be sent by the driver's interrupt service routine, which will be
 * faster than the receiver callback function.
 *
 * Once this function completes, the reply will be sent whenever a request message
 * with a matching ID is received.  The reply message can be updated at any time
 * by calling this function again.
 *
 * When an automatic reply is generated for a request message, the request is still
 * sent to the receiver callback function, but the 
 * @link NA_CBUS_SENT_AUTO_RESPONSE bit is set in the flags argument to the call back
 * function.
 *
 * Note that automatic reply messages are performed by an ISR and will adversely
 * impact system interrupt latency.  You should only use automatic replies for time
 * critical applications.  If a reply message is not time critical, then use the
 * receive call back function to send a reply.
 *
 * @since 7.4
 * @external   
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     must be set to 0
 * @param rtrIndex      set to indicate which automatic reply to set.  The maximum value was set
 *                      by the naCBusAutoReplyAllocate function.
 * @param id            ID of the CAN message to match
 * @param data          pointer to data bytes to send, may be NULL if there is no data
 * @param length        value to be passed in the length field of the frame.  In most cases, 
 *                      this to the number of bytes of data in the message.
 *
 * @return NA_SUCCESS                   The automatic reply has been set
 * @return NASTATUS_CBUS_BAD_INTERFACE  Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT   count is larger than the maximum number of 
 *                                      control blocks the driver supports
 * @return NASTATUS_CBUS_RTRS_NOT_ALLOCATED The automatic reply control blocks have not been allocated
 *
 * @link naCBusAutoReplyAllocate
*ad*/
NaStatus naCBusAutoReplySet(unsigned interface, unsigned rtrIndex, unsigned id, BYTE * data, unsigned length)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    unsigned dataLength = length;
    TX_INTERRUPT_SAVE_AREA;
    
    FUNCTION_ENTRY(interface);

    if ((length == 0) && (data != NULL))
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
    }
    
    if ((length != 0) && (data == NULL))
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
    }
    
    if (dataLength > NA_CBUS_MAX_DATA_LENGTH)
    {
        dataLength = NA_CBUS_MAX_DATA_LENGTH;
    }
    
    if (naCanInfo[interface].rtr == NULL)
    {
        ccode = NASTATUS_CBUS_RTRS_NOT_ALLOCATED;
        goto naCBusAutoReplySetExit;
    }
    
    if (rtrIndex >= naCanInfo[interface].rtrCount)
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusAutoReplySetExit;
    }
    
    id &= ~NA_CBUS_CANID_IS_REQUEST;                /* make sure using isn't setting the RTR BIT */
    
    TX_DISABLE;                                 /* data structure is read by ISR */
    naCanInfo[interface].rtr[rtrIndex].id = id;
    memcpy(naCanInfo[interface].rtr[rtrIndex].data, data, dataLength);
    naCanInfo[interface].rtr[rtrIndex].length = length;
    naCanInfo[interface].rtr[rtrIndex].isActive = TRUE;
    TX_RESTORE;
    
naCBusAutoReplySetExit:
    FUNCTION_EXIT(interface);
    
    return ccode;
}



/*ad*
 * This function disables an automatic reply message previously set with 
 * @link naCBusAutoReplySet.
 *
 * @since 7.4
 * @external   
 * @category BSP:Device_Drivers:CAN
 *
 * @param interface     must be set to 0
 * @param rtrIndex      set to indicate which automatic reply to disable.
 *
 * @return NA_SUCCESS                   Automatic reply has been disabled
 * @return NASTATUS_CBUS_BAD_INTERFACE  Invalid interface specified
 * @return NASTATUS_CBUS_BAD_ARGUMENT   count is larger than the maximum number of 
 *                                      control blocks the driver supports
 *
 * @link naCBusAutoReplyAllocate
*ad*/
NaStatus naCBusAutoReplyDisable(unsigned interface, unsigned rtrIndex)
{
    NaStatus ccode = NASTATUS_SUCCESS;
    
    FUNCTION_ENTRY(interface);

    if (rtrIndex >= naCanInfo[interface].rtrCount)
    {
        ccode = NASTATUS_CBUS_BAD_ARGUMENT;
        goto naCBusAutoReplyDisableExit;
    }
    
    if (naCanInfo[interface].rtr == NULL)
    {
        ccode = NASTATUS_CBUS_RTRS_NOT_ALLOCATED;
        goto naCBusAutoReplyDisableExit;
    }
    
    naCanInfo[interface].rtr[rtrIndex].isActive = FALSE;

naCBusAutoReplyDisableExit:
    FUNCTION_EXIT(interface);
    
    return ccode;
}



