/*****************************************************************************
*
* Freescale Semiconductor Inc.
* (c) Copyright 2004-2007 Freescale Semiconductor, Inc.
* (c) Copyright 2001-2004 Motorola, Inc.
* ALL RIGHTS RESERVED.
*
******************************************************************************
*
* File Name: ucan_ms.c
*
* Description: uCAN driver for msCAN
*
* $Version: 1.0.14.0$
*
*****************************************************************************/

#include "types.h"
#include "arch.h"
#include "periph.h"
#include "appconfig.h"

#include "ucan.h"		// public declarations

#if UCAN_USE_MSCAN		// compile this file only if msCAN is to be used

#include "ucan_ms.h"	// internal driver definitions

// global array of message buffers
UCAN_MSGBUFFER ucan_msgBuff[UCAN_MSGBUFF_COUNT];
// pointers to callback methods
static UCAN_MBCALLBACK_FUNC ucan_pMBCallback[UCAN_MSGBUFF_COUNT];

// array of acceptance masks
UWord32 ucan_acceptMask[1+UCAN_MSGBUFFEX_COUNT];

// mask of listening rx buffers
UWord32 ucan_rxlistenMask = 0;

// mask of prepared tx buffers
UWord32 ucan_txreadyMask = 0;

// local functions
void UCAN_WakeUpISR(void);
void UCAN_ErrorISR(void);
void UCAN_RxISR(void);
void UCAN_TxISR(void);

/****************************************************************************
*
* UCAN Driver Configuration Functions
*
*****************************************************************************/

// Initialization

UCANRESULT UCAN_Init(void)
{
	// if sleep request is pending, wait for sleep first
	if(ioctl(MSCAN, MSCAN_GET_SLEEP_MODE, NULL) == MSCAN_SLEEP_REQUESTED)
		ioctl(MSCAN, MSCAN_WAIT_SLEEP, NULL);
		
	// initalize low-level driver - use appconfig.h 
	// values to setup msCAN interface
	ioctl(MSCAN, MSCAN_INIT, NULL);
	
	// enable RX interrupt
	ioctl(MSCAN, MSCAN_ERINT_ENABLE, MSCAN_RXFULL);
	
	return UCAN_OK;
}

// test bus-on/bus-off condition

UWord16 UCAN_IsSynchronized(void)
{
	return ioctl(MSCAN, MSCAN_TEST_SYNCH, NULL);
}

// request sleep state

UCANRESULT UCAN_Sleep()
{
	// request sleep state
	ioctl(MSCAN, MSCAN_SLEEP, MSCAN_ON);
	return UCAN_OK;
}

// wake up

UCANRESULT UCAN_Wakeup()
{
	// if sleep request is pending, wait for sleep first
	if(ioctl(MSCAN, MSCAN_GET_SLEEP_MODE, NULL) == MSCAN_SLEEP_REQUESTED)
		ioctl(MSCAN, MSCAN_WAIT_SLEEP, NULL);
		
	// awake device
	ioctl(MSCAN, MSCAN_SLEEP, MSCAN_OFF);
	return UCAN_OK;
}

/****************************************************************************
*
* MB configuration
*
*****************************************************************************/

// prepare & initialize buffer

UCANRESULT UCAN_ConfigMB(UWord16 bNum, UCANBMODE bMode, UWord32 id)
{
	register UCAN_MSGBUFFER* pmb;
	
	UCAN_ASSERT(bNum < UCAN_MSGBUFF_COUNT, UCAN_ERR_MB);
	UCAN_ASSERT(bMode < UCAN_BM_COUNT, UCAN_ERR_MODE);

	// address of our message buffer
	pmb = &ucan_msgBuff[bNum];

	// activate receiveing ?
	if(bMode == UCAN_BM_RXDF)
	{
		UCAN_MB_SET_STATE(pmb, UCANB_RXEMPTY);
		ucan_rxlistenMask |= 1 << bNum;
	}
	else // TXDF or CLOSED
	{
		UCAN_MB_SET_STATE(pmb, UCANB_VOID);
	}

	// should we configure buffer ID ?
	if(id)
	{
		pmb->idr = MSCAN_Id2Idr_V(id);
	}

	// OK
	return UCAN_OK;
}

// set receive mask for group of MB with common mask

UCANRESULT UCAN_SetRxMaskGlobal(UWord32 mask)
{
	UWord32 idr;

	// convert to IDR
	idr = MSCAN_Id2Idr_V(mask);
	// RTR bits are don't care
	idr &= ~(((UWord32)MSCAN_MB_IDR1_SRTR << 16) | MSCAN_MB_IDR3_ERTR);
	// IDE bit always matters
	idr |= ((UWord32)MSCAN_MB_IDR1_IDE) << 16;
	ucan_acceptMask[0] = idr;
	
	return UCAN_OK;
}


// set receive mask for a buffer

UCANRESULT UCAN_SetRxMaskMB(UWord16 bNum, UWord32 mask)
{
	UWord32 idr;
	UCAN_ASSERT(bNum < UCAN_MSGBUFF_COUNT, UCAN_ERR_MB);
	UCAN_ASSERT(bNum >= (UCAN_MSGBUFF_COUNT-UCAN_MSGBUFFEX_COUNT), UCAN_ERR_MB);

	// convert to IDR
	idr = MSCAN_Id2Idr_V(mask);
	// RTR bits are don't care
	idr &= ~(((UWord32)MSCAN_MB_IDR1_SRTR << 16) | MSCAN_MB_IDR3_ERTR);
	// IDE bit always matters
	idr |= ((UWord32)MSCAN_MB_IDR1_IDE) << 16;
	ucan_acceptMask[bNum - (UCAN_MSGBUFF_COUNT - UCAN_MSGBUFFEX_COUNT) + 1] = idr;
	
	return UCAN_OK;
}


/****************************************************************************
*
* MB Operations
*
*****************************************************************************/

// setup the callback function which is to be called when MB state changes

#pragma interrupt called
UCANRESULT UCAN_SetCallbackMB(UWord16 bNum, UCAN_MBCALLBACK_FUNC pCallbackFunc)
{
	UCAN_ASSERT(bNum < UCAN_MSGBUFF_COUNT, UCAN_ERR_MB);
	
	// address of our message buffer
	ucan_pMBCallback[bNum] = pCallbackFunc;
	
	return UCAN_OK;
}


// get status of the message buffer

#pragma interrupt called
UCANBSTATUS UCAN_CheckStatusMB(UWord16 bNum)
{
	register UCAN_MSGBUFFER* pmb;
	register UCANBSTATUS code;
	
	UCAN_ASSERT(bNum < UCAN_MSGBUFF_COUNT, UCAN_BS_BUSY);

	// address of our message buffer
	pmb = &ucan_msgBuff[bNum];
	
	// disable RX interrupt
	ioctl(MSCAN, MSCAN_ERINT_DISABLE, MSCAN_RXFULL);
	asm { nop; nop; }

	// get MB status code	
	code = (UCANBSTATUS) UCAN_MB_GET_STATE(pmb);

	// void ?
	if(code == UCANB_VOID)
	{
		code = UCAN_BS_VOID;
	}
	// RX data ready ?
	else if(code == UCANB_RXFULL)
	{
		if(pmb->csw & UCANB_RXOLD_FLAG)
		{
			code = UCAN_BS_RXFULL_OLD;
		}
		else
		{
			code = UCAN_BS_RXFULL;
			pmb->csw |= UCANB_RXOLD_FLAG;
		}
	}
	// RX data overrun ?
	else if(code == UCANB_RXOVERRUN)
	{
		if(pmb->csw & UCANB_RXOLD_FLAG)
		{
			code = UCAN_BS_RXOVR_OLD;
		}
		else
		{
			code = UCAN_BS_RXOVR;
			pmb->csw |= UCANB_RXOLD_FLAG;
		}
	}

	// enable RX interrupt
	ioctl(MSCAN, MSCAN_ERINT_ENABLE, MSCAN_RXFULL);

	// return MB status
	return code;
}

// abort any operation activated for given buffer

#pragma interrupt called
UCANRESULT UCAN_AbortMB(UWord16 bNum)
{
	register UCAN_MSGBUFFER* pmb;
	UCAN_ASSERT(bNum < UCAN_MSGBUFF_COUNT, UCAN_ERR_MB);

	// address of our message buffer
	pmb = &ucan_msgBuff[bNum];

	// remove from interrupt queue
	ucan_rxlistenMask &= ~(1 << bNum);
	ucan_txreadyMask &= ~(1 << bNum);
	
	// mark buffer void
	UCAN_MB_SET_STATE(pmb, UCANB_VOID);

	return UCAN_OK;
}

// enqueue buffer for transmission

#pragma interrupt called
UCANRESULT UCAN_TransmitMB(UWord16 bNum, UCANTXMODE txMode)
{
	register UCAN_MSGBUFFER* pmb;

	UCAN_ASSERT(bNum < UCAN_MSGBUFF_COUNT, UCAN_ERR_MB);
	UCAN_ASSERT(txMode < UCAN_TXMODE_COUNT, UCAN_ERR_MODE);

	// address of our message buffer
	pmb = &ucan_msgBuff[bNum];

	// transmit postponed until the matching remote frame request received ?
	if(txMode == UCAN_TX_AUTORESP)
	{
		UCAN_MB_SET_STATE(pmb, UCANB_TXRALWAYS);
		
		// listen on this MB
		ucan_rxlistenMask |= 1 << bNum;
	}
	// transmit NOW !
	else
	{
		// set RTR bit ?
		if(txMode == UCAN_TX_RTR)
		{
			// send RTR
			pmb->idr |= (((UWord32)MSCAN_MB_IDR1_SRTR) << 16) | MSCAN_MB_IDR3_ERTR;
			// but don't callback after RTR is transmitted
			pmb->csw |= UCANB_NOTXCB_FLAG;
		}
		else if(txMode == UCAN_TX_RTR_TXCB)
		{
			// send RTR and do callback after RTR is transmitted
			pmb->idr |= (((UWord32)MSCAN_MB_IDR1_SRTR) << 16) | MSCAN_MB_IDR3_ERTR;
		}

		UCAN_MB_SET_STATE(pmb, UCANB_TXONCE);
		
		// enqueue for transmission
		ucan_txreadyMask |= 1 << bNum;
		
		// (re-)enable all TXE interrupts
		ioctl(MSCAN, MSCAN_TINT_ENABLE, MSCAN_TXEMPTY_ALL);
	}
	
	return UCAN_OK;
}

// enqueue buffer for transmission (once)

#pragma interrupt called
UCANRESULT UCAN_TransmitOnceMB(UWord16 bNum)
{
	register UCAN_MSGBUFFER* pmb;

	UCAN_ASSERT(bNum < UCAN_MSGBUFF_COUNT, UCAN_ERR_MB);
	// address of our message buffer
	pmb = &ucan_msgBuff[bNum];
	
	UCAN_MB_SET_STATE(pmb, UCANB_TXONCE);
	
	// enqueue for transmission
	ucan_txreadyMask |= 1 << bNum;
	
	// (re-)enable all TXE interrupts
	ioctl(MSCAN, MSCAN_TINT_ENABLE, MSCAN_TXEMPTY_ALL);
	
	return UCAN_OK;
}

// read (copy-out) MB data and return length

#pragma interrupt called
UWord16 UCAN_ReadMB(UWord16 bNum, UWord16* pDestBuff, UCANDMODE copyMode)
{
	register UCAN_MSGBUFFER* pmb;
	register UWord16 len;

	UCAN_ASSERT(bNum < UCAN_MSGBUFF_COUNT, 0);

	// address of our message buffer
	pmb = &ucan_msgBuff[bNum];

	// disable RX interrupt
	ioctl(MSCAN, MSCAN_ERINT_DISABLE, MSCAN_RXFULL);

	if(pDestBuff)
	{
		if(copyMode == UCAN_DM_COPY)
		{
			UCAN_Copy(pmb->data, pDestBuff);
		}
		else if(copyMode == UCAN_DM_SWAB16)
		{
			UCAN_CopySwab(pmb->data, pDestBuff);
		}
		else if(copyMode == UCAN_DM_HALF8)
		{
			UCAN_CopySplit(pmb->data, pDestBuff);
		}
	}

	len = UCAN_MB_GET_LENGTH(pmb);
	
	// enable interrupt again
	ioctl(MSCAN, MSCAN_ERINT_ENABLE, MSCAN_RXFULL);
	
	// return length
	return len;
}

#pragma interrupt called
UCANRESULT UCAN_ReceiveMB(UWord16 bNum, UCANRXINFO* pDestInfo, UCANDMODE copyMode)
{
	register UCAN_MSGBUFFER* pmb;

	UCAN_ASSERT(bNum < UCAN_MSGBUFF_COUNT, UCAN_ERR_MB);

	// address of our message buffer
	pmb = &ucan_msgBuff[bNum];

	// disable RX interrupt
	ioctl(MSCAN, MSCAN_ERINT_DISABLE, MSCAN_RXFULL);

	// copy frame data
	if(pDestInfo->pDataBuff)
	{
		if(copyMode == UCAN_DM_COPY)
		{
			UCAN_Copy(pmb->data, pDestInfo->pDataBuff);
		}
		else if(copyMode == UCAN_DM_SWAB16)
		{
			UCAN_CopySwab(pmb->data, pDestInfo->pDataBuff);
		}
		else if(copyMode == UCAN_DM_HALF8)
		{
			UCAN_CopySplit(pmb->data, pDestInfo->pDataBuff);
		}
	}

	// get frame infoemation	
	pDestInfo->length = UCAN_MB_GET_LENGTH(pmb);
	pDestInfo->realID = MSCAN_Idr2Id_V(pmb->idr);
	pDestInfo->timeStamp = pmb->timeStamp;

	// enable interrupt again
	ioctl(MSCAN, MSCAN_ERINT_ENABLE, MSCAN_RXFULL);
	
	// no error
	return UCAN_OK;
}

// fill MB with data 

#pragma interrupt called
UCANRESULT UCAN_LoadMB(UWord16 bNum, UWord16* pSrcBuff, UCANDMODE copyMode, UWord16 len)
{
	register UCAN_MSGBUFFER* pmb;
	
	UCAN_ASSERT(bNum < UCAN_MSGBUFF_COUNT, UCAN_ERR_MB);
	UCAN_ASSERT(len <= 8, UCAN_ERR_LEN);

	// address of our message buffer
	pmb = &ucan_msgBuff[bNum];

	// copy data
	if(pSrcBuff)
	{
		if(copyMode == UCAN_DM_COPY)
		{
			UCAN_Copy(pSrcBuff, pmb->data);
		}
		else if(copyMode == UCAN_DM_SWAB16)
		{
			UCAN_CopySwab(pSrcBuff, pmb->data);
		}
		else if(copyMode == UCAN_DM_HALF8)
		{
			UCAN_CopyBuild(pSrcBuff, pmb->data);
		}
	}

	// set length
	UCAN_MB_SET_LENGTH(pmb, len);

	return UCAN_OK;
}


/****************************************************************************
*
* Helper routines for ISR bellow
*
*****************************************************************************/

// find index of first non-zero bit in given 32bit word

inline Word16 FindBit(UWord32 n)
{
	int b = 0;
	
	while(!(n&1))
	{
		if(++b >= 32)
			return -1;
			
		n >>= 1;
	}
	
	return b;
}

// 8-byte received data (DSR) to 4 words in MB

inline void MEM_BUILD(register volatile UWord16* pdsr, register UWord16* pmem)
{
	#if UCAN_DATA_LSBFIRST
	
	asm {
		move.w X:(pdsr+0),Y0
		move.b Y0,X:(pmem+0)
		move.w X:(pdsr+1),Y0
		move.b Y0,X:(pmem+1)
		move.w X:(pdsr+2),Y0
		move.b Y0,X:(pmem+2)
		move.w X:(pdsr+3),Y0
		move.b Y0,X:(pmem+3)
		move.w X:(pdsr+4),Y0
		move.b Y0,X:(pmem+4)
		move.w X:(pdsr+5),Y0
		move.b Y0,X:(pmem+5)
		move.w X:(pdsr+6),Y0
		move.b Y0,X:(pmem+6)
		move.w X:(pdsr+7),Y0
		move.b Y0,X:(pmem+7)
	}

	#else /* UCAN_DATA_LSBFIRST */

	asm {
		move.w X:(pdsr+0),Y0
		move.b Y0,X:(pmem+1)
		move.w X:(pdsr+1),Y0
		move.b Y0,X:(pmem+0)
		move.w X:(pdsr+2),Y0
		move.b Y0,X:(pmem+3)
		move.w X:(pdsr+3),Y0
		move.b Y0,X:(pmem+2)
		move.w X:(pdsr+4),Y0
		move.b Y0,X:(pmem+5)
		move.w X:(pdsr+5),Y0
		move.b Y0,X:(pmem+4)
		move.w X:(pdsr+6),Y0
		move.b Y0,X:(pmem+7)
		move.w X:(pdsr+7),Y0
		move.b Y0,X:(pmem+6)
	}
	
	#endif /* UCAN_DATA_LSBFIRST */
}


// 4-word data to 8 bytes (unpack words to bytes)

inline void MEM_SPLIT(register UWord16* pmem, register volatile UWord16* pdsr)
{
	#if UCAN_DATA_LSBFIRST
	
	asm {
		moveu.b X:(pmem+0),Y0
		move.w  Y0,X:(pdsr+0)
		moveu.b X:(pmem+1),Y0
		move.w  Y0,X:(pdsr+1)
		moveu.b X:(pmem+2),Y0
		move.w  Y0,X:(pdsr+2)
		moveu.b X:(pmem+3),Y0
		move.w  Y0,X:(pdsr+3)
		moveu.b X:(pmem+4),Y0
		move.w  Y0,X:(pdsr+4)
		moveu.b X:(pmem+5),Y0
		move.w  Y0,X:(pdsr+5)
		moveu.b X:(pmem+6),Y0
		move.w  Y0,X:(pdsr+6)
		moveu.b X:(pmem+7),Y0
		move.w  Y0,X:(pdsr+7)
	}
	
	#else /* UCAN_DATA_LSBFIRST */

	asm {
		moveu.b X:(pmem+1),Y0
		move.w  Y0,X:(pdsr+0)
		moveu.b X:(pmem+0),Y0
		move.w  Y0,X:(pdsr+1)
		moveu.b X:(pmem+3),Y0
		move.w  Y0,X:(pdsr+2)
		moveu.b X:(pmem+2),Y0
		move.w  Y0,X:(pdsr+3)
		moveu.b X:(pmem+5),Y0
		move.w  Y0,X:(pdsr+4)
		moveu.b X:(pmem+4),Y0
		move.w  Y0,X:(pdsr+5)
		moveu.b X:(pmem+7),Y0
		move.w  Y0,X:(pdsr+6)
		moveu.b X:(pmem+6),Y0
		move.w  Y0,X:(pdsr+7)
	}

	#endif /* UCAN_DATA_LSBFIRST */
}

/****************************************************************************
*
* Interrupt service routines
*
*****************************************************************************/

#pragma interrupt on

void UCAN_WakeUpISR(void)
{
	ioctl(MSCAN, MSCAN_CLEAR_WINT_FLAG, NULL);
}

void UCAN_ErrorISR(void)
{
	UWord16 eFlags = ioctl(MSCAN, MSCAN_READ_EINT_FLAGS, NULL);
		
	ioctl(MSCAN, MSCAN_CLEAR_EINT_FLAGS, eFlags);
}

#pragma interrupt saveall on
void UCAN_RxISR(void)
{
	register UWord16 bNum;
	register UCAN_MSGBUFFER* pmb;
	register UWord16 state;
	UWord32 rxIDR;
	UWord32 maskIDR;
	UWord32 listen = ucan_rxlistenMask;
	
	// retrieve IDR field of received frame (as one 32bit word)
	rxIDR = ioctl(MSCAN_RB, MSCANMB_GET_ID_RAW, NULL);
	
	// global acceptance mask for the first group of buffers
	maskIDR = ucan_acceptMask[0];

	// walk thru all listenning message buffers
	for(bNum = 0; listen; bNum++, listen >>= 1)
	{
		// is buffer 'bNum' listening ?
		if(listen & 1)
		{
			pmb = &ucan_msgBuff[bNum];
	
			// is this buffer with private acceptance mask ?
			if(bNum >= (UCAN_MSGBUFF_COUNT - UCAN_MSGBUFFEX_COUNT))
				maskIDR = ucan_acceptMask[bNum - (UCAN_MSGBUFF_COUNT - UCAN_MSGBUFFEX_COUNT) + 1];
			
			// test whether MB ID matches the received ID
			if(!((rxIDR ^ pmb->idr) & maskIDR))
			{
				// retrieve state
				state = UCAN_MB_GET_STATE(pmb);
				
				// is this ready to transmit (i.e. RTR response) ? 
				if(state & 0x8)
				{
					// must be RTR and id must exactly match REGARDLESS the MASK !!!
					if((rxIDR & 0x100001) && !((rxIDR ^ pmb->idr) & 0xffeffffe))
					{
						// just enqueue for transmission
						ucan_txreadyMask |= 1 << bNum;
						// (re-)enable all TXE interrupts
						ioctl(MSCAN, MSCAN_TINT_ENABLE, MSCAN_TXEMPTY_ALL);
					}
				}
				else
				{
					// retrieve real ID of received frame
					pmb->idr = rxIDR;

					// retrieve length & data				
					UCAN_MB_SET_LENGTH(pmb, ioctl(MSCAN_RB, MSCANMB_GET_LEN, NULL));
					MEM_BUILD(ioctl(MSCAN_RB, MSCANMB_GET_DATAPTR, NULL), pmb->data);
					
					// time stamp
					pmb->timeStamp = ioctl(MSCAN_RB, MSCANMB_GET_TIMESTAMP, NULL);
					
					// buffer already full ? => mark buffer overrun
					if(state & 0x2)
					{
						// avoid PCTL_018b warning (compiler problem ?)
						asm { nop; nop; } 
						
						UCAN_MB_SET_STATE(pmb, UCANB_RXOVERRUN);
					}
					// buffer empty ! => mark buffer full
					else
						UCAN_MB_SET_STATE(pmb, UCANB_RXFULL);
					
					// in any case, a new data arrived	
					pmb->csw &= ~UCANB_RXOLD_FLAG;
					
					// call back ?
					if(ucan_pMBCallback[bNum])
						ucan_pMBCallback[bNum](bNum);
				}
			}
		}
	}

	// irq ack
	ioctl(MSCAN, MSCAN_CLEAR_RINT_FLAG, NULL);
}

void UCAN_TxISR(void)
{
	static Word16 txbUser[3] = 
	{
		-1, -1, -1,
	};
	
	register UWord16 txbNum;
	register UCAN_MSGBUFFER* pmb;
	register UWord16 bflag;
	Word16 bNum;

	// remember all hardware buffers available for transmission
	UWord16 tEmpty = ioctl(MSCAN, MSCAN_READ_TINT_FLAGS, NULL);

	// walk thru all hardware buffers
	for(txbNum = 0; txbNum < 3; txbNum ++)
	{
	    bflag = 1 << txbNum;
	    
		// is the buffer empty now ?
		if(ioctl(MSCAN, MSCAN_SELECT_TXBUFF, bflag))
		{
			// does it already have any MB assigned ? (=> MB tx have just finished)
			if((bNum = txbUser[txbNum]) >= 0)
			{
				pmb = &ucan_msgBuff[bNum];

				// time stamp
				pmb->timeStamp = ioctl(MSCAN_TB, MSCANMB_GET_TIMESTAMP, NULL);
				
				// call the user
				if(ucan_pMBCallback[bNum])
					ucan_pMBCallback[bNum](bNum);

				// this hw-buffer has no user any more
				txbUser[txbNum] = -1;
			}
			
			// do we have a candidate to be filled into this hardware buffer ?
			if(ucan_txreadyMask)
			{
				// find first enqueued buffer
				bNum = FindBit(ucan_txreadyMask);
				pmb = &ucan_msgBuff[bNum];

				// copy MB to hw buffer
				MEM_SPLIT(pmb->data, ioctl(MSCAN_TB, MSCANMB_GET_DATAPTR, NULL));
				ioctl(MSCAN_TB, MSCANMB_SET_ID_RAW, pmb->idr);
				ioctl(MSCAN_TB, MSCANMB_SET_LEN, UCAN_MB_GET_LENGTH(pmb));
				ioctl(MSCAN_TB, MSCANMB_SET_TBP, 0);
				
				// initiate the transmission
				ioctl(MSCAN, MSCAN_TRANSMIT, bflag);

				// this buffer is no more empty
				tEmpty &= ~bflag;
				// this hw-buffer has just been used
				txbUser[txbNum] = bNum;
				// the MB has just been sent
				ucan_txreadyMask &= ~(1 << bNum);
				
				// was it RTR ? (auto-change the MB status code) ? 
				if(pmb->idr & 0x100001)
				{
					// activate RX listening on this MB
					UCAN_MB_SET_STATE(pmb, UCANB_RXEMPTY);
					ucan_rxlistenMask |= 1 << bNum;
					
					if(pmb->csw & UCANB_NOTXCB_FLAG)
					{
						// cancel the callback callin after this transmission
						txbUser[txbNum] = -1;
						pmb->csw &= ~UCANB_NOTXCB_FLAG;
					}
				}
				// was it UCANB_TXRALWAYS
				else if(UCAN_MB_GET_STATE(pmb) & 2)
				{
					// re-activate RX listening on this MB
					// (not needed this bit remians set)
					// ucan_rxlistenMask |= 1 << bNum;
					
					// cancel the callback callin after this transmission
					txbUser[txbNum] = -1;
				}
				// it was just tx-once
				else
				{
					UCAN_MB_SET_STATE(pmb, UCANB_VOID);
				}
			}
		}
	}
	
	// disable those interrupts for which we have not found any MB to be transmited
	ioctl(MSCAN, MSCAN_TINT_DISABLE, tEmpty);
}

#pragma interrupt off


/****************************************************************************
*
* Helper copy routines optimized using assembly code
*
*****************************************************************************/

void UCAN_Copy(register UWord16* psrcBuff, register UWord16* pdestBuff)
{
	pdestBuff[0] = psrcBuff[0];
	pdestBuff[1] = psrcBuff[1];
	pdestBuff[2] = psrcBuff[2];
	pdestBuff[3] = psrcBuff[3];
}

// unpair 4 words into 8 separate bytes each stored in its own word
// note that we can't use move.l as the buffers may be unaligned

void UCAN_CopySplit(register UWord16* psrcBuff, register UWord16* pdestBuff)
{
	MEM_SPLIT(psrcBuff, pdestBuff);
}

// pair 8 separate bytes into 4 words

void UCAN_CopyBuild(register UWord16* psrcBuff, register UWord16* pdestBuff)
{
    MEM_BUILD(psrcBuff, pdestBuff);
}

// copy 4 words from srcBuff to destBuff while swapping bytes

asm void UCAN_CopySwab(register UWord16* psrcBuff, register UWord16* pdestBuff)
{
	adda        #2,SP
	move.l      Y,X:(SP)

	move.w X:(psrcBuff),Y
	lsrr.l #8,Y
	or.w Y0,Y1
	move.w Y1,X:(pdestBuff)

	move.w X:(psrcBuff+1),Y
	lsrr.l #8,Y
	or.w Y0,Y1
	move.w Y1,X:(pdestBuff+1)

	move.w X:(psrcBuff+2),Y
	lsrr.l #8,Y
	or.w Y0,Y1
	move.w Y1,X:(pdestBuff+2)

	move.w X:(psrcBuff+3),Y
	lsrr.l #8,Y
	or.w Y0,Y1
	move.w Y1,X:(pdestBuff+3)

	// pop Y		
	move.l      X:(SP)-,Y
	rts
}

#endif /* UCAN_USE_MSCAN */

