/*
 *  Copyright (c) 1996-2008 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
 *
 */

/*
 * Common functions providing analogous capabilities to the tcpip stack.
 * These are called from both drivers, rather than being specific to 
 * either one. Neutrality is generally maintained by the caller
 * providing his own callback functions for interface-specific needs.
 */

#include "Npttypes.h"
#include "nastatus.h"
#include "tx_api.h"
#include "boardParams.h"
#include "naifconfig.h"
#include "trsocket.h"
#include "ewb_api.h"
#include "ewbtype.h"
#include "ewbmacro.h"
#include "ewbproto.h"

extern btContext ewbContext;
int ewbContextInited = 0;

/*
 * Build a buffer pool of buffers using the treck allocation logic.
 */

// ttUserPacketPtr freeBufList = NULL;
int	freeBufSize = EWB_ETH_BUFSZ;	/* may not be optimal yet */
int	freeBufCount = 0;
/* the following exceeds TM_ETHER_HW_ALIGN (4) used by tfGetEthernetBuffer */
int	freeBufAlign = 32;	/* ETH_ALIGN_DMA_BUFFER. could be 4 for 7520 proc */
btLockEntry freeBufferListLock;

/*
 * Get a buffer to hold a frame from the interface.
 * This buffer will be in common format, conforming to an ethernet MAC frame.
 * Analogous to tfGetSharedBuffer and tfGetEthernetBuf.
 */

ttUserBufferPtr bfGetSharedBuffer(btPktLen dataSize)
{
	ttUserBuffer bufferPtr;
	char *pBuf;
	int align = freeBufAlign;

	if ((int) dataSize > freeBufSize)
		return NULL;

	// bf_call_lock_wait(&freeBufferListLock);

		/* this is the api function which calls tfGetSharedBuffer.
		 * User data is filled in by treck. */
		if ((pBuf = tfGetDriverBuffer(&bufferPtr, dataSize, align)) == NULL)
		{
			bufferPtr = NULL;
		}
		else
			ewb_buffergets++;

	// bf_call_unlock(&freeBufferListLock);
	return (bufferPtr);
}

void bfFreeSharedBuffer(btUserPacketPtr packetPtr, tt8Bit lockFlag)
{
	NA_UNUSED_PARAM(lockFlag);

	if (packetPtr == NULL)
		return;

	tfFreeDriverBuffer((ttUserBuffer) packetPtr);
	XPRINTF(TRACE_RECV, "bfFreeSharedBuffer buf %x\n\r", (int) packetPtr);
	ewb_bufferfrees++;
}

/*
 * Called from the wireless driver to get a shared buffer that will
 * hold an ethernet MAC frame.
 * userBufferPtr is an opaque handle. The caller uses the data pointer.
 *
 * We use parameters for the common allocation that exceed those used
 * by tfGetEthernetBuffer, in order to have a common set of buffers used
 * by both the ethernet and wifi drivers.
 */

char TM_FAR *bfGetEthernetBuffer(btUserBufferPtr userBufferPtr)
{
	ttUserBufferPtr pBuf;
	btUserPacketPtr pPkt;
	char TM_FAR * dataPtr = NULL;

	// ewb_getetherbufs++;

	/* we should get length from the link layer data structure */
	pBuf = bfGetSharedBuffer( (btPktLen) freeBufSize);

	if (pBuf != NULL)
	{
		pPkt = (btUserPacketPtr) pBuf;
		/* Treck requires packet body be quad aligned */
		pPkt->pktuLinkDataPtr += EWB_ETHER_IP_ALIGN;
		/* data pointer is filled in by tfGetSharedBuffer */
		dataPtr = (char TM_FAR *) pPkt->pktuLinkDataPtr;
	}
	*userBufferPtr = (btUserBufferPtr) pBuf;

	return (dataPtr);
}

/*
 * Called from the ethernet driver.
 * This is more or less identical to bfGetEthernetBuffer, with perhaps
 * some different alignment concerns.
 *
 * Ethernet driver would use:
 * ETH_ADJUST_FOR_CACHE(ETH_RX_MAX_LEN[channel]), ETH_ALIGN_DMA_BUFFER
 * which is:
 * ETH_RX_MAX_PACKET_LEN = 1538, ETH_ALIGN_DMA_BUFFER = 32
 * Those are the values we assign to freeBufSize and freeBufAlign.
 *
 * userBufferPtr is an opaque handle. The caller uses the data pointer.
 */

char TM_FAR *bfGetDriverBuffer(btUserBufferPtr userBufferPtr, int length, int alignment)
{
	ttUserBufferPtr pBuf;
	ttUserPacketPtr pPkt;
	char TM_FAR * dataPtr = NULL;

	NA_UNUSED_PARAM(alignment);

	// ewb_getdriverbufs++;

	if (length < freeBufSize)
		length = freeBufSize;

	/* we should get length from the link layer data structure */
	pBuf = bfGetSharedBuffer( (btPktLen) length);

	if (pBuf != NULL)
	{
		pPkt = (ttUserPacketPtr) pBuf;
		/* data pointer is filled in by tfGetSharedBuffer */
		dataPtr = (char TM_FAR *) pPkt->pktuLinkDataPtr;
	}
	*userBufferPtr = (btUserBufferPtr) pBuf;

	return (dataPtr);
}

int ewbGetIfPhyAddr(btUserInterface ifHandle, char *phyAddr)
{
	int errorCode = NASTATUS_SUCCESS;
	btDeviceEntryPtr devEntryPtr;

	devEntryPtr = (btDeviceEntryPtr) ifHandle;

	if (devEntryPtr->devGetPhysAddrFuncPtr != NULL)
	{
		errorCode = (*(devEntryPtr->devGetPhysAddrFuncPtr))(ifHandle, phyAddr);
	}
	else
		errorCode = NASTATUS_EWB_NOT_INITIALIZED;

	return errorCode;
}

/*
 * analog of tfOpenInterface (which ends up as tf4ConfigInterface). 
 * ipv6 version is tfNgConfigInterface (tf6ConfigInterface).
 * The bridge calls this from its open function for each of the
 * network interface drivers.
 */

int ewbOpenInterface ( btUserInterface ifHandle, bt32Bit ipAddress,
	bt32Bit netMask, bt32Bit flags, bt32Bit buffersPerFrameCount)
{
	int errorCode = NASTATUS_SUCCESS;
	btDeviceEntryPtr devEntryPtr;

	/* TODO this function is currently of minimal use */
	NA_UNUSED_PARAM(ipAddress);
	NA_UNUSED_PARAM(netMask);
	NA_UNUSED_PARAM(flags);
	NA_UNUSED_PARAM(buffersPerFrameCount);

	devEntryPtr = (btDeviceEntryPtr) ifHandle;

	/* Make sure there is a send function */
	if ((devEntryPtr->devSendFuncPtr == (ttDevSendFuncPtr) 0)
		&& (devEntryPtr->devOneScatSendFuncPtr == (btDevOneScatSendFuncPtr) 0))
	{
		errorCode = NASTATUS_EWB_NOT_INITIALIZED;
		goto configInterfaceFinish;
	}
#ifdef TM_USE_DRV_SCAT_RECV
	/* Make sure there is a receive function */
	if ((devEntryPtr->devRecvFuncPtr == (ttDevRecvFuncPtr) 0)
			&& (devEntryPtr->devScatRecvFuncPtr == (btDevScatRecvFuncPtr) 0))
	{
		errorCode = NASTATUS_EWB_NOT_INITIALIZED;
		goto configInterfaceFinish;
	}
#endif
	/* open the device */
	if (devEntryPtr->devOpenFuncPtr != (ttDevOpenCloseFuncPtr) 0)
	{
		/*
		 * Open the Device itself, if not already opened.
		 * This will initialize driver, install ISR, start the
		 * duty threads, etc.
		 * Functions are naEthOpen (...) or wlnOpen (start the
		 * wln_task thread).
		 */
		errorCode = (*(devEntryPtr->devOpenFuncPtr))(ifHandle);
	}
	if (errorCode != NASTATUS_SUCCESS)
	{
		goto configInterfaceFinish;
	}

	/* our lnkOpenFuncPtr ewbLinkOpen does not do much of anything */
	errorCode = (*(devEntryPtr->devLinkLayerProtocolPtr->lnkOpenFuncPtr))
			( ((void TM_FAR *) ifHandle), PF_INET );
	if (errorCode == NASTATUS_SUCCESS)
	{
		devEntryPtr->openFlag = 1;
	}
	else
	{
		goto configInterfaceFinish;
	}
#if	0
	/* there is nothing for us here. It would set up local and
	 * static routes, deal with multi-home, send an arp 
	 * (is the timing of the arp going to be a problem?), etc. */
	errorCode = tfDeviceStart(ifHandle);
	if (errorCode != NASTATUS_SUCCESS)
	{
		goto configInterfaceFinish;
	}
#endif
configInterfaceFinish:
	return(errorCode);
}

/* 
 * Used to initialize the link layer in the link layer list.
 * We allow at most one entry of type ETHDIX.
 * The return type btUserLinkLayer is an opaque handle for the caller.
 * This link layer entry is internal to the bridge.
 */
 
btUserLinkLayer bfUseEthernet(void)
{
	btLinkLayerEntryPtr linkLayerEntryPtr;

	/* Search for Ethernet Protocol in Link Layer List */
#if	DONE_IN_EWB_INSTALL
	if (ewbContextInited == 0)
	{
		memset(&ewbContext, 0, sizeof(ewbContext));
		// ewbContextInitVars(&ewbContext);
		ewbContextInited = 1;
	}
#endif
	/* Lock the Link Layer list */
	bf_call_lock_wait(&bm_context(bvLinkLayerListLock));

	linkLayerEntryPtr = bm_context(bvLinkLayerListPtr);

	/* Check for duplicate */
	while (linkLayerEntryPtr != NULL)
	{
		if (strcmp((char *) linkLayerEntryPtr->lnkNameArray, "ETHDIX") == 0)
		{
			goto useEthernetUnlock; /* entry already in */
		}
		linkLayerEntryPtr = linkLayerEntryPtr->lnkNextPtr;
	}
	/* if none exists then malloc an entry */
	linkLayerEntryPtr = (btLinkLayerEntryPtr) EWB_MALLOC(sizeof(btLinkLayerEntry));

	if (linkLayerEntryPtr == NULL)
	{
		/* if the malloc fails then return a null pointer */
		goto useEthernetUnlock;
	}

	/*
	 * The default MTU for an Ethernet interface is 1500 for
	 * both IPv4 and IPv6.  [RFC2464].R2:10
	 * The maximum on the WIFI interface can be larger, but we enforce the
	 * ethernet maximum.
	 */ 
	linkLayerEntryPtr->lnkMtu = 1500;
   	/* tfDeviceSend, Arp */
	linkLayerEntryPtr->lnkSendFuncPtr = ewbEtherSend;
	/* tfRecvInterface, tfIntfDrvSend, ... */
	linkLayerEntryPtr->lnkRecvFuncPtr = ewbEtherRecv;
	/* tfDeviceSend, if not devOneScatSendFuncPtr */
	linkLayerEntryPtr->lnkPostFuncPtr = ewbDeviceSendOnePacket;
	linkLayerEntryPtr->lnkOpenFuncPtr = ewbLinkOpen; /* from naEwbOpen */
#if	UNUSED	
	linkLayerEntryPtr->lnkCloseFuncPtr = tfEtherCommonClose; /* tfDeviceClose */
	linkLayerEntryPtr->lnkIoctlFuncPtr = ewbEtherIoctl; /* unused */
	linkLayerEntryPtr->lnkErrorFuncPtr = ewbEtherError; /* unused */
	linkLayerEntryPtr->lnkMcastFuncPtr = tm_ether_mcast_func; /* igmp */
	linkLayerEntryPtr->lnkNotifyFuncPtr = TM_LL_NOTIFY_FUNC_NULL_PTR; /* ppp */
#endif
	linkLayerEntryPtr->lnkHeaderLength = BM_ETHER_HEADER_SIZE;
	linkLayerEntryPtr->lnkLinkLayerProtocol = TM_LINK_LAYER_ETHERNET;
	linkLayerEntryPtr->lnkPhysAddrLen = TM_ETHER_ADDRESS_LENGTH;
	memcpy(linkLayerEntryPtr->lnkPhysBroadAddr,
			bm_context(bvEtherBroadcast).enuAddress, ETH_MAC_ADDR_SIZE);
	// tm_ether_copy( tm_global(bvEtherBroadcast).enuAddress,
				   // linkLayerEntryPtr->lnkPhysBroadAddr );
	strcpy((char TM_FAR *)(linkLayerEntryPtr->lnkNameArray),"ETHDIX");

	/* insert at head of list */
	linkLayerEntryPtr->lnkNextPtr = bm_context(bvLinkLayerListPtr);
	bm_context(bvLinkLayerListPtr) = linkLayerEntryPtr;
useEthernetUnlock:
	bf_call_unlock(&bm_context(bvLinkLayerListLock));

	/* return the link layer pointer */
	return((btUserLinkLayer) linkLayerEntryPtr);    
}

/*
 * The packet in memory is an ethernet MAC frame (transformed from WIFI
 * format by the wireless driver, or unchanged by the ethernet driver).
 *
 * This function is called by the ethernet and wifi drivers in
 * order to pass data frames up the stack. It calls the registered
 * receive function for the respective driver to retrieve a receive packet,
 * after which the bridge will determine what next to do with it -- forward
 * to other interface, send up the stack, or drop it.
 *
 * Receive a packet from the Interface (ala tfRecvInterface)
 */

unsigned int recvInterfaceCalls = 0;

int bfRecvInterface(btUserInterface ifHandle)
{
	int retCode;
	btDeviceEntryPtr devEntryPtr;
	btUserPacketPtr	packetPtr;
	char * dataPtr;
	int TM_FAR dataSize;

	recvInterfaceCalls++;

	/* recast opaque user handle to DeviceEntryPtr  */
	devEntryPtr = (btDeviceEntryPtr) ifHandle;
	devEntryPtr->devRecvEventCount++;

	if (devEntryPtr->openFlag == 0)
	{
		retCode = NASTATUS_EWB_NOT_OPEN;
		goto rcvInterfaceExit;
	}
	/* callback set to wlnReceive or naEthReceive, which fills in the
	 * values for the packet */
	retCode = (*(devEntryPtr->devRecvFuncPtr)) (ifHandle, &dataPtr,
		&dataSize, (btUserBufferPtr) &packetPtr);
	if (retCode != NASTATUS_SUCCESS)
	{
		XPRINTF(TRACE_RECV, "RecvFunc %x FAIL if %x ret %x\n\r",
					   	devEntryPtr->devRecvFuncPtr, (int) ifHandle, retCode);
		goto rcvInterfaceExit;
	}
	/* store these values in the device entry struct in case we end up getting
	 * called back from the treck stack */
	devEntryPtr->bufHandle = (btUserBufferPtr) packetPtr;
	devEntryPtr->dataPtr = dataPtr;
	devEntryPtr->dataLength = dataSize;

	devEntryPtr->devRxPkts++;
	devEntryPtr->devRxBytes += dataSize;

	/* user packet info needed by ewbEtherRecv */
	packetPtr->pktuLinkDataPtr = dataPtr;
	packetPtr->pktuLinkDataLength = (btPktLen) dataSize;
	packetPtr->pktuChainDataLength = (btPktLen) dataSize;
	packetPtr->pktuLinkExtraCount = 0;

	/* pass to the bridging logic to forward to other interface
	 * or to the tcpip stack. This can be the lnkRecvFuncPtr callback. */
	/* bfUseEthernet set lnkRecvFuncPtr = ewbEtherRecv */
	retCode = (*(devEntryPtr->devLinkLayerProtocolPtr->lnkRecvFuncPtr))(
		   (void TM_FAR *) ifHandle, (void TM_FAR *) packetPtr );

rcvInterfaceExit:
	return (retCode);
}


/*
 * Walk the packet link and free all the packet/shared buffers
 */

void bfFreePacket(btUserPacketPtr packetPtr, bt8Bit lockFlag)
{
	btUserPacketPtr nextPacketPtr;

	while (packetPtr != NULL)
	{
		nextPacketPtr = packetPtr->pktuLinkNextPtr;
		bfFreeSharedBuffer(packetPtr, lockFlag);
		packetPtr = nextPacketPtr;
	}
}

/*
 * This function is called by the bridge in order to send data packets to
 * either driver. The packet may have been forwarded from another driver
 * interface or from the treck stack.
 * Its mechanics are set up by callback functions in the
 * drivers' initialization code (naEwbConfigCreateInterface) through the 
 * devxxxSendFuncPtr entries.
 *
 * ScatSend and Saved... confuse the air, though.
 * It does for the bridge what tfDeviceSend does for the stack.
 * Note that the lnkSendFuncPtr is not needed here as we are bridging 
 * between ethernet-type networks.
 * Parameters:
 * pPkt:		pointer to packet to send. May be from another driver or
 * 				from the treck stack.
 * ifHandle:	device handle for outgoing driver interface. Value assigned
 * 				during the naIfConfigCreateInterface sequence.
 */

int bfDeviceSend(btUserPacketPtr pPkt, void *ifHandle)
{
	int rc = NASTATUS_SUCCESS;
	btDeviceEntryPtr devEntryPtr;
	btLinkLayerEntryPtr devLinkLayerProtocolPtr;

	/* The packet device entry field is updated as a result of the
	 * lnkRecvFuncPtr (ewbEtherRecv) set it to the device it came in on,
	 * but we need to have the outgoing device at this point.  */
	devEntryPtr = (btDeviceEntryPtr) ifHandle;
#if	0
	DPRINTF("bfDeviceSend pPkt %x hndl %x data %x\n\r",
					(int) pPkt, (int) ifHandle, (int) pPkt->pktuLinkDataPtr);
#endif

	devEntryPtr->devTxPkts++;
	devEntryPtr->devTxBytes += pPkt->pktuLinkDataLength;

	devLinkLayerProtocolPtr = devEntryPtr->devLinkLayerProtocolPtr;

	/* MAC header is already formed, either by tfEtherSend if we came
	 * though the tfDeviceSend (treck) path, or by the driver
	 * receiver otherwise. */
	/* bfUseEthernet set lnkSendFuncPtr = ewbEtherSend */
	if ((rc = (*(devLinkLayerProtocolPtr->lnkSendFuncPtr))( pPkt))
					!= NASTATUS_SUCCESS)
	{
	}

	/* queue the packets that we send at the end of the devSendQueue
	 * until the transmitter tells us the send has completed. */
#if	1
	tfUserQueueAdd(&devEntryPtr->devSendQueue, (ttPacketPtr) pPkt);
#else
	/* lock queue */
	bf_call_lock_wait(&devEntryPtr->devLockSendQ);

	if ( devEntryPtr->devSendQueueLastPtr == NULL )
	{
		/* No entries in the queue. this packet is first and last entry */
		devEntryPtr->devSendQueueLastPtr = pPkt;
		devEntryPtr->devSendQueuePtr = pPkt;
	}
	else
	{
		/* There are other packets in the queue so
		 * queue our packet to the end of the chain */
		devEntryPtr->devSendQueueLastPtr->pktuLinkNextPtr = pPkt;
		/* update last pointer  */
		devEntryPtr->devSendQueueLastPtr = pPkt;
	}
	/* unlock queue */
	bf_call_unlock(&devEntryPtr->devLockSendQ);
#endif
	/* naEthOneScatSend via bfUseInterfaceOneScatSend */
	if (devEntryPtr->devOneScatSendFuncPtr != (btDevOneScatSendFuncPtr) 0)
	{
		/* 
		 * Preempt lnkPostFuncPtr (ewbDeviceSendOnePacket), and
		 * call the device driver
		 * send function only once with the scattered send buffer.
		 * Used by ethernet driver, but not WIFI.
		 */
		rc = (*(devEntryPtr->devOneScatSendFuncPtr)) (ifHandle, pPkt);
	}
	else
	{
		/* set to ewbDeviceSendOnePacket in ewbInstall */
		/* TODO fix prototype, as we benefit from the extra parameter */
		rc = (*(devLinkLayerProtocolPtr->lnkPostFuncPtr))( ifHandle, pPkt);
	}
	/* 
	   If the driver was out of memory (buffers, or DMA space), bfSendCompleteInterface 
	   is not called and the packet is going to sit in devSendQueue forever, Treck does not
	   free this packet. So we need to find this packet in the devSendQueue, remove it from
	   the queue, and free it.
	*/
	if (rc)
	{
		tfUserQueueRemovePacket(&devEntryPtr->devSendQueue, (ttPacketPtr)pPkt);
		bfFreePacket((btUserPacketPtr) pPkt, (bt8Bit) 0);
	}

	return rc;
}

/*
 * Callback registered at lnkOpenFuncPtr as a result of bfUseEthernet.
 * Called from naEwbOpen for each driver. The work done here in the
 * treck version dealt with link characteristics needed by ip. 
 * We may not need to do anything here.
 */

int ewbLinkOpen(void TM_FAR * deviceEntryPtr, int protocolFamily)
{
	NA_UNUSED_PARAM(deviceEntryPtr);
	NA_UNUSED_PARAM(protocolFamily);
	return NASTATUS_SUCCESS;
}

/*
 * This is the callback registered in the lnkPostFuncPtr in ewbInstall
 * as a result of calling bfUseEthernet.
 * The lnkPostFuncPtr is only used (from bfDeviceSend) if OneScatSend
 * function is not supplied.
 *
 * The wireless driver will use this path. Ethernet uses OneScatSend.
 */

int ewbDeviceSendOnePacket (void *ifHandle, btUserPacketPtr packetPtr)
{
	btUserPacketPtr nxtUPkt;
	btUserPacketPtr pUPkt = (btUserPacketPtr) packetPtr;
	btDevSendFuncPtr devSendFuncPtr;
	btDeviceEntryPtr devEntryPtr = (btDeviceEntryPtr) ifHandle;

	// devEntryPtr = packetPtr->pktDeviceEntryPtr;
	devSendFuncPtr = devEntryPtr->devSendFuncPtr;
#if	0
	DPRINTF("ewbDeviceSendOnePacket handle %x pkt %x",
				   	(int) ifHandle, (int) packetPtr);
#endif

	if (pUPkt->pktuLinkDataLength != pUPkt->pktuChainDataLength)
	{
		while ( pUPkt->pktuLinkNextPtr != NULL )
		{
			nxtUPkt = pUPkt->pktuLinkNextPtr;

			/* This is wlnSend or naEthSend, via naEwbConfigCreateInterface */
			(void)(*(devSendFuncPtr))((void *) devEntryPtr,
				(char TM_FAR *)(pUPkt->pktuLinkDataPtr),
				(int)(pUPkt->pktuLinkDataLength), TM_USER_BUFFER_MORE);

			pUPkt = nxtUPkt;                         
		}                                                     
	}
	/* This is wlnSend or naEthSend, via naEwbConfigCreateInterface */
	/* we always want to send at least one packet */
	/* Returning an error code is important, because bfSendCompleteInterface 
	   is not called in case of error, and the caller of this function needs 
	   to remove this packet from the queue, and free it.
	*/
	return (*(devSendFuncPtr))((void *) devEntryPtr,
			(char TM_FAR *)(pUPkt->pktuLinkDataPtr),
			(int)(pUPkt->pktuLinkDataLength), TM_USER_BUFFER_LAST);
}

/*
 * utility functions to cross-reference the handles between the driver
 * and the bridge
 */

/*
 * bfValidInterface function description:
 * Check that the Interface handle passed by the user is valid, i.e
 * is in our list of interfaces.
 *
 * Parameter
 * checkDevEntryPtr Interface hande passed by the user
 * Return
 * 0                success 
 * TM_EINVAL        bad parameter
 */

NaStatus bfValidInterface (btDeviceEntryPtr checkDevEntryPtr )
{
    int errorCode;
    btDeviceEntryPtr devEntryPtr;

    errorCode = NASTATUS_ENTRY_NOT_FOUND;

	/* Lock the device list */
    bf_call_lock_wait(&bm_context(bvDeviceListLock));

    devEntryPtr = bm_context(bvDeviceList);

    while (devEntryPtr != NULL)
    {
        if (devEntryPtr == checkDevEntryPtr)
        {
            errorCode = NASTATUS_SUCCESS;
            break;
        }
        devEntryPtr = devEntryPtr->devNextDeviceEntry;
    }

    bf_call_unlock(&bm_context(bvDeviceListLock));
    return errorCode;
}

/* AddInterface uses native logic rather than this function */

int bfEnqueueInterface (btDeviceEntryPtr devEntryPtr )
{
	int errorCode = NASTATUS_SUCCESS;

	/* make sure it is not already allocated */
	if (bfValidInterface(devEntryPtr) == NASTATUS_SUCCESS)
	{
		errorCode = NASTATUS_IF_ALREADY_CONFIGURED;
		goto exitEnqueueIf;
	}

	/* Lock the device list */
    bf_call_lock_wait(&bm_context(bvDeviceListLock));

	devEntryPtr->devNextDeviceEntry = bm_context(bvDeviceList);
	bm_context(bvDeviceList) = devEntryPtr;

    bf_call_unlock(&bm_context(bvDeviceListLock));
exitEnqueueIf:
    return errorCode;
}

/* The ethernet driver makes use of this function, but the wifi driver
 * does not. */

int bfDeviceStorePointer( btUserInterface ifHandle, void * deviceDriverPtr)
{
	btDeviceEntryPtr devEntryPtr;
	int errorCode;

	devEntryPtr = (btDeviceEntryPtr) ifHandle;
	errorCode = bfValidInterface(devEntryPtr);
	if (errorCode == NASTATUS_SUCCESS )
	{
		if (devEntryPtr->devDriverPtr == (void *) 0)
		{
			devEntryPtr->devDriverPtr = deviceDriverPtr;
		}
		else
		{
			errorCode = NASTATUS_EWB_EALREADY;
		}
	}
	return errorCode;
}

void * bfDeviceClearPointer( btUserInterface ifHandle )
{
	btDeviceEntryPtr devEntryPtr;
	void * deviceDriverPtr;
	int errorCode;

	devEntryPtr = (btDeviceEntryPtr) ifHandle;
	errorCode = bfValidInterface(devEntryPtr);
	deviceDriverPtr = (void *) 0;
	if (errorCode == NASTATUS_SUCCESS )
	{
		if (devEntryPtr->devDriverPtr != (void *) 0)
		{
			deviceDriverPtr = devEntryPtr->devDriverPtr;
			devEntryPtr->devDriverPtr = (void *) 0;
		}
	}
	return deviceDriverPtr;
}

void * bfDeviceGetPointer(btUserInterface ifHandle)
{
	return ((btDeviceEntryPtr) ifHandle)->devDriverPtr;
}

/*
 * Set up the buffer queues for communicating with the drivers.
 * From here we present ethernet MAC frames to the driver below.
 * We receive from the drivers using the model of the WIFI driver.
 *
 * How do we receive a frame from the tcip stack? tfDeviceSend
 * via lnkPostFuncPtr = ewbDeviceSendOnePacket (unless ScatSend).
 */

void ewbDriverDataInit(btUserInterface ifHandle)
{
	btDeviceEntryPtr devEntryPtr;

	devEntryPtr = (btDeviceEntryPtr) ifHandle;
#ifdef	THIS_IS_NOT_USED
	/* Head of recv queue (loop back only) */
    devEntryPtr->devReceiveQueuePtr = NULL;
	/* Pointer to last packet in recv queue (loop back only) */
    devEntryPtr->devRcvLastQueuePtr = NULL;
#endif
#if	1
	tfUserQueueInit(&devEntryPtr->devSendQueue);
#else
	/* Head of send queue (for packets waiting to be sent) */
    devEntryPtr->devSendQueuePtr = NULL;
	/* Last packet in the send queue */
    devEntryPtr->devSendQueueLastPtr = NULL;

	/* Current packet being sent in the device driver (we don't need this) */
    devEntryPtr->devSendQueueCurPtr = NULL;
#endif
}

/*
 * bfUseInterfaceOneScatSend Function Description
 *
 * Specify a driver driver scattered send function. This enables the stack
 * to call the device driver with a single send function per packet
 * (scattered or not).
 * The driver must call bfUseInterfaceOneScatSend between the 
 * naEwbConfigCreateInterface and naEwbOpen calls.
 */

int bfUseInterfaceOneScatSend ( btUserInterface ifHandle,
                btDevOneScatSendFuncPtr drvOneScatSendFuncPtr )
{
    int errorCode;
    btDeviceEntryPtr devEntryPtr;

    errorCode = NASTATUS_SUCCESS;
    devEntryPtr = (btDeviceEntryPtr) ifHandle;

	/* is interface configured? */
    if ((errorCode = bfValidInterface(devEntryPtr)) != NASTATUS_SUCCESS)
		goto exitOneScatSetup;

	/* check for NULL function pointer */
    if (drvOneScatSendFuncPtr == (btDevOneScatSendFuncPtr) 0)
    {
        errorCode = NASTATUS_EWB_INV_PARAM;
		goto exitOneScatSetup;
    }
	/* Cannot set after device has been opened.  */
    if (devEntryPtr->devFlag & TM_DEV_OPENED)
    {
        errorCode = NASTATUS_EWB_IN_USE;
		goto exitOneScatSetup;
    }

    devEntryPtr->devOneScatSendFuncPtr = drvOneScatSendFuncPtr;

exitOneScatSetup:
    return (errorCode);
}


/*
 * Free a user device driver buffer (allocated with either tfGetEthernetBuffer
 * or tfGetDriverBuffer)
 */

int bfFreeDriverBuffer (btUserBuffer userBuffer)
{
	ewb_bufferfrees++;
	return tfFreeDriverBuffer((ttUserBuffer) userBuffer);
}

/*
 * The driver calls this function when it is finished with a packet.
 * We can free the packet at the head of the send queue.
 * We queue each packet we send to the device to transmit in bfDeviceSend.
 */

void bfSendCompleteInterface(btUserInterface ifHandle, int devDriverLockFlag)
{

	NA_UNUSED_PARAM(devDriverLockFlag);

	btDeviceEntryPtr devEntryPtr = (btDeviceEntryPtr) ifHandle;
	/* ifHandle is the device structure created with AddInterface */
	ttPacketPtr pPkt;

	/* dequeue head of Send Queue */
#if	1
	if ((pPkt = tfUserQueueRemove(&devEntryPtr->devSendQueue)) != NULL)
	{
		bfFreePacket((btUserPacketPtr) pPkt, (bt8Bit) 0);
	}
#else
	if (devEntryPtr->devSendQueuePtr)
	{
		pPkt = devEntryPtr->devSendQueuePtr;
		devEntryPtr->devSendQueuePtr = pPkt->pktuLinkNextPtr;
		if (devEntryPtr->devSendQueueLastPtr == pPkt)
			devEntryPtr->devSendQueueLastPtr = NULL;

		bfFreePacket(pPkt, (bt8Bit) 0);
	}
#endif
}

/*
 * This routine is used to call driver functions. 
 * TODO: It may not be right for the bridge yet.
 */
int bfIoctlInterface(btUserInterface ifHandle, int flag, void TM_FAR * optionPtr, int optionLen)
{
	btDeviceEntryPtr devEntryPtr;
	int retCode;

	NA_UNUSED_PARAM(optionPtr);
	NA_UNUSED_PARAM(optionLen);

	devEntryPtr = (btDeviceEntryPtr) ifHandle;

	/* Lock the driver to provide single threaded access to the driver */
	bf_call_lock_wait(&(devEntryPtr->devDriverLockEntry));
	if (devEntryPtr->devFlag & TM_DEV_OPENED)
	{
	    retCode = TM_ENOENT;

	    if (flag & TM_DEV_IOCTL_EMPTY_XMIT_FLAG)
	    {
			if (devEntryPtr->devEmptyXmitFuncPtr != (ttDevEmptyXmitFuncPtr) 0)
			{
			    retCode = (*(devEntryPtr->devEmptyXmitFuncPtr))(ifHandle);
			}
	    }
	}
	else
	{
	    retCode = TM_ENXIO;
	}
	/* Unlock device driver. */
	bf_call_unlock(&(devEntryPtr->devDriverLockEntry));

	// bfSendCompleteInterface(devEntryPtr, TM_DEV_SEND_COMPLETE_APP);
	return(retCode);
}

void bfKernelCreateEvent(TX_SEMAPHORE * * eventPtr)
{
	TX_SEMAPHORE *sem_Ptr;
	UINT status;
	char * eventStr;

	eventStr = "EwbEvent";
	// tlThreadXEventNum++;

	/* Initialize the semaphore to zero */
	sem_Ptr = EWB_MALLOC(sizeof(TX_SEMAPHORE));
	if (sem_Ptr)
		memset(sem_Ptr, 0, sizeof(TX_SEMAPHORE));
	else
	{
		__panic("bfKernelCreateEvent", 0, "Fail to malloc semaphore", 0, 0);
		for(;;);    /* just hang the thread */
	}

	status =  tx_semaphore_create(sem_Ptr, eventStr, 0);

	if (status == TX_SUCCESS)
	{
		*eventPtr = (void TM_FAR *) sem_Ptr;
	}
	else
	{  
		__panic("bfKernelCreateEvent", 0,
					   	"Fail to Create ThreadX Semaphore", 0, 0);
		for(;;);	/* just hang the thread */
	}
	return;
}

#if	1
int bfWaitEventInterface(btEventEntryPtr eventEntryPtr)
{
	int eventCount;

	for (;;)
	{
		(void) tx_interrupt_control(TX_INT_DISABLE);
		eventCount = eventEntryPtr->eventCount;
		(void) tx_interrupt_control(TX_INT_ENABLE);

		if (eventCount != 0)
		{
			break;
		}
		tx_semaphore_get(eventEntryPtr->semPtr, TX_WAIT_FOREVER);
	}
	(void) tx_interrupt_control(TX_INT_DISABLE);
	eventEntryPtr->eventCount--;
	(void) tx_interrupt_control(TX_INT_ENABLE);

	return NASTATUS_SUCCESS;
}

#else
static int bfWaitEventInterface(btEventEntryPtr pEventEntry)
{
	unsigned long flags;	/* we make no use of this information */
	int rc = NASTATUS_SUCCESS;

	tx_event_flags_get(&pEventEntry->events, 1, TX_OR_CLEAR, &flags,
				   	TX_WAIT_FOREVER);
	if (pEventEntry->eventCount-- <= 0)	/* protect this action? */
			rc = NASTATUS_ERROR;
	return rc;
}
#endif

/*
 * Pend waiting for event flag to be incremented (by the interrupt handler).
 */

int bfWaitSentInterface(btUserInterface ifHandle)
{
	btDeviceEntryPtr devEntryPtr = (btDeviceEntryPtr) ifHandle;

	return bfWaitEventInterface(&devEntryPtr->devSentEvent);
}

unsigned int waitReceiveInterfaceCalls = 0;

int bfWaitReceiveInterface(btUserInterface ifHandle)
{
	btDeviceEntryPtr devEntryPtr = (btDeviceEntryPtr) ifHandle;

	waitReceiveInterfaceCalls++;

	return bfWaitEventInterface(&devEntryPtr->devRecvEvent);
}

/*
 * Increment the appropriate event flag for sent packets or recvd packets.
 * Can we use tx_event_flags_create, _get, _set, which only works bitwise?
 * Else use a counting semaphore or use the counters in the device struct?
 */

void bfNotifyInterfaceIsr( btUserInterface ifHandle,
			   	int numberRecvPackets, int numberSendCompletePackets,
			   	btUser32Bit numberBytesSent, btUser32Bit flag )
{
    btDeviceEntryPtr devEntryPtr;
    btEventEntryPtr  recvEventEntryPtr;
    btEventEntryPtr  sentEventEntryPtr;
    bt32Bit          devBytesNotified;
    bt32Bit          devPacketsNotified;
    bt16Bit          savRecvEventCount;
    bt16Bit          savSentEventCount;
	TX_INTERRUPT_SAVE_AREA

    NA_UNUSED_PARAM(flag);
	devEntryPtr = (btDeviceEntryPtr) ifHandle;
    recvEventEntryPtr = &(devEntryPtr->devRecvEvent);
    sentEventEntryPtr = &(devEntryPtr->devSentEvent);

	/* Save current event counts. This tells us if we have backed up work. */
    /*
		Disabling interrupts around recvEventEntryPtr->eventCount was added because 
		the synchronization of recvEventEntryPtr->eventCount gets broken for Ethernet 
		on the bridge device. Does this mean that bfNotifyInterfaceIsr is called 
		on receive from different places (2 different interrupt handlers).
	 */
TX_DISABLE
    savRecvEventCount = recvEventEntryPtr->eventCount;

	/* we're in the interrupt handler, so ints are disabled. */
    recvEventEntryPtr->eventCount += (bt16Bit)( numberRecvPackets );
TX_RESTORE

    savSentEventCount = sentEventEntryPtr->eventCount;

    devPacketsNotified = 0;
    if (numberSendCompletePackets != 0)
    {
        devBytesNotified = devEntryPtr->devBytesNotified + numberBytesSent;
        devPacketsNotified = devEntryPtr->devPacketsNotified
                             + (bt32Bit)numberSendCompletePackets;
        if (devBytesNotified > bm_context(bvNotifySendCompLowWater))
        {
            sentEventEntryPtr->eventCount += (bt16Bit)( devPacketsNotified );
            devEntryPtr->devBytesNotified = 0;
            devEntryPtr->devPacketsNotified = 0;
        }
        else
        {
            devEntryPtr->devPacketsNotified = devPacketsNotified;
            devEntryPtr->devBytesNotified = devBytesNotified;
			/* Postpone notification until we reach the threshold */
            devPacketsNotified = 0;
        }
    }
	/*
	 * Since the OS could re-enable the interrupt when we post from the ISR,
	 * we postpone posting to the tasks up to the end of this routine, so that
	 * all our variable updates are protected from another ISR call.
	 */
	/*
	 * post only if we are notified of a received packet, and the recv
	 * task could potentially be pending.
	 */
    if ((savRecvEventCount == (bt16Bit) 0) && (numberRecvPackets != 0) )
    {
		tx_semaphore_put(recvEventEntryPtr->semPtr);
    }
	/*
	 * post only if we are notified of a sent packet (and we reached
	 * the threshold), and the send complete task could potentially be pending.
	 */
    if ((devPacketsNotified != 0) && (savSentEventCount == (bt16Bit) 0) )
    {
		tx_semaphore_put(sentEventEntryPtr->semPtr);
    }
}


