/*
 *  Copyright (c) 1996-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
 *
 *     NET+OS/Treck interface to Embedded 802.11b wireless network interface
 *     Based on Digi International embedded ethernet driver, narmenet.c
 */

#include "mac.h"
#include "md5.h"

/* treck */
#include "trsocket.h"

/* threadx */
#include "tx_api.h"
#include "tx_port.h"

/* Digi International */
#include "bsp_api.h"
#include "bsptimer.h"
#include "Efe_def.h"
#include "reg_def.h"
#ifdef NET_OS_SPECIFIC
#include "naifconfig.h"
#include "ewb_api.h"
#include "bsp.h"
#endif
#include "wpa_api.h"

#ifdef NET_OS_SPECIFIC
#include "minmax.h"
#endif


#define	WLN_TXQ_SIZE	256

BOOL wln_params_set = FALSE;
TX_MUTEX wln_mutex;
TX_MUTEX wln_mutex2;
TX_MUTEX wln_mutex3;
BOOLEAN wln_up = FALSE;
static ttUserInterface wln_ifc;

static char *rx_data;
static int rx_length;
static ttUserBuffer rx_handle;

// Transmit/receive processing task
TX_THREAD wln_thread;
static TX_EVENT_FLAGS_GROUP wln_event_flags;

#define WLN_STACK 8192
static char wln_stack[WLN_STACK];
static void wln_task (ULONG unused);

#if (USB_WIFI_ENABLED == 1)
TX_MUTEX wln_tx_mutex;
int start_polling_wiwave(void);
#endif

void * naWlnGetWirelessHandle(char * ifname)
{
#if BSP_WIFI_MULTIPLE_MAC
    epStatus  * pSta = NULL;
    int       i;

    for (i=0; i < WLN_MAX_ADDRS; i++)
    {
        if (strcmp(rootEndPoint[i]->ifname, ifname) == 0)
        {
            pSta = rootEndPoint[i];
            break;
        }
    }

    return (void *)pSta;
#else
    NA_UNUSED_PARAM(ifname);
    return NULL;
#endif
}

void * wln_new_rootEndpoint(const char * ifname)
{
#if BSP_WIFI_MULTIPLE_MAC
    epStatus  * pSta;
    /* currently we only support wln0 interface on index 0 */
    int        idx = 0;  

    pSta = rootEndPoint[idx];

    if (rootEndPoint[idx] == NULL)
    {
        /*
         * Allocate memory for the root endpoint, this is used to
         * store information globally.
         */
        pSta = (epStatus *) malloc(sizeof (epStatus));
        if (pSta != NULL)
        {
            memset(pSta, 0, sizeof (epStatus));

            pSta->ifname        = (char *)ifname;
            pSta->entryNdx      = 0;
            rootEndPoint[idx]   = pSta;
            pSta->root          = pSta;	/* point to self */
        }
    }
    return (void *)pSta;
#else
    NA_UNUSED_PARAM(ifname);
    return NULL;
#endif
}

void wln_free_rootEndpoint(void * pSta)
{

#if BSP_WIFI_MULTIPLE_MAC
    int i;

    for (i=0; i < WLN_MAX_ADDRS; i++)
    {
        if (rootEndPoint[i] == (epStatus *)pSta)
        {
            /* found it */
            free(pSta);
            rootEndPoint[i] = NULL;
            break;
        }
    }
#else
    NA_UNUSED_PARAM(pSta);
    return;
#endif
}

/*
 * Driver entry functions called by TCP/IP stack
 */

static int wlnOpen(ttUserInterface interfaceHandle)
{
	NA_UNUSED_PARAM(interfaceHandle);
	
    if (wln_up)
	{
		DPRINTF ("wlnOpen: driver aleady open\n");
		return TM_DEV_ERROR;
	}
	
	/* wireless params must be initialized */
	if (!wln_params_set)
	{
		DPRINTF ("wlnOpen: params not initialized\n");
		return TM_DEV_ERROR;
	}


	/* Confirm that radio device is present and responsive 
           and perform MAC/RF layer initialization */
	if (ERR_OK != wln_start())
	 {
	    DPRINTF ("wlnOpen: MAC/RF layer initialization failed\n");
	    return TM_DEV_ERROR;
	 }

        /* Spawn a higher level processing wireless task IF AND ONLY IF 
           the step above has succeeded. */

	/* start driver task */
	tx_thread_delete(&wln_thread);
	tx_thread_create(&wln_thread, "wireless task", wln_task,
		0, &wln_stack[0], WLN_STACK, 2, 2, TX_NO_TIME_SLICE, TX_AUTO_START);

	return TM_DEV_OKAY;
}

#if (BSP_WANT_WIRELESS == TRUE) && (BSP_ENABLE_DIALOG == FALSE)

static int wln_tcp_up=0;
/*
 * If the dialog is disabled the driver has already been started.
 * We just set a flag indicitating that the network stack is up.
 */
int wlnOpenStub(ttUserInterface interfaceHandle)
{
    NA_UNUSED_PARAM(interfaceHandle);

    /* Set a flag to indicate the netork stack is up */
    wln_tcp_up = 1;

    return TM_DEV_OKAY;
}
#endif



static int wlnClose(ttUserInterface interfaceHandle)
{
	NA_UNUSED_PARAM(interfaceHandle);

	/* set the event flag to wake up and exit the driver task. */
	wln_up = FALSE;
	tx_event_flags_set(&wln_event_flags, 1, TX_OR);

	return TM_DEV_OKAY;
}

static int wlnSend(ttUserInterface interfaceHandle,
	char TM_FAR *   dataPtr,
	int             dataLength,
	int             flag)
{
	// Message starts with an ethernet header
	ether_hdr *eth = (ether_hdr *) dataPtr;
	int length = dataLength - sizeof (ether_hdr);
	int ret;
#ifdef	WIFI_VERBOSE
	XPRINTF(TRACE_XMIT, "wlnSend len %d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ",
			dataLength, dataPtr[0], dataPtr[1], dataPtr[2], dataPtr[3],
			dataPtr[4], dataPtr[5], dataPtr[6], dataPtr[7],
			dataPtr[8], dataPtr[9], dataPtr[10], dataPtr[11]);
#endif
	NA_UNUSED_PARAM(interfaceHandle);
	NA_UNUSED_PARAM(flag);

    if (!wln_up)
	{
		XPRINTF (TRACE_DEBUG, "wlnSend: driver not open\n");
		return TM_DEV_ERROR;
	}

	// Send to MAC layer
	ret = MacSendData (eth->src, eth->dst, eth->eptype, eth->data, length);

	// Out of buffers.
	// Return error so stack will queue the packet and send again later.
	if (ret == ERR_MEMORY)
		return TM_DEV_ERROR;
		
	if (ret != ERR_OK)
	{
		XPRINTF(TRACE_DEBUG, "wlnSend: error %d length %d\n", ret, length);
	}

	// Success or unrecoverable error.
	// Tell stack to free the packet, not queue it.
#if	BSP_ETH_WIFI_BRIDGE
	bfSendCompleteInterface(interfaceHandle, TM_DEV_SEND_COMPLETE_DRIVER);
#else
	tfSendCompleteInterface(interfaceHandle, TM_DEV_SEND_COMPLETE_DRIVER);
#endif
	return TM_DEV_OKAY;
}

/*
 * wlnReceive is supplied as the callback invoked from treck in
 * tfRecvInterface.
 * rx_data points to the data, looking like an ethernet MAC frame
 * rx_handle (opaque to WIFI) points to the ttPacket structure.
 */

static int wlnReceive(ttUserInterface interfaceHandle,
	char TM_FAR * TM_FAR * dataPtr,
	int TM_FAR *           dataLength,
	ttUserBufferPtr        bufHandlePtr)
{
	NA_UNUSED_PARAM(interfaceHandle);

	// Return buffer allocated in MacRecvData()
	*dataPtr = rx_data;

	if (rx_data == NULL)
		return TM_DEV_ERROR;
		
	*dataLength = rx_length;
	*bufHandlePtr = rx_handle;
	
	rx_data = NULL;
	return TM_DEV_OKAY;
}

static int wlnIoctl(ttUserInterface interfaceHandle,
	int             flag,
	void TM_FAR *   optionPtr,
	int             optionLen)
{
	NA_UNUSED_PARAM(interfaceHandle);
	NA_UNUSED_PARAM(optionPtr);
	NA_UNUSED_PARAM(optionLen);

	// Nothing to do for multicast, stack will filter packets
    if ((flag & TM_DEV_SET_MCAST_LIST) || (flag & TM_DEV_SET_ALL_MCAST))
		return TM_DEV_OKAY;
		
    return TM_DEV_ERROR;
}

static int wlnGetPhyAddr(ttUserInterface interfaceHandle,
	char TM_FAR *   phyAddr)
{
	NA_UNUSED_PARAM(interfaceHandle);

	/* wireless params must be initialized */
	if (!wln_params_set)
		return TM_DEV_ERROR;

	SET_ADDR (phyAddr, macParams.sta_addr);
    return TM_DEV_OKAY;
}

#if (BSP_WANT_WIRELESS == TRUE) && (BSP_ENABLE_DIALOG == FALSE)
/*
 * Initialize the wireless driver before OS network stack, this can only
 * be done if the dialog is disabled, this is done to speed up
 * boot time by starting scanning for AP's early.
 */
int wln_init (void)
{
//    wln_up = FALSE;
	
    /* wireless params must be initialized */
    if (!wln_params_set)
	{
        DPRINTF ("WL params not initialized\n");
        return -1;
	}

    /* create event flag */
    memset(&wln_event_flags, 0, sizeof wln_event_flags);
    tx_event_flags_create (&wln_event_flags, "Wireless Event Flags");
    tx_event_flags_set(&wln_event_flags, 0, TX_AND);
    
    /* create mutex */
    memset(&wln_mutex, 0, sizeof wln_mutex);
    memset(&wln_mutex2, 0, sizeof wln_mutex2);
    memset(&wln_mutex3, 0, sizeof wln_mutex3);
    tx_mutex_create(&wln_mutex, "Wireless mutex", TX_NO_INHERIT);
    tx_mutex_create(&wln_mutex2, "Wireless mutex2", TX_NO_INHERIT);
    tx_mutex_create(&wln_mutex3, "Wireless mutex3", TX_NO_INHERIT);  //for 802,11h stationSupportedChannel[]

    wln_up = FALSE;
    rx_data = NULL;

    wlnOpen((ttUserInterface)NULL);

   	return 0;
}

/* This function is used only to initialize the context of rootEndpoint.
 *
 * This function is called when the dialog is disabled and we need
 * to initialize the root endpoint of the wireless interface before
 * wpa_supplicant_init call which requires the root endpoint 
 * (wln_install doesn't get call after wpa_suppicant_init call when
 * dialog is disable).
 */
int wln_setup (const char *ifname, void **ppSta)
{
    int     rc = 0;

    wln_up = FALSE;
	
    *ppSta = wln_new_rootEndpoint(ifname);

    rx_data = NULL;

   	return rc;
}
#endif



/*
 * Public API
 */

//
// Install wireless driver into OS network stack
// @param ifname network interface name (e.g. "wln0")
//
#if BSP_WIFI_MULTIPLE_MAC
int wln_install (const char *ifname, void **ppSta)
#else
int wln_install (char *ifname)
#endif
{
    int errorCode;
    NaIfAddInterfaceParams_t if_config_params;
    int j;

#ifdef NET_OS_SPECIFIC
#if	(!BSP_ETH_WIFI_BRIDGE)
    unsigned multihome = 0;
#endif 
    /* Install network interface */
#if BSP_WIFI_MULTIPLE_MAC
    /* The root endpoint is initialized in wireless_init() in starttcp.c */
    /* MLC - Why we initialize root endpoint in wireless_init.
     *       Added code to initialize root endpoint in this function.
     *       we only initialize 0 index of root endpoint.
     */
/*    *ppSta = NULL; */
    *ppSta = wln_new_rootEndpoint(ifname);
    if (*ppSta == NULL)
    {
        DPRINTF ("wln_install: wln_new_rootEndpoint failed!\n");
        return -1;
    }
#endif

    /* Set interface type info */
    strcpy(if_config_params.interface_type.if_name, ifname);
    if_config_params.interface_type.if_linklayer_type = NA_IF_LINK_LAYER_TYPE_ETH;
    if_config_params.interface_type.if_multihomed = TRUE;

    /* Set driver functions for network interface */
#if (BSP_ENABLE_DIALOG == FALSE)
     /* 
      * If the dialog is not enabled we start the driver early,
      * instead of waiting for wlnOpen to be called.
      */
    if_config_params.driver_func.driver_open = wlnOpenStub;
#else
    if_config_params.driver_func.driver_open = wlnOpen;
#endif

    if_config_params.driver_func.driver_close = wlnClose;
    if_config_params.driver_func.driver_send = wlnSend;
    if_config_params.driver_func.driver_recv = (NaIfDriverRecvFn_t)wlnReceive;
    if_config_params.driver_func.driver_free_recv = NULL;
    if_config_params.driver_func.driver_ioctl = wlnIoctl;
    if_config_params.driver_func.driver_get_phy_addr = wlnGetPhyAddr;

    /* set interface configuration parameters                           */
    /* Note: if if_config_params.interface_type.if_multihomed is FALSE, */
    /* only index 0 of if_config_params.config_params.if_config_flags[] */
    /* and if_config_params.config_params.buffers_per_frame[] are used  */
    for (j = 0; j < NA_IF_MAX_MULTIHOMES_PER_IF; j++)
    {
        if_config_params.config_params[j].if_config_flags = TM_DEV_MCAST_ENB;
#if defined(VPN_ENABLED) || defined(FORWARDING_ENABLED)
        if_config_params.config_params[j].if_config_flags |= TM_DEV_IP_FORW_ENB; /* allow forwarding through this interface */
#endif
        if_config_params.config_params[j].buffers_per_frame = 1;
    }
#if	BSP_ETH_WIFI_BRIDGE
    if (naEwbConfigCreateInterface(&if_config_params, BT_IFTYPE_DRIVER)
				   	!= NASTATUS_SUCCESS)
    {
        wln_free_rootEndpoint((void *)*ppSta);
        DPRINTF ("wln_install: naEwbConfigCreateInterface failed!\n");
        return TM_DEV_ERROR;
    }
	/* can we use the DIGI_USE_SCATTER_SEND option for the WIFI driver? */
	/* do we want to enable the BYPASS option while bridging? */

    // this is the ethernet approach: result = ethBridgeIFTasks();
    if (naEwbConfigGetInterfaceHandle(ifname, &wln_ifc) != NASTATUS_SUCCESS)
    {
        wln_free_rootEndpoint((void *)*ppSta);
	    DPRINTF ("wln_install: naIfConfigGetInterfaceHandle failed!\n");
    	return -2;
    }
#else	/* not BSP_ETH_WIFI_BRIDGE */

    if (naIfConfigCreateInterface(&if_config_params) != NASTATUS_SUCCESS)
    {
        wln_free_rootEndpoint((void *)*ppSta);
	    DPRINTF ("wln_install: naIfConfigCreateInterface failed!\n");
        return -1;
    }
    
    if (naIfConfigGetInterfaceHandle(ifname, &wln_ifc, &multihome) != NASTATUS_SUCCESS)
    {
        wln_free_rootEndpoint((void *)*ppSta);
	    DPRINTF ("wln_install: naIfConfigGetInterfaceHandle failed!\n");
    	return -2;
    }
#endif /* not BSP_ETH_WIFI_BRIDGE */
#endif // NET_OS_SPECIFIC

#ifdef NDS_SPECIFIC

    wln_ifc = tfAddInterface(
		ifname,
		tfUseEthernet(),
		wlnOpen,
		wlnClose,
		wlnSend,
		wlnReceive,
		(ttDevFreeRecvFuncPtr)0,
		wlnIoctl,
		wlnGetPhyAddr,
		&errorCode);
            
    if (wln_ifc == NULL)
    {
    	DPRINTF ("wln_install: tfAddInterface error %d\n", errorCode);
    	return -1;
    }
#endif // NDS_SPECIFIC
	/* EWB doesn't need to support this additional queueing level */
#if	!BSP_ETH_WIFI_BRIDGE
    /* set up transmit queue */
	errorCode = tfUseInterfaceXmitQueue (wln_ifc, WLN_TXQ_SIZE);
    if (errorCode != 0)
    {
#if defined NET_OS_SPECIFIC && (BSP_WIFI_MULTIPLE_MAC == 1)
        wln_free_rootEndpoint((void *)*ppSta);
#endif
    	DPRINTF ("wln_install: tfUseInterfaceXmitQueue error %d\n", errorCode);
    	return -3;
    }
#else
	NA_UNUSED_PARAM(errorCode);
#endif

/*
 * If the dialog is enabled event flag and thread is created in wln_init()
 */
#if (BSP_ENABLE_DIALOG == TRUE)
    /* create event flag */
    tx_event_flags_create (&wln_event_flags, "Wireless Event Flags");
    tx_event_flags_set(&wln_event_flags, 0, TX_AND);
    
    /* create mutex */
    tx_mutex_create(&wln_mutex, "Wireless mutex", TX_NO_INHERIT);
    tx_mutex_create(&wln_mutex2, "Wireless mutex2", TX_NO_INHERIT);
    tx_mutex_create(&wln_mutex3, "Wireless mutex3", TX_NO_INHERIT);
	wln_up = FALSE;
	rx_data = NULL;
#endif

#if (USB_WIFI_ENABLED == 1)
    tx_mutex_create(&wln_tx_mutex, "Wireless tx mutex", TX_NO_INHERIT);
#endif

   	return 0;
}

//
// Send a packet to wireless network
// @param data packet containing ethernet header and payload
// @param length total length in bytes
//
int wln_send (uint8 *data, int length)
{
	int ret;
	
	// Message starts with an ethernet header
	ether_hdr *eth = (ether_hdr *) data;
	length -= sizeof (ether_hdr);

    if (!wln_up)
	{
		DPRINTF ("wln_send: driver not open\n");
		return -1;
	}

	// Send to MAC layer
	{
		char pText[76];
		int msglen = min(24, length);
		hexToText((char *) data, pText, msglen);
		XPRINTF(TRACE_WARNING, "from wpa (%d): %s\n\r", length, pText);
	}
	ret = MacSendData (eth->src, eth->dst, eth->eptype, eth->data, length);

	if (ret != ERR_OK)
	{
		DPRINTF ("wln_send: error %d length %d\n", ret, length);
		return -2;
	}
	
	return 0;
}


/*
 * OS interface functions called by the MAC layer
 */

//
// Receive a data MSDU.
// Called my MAC layer to provide data to an upper layer.
// This function must copy data by calling MacGetData() before returning.
// @param buf Start of buffer list
// @param dst Destination MAC address
// @param src Source MAC address
// @param epType Ethernet protocol type
// @param length Length of data in bytes
//
void MacRecvData (MacBuffer *buf, MacAddr dst, MacAddr src, int epType, int length)
{
#if	BSP_WIFI_MULTIPLE_MAC 
	epStatus *pSta;
#endif
	ether_hdr *eth;

	// Get an OS buffer
#if	BSP_ETH_WIFI_BRIDGE
	/* data pointer comes back aligned to 4x +2 boundary for Treck */
	rx_data = bfGetEthernetBuffer ((btUserBuffer) &rx_handle);
#else
	rx_data = tfGetEthernetBuffer (&rx_handle);
#endif
	if (rx_data == NULL)
    {
		macStats.rxDropBuffer++;
		return;
    }

	// Copy header and data to OS buffer
	eth = (ether_hdr *) rx_data;
	SET_ADDR (eth->dst, dst);
	SET_ADDR (eth->src, src);
	eth->eptype = epType;
	MacGetData (buf, eth->data, length);

	length += sizeof(ether_hdr);
	rx_length = length;
#if	BSP_WIFI_MULTIPLE_MAC
	pSta = MacGetStationPtr((char *) dst);
    if (pSta == NULL)
    {   /* if broadcast address use default information */
        pSta = rootEndPoint[0];
    }
    
	// Send to receive callback if any
	if (wln_receive (pSta, (uint8 *)rx_data, length))
#else
	// Send to receive callback if any
	if (wln_receive ((uint8 *)rx_data, length))
#endif	
	{
		char pText[64];
		int msglen = min(20, length - 14);
		hexToText(rx_data + 14, pText, msglen);
		XPRINTF(TRACE_WARNING, "to wpa (%d): %s\n\r", length, pText);

#if	BSP_ETH_WIFI_BRIDGE
		bfFreeDriverBuffer ((btUserBuffer) rx_handle);
#else
		tfFreeDriverBuffer (rx_handle);
#endif
		rx_data = NULL;
	}
		
	// No callback, pass up to the TCP/IP stack.
	// Stack will call wlnReceive() to get the data.
	else
	{
#if	BSP_ETH_WIFI_BRIDGE
		bfRecvInterface (wln_ifc);
#else
		tfRecvInterface (wln_ifc);
#endif
		ASSERT (rx_data == NULL);
	}
}

/*
 * MacEvent() is called when a MAC layer event occurs.
 * This may be called at interrupt level.
 */
void MacEvent (int event, void *param)
{
	NA_UNUSED_PARAM(param);

	switch (event)
	{
		case MAC_EVT_RX_INTR:
		case MAC_EVT_TX_INTR:
			tx_event_flags_set (&wln_event_flags, 1, TX_OR);
			break;
#if (USB_WIFI_ENABLED == 1)
        // These are events which cannot be handled in the WiWave ISR
        // because they may block over USB.
		case MAC_EVT_NO_BUFFERS:  // WiWave has no buffers
			tx_event_flags_set (&wln_event_flags, 2, TX_OR);
			break;
		case MAC_EVT_CLEAR_TX_END:  
			tx_event_flags_set (&wln_event_flags, 4, TX_OR);
			break;
		case MAC_EVT_RESET_RX_FIFO:  
			tx_event_flags_set (&wln_event_flags, 8, TX_OR);
			break;
#endif
	}
}

#if (USB_WIFI_ENABLED == 1)
#define WIFI_READ_LEN BUF_SIZE /* 512*3+24 */
extern int wifi_not_polling;
extern int usb_wifi_read(unsigned char *buffer, int bytesToTransfer, 
          int *bytesTransferred, int *errorCode, MacBuffer *mac_buffer);
int start_polling_wiwave(void)
{
    MacBuffer     *new_buffer;
    unsigned char *ptr;
    int           error=0, bytesTransferred;
    unsigned int  len, ret;
    void          *callback_ptr;
    int           level;

    /* If we are already polling, just return */
    if (wifi_not_polling == 0)
    {
        return 1;
    }
    trace(USBHOST_TRACE, TRACE_INFO, 
          "start_polling_wiwave");

    /* Get a buffer for the next read */ 
    new_buffer = MacGetRecvBuffer();
    if (new_buffer == (MacBuffer *)NULL)
    {
        wifi_not_polling = 1; 
        MacEvent(MAC_EVT_NO_BUFFERS, (void *)NULL);

        trace(USBHOST_TRACE, TRACE_INFO, 
          "start_polling_wiwave no buffers");
        return 0;
    }
    
    wifi_not_polling = 0; 
    ptr = (unsigned char *)&new_buffer->usb_encoded_length;
    len = WIFI_READ_LEN;
    callback_ptr = (unsigned char *)new_buffer;
    
    /* Start polling the WiWave device */
    level = DISABLE();
    ret = usb_wifi_read(ptr,
                       len,
                       &bytesTransferred, &error, callback_ptr);
    ENABLE(level);
    if (error)
    {
        panic("wifi_handle_packet: usb_wifi_read failed.\n"); 
        trace(USBHOST_TRACE, TRACE_CRITICAL, 
          "wifi_handle_packet: usb_wifi_read errorCode %d\n", error);
    }
    return 1;
}
#endif

/*
 * Get system time value in milliseconds
 */
uint32 getTimeMS()
{
	return (tx_time_get() * (1000 / NABspTicksPerSecond));
}


/******************************************************************
 *   support functions
 ******************************************************************/
int wln_start(void)
{
    int ret;
    
    GET_MUTEX2();
    ret = MacInit();
	if (ret != ERR_OK)
	{
		DPRINTF ("wln_start: MacInit error %d\n", ret);
		return -1;
	}

	wln_up = TRUE;
	PUT_MUTEX2();
	return ERR_OK;
}
void wln_stop(void)
{
    ShutdownHW();  /*so the driver stops receiving*/
    GET_MUTEX2();
    wln_up = FALSE;
    MacShutdown ();
    tx_thread_sleep(100 * NABspTicksPerSecond/1000);
    PUT_MUTEX2();
    
}

/*
 *
 *  Function: void wln_task (ULONG unused)
 *
 *  Description:
 *
 *      This routine is the entry point to the driver thread.
 *      This thread is used to pass received packets up to the
 *      MAC layer and TCP/IP stack, transmit queued packets,
 *		and process periodic tasks for the MAC layer.
 *
 *      This thread waits for an event flag which the ISR
 *      uses to signal us when packets are available.
 *
 *  Parameters:
 *
 *      unused      a parameter passed to newly created thread which we
 *                  do not use.
 *
 *  Return Values:
 *
 *      none
 *
 */
static void wln_task (ULONG unused)
{
    ULONG flags;

	NA_UNUSED_PARAM(unused);

    while (1)
    {
        GET_MUTEX2();
        if (wln_up)
        {
    	    // Process receive queue and periodic tasks
    	    uint32 next = MacTimer();

		    // Process driver transmit queue
#if (BSP_WANT_WIRELESS == TRUE) && (BSP_ENABLE_DIALOG == FALSE)
            if (wln_tcp_up)
            {
#if	BSP_ETH_WIFI_BRIDGE
		        bfIoctlInterface (wln_ifc, TM_DEV_IOCTL_EMPTY_XMIT_FLAG, NULL, 0);
#else
		        tfIoctlInterface (wln_ifc, TM_DEV_IOCTL_EMPTY_XMIT_FLAG, NULL, 0);
#endif	
            }
#else

#if	BSP_ETH_WIFI_BRIDGE
		    bfIoctlInterface (wln_ifc, TM_DEV_IOCTL_EMPTY_XMIT_FLAG, NULL, 0);
#else
		    tfIoctlInterface (wln_ifc, TM_DEV_IOCTL_EMPTY_XMIT_FLAG, NULL, 0);
#endif	
#endif // (BSP_WANT_WIRELESS == TRUE) && (BSP_ENABLE_DIALOG == FALSE)

            // Wait for an event
            // Wake up periodically to process timer tasks
            tx_event_flags_get(&wln_event_flags, 0xf, TX_OR_CLEAR, &flags, 
        	    next*NABspTicksPerSecond/1000);
#if (USB_WIFI_ENABLED == 1)	    
            if (flags & 2)
            {
                /* The wiwave is out of buffers */
                if (start_polling_wiwave() == 0)
                {
                   tx_thread_sleep(10);
                   continue;
                }
            }
            if (wifi_not_polling)
            {
                if (start_polling_wiwave() == 0)
                {
                   // The wiwave is out of MAC recv buffers 
                   tx_thread_sleep(10);
                   continue;
                }
            }
            if (flags & 4)
            {
                extern void MacClearTxEnd(void);
                MacClearTxEnd();
            }
            if (flags & 8)
            {
                extern void MacResetRxFifo(void);
                MacResetRxFifo();
            }

            tx_mutex_get (&wln_tx_mutex, TX_WAIT_FOREVER);

            if ((txState == ST_IDLE) && (wifi_not_polling == 0))
            {
                SendQueue();
            }
            tx_mutex_put (&wln_tx_mutex);
#endif	    
        }
        else
            tx_thread_sleep(10 * NABspTicksPerSecond/1000);
        
        PUT_MUTEX2();
    }
     
}

#ifdef NDS_SPECIFIC

/*
 * Get a cryptographic random number
 */
unsigned NA_Rand(void)
{
   MD5_CTX mdContext;
   unsigned char digest[16];
#if PROCESSOR == ns7520
   unsigned int t;
#endif

   MD5_Init(&mdContext);   /* initialize MD5 */

   /* get MAC random number, and MD5 it*/
   MD5_Update(&mdContext, &macRandom, sizeof macRandom);
   
#if PROCESSOR == ns7520
   /* get current timer 1 count and MD5 it */
   t = *NARM_T1SR_ADDR;
   MD5_Update(&mdContext, &t, sizeof t);
#endif

#if PROCESSOR == ns9750
    // TODO
#endif

   MD5_Final(digest, &mdContext);
   return *((unsigned *) digest);
}

// Called by nif_is_link_up()
int is_wireless_up(void)
{
    /* Check Link Status */
    switch (macStatus.state)
    {
		case WLN_ST_ASSOC_ESS:
		case WLN_ST_AUTH_ESS:
			return 1;

		default:
			return 0;
    }
}

#endif // NDS_SPECIFIC
