

//
// Embedded 802.11 wireless network interface
// MAC layer management functions
// Copyright 2005 Digi International
//
#include "mac.h"
#include "mac_api.h"
#include "wpa_api.h"
#include "bsp_api.h"

#if (BSP_WPS_ENABLED == TRUE)
#include "mac_wps.h"
#include "wps_api.h"
#endif

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


#if     BSP_WIFI_MULTIPLE_MAC
/* data */
epStatus *rootEndPoint[WLN_MAX_ADDRS];
#else
wln_status macStatus;   // Operating status
StaState staState;      // Station state: one of SS_xxx
static AuthState authState;         // Authentication state
static uint8 challenge[CHAL_SIZE];  // Authentication challenge
#endif

#define DEFAULT_MILLS_PER_BEACON        (100)


wln_params macParams;   // Operating parameters
MacBuffer *bcnFrame;    // Beacon frame if IBSS
MacBuffer *respFrame;   // Probe response frame if IBSS
uint16 bcnInt;          // Beacon interval (in TU)
uint16 atimWindow;      // ATIM window size, 0 if none
uint16 basicRates;      // Basic rate set
uint8 erpInfo;          // Extended rate PHY info
uint32 macRandom;       // Random value generated from network

// Scan parameters
static uint8 scan_ssid[WLN_SSID_SIZE];  // Service set ID
static int scan_ssid_len;           // SSID length, 0 for any
static uint32 scan_chan_mask[2];       // Channels to scan
static BOOL scan_requested;         // TRUE if requested by wln_scan()

static wln_scan_data *scan_data;    // Networks found by scan
// static StaState scan_state;        // Station state before scan
static int scan_count;              // Number of networks found
static int scan_max;                // Number of networks allocated
static int scan_channel;            // Channel being scanned
static int scan_index;              // Next network to try
static uint32 scan_time;            // Time of last scan
#if (BSP_WIFI_MULTIPLE_MAC == 0)
static StaState scan_state;         // Station state before scan
#endif
static uint32 mgmtTime;             // Last time management timer ran
// static int retry;                   // Management retry counter

// Broadcast MAC address
static MacAddr bc_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

uint8   global_chan = 1;

// WPA information element
static const uint8 wpaOUI[] = { 0x00, 0x50, 0xf2, 0x01 };

// 802.11h Station supported channel
typedef struct {
        uint8 startChannel;
        uint8 channelNum;
        uint8 txPwrLimit;
} stationSupportedChannel_T;

#define MAX_CHANNEL_GROUPS          (30)        //is 30 groups enough?
static stationSupportedChannel_T stationSupportedChannel[MAX_CHANNEL_GROUPS];  

/*
 * List of all Tx Power contraints.  Note that stationSupportedChannel
 * only lists 802.11a channels.  The naWifiChannelConstraint array lists
 * all the known constraints for all channels.
 */
uint8 naWifiChannelConstraint[NA_WIFI_MAX_CHANNEL_INDEX];


static void RecvScan (MacBuffer *buf);
static void RecvBeacon (MacBuffer *buf);
static void RecvAuthentication (MacBuffer *buf);
static void StartScan (epStatus *pSta, uint8 *ssid, int ssid_len, int channel, BOOL requested);
static void ScanChannel (void);
//static void SendMgmtFrame (epStatus *pSta);
static void SendNextMgmtFrame (epStatus *pSta);
static BOOL StartIBSS (epStatus *pSta);
static BOOL MakeBeacon (int interval);
static void SetStatusLED (void);
NA802dot11_t naDot11;
static void naWifiSetChannelSwitch(uint32 channelSwitchMilliseconds,
                            uint8 channelSwitchMode, uint8 channelSwitchChannel);

#if     BSP_ETH_WIFI_BRIDGE
    static void MacLinkUpToSecondary(wln_status *gStatus);
#endif

char *textAuthState[] = 
{
        "SA_LEAP (SA_INIT)",    // LEAP or Initial state
        "SA_SHARED",                    // Shared key
        "SA_OPEN",                      // Open System
        "SA_FAIL",                      // Above attempts failed
        "SA_SHARED2",                   // Shared key response
        "SA_FAIL2",                     // Shared key response failed

};

char *textStaState[] = 
{
    "SS_WAIT",    // Waiting to restart
        "SS_SCAN",    // Scanning
        "SS_BEACON",  // Waiting to receive beacon
        "SS_JOIN",    // Joined a BSS
        "SS_AUTH",    // Authenticated
        "SS_ASSOC"   // Associated
};

char *macStatusState[] =
{
    "WLN_ST_STOPPED",
        "WLN_ST_SCANNING",
        "WLN_ST_ASSOC_ESS",
        "WLN_ST_AUTH_ESS",
        "WLN_ST_JOIN_IBSS",
        "WLN_ST_START_IBSS"
};

int wln_cli_get_global_params(wln_params * params)
{
    int result = -1;
    
    if (params != NULL)
    {
        memcpy(params, &macParams, sizeof(*params));
        result = 0;
    }
    
    return result;
}


//
// Initialize the MAC layer and enable the network interface.
// Parameters must be set by wln_set_params() before we get here.
// Called from customizeIamStart() -> wlnOpen() -> wln_start().
// epStatus for primary address is allocated in wireless_init()->wln_install().
// MacInit must be called after wireless_init() calls wln_set_params().
//
MacReturn MacInit (void)
{
    MacReturn ret;

#if     BSP_WIFI_MULTIPLE_MAC
        epStatus *pSta = rootEndPoint[0];

        if (pSta == NULL)
                return WLN_ERR_NULL;
    memset (&pSta->macStatus, 0, sizeof pSta->macStatus);
#else
    memset (&macStatus, 0, sizeof macStatus);
#endif
    bcnFrame = NULL;
    respFrame = NULL;
    scan_data = NULL;
    scan_max = 0;
    scan_time = 0;
        /* entry 0 gets primary (interface) address */
#if     BSP_WIFI_MULTIPLE_MAC
        SET_ADDR(pSta->staAddr, macParams.sta_addr);
        pSta->staState = pSta->scan_state = SS_WAIT;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
        XPRINTF(TRACE_INFO, "scan_state -> %s\n\r", textStaState[pSta->scan_state]);
    pSta->mgmtTime = GET_TIME();
#else
        staState = scan_state = SS_WAIT;
    mgmtTime = GET_TIME();
#endif

    ret = InitBuffers();
    if (ret != ERR_OK)
        return ret;

#if (BSP_WPS_ENABLED == TRUE)
    naWpsRestart(FALSE);
#endif


    if ((macParams.options & WLN_OPT_MULTI_DOMAIN) || (macParams.options & WLN_OPT_SPECTRUM_MGMT))
    {
        pSta->macStatus.tx_power = -1;
        macParams.tx_power = WLN_TXPOWER_MIN;
    }

    /*
     * I have to move InitCRC32() to early execution because the calibration routines in InitHW
     * needs it
     */
    InitCRC32();  
    
    ret = InitHW();
    if (ret != ERR_OK)
    {
        ReleaseBuffers();
        return ret;
    }

    //InitCRC32();
    InitData();
    InitIR();

    // Seed the PRNG with the time and station MAC address
    // so that identical devices use a different sequence
    srand ((uint16) pSta->mgmtTime ^ macParams.sta_addr[3] ^
                                        macParams.sta_addr[4] ^ macParams.sta_addr[5]);
    memcpy (&macRandom, macParams.sta_addr+2, sizeof macRandom);

    SetLED (FALSE);

    if (macParams.bss_type != WLN_BSS_NONE)
        StartScan (pSta, macParams.ssid, macParams.ssid_len, macParams.channel, FALSE);
    else
    {
         
        SetChannel (macParams.channel);
        // Set BSSID and SSID to receive all frames
        SetBSS (0, bc_addr, NULL, 0, 0, 0);
    }

    return ERR_OK;
}

/*
 * Called by wln_register_mac_address to initialize a secondary
 * end point.
 */
MacReturn MacSecondaryInit(epStatus *pSta)
{
    memset (&pSta->macStatus, 0, sizeof pSta->macStatus);
        pSta->staState = SS_WAIT;

        return ERR_OK;
}

//
// Shutdown the MAC layer and disable the network interface
//
void MacShutdown (void)
{
#if     BSP_WIFI_MULTIPLE_MAC
        /* called from wln_stop which doesn't seem to be used. */
#else
    macStatus.state = WLN_ST_STOPPED;
#endif
    SetLED (FALSE);
    ShutdownHW();
    ReleaseBuffers();
    free (scan_data);
}


chan_counts_t recvCounts[WLN_CHAN_COUNT];       /* per channel */

void LogRecvStats(FrameControl fc)
{
        chan_counts_t *pChan = &recvCounts[global_chan];

        /* type counts are stored 0-12 mgmt 0x00 to 0xc0, 14-17 0xa4 to 0xd4
         * (rtc, cts, ack, poll), 19 data (0x08) */
        if ((fc.type & 0x0f) == 0)      /* mgmt type */
                pChan->byType[fc.type >> 4]++;
        /* store counts for types 0xa4 - 0xd4 starting at offset 14 */
        else if (fc.type & 4)
                pChan->byType[14 + (((fc.type >> 4) - 10) & 3)]++;
        else /* must be data frame */
                pChan->byType[19]++;
}


//
// Process received frames and periodic tasks.
// Called from the network task.
// @return time interval in msecs to call again
//
uint32 MacTimer (void)
{
    MacBuffer *buf;
    uint32 now;
    
    // Process receive queue
    while (TRUE)
    {
        int level = DISABLE();
        RXQ_GET (buf);
        ENABLE (level);

        if (buf)
                {
            RecvFrame (buf);
                }
        else
            break;
    }

    GET_MUTEX();

    IRTimer();

    // Process channel scanning
#if     BSP_WIFI_MULTIPLE_MAC
    if (rootEndPoint[0]->staState == SS_SCAN)
#else
    if (staState == SS_SCAN)
#endif
    {
        now = GET_TIME();
        if (now-scan_time >= SCAN_PERIOD)
        {
            scan_time = now;
            ScanChannel();
        }
    }

    if (macParams.bss_type != WLN_BSS_NONE)
    {
                int ep;

        // Process management tasks
        now = GET_TIME();
                for (ep = 0; ep < WLN_MAX_ADDRS; ep++)
                {
                        epStatus *pSta = rootEndPoint[ep];

                        if (pSta == NULL)
                                continue;

                if ((now - pSta->mgmtTime) >= MGMT_PERIOD)
                {
                pSta->mgmtTime = now;
                AgeRecvCache(); /* for just root? */
                SendMgmtFrame(pSta);
                SetStatusLED(); /* for just root? */
                UpdateRate(pSta);       /* not for just root? */
                }
                
                if (pSta->waitingForRoot)
                {
                    MacCheckPrimaryLink(pSta);
                }
                }
    }
    
    UpdateActivityLED (FALSE);

    PUT_MUTEX();

    if (rootEndPoint[0]->staState == SS_SCAN)
        return SCAN_PERIOD;
    else
        return TIMER_PERIOD;
}

//
// Scan for wireless networks.
// Results are returned with a WLN_EVT_SCAN event.
// @param ssid      Service set ID
// @param ssid_len  SSID length in bytes, 0 to scan all
// @param channel   Channel: 1-WLN_CHAN_NUM, 0 to scan all
//
#if     BSP_WIFI_MULTIPLE_MAC
int wln_scan (void *context, uint8 *ssid, int ssid_len, int channel)
{
    epStatus * pSta;

/*    if ((pSta = (epStatus *) wpa_get_usr_ptr(context)) == NULL)
 *               return -1;
 */
    pSta = (epStatus *)context;

    GET_MUTEX();
    StartScan (pSta, ssid, ssid_len, channel, TRUE);
    PUT_MUTEX();

    return 0;
}
#else
int wln_scan (uint8 *ssid, int ssid_len, int channel)
{
    GET_MUTEX();
    StartScan (ssid, ssid_len, channel, TRUE);
    PUT_MUTEX();

    return 0;
}
#endif
/*
 * This is the associate function we call from inside the driver. The
 * other is called from wpa
 */

#if     BSP_WIFI_MULTIPLE_MAC
int MacAssociate (epStatus *pSta, uint8 *ssid, int ssid_len, int channel, mac_addr bss_addr, uint16 bss_caps, uint8 *wpa_ie)
{
    GET_MUTEX();


    if (pSta->macStatus.state >= WLN_ST_ASSOC_ESS)
        wln_send_event (pSta, WLN_EVT_DISASSOC, pSta->macStatus.bss_addr);

    XPRINTF (TRACE_INFO, "Join bssid="WLN_MACSTR" ssid=%.*s chan=%d\n",
                WLN_MAC2STR(bss_addr), ssid_len, ssid, channel);

    // Set BSS info
    //__FIX_NULL_SSID_MEMORY_ACCESS
    pSta->macStatus.ssid_len = (ssid_len < WLN_SSID_SIZE) ? ssid_len : WLN_SSID_SIZE;

    if(pSta->macStatus.ssid_len)  //this is to fix vantive 29314
    {
        memset(pSta->macStatus.ssid + pSta->macStatus.ssid_len, 0, sizeof(pSta->macStatus.ssid)- pSta->macStatus.ssid_len);
        memcpy (pSta->macStatus.ssid, ssid, pSta->macStatus.ssid_len);
    }

    else
        memset(pSta->macStatus.ssid, 0, sizeof(pSta->macStatus.ssid));

    
    pSta->macStatus.channel = channel;
    pSta->macStatus.bss_caps = bss_caps;
    SET_ADDR (pSta->macStatus.bss_addr, bss_addr);
    memset (pSta->macStatus.wpa_info, 0, WLN_WPAIE_SIZE);
    memset (pSta->macStatus.rsn_info, 0, WLN_WPAIE_SIZE);
    
    if (wpa_ie)
    {
        if (wpa_ie[0] == ELEM_RSN)
            memcpy (pSta->macStatus.rsn_info, wpa_ie, wpa_ie[1] + 2);
        else
        {
                memcpy (pSta->macStatus.wpa_info, wpa_ie, wpa_ie[1] + 2);
        }
    }

    // Set BSS info in hardware
        if (pSta->entryNdx == 0)
        SetChannel (channel);
    SetBSS (pSta->macStatus.bss_caps, pSta->macStatus.bss_addr,
            pSta->macStatus.ssid, pSta->macStatus.ssid_len, 0, 0);

    // Wait to receive a beacon
        // rootEndPoint[0]->staState = SS_BEACON;
        pSta->staState = SS_BEACON;
        XPRINTF(TRACE_INFO, "%d staState -> %s (MacAssociate)\n\r", pSta->entryNdx, textStaState[pSta->staState]);

        pSta->scan_state = SS_WAIT;
        XPRINTF(TRACE_INFO, "scan_state -> %s\n\r", textStaState[pSta->scan_state]);
    mgmtTime = GET_TIME();

    PUT_MUTEX();
    return 0;
}

//
// Associate with a BSS
// @param ssid      Service set ID
// @param ssid_len  SSID length in bytes, 0 to scan all
// @param channel   Channel: 1-WLN_CHAN_NUM, 0 to scan all
// @param bss_addr  BSS to associate with
// @param bss_caps  BSS capabilities, one of more of @link WirelessCapValues
// @param wpa_ie WPA or RSN info element to send, NULL if none
//
int wln_associate (void *context, uint8 *ssid, int ssid_len, int channel, mac_addr bss_addr, uint16 bss_caps, uint8 *wpa_ie)
{
    epStatus * pSta;

/*    if ((pSta = (epStatus *) wpa_get_usr_ptr(context)) == NULL)
 *           return -1;
 */
    pSta = (epStatus *)context;

    if (wpa_ie && wpa_ie[1] + 2 > WLN_WPAIE_SIZE)
        return -1;

        return MacAssociate(pSta, ssid, ssid_len, channel, bss_addr,
                                        bss_caps, wpa_ie);
}
#else
int wln_associate (uint8 *ssid, int ssid_len, int channel, mac_addr bss_addr, uint16 bss_caps, uint8 *wpa_ie)
{
    if (wpa_ie && wpa_ie[1] + 2 > WLN_WPAIE_SIZE)
        return -1;

    GET_MUTEX();

    if (macStatus.state >= WLN_ST_ASSOC_ESS)
        wln_send_event (WLN_EVT_DISASSOC, macStatus.bss_addr);

    DPRINTF ("Join bssid=%02x:%02x:%02x:%02x:%02x:%02x ssid=%.*s chan=%d\n",
        bss_addr[0], bss_addr[1], bss_addr[2],
        bss_addr[3], bss_addr[4], bss_addr[5],
        ssid_len, ssid, channel);

    // Set BSS info
    //__FIX_NULL_SSID_MEMORY_ACCESS
    pSta->macStatus.ssid_len = (ssid_len < WLN_SSID_SIZE) ? ssid_len : WLN_SSID_SIZE;
    memset(pSta->macStatus.ssid, 0, sizeof(pSta->macStatus.ssid));
    if (pSta->macStatus.ssid_len)
    {
        memcpy (pSta->macStatus.ssid, ssid, ssid_len);
    }
    
    macStatus.channel = channel;
    macStatus.bss_caps = bss_caps;
    SET_ADDR (macStatus.bss_addr, bss_addr);
    memset (macStatus.wpa_info, 0, WLN_WPAIE_SIZE);
    memset (macStatus.rsn_info, 0, WLN_WPAIE_SIZE);
    
    if (wpa_ie)
    {
        if (wpa_ie[0] == ELEM_RSN)
            memcpy (macStatus.rsn_info, wpa_ie, wpa_ie[1] + 2);
        else
        memcpy (macStatus.wpa_info, wpa_ie, wpa_ie[1] + 2);
    }

    // Set BSS info in hardware
    SetChannel (channel);
    SetBSS (macStatus.bss_caps, macStatus.bss_addr,
            macStatus.ssid, macStatus.ssid_len, 0, 0);

    // Wait to receive a beacon
    staState = SS_BEACON;
        scan_state = SS_WAIT;
    mgmtTime = GET_TIME();

    PUT_MUTEX();
    return 0;
}
#endif

/*
 * This is the disassociate function we call from inside the driver.
 * wln_disassociate is called from wpa.
 */
#if     BSP_WIFI_MULTIPLE_MAC
int MacDisassociate (epStatus *pSta, mac_addr bss_addr, int reason)
{

    GET_MUTEX();

    // Not associated with the BSS
    if (pSta->macStatus.state < WLN_ST_ASSOC_ESS ||
        !EQUAL_ADDR (bss_addr, pSta->macStatus.bss_addr))
    {
        PUT_MUTEX();
        return -1;
    }

    if (pSta->macStatus.bss_caps & CAP_ESS)
    {
        struct MacBuffer *buf;
        // Send disassociation
        if (pSta->staState == SS_ASSOC)
        {
                buf = MacGetBuffer();
                if (buf)
                {
                buf->macHdr.fc.type = TYPE_DISASSOC;
                buf->length = DATA_SIZE + 2;
                SET16 (buf->body, reason);
                SendFrames (pSta, buf, pSta->macStatus.bss_addr);
                }
        }

        // Send deauthentication
        if (pSta->staState == SS_AUTH || pSta->staState == SS_ASSOC)
        {
                buf = MacGetBuffer();
                if (buf)
                {
                buf->macHdr.fc.type = TYPE_DEAUTH;
                buf->length = DATA_SIZE + 2;
                SET16 (buf->body, reason);
                SendFrames (pSta, buf, pSta->macStatus.bss_addr);
                }
        }
    }

    if (pSta->macStatus.state >= WLN_ST_ASSOC_ESS)
        wln_send_event (pSta, WLN_EVT_DISASSOC, pSta->macStatus.bss_addr);

    // Clear BSSID so no more frames are received
    memset (pSta->macStatus.bss_addr, 0, WLN_ADDR_SIZE);
    pSta->macStatus.bss_caps = CAP_ESS;

    // Continue scanning. Timer will call NextBSS().
    pSta->staState = SS_BEACON;
        XPRINTF(TRACE_INFO, "%d staState -> %s (MacDisassociate)\n\r", pSta->entryNdx, textStaState[pSta->staState]);

        pSta->scan_state = SS_WAIT;
        XPRINTF(TRACE_INFO, "scan_state -> %s\n\r", textStaState[pSta->scan_state]);
    mgmtTime = GET_TIME();

    /*
     * Reset dot11SpectrumManagementRequired to value stored in NVRAM at first.  We may
     * update it if we receive beacons with this option set.
     */
    naDot11.spectrumManagementRequired = ((macParams.options & WLN_OPT_SPECTRUM_MGMT) != 0);

    PUT_MUTEX();
    return 0;
}

//
// Disassociate from a BSS
// @param bss_addr BSS to disassociate from
// @param reason reason code to send
//
int wln_disassociate (void *context, mac_addr bss_addr, int reason)
{
    epStatus *pSta;

/*    if ((pSta = (epStatus *) wpa_get_usr_ptr(context)) == NULL)
 *           return -1;
 */
    pSta = (epStatus *)context;

    return MacDisassociate(pSta, bss_addr, reason);
}
#else
int wln_disassociate (mac_addr bss_addr, int reason)
{
    MacBuffer *buf;

    GET_MUTEX();

    // Not associated with the BSS
    if (macStatus.state < WLN_ST_ASSOC_ESS ||
        !EQUAL_ADDR (bss_addr, macStatus.bss_addr))
    {
        PUT_MUTEX();
        return -1;
    }

    if (macStatus.bss_caps & CAP_ESS)
    {
    // Send disassociation
    if (staState == SS_ASSOC)
    {
        buf = MacGetBuffer();
        if (buf)
        {
            buf->macHdr.fc.type = TYPE_DISASSOC;
            buf->length = DATA_SIZE + 2;
            SET16 (buf->body, reason);
            SendFrames (buf, macStatus.bss_addr);
        }
    }

    // Send deauthentication
    if (staState == SS_AUTH || staState == SS_ASSOC)
    {
        buf = MacGetBuffer();
        if (buf)
        {
            buf->macHdr.fc.type = TYPE_DEAUTH;
            buf->length = DATA_SIZE + 2;
            SET16 (buf->body, reason);
            SendFrames (buf, macStatus.bss_addr);
        }
        }
    }

    if (macStatus.state >= WLN_ST_ASSOC_ESS)
        wln_send_event (WLN_EVT_DISASSOC, macStatus.bss_addr);

    // Clear BSSID so no more frames are received
    memset (macStatus.bss_addr, 0, WLN_ADDR_SIZE);
    macStatus.bss_caps = CAP_ESS;

    // Continue scanning. Timer will call NextBSS().
    staState = SS_BEACON;
        scan_state = SS_WAIT;
    mgmtTime = GET_TIME();

    PUT_MUTEX();
    return 0;
}
#endif
//
// Send a deauthentication or disassociation frame to the sender
// of a frame received when not associated
// @param rxBuf received frame
// @param reason reason code to send
// @param deauth deauth or disassociate type to send
//
int force_disassociate (epStatus *pSta, MacBuffer *rxBuf, int reason, int deauth)
{
    MacBuffer *buf;
    int state;

    GET_MUTEX();

    buf = MacGetBuffer();
    if (buf)
    {
        if(!deauth)
            buf->macHdr.fc.type = TYPE_DISASSOC; // Send disassociation
        else
            buf->macHdr.fc.type = TYPE_DEAUTH;   // Send deauthentication

        buf->length = DATA_SIZE + 2;
        SET_ADDR (buf->macHdr.addr3, rxBuf->macHdr.addr3);
        SET16 (buf->body, reason);
    
        // Prevent SendFrames() from setting the addr3 field
        state = pSta->staState;
        pSta->staState = SS_BEACON;
                XPRINTF(TRACE_INFO, "%d staState -> %s (force_disassociate)\n\r",
                                                pSta->entryNdx, textStaState[pSta->staState]);
        SendFrames (pSta, buf, rxBuf->macHdr.addr2);
        pSta->staState = state;
                XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
    }

    PUT_MUTEX();
    return 0;
}

#if     BSP_ETH_WIFI_BRIDGE

static void MacLinkUpToSecondary(wln_status *gStatus)
{
        int ndx;

        for (ndx = 1; ndx < WLN_MAX_ADDRS; ndx++)
        {
                epStatus *pSta = rootEndPoint[ndx];

                if (pSta == NULL)
                        continue;

                /* these states may be different for open authentication or
                 * for IBSS configuration */
                pSta->staState = SS_JOIN;
                pSta->authState = SA_INIT;

                /* adopt the root's macStatus, but start our own state machine */
                memcpy(&pSta->macStatus, gStatus, sizeof *gStatus);
                pSta->macStatus.state = WLN_ST_SCANNING;
                //memset(&pSta->macStatus.wpa_info, 0, sizeof pSta->macStatus.wpa_info);
                //memset(&pSta->macStatus.rsn_info, 0, sizeof pSta->macStatus.rsn_info);

                SMPRINTF(TRACE_INFO, "Link Up "WLN_MACSTR" state %s (%s)\n\r",
                        WLN_MAC2STR(pSta->staAddr), textStaState[pSta->staState],
                        macStatusState[pSta->macStatus.state]);

                /* Go right to association sequence without requiring a beacon.
                 * The primary address has already gotten a beacon. This is the
                 * sequence from RecvBeacon. */

        if (pSta->macStatus.bss_caps & CAP_ESS)
        {
            // Send authentication frame if ESS
            pSta->staState = SS_JOIN;
        SMPRINTF(TRACE_INFO, "%d staState -> %s (LinkUp)\n\r", pSta->entryNdx, textStaState[pSta->staState]);
            pSta->authState = SA_INIT;
        SMPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
            SendNextMgmtFrame(pSta);
        }
        else
        {
            pSta->staState = SS_ASSOC;
        SMPRINTF(TRACE_INFO, "%d staState -> %s (LinkUp)\n\r", pSta->entryNdx, textStaState[pSta->staState]);
            pSta->macStatus.state = WLN_ST_JOIN_IBSS;
        SMPRINTF(TRACE_INFO, "%d macStatus.state -> %s\n\r", pSta->entryNdx, macStatusState[pSta->macStatus.state]);
            wln_send_event (pSta, WLN_EVT_ASSOC, pSta->macStatus.bss_addr);
        }
        }
}

#endif

void MacCheckPrimaryLink(epStatus *pSta)
{
        epStatus *pRoot;
        wln_status *gStatus;
    unsigned readyToStartSecondaryEndpoints = FALSE;
    
        if (pSta == NULL)
                return;
        pRoot = rootEndPoint[0];

    /*
     * The code below determines if the wireless endpoint has associated with
     * the AP yet.  It is used when we are supporting the Wifi/Ethernet bridge.
     * We wait until we have associated with the primary MAC address before adding
     * any new ones.  We only support briding in EBSS mode.  
     */
    if ((pRoot->staState == SS_ASSOC) && (pRoot->macStatus.bss_caps & CAP_ESS))
    {
        if (pRoot->macStatus.state == WLN_ST_AUTH_ESS)
        {
            /*
             * We are ready if the primary endpoint has authenticated.
             */
            readyToStartSecondaryEndpoints = TRUE;
        }
        else if (    (pRoot->macStatus.state == WLN_ST_ASSOC_ESS)
                  && (pRoot->macStatus.wpa_info_len == 0))
        {
            /* 
             * We are ready if the primary endpoint has associated and 
             * we are not using WPA.
             */
            readyToStartSecondaryEndpoints = TRUE;
        }
    }

#define SECONDARY_ENDPOINT_DELAY    (10)
    if (readyToStartSecondaryEndpoints)
    {
        if (pSta->secondaryEndpointDelayTimer != SECONDARY_ENDPOINT_DELAY)
        {
            readyToStartSecondaryEndpoints = FALSE;
            pSta->secondaryEndpointDelayTimer++;
        }
    }
    else
    {
        pSta->secondaryEndpointDelayTimer = 0;
    }
    
        if (readyToStartSecondaryEndpoints)
        {
            pSta->waitingForRoot = FALSE;
            pSta->secondaryEndpointDelayTimer = 0;
                gStatus = &pRoot->macStatus;

                /* these states may be different for open authentication or
                 * for IBSS configuration */
                pSta->staState = SS_JOIN;
                pSta->authState = SA_INIT;

                /* adopt the root's macStatus, but start our own state machine */
                memcpy(&pSta->macStatus, gStatus, sizeof *gStatus);
                pSta->macStatus.state = WLN_ST_SCANNING;
        SetDefaultKeys(pSta);                   /* set default encryption keys in case open authentication */

                SMPRINTF(TRACE_INFO, "Link Up "WLN_MACSTR" state %s (%s)\n\r",
                        WLN_MAC2STR(pSta->staAddr), textStaState[pSta->staState],
                        macStatusState[pSta->macStatus.state]);

                /* Go right to association sequence without requiring a beacon.
                 * The primary address has already gotten a beacon. This is the
                 * sequence from RecvBeacon. */

        if (pSta->macStatus.bss_caps & CAP_ESS)
        {
            /*
             * If we reach here, then we are associated with an AP
             * and are in infrastructure mode.
             */
            // Send authentication frame if ESS
            pSta->staState = SS_JOIN;
        SMPRINTF(TRACE_INFO, "%d staState -> %s (LinkUp)\n\r", pSta->entryNdx, textStaState[pSta->staState]);
            pSta->authState = SA_INIT;
        SMPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
            SendNextMgmtFrame(pSta);
        }
        else
        {
            /*
             * If we come here, then we are not associated with an AP
             * and are in ad hoc mode.
             */
            pSta->staState = SS_ASSOC;
        SMPRINTF(TRACE_INFO, "%d staState -> %s (LinkUp)\n\r", pSta->entryNdx, textStaState[pSta->staState]);
            pSta->macStatus.state = WLN_ST_JOIN_IBSS;
        SMPRINTF(TRACE_INFO, "%d macStatus.state -> %s\n\r", pSta->entryNdx, macStatusState[pSta->macStatus.state]);
            wln_send_event (pSta, WLN_EVT_ASSOC, pSta->macStatus.bss_addr);
                pSta->waitingForRoot = TRUE;       /* Need to try again on timer */
        }
        }
        else
        {
            pSta->waitingForRoot = TRUE;       /* Need to try again on timer */
    }
}


/*
 * Determine the link margin for a receive frame.  
 * The link margin is the difference between the receive sensitivity of the 
 * radio (fixed for each rate) and the receive signal strength of the signal.  
 * Our measurement of the received signal strength is based upon the receive 
 * gain setting that the AGC converges upon.  This, of course, can vary over 
 * temperature and device process, but we can use a typical receive 
 * strength (dBm) versus receive gain level for now.  
 *
 * LINK MARGIN = RX_PWR - SENSITIVITY
 * RX_PWR = -5 - RX_GAIN
 * RX GAIN = LNA1*20dB + LNA2*17dB + VGA*2dB 
 *
 * We determine sensitivity by using the data rate to index a look up table.  
 * The table values were determined by Mike Schaffner (as was the above formula).
 *
 */
static uint8 determineLinkMargin(MacBuffer *receiveFrame)
{
    /*
     * This table relates the data rate to the sensivity.  The data rate is in
     * the first column and is multiplied by 2 because the 4'th data rate is actually
     * 5.5 MBps (not 11), and I don't want to use floating point numbers.  The sensivity
     * is in dBm.
     */
    const int16 sensitivity[][2] = 
    { 
        {2,   -95},
        {4,   -92},
        {11,  -91},
        {22,  -87},
        {12,  -89},
        {18,  -88},
        {24,  -87},
        {36,  -85},
        {48,  -83},
        {72,  -79},
        {96,  -75},
        {108, -72}
    };
    uint16 tableRate;
    int16 linkMargin = 0;
    unsigned i;
    
    uint16 ln1 = (receiveFrame->frameHdr.rx.rssiLNA & 2) >> 1;
    uint16 ln2 = (receiveFrame->frameHdr.rx.rssiLNA & 1);

    /*
     * Actually, this formula becomes inaccurate when the RSSI value is larger
     * than 0x76.  However, the noise level becomes so high that it is also
     * unlikely that we will receive packets at that point either.
     */
    int16 rxPower = -5 - (ln1*20 + ln2*17 + receiveFrame->frameHdr.rx.rssiVGA*2);
    
    if (receiveFrame->frameHdr.rx.modType == MOD_OFDM)
    {
        /*
         * This look up table is used to convert the OFDM rate field in the
         * packet header into a real rate.
         */
        const uint16 ofdmRateTable[] = 
        {
            48, 24, 12, 6, 54, 36, 18, 9
        };
        
        PlcpHeader plcp;
        unsigned rateIndex;
        
        plcp.plcp32 = SWAP32 (receiveFrame->plcpHdr.plcp32);
        rateIndex = (plcp.ofdm.rate & 0x7);
        
        tableRate = 2 * ofdmRateTable[rateIndex];

    }
    else /* if PSK modulation */
    {
        /*
         * Signal field in psk header is rate in 100KHz.  Multiply it by
         * 20 to get 2 times the rate in MHz.  Remember that we use 2 times
         * because we don't want to have to deal with the 5.5 float number.
         */
        tableRate = receiveFrame->plcpHdr.pskcck.signal / 5;
    }

    /*
     * We have a rate now, which is actually 2 times the real rate, but
     * will work for our table with is based on 2 times the real rate.
     */
    for (i = 0; i < sizeof (sensitivity) / sizeof(sensitivity[0]); i++)
    {
        if (sensitivity[i][0] == tableRate)
        {
            linkMargin = rxPower - sensitivity[i][1];
            break;
        }
    }

    if (linkMargin < 0)
    {
        linkMargin = 0;
    }
    
    return (uint8) linkMargin;
}




/*
 * Seperate routine to generate a TCP report element because we used them
 * in IBSS beacons as well as action messages.
 */
static uint8 *generateTpcElement(uint8 linkMargin, uint8 *bp)
{
    *bp = ELEM_TPC_REPORT;
    bp++;
    *bp = 2;        /* length is 2 */
    bp++;
    *bp = naDot11.currentTxPower;
    bp++;
    *bp = linkMargin;
    bp++;
    
    return bp;
}

/*
 * Generate an action message with a TPC report.
 */
static void sendTpcReport(epStatus *pSta, MacAddr destAddress, uint8 linkMargin, uint8 dialogToken)
{
    uint8 *bp;

    MacBuffer *buf = MacGetBuffer();
    if (buf == NULL)
        return;
        
    bp = buf->body;
    *bp = ACTION_CAT_SPECTRUM_MGMT;
    bp++;
    *bp = ACTION_SPECTRUM_MGMT_TPC_REPORT;
    bp++;
    *bp = dialogToken;
    bp++;
    bp = generateTpcElement(linkMargin, bp);

    buf->macHdr.fc.type = TYPE_ACTION;
    buf->length = DATA_SIZE + (bp - buf->body);
    SendFrames (pSta, buf, destAddress);
}
    


/*
 * This routine sends out a measure report.  Usually these are requested by the AP we are
 * currently associated with.  
 */
void sendMeasureReport(epStatus *pSta, MacAddr destAddress, uint8 dialogToken, uint8 measureToken, uint8 reportMode, uint8 reportType, uint8 *report, uint8 reportLength)
{
    uint8 *bp;

    MacBuffer *buf = MacGetBuffer();
    if (buf == NULL)
        return;
        
    bp = buf->body;
    *bp = ACTION_CAT_SPECTRUM_MGMT;
    bp++;
    *bp = ACTION_SPECTRUM_MGMT_MEASURE_REPORT;
    bp++;
    *bp = dialogToken;
    bp++;
    *bp = ELEM_MEASURE_REPORT;
    bp++;
    *bp = 3 + reportLength;
    bp++;
    *bp = measureToken;
    bp++;
    *bp = reportMode;
    bp++;
    *bp = reportType;
    bp++;
    
    if (reportLength)
    {
        memcpy(bp, report, reportLength);
        bp += reportLength;
    }
    
    buf->macHdr.fc.type = TYPE_ACTION;
    buf->length = DATA_SIZE + (bp - buf->body);
    SendFrames (pSta, buf, destAddress);
}

/* #define WANT_802dot11h_DEBUG_CODE*/
#ifdef WANT_802dot11h_DEBUG_CODE

void sendChannelSwitch(MacAddr destAddress, uint8 channel)
{
    uint8 *bp;

    MacBuffer *buf = MacGetBuffer();
    if (buf == NULL)
        return;
        
    bp = buf->body;
    *bp = ACTION_CAT_SPECTRUM_MGMT;
    bp++;
    *bp = ACTION_SPECTRUM_MGMT_CHANNEL_SWITCH_ANNOUNCEMENT;
    bp++;
    *bp = ELEM_CHANNEL_SWITCH;
    bp++;
    *bp = 3;
    bp++;
    *bp = 1;
    bp++;
    *bp = channel;
    bp++;
    *bp = 1;
    bp++;
    
    buf->macHdr.fc.type = TYPE_ACTION;
    buf->length = DATA_SIZE + (bp - buf->body);
    SendFrames (rootEndPoint[0], buf, destAddress);
}


void sendTpcRequest(MacAddr destAddress, uint8 dialogToken)
{
    uint8 *bp;

    MacBuffer *buf = MacGetBuffer();
    if (buf == NULL)
        return;
        
    bp = buf->body;
    *bp = ACTION_CAT_SPECTRUM_MGMT;
    bp++;
    *bp = ACTION_SPECTRUM_MGMT_TPC_REQUEST;
    bp++;
    *bp = dialogToken;
    bp++;
    *bp = ELEM_TPC_REQUEST;
    bp++;
    *bp = 0;
    bp++;
    
    buf->macHdr.fc.type = TYPE_ACTION;
    buf->length = DATA_SIZE + (bp - buf->body);
    SendFrames (rootEndPoint[0], buf, destAddress);
}



void sendMeasureRequest(MacAddr destAddress, uint8 dialogToken, unsigned basicCount, unsigned ccaCount, unsigned rpiCount)
{
    uint8 *bp;
    unsigned i;
    uint8 measureToken = 0;
    uint8 channelNumber = wln_get_chan_info(global_chan, NULL);
    
    MacBuffer *buf = MacGetBuffer();
    if (buf == NULL)
        return;
        
    bp = buf->body;
    *bp = ACTION_CAT_SPECTRUM_MGMT;
    bp++;
    *bp = ACTION_SPECTRUM_MGMT_MEASURE_REQUEST;
    bp++;
    *bp = dialogToken;
    bp++;

    for (i = 0; i < basicCount; i++)
    {        
        uint8 *start = bp;
        uint8 *length = bp+1;
        uint32 tsfLow, tsfHigh;
        
        *bp = ELEM_MEASURE_REQUEST;
        bp++;
        /* We'll poke the length in later*/
        bp++;
        *bp = measureToken++;
        bp++;
        *bp = 0xe;          /* enable measure responses */
        bp++;
        *bp = MEASURE_REQUEST_TYPE_BASIC;
        bp++;
        *bp = channelNumber++;
        bp++;
        tsfLow = naDot11.tsfLow+200000;
        tsfHigh = naDot11.tsfHigh;
        if (tsfLow < naDot11.tsfLow)
        {
            tsfHigh++;
        }
        SET_TSF(tsfHigh, tsfLow, bp);
        bp += 8;
        *bp = 0;            /* 2 mills measure time */
        bp++;
        *bp = 2;
        bp++;
        *length = (bp - start) - 2;
    }
    for (i = 0; i < ccaCount; i++)
    {        
        uint8 *start = bp;
        uint8 *length = bp+1;
        *bp = ELEM_MEASURE_REQUEST;
        bp++;
        /* We'll poke the length in later*/
        bp++;
        *bp = measureToken++;
        bp++;
        *bp = 0xe;          /* enable measure responses */
        bp++;
        *bp = MEASURE_REQUEST_TYPE_CAA;
        bp++;
        *bp = channelNumber++;
        bp++;
        memset(bp, 0, 8);
        bp += 8;
        *bp = 0;            /* 2 mills measure time */
        bp++;
        *bp = 2;
        bp++;
        *length = (bp - start) - 2;
    }
    for (i = 0; i < rpiCount; i++)
    {        
        uint8 *start = bp;
        uint8 *length = bp+1;
        *bp = ELEM_MEASURE_REQUEST;
        bp++;
        /* We'll poke the length in later*/
        bp++;
        *bp = measureToken++;
        bp++;
        *bp = 0xe;          /* enable measure responses */
        bp++;
        *bp = MEASURE_REQUEST_TYPE_RPI_HISTOGRAM;
        bp++;
        *bp = channelNumber++;
        bp++;
        memset(bp, 0, 8);
        bp += 8;
        *bp = 0;            /* 2 mills measure time */
        bp++;
        *bp = 2;
        bp++;
        *length = (bp - start) - 2;
    }
        
    buf->macHdr.fc.type = TYPE_ACTION;
    buf->length = DATA_SIZE + (bp - buf->body);
    SendFrames (rootEndPoint[0], buf, destAddress);
}


#endif




/*
 * This function may be called from a timer to start a quiet period announced
 * by a quiet element in a beacon, or to quiet the transmitter in preparation
 * for a measure test.
 *
 * Since this function may be called from a timer, the ULONG parameter is actually
 * a pointer to the duration in milliseconds.
 */
void naWifiStartQuietPeriod(ULONG durationPtr)
{
    uint32 duration = *(uint32 *) durationPtr;
    uint32 ticks = tx_time_get();
    uint32 periodEnd;
    uint32 overflowCorrection;
    
    /*
     * Convert the duration in milliseconds, to ticks.
     */
    duration = ((duration * NABspTicksPerSecond) + (NABspTicksPerSecond / 2)) / 1000;
    periodEnd = ticks + duration;
    
    overflowCorrection = (periodEnd < ticks) ? 0 - ticks : 0;
    
    if (    (naDot11.quiet.transmitterStopped) 
        &&  ((naDot11.quiet.periodEnd + overflowCorrection + naDot11.quiet.overflowCorrection) 
                < (periodEnd + overflowCorrection + naDot11.quiet.overflowCorrection)))
    {
        /*
         * Sorry for all the annoying math above, it's to compensate for an overflow condition
         * that happens about once every thousand years.  The code checks for the case where
         * we are called while a quiet period is in progress, and the new quiet period would
         * end before the current one.  In this case, we just ignore the request.
         */
    }
    else /* if transmitter not stopped, or current quiet period ends before new one*/
    {
        naDot11.quiet.periodEnd = periodEnd;
        naDot11.quiet.overflowCorrection = overflowCorrection;
        naDot11.quiet.transmitterStopped = TRUE;
    }
}



void naFinishBasicMeasure(void)
{
#define BASIC_MEASURE_REPORT_SIZE       (12)
#define CHANNEL_INDEX                   (0)
#define START_TIME_INDEX                (1)
#define DURATION_INDEX                  (9)
#define MAP_INDEX                       (11)
    uint8 report[BASIC_MEASURE_REPORT_SIZE];

    report[CHANNEL_INDEX] = naDot11.basicMeasureRequest.channel;
    SET_TSF(naDot11.basicMeasureRequest.startHigh, naDot11.basicMeasureRequest.startLow, &report[START_TIME_INDEX]);
    memcpy(&report[DURATION_INDEX], &naDot11.basicMeasureRequest.duration, sizeof(naDot11.basicMeasureRequest.duration));
    report[MAP_INDEX] = naWifiStartHWGetBasicMeasureMap();
    
    sendMeasureReport(naDot11.basicMeasureRequest.pSta, 
                      naDot11.basicMeasureRequest.destAddress, 
                      naDot11.basicMeasureRequest.dialogToken, 
                      naDot11.basicMeasureRequest.measureToken, 
                      MEASURE_REPORT_MODE_SUCCESS, 
                      MEASURE_REQUEST_TYPE_BASIC, 
                      report, 
                      BASIC_MEASURE_REPORT_SIZE);
}




int32 naWifiMicrosecondsLeft(uint32 timerHigh, uint32 timerLow)
{
    int32 result;

    if ((timerHigh == 0) && (timerLow == 0))
    {   /* 0,0 means start immediately */
        result = 0;
    }
    else
    {
        uint32 uSperTick = 1000000L / NABspTicksPerSecond;
        long long currentTime = (((long long) naDot11.tsfHigh) << 32) + naDot11.tsfLow + ((tx_time_get() - naDot11.ticksAtLastBeacon) * uSperTick);
        long long timer = (((long long) timerHigh) << 32) + timerLow;
        
        result = (int32) ((timer - currentTime) & 0xffffffff);
    }
        
    return result;
}


    
void naStartBasicMeasure(ULONG notUsed)
{
#define MAX_MEASURE_DURATION        (10)
    int32 uS = naWifiMicrosecondsLeft(naDot11.basicMeasureRequest.timerHigh, naDot11.basicMeasureRequest.timerLow);
    
    NA_UNUSED_PARAM(notUsed);
    
    if (uS < 0)
    {
        sendMeasureReport(naDot11.basicMeasureRequest.pSta, 
                          naDot11.basicMeasureRequest.destAddress, 
                          naDot11.basicMeasureRequest.dialogToken, 
                          naDot11.basicMeasureRequest.measureToken, 
                          MEASURE_REPORT_MODE_LATE, 
                          MEASURE_REQUEST_TYPE_BASIC, 
                          NULL, 0);
    }
    else
    {
        if (naDot11.basicMeasureRequest.duration < MAX_MEASURE_DURATION)
        {
            ULONG ticks = ((naDot11.basicMeasureRequest.duration * NABspTicksPerSecond) + (NABspTicksPerSecond / 2)) / 1000;
            unsigned duration32 = naDot11.basicMeasureRequest.duration;   /* convert 16-bit to 32-bit*/
            
            naWifiWaitUS(uS);
            naWifiStartQuietPeriod((ULONG) &duration32);
            naWifiStartHWBasicMeasure();
            if ((naDot11.basicMeasureRequest.timerHigh == 0) && (naDot11.basicMeasureRequest.timerLow == 0))
            {
                naDot11.basicMeasureRequest.startLow = naDot11.tsfLow + naWifiMicrosecondsLeft(naDot11.tsfHigh, naDot11.tsfLow);
                naDot11.basicMeasureRequest.startHigh = naDot11.tsfHigh;
                if (naDot11.basicMeasureRequest.startLow < naDot11.tsfLow)
                {
                    naDot11.basicMeasureRequest.startHigh++;
                }
            }
            else
            {
                /*
                 * We started the measure as close to the requested time as we could, and 
                 * we don't have a way of knowing exactly how many uS we are off, so just
                 * say we started it on time.
                 */
                naDot11.basicMeasureRequest.startLow = naDot11.basicMeasureRequest.timerLow;
                naDot11.basicMeasureRequest.startHigh = naDot11.basicMeasureRequest.timerHigh;
            }
            tx_thread_sleep(ticks);
            naFinishBasicMeasure();
        }
        else
        {
            /*
             * There really is no way we can support timer periods longer than a few milliseconds,
             * so just refuse reports that request that.
             */
            sendMeasureReport(naDot11.basicMeasureRequest.pSta, 
                              naDot11.basicMeasureRequest.destAddress, 
                              naDot11.basicMeasureRequest.dialogToken, 
                              naDot11.basicMeasureRequest.measureToken, 
                              MEASURE_REPORT_MODE_REFUSED, 
                              MEASURE_REQUEST_TYPE_BASIC, 
                              NULL, 0);
        }
    }
}        
    


static void naWifiSetMeasureTimer(uint32 startTimeHi, uint32 startTimeLow)
{
    static TX_TIMER measureTimer;
    static unsigned initialized = FALSE;
    uint32 milliseconds = (naWifiMicrosecondsLeft(startTimeHi, startTimeLow) / 1000) - 1;
    uint32 ticksToNextMeasure = ((milliseconds * NABspTicksPerSecond) + (NABspTicksPerSecond / 2)) / 1000;
    
    if (ticksToNextMeasure == 0)
    {
        ticksToNextMeasure++;
    }
    
    if (!initialized)
    {
        /*
         * If first time, then create the timer and program it to start the quiet
         * period.
         */
        UINT ccode;
        
        memset(&measureTimer, 0, sizeof(measureTimer));
        ccode = tx_timer_create(&measureTimer, "Wifi Measure", naStartBasicMeasure, 
                                0, ticksToNextMeasure, 0, TX_AUTO_ACTIVATE);
        if (ccode != TX_SUCCESS)
        {
            panic("naWifiSetMeasureTimer: tx_timer_create failed.");
        }
        initialized = TRUE;
    }
    else
    {
        /*
         * If the timer has already been created, then find it its current state.
         */
        CHAR *name = NULL;
        UINT active = 0;
        ULONG ticksToGo = 0;
        ULONG rescheduleTicks = 0;
        TX_TIMER *next = NULL;
        
        UINT ccode = tx_timer_info_get(&measureTimer, &name, &active, &ticksToGo,
                                       &rescheduleTicks, &next);
        if (ccode != TX_SUCCESS)
        {
            panic("naWifiSetMeasureTimer: tx_timer_info_get failed");
        }
        
        if (active)
        {
            if (ticksToGo > ticksToNextMeasure)
            {
                /*
                 * If it is already running, but the new quiet period is sooner than
                 * the current one, then reprogram it for the new period.
                 */
                tx_timer_deactivate(&measureTimer);
                tx_timer_change(&measureTimer, ticksToNextMeasure, 0);
                tx_timer_activate(&measureTimer);
            }
            /* else if current comes before the new, then stick with the current program */
        }
        else
        {
            /*
             * If the timer has already expired, then reprogram and reactivate it.
             */
            tx_timer_change(&measureTimer, ticksToNextMeasure, 0);
            tx_timer_activate(&measureTimer);
        }
    }
}



/*
 * This routine handles a Basic Measure request.  
 */
static void handleBasicMeasureRequest(epStatus *pSta, MacAddr destAddress, uint8 dialogToken, 
                               uint8 measureToken, uint8 channelNumber, 
                               uint32 startTimeHi, uint32 startTimeLo, 
                               uint32 duration)
{
    (void) channelNumber;
    (void) startTimeHi;
    (void) startTimeLo;
    (void) duration;
    
    if (wln_get_chan_info(global_chan, NULL) != channelNumber)
    {
        sendMeasureReport(pSta, destAddress, dialogToken, measureToken, MEASURE_REPORT_MODE_INCAPABLE, MEASURE_REQUEST_TYPE_BASIC, NULL, 0);
    }
        
    naDot11.basicMeasureRequest.timerHigh = startTimeHi;
    naDot11.basicMeasureRequest.timerLow = startTimeLo;
    naDot11.basicMeasureRequest.duration = duration;
    naDot11.basicMeasureRequest.dialogToken = dialogToken;
    naDot11.basicMeasureRequest.measureToken = measureToken;
    naDot11.basicMeasureRequest.pSta = pSta;
    memcpy (naDot11.basicMeasureRequest.destAddress, destAddress, sizeof (naDot11.basicMeasureRequest.destAddress));
    naDot11.basicMeasureRequest.channel = channelNumber;
    
    if (naWifiMicrosecondsLeft(startTimeHi, startTimeLo) < (2*NA_WIFI_ONE_MILLISECOND))
    {
        naStartBasicMeasure(0);
    }
    else 
    {
        naWifiSetMeasureTimer(startTimeHi, startTimeLo);
    }
}


void handleActionFrameMeasureRequest(epStatus *pSta, MacBuffer *buf)
{
    uint8 dialogToken = buf->body[2];
    uint8 *p = &buf->body[3];
    uint8 *end = &buf->body[buf->length - FCS_SIZE];       /* don't count crc field*/
    
    while ((*p == ELEM_MEASURE_REQUEST) && (p < end))
    {
        uint length, measureRequestType, measureToken;
        
        length = p[1];
        measureToken = p[2];
        measureRequestType = p[4];
        
        if (measureRequestType == MEASURE_REQUEST_TYPE_BASIC)
        {
            /*
             * We branch here if we receive a basic request type.  We are required to handle
             * those.
             */
            uint32 startTimeHi;
            uint32 startTimeLo;
            uint16 duration;
            
            GET_TSF(startTimeHi, startTimeLo, &p[6]);
            memcpy(&duration, &p[14], sizeof(duration));
            handleBasicMeasureRequest(pSta, buf->macHdr.addr2, dialogToken, measureToken, p[5], startTimeHi, startTimeLo, duration);
        }
        else
        {
            /*
             * We branch here if we receive some other request type.  We are not required to
             * handle it so send a response that says our H/W doesn't support it.
             */
            sendMeasureReport(pSta, buf->macHdr.addr2, dialogToken, measureToken, MEASURE_REPORT_MODE_INCAPABLE, measureRequestType, NULL, 0);
        }
        p = p + length + 2;
    }
        
}


void handleActionFrameMeasureReport(epStatus *pSta, MacBuffer *buf)
{
    NA_UNUSED_PARAM(pSta);
    NA_UNUSED_PARAM(buf);
    
    XPRINTF(TRACE_WARNING, "TODO: Implement handleActionFrameMeasureReport\n");
}


void handleActionFrameTpcRequest(epStatus *pSta, MacBuffer *buf)
{
    sendTpcReport(pSta, buf->macHdr.addr2, determineLinkMargin(buf), buf->body[2]);
}


void handleActionFrameTpcReport(epStatus *pSta, MacBuffer *buf)
{
    NA_UNUSED_PARAM(pSta);
    NA_UNUSED_PARAM(buf);
    
    /* nothing to do since we ignore them */
}


void handleActionFrameChannelSwitchAnnouncement(epStatus *pSta, MacBuffer *buf)
{
    uint8 mode = buf->body[4];
    uint8 channel = buf->body[5];
    uint32 millsPerBeacon = rootEndPoint[0]->tbttMilliseconds;
    uint32 millsBeforeSwitch = 0;


    if (millsPerBeacon == 0)
    {
        millsPerBeacon = DEFAULT_MILLS_PER_BEACON;
    }
    
    millsBeforeSwitch = buf->body[6] * millsPerBeacon;
    
    NA_UNUSED_PARAM(pSta);
    
    naWifiSetChannelSwitch(millsBeforeSwitch, mode, channel);
}





/*
 * This routine handles an action message from the AP.  We parse the message type and
 * call the appropriate dispatch routine.
 */
void handleActionMessage(epStatus *pSta, MacBuffer *buf)
{
    uint8 category = buf->body[0];
    uint8 actionCode = buf->body[1];
    
    if (category == ACTION_CAT_SPECTRUM_MGMT)
    {
        switch (actionCode)
        {
            case ACTION_SPECTRUM_MGMT_MEASURE_REQUEST:
                    XPRINTF(TRACE_WARNING, "Action message measure request received\n");
                handleActionFrameMeasureRequest(pSta, buf);
                break;
            case ACTION_SPECTRUM_MGMT_MEASURE_REPORT:
                    XPRINTF(TRACE_WARNING, "Action message measure report received\n");
                handleActionFrameMeasureReport(pSta, buf);
                break;
            case ACTION_SPECTRUM_MGMT_TPC_REQUEST:
                    XPRINTF(TRACE_WARNING, "Action message TPC request received\n");
                handleActionFrameTpcRequest(pSta, buf);
                break;
            case ACTION_SPECTRUM_MGMT_TPC_REPORT:
                    XPRINTF(TRACE_WARNING, "Action message TPC report received\n");
                handleActionFrameTpcReport(pSta, buf);
                break;
            case ACTION_SPECTRUM_MGMT_CHANNEL_SWITCH_ANNOUNCEMENT:
                    XPRINTF(TRACE_WARNING, "Action message channel switch announcement received\n");
                handleActionFrameChannelSwitchAnnouncement(pSta, buf);
                break;
            default:
                    XPRINTF(TRACE_WARNING, "***** handleActionMessage: Unknown spectrum management message %d received\n", actionCode);
                    break;
        }
    }
    else
    {
            XPRINTF(TRACE_WARNING, "***** handleActionMessage: Unknown action message category %d\n", category);
        }
}

                



//
// Process a received management frame
// @param buf Received frame
//
// Use staState for the primary (root) address for beacons, scans, etc.
// but use staState for the specific target for auth and assoc, etc.
//
void RecvMgmtFrame (epStatus *pSta, MacBuffer *buf)
{
        epStatus *pRoot = rootEndPoint[0];

        if (pSta == NULL)       /* broadcast address */
                pSta = rootEndPoint[0];

        if (pSta->entryNdx != 0)
                SMPRINTF(TRACE_INFO, "RecvMgmt "WLN_MACSTR" type %x state %s (%s)\n\r",
                        WLN_MAC2STR(pSta->staAddr), buf->macHdr.fc.type,
                        textStaState[pSta->staState],
                        macStatusState[pSta->macStatus.state]);

    // Ignore frame if not:
    //   Beacon or probe response, or
    //   Joined ESS and from this BSS
    if (!(buf->macHdr.fc.type == TYPE_BEACON ||
/*** Debug Code  buf->macHdr.fc.type == TYPE_ACTION || */
          buf->macHdr.fc.type == TYPE_PROBE_RESP ||
          ((pSta->macStatus.bss_caps & CAP_ESS) &&
                         EQUAL_ADDR (buf->macHdr.addr2, pSta->macStatus.bss_addr))))
    {
        MacFreeBuffers (buf);
        return;
    }

        if ((buf->macHdr.fc.type != TYPE_BEACON)        /* too many of these */
                && (buf->macHdr.fc.type != TYPE_PROBE_RESP))
    {
            XPRINTF(TRACE_WARNING, "RecvMgmtFrame type %x\n\r", buf->macHdr.fc.type);
        }

    GET_MUTEX();

    switch (buf->macHdr.fc.type)
    {
        case TYPE_BEACON:
            // Accumulate timestamp and RSSI into random value
            macRandom ^= GET32 (buf->body) ^ buf->rx_signal;

            // Accept beacon as scan result if passively scanning for 802.11d.
            // Otherwise, don't accept beacon because it may not contain
                        // the SSID.
            // Probe responses are better because they do contain the SSID.
            if (pRoot->staState == SS_SCAN &&
                ((macParams.options & WLN_OPT_MULTI_DOMAIN) || (macParams.options & WLN_OPT_SPECTRUM_MGMT)) &&
                pSta->macStatus.tx_power < 0)
            {
                RecvScan (buf);
            }
            else if (pRoot->staState >= SS_BEACON)
            {
                RecvBeacon (buf);
            }
            break;

        case TYPE_PROBE_RESP:
            if (pRoot->staState == SS_SCAN)
                RecvScan (buf);

            break;
            
        case TYPE_DEAUTH:
                        /* see REASON_xxx codes in wpa.h */
            XPRINTF (TRACE_INFO, "Deauthentication %d\n", GET16 (&buf->body[0]));
            if (pSta->staState == SS_ASSOC)
            {
                wln_send_event (pSta, WLN_EVT_DISASSOC, pSta->macStatus.bss_addr);

                if (GET16 (&buf->body[0]) == REAS_NOLONGERVALID ||
                    GET16 (&buf->body[0]) == REAS_INACTIVITY)
                {
                    // Send authentication request
                    pSta->staState = SS_JOIN;
        XPRINTF(TRACE_INFO, "%d staState -> %s (TYPE_DEAUTH)\n\r", pSta->entryNdx, textStaState[pSta->staState]);
                    pSta->authState = SA_INIT;
        XPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
                    SendNextMgmtFrame(pSta);
                }
                else
                    NextBSS(pSta);
            }
            else if (pSta->staState == SS_AUTH)
                NextBSS(pSta);
            break;

        case TYPE_AUTH:
            XPRINTF (TRACE_INFO, "Authentication %d\n", GET16 (&buf->body[4]));
            if (pSta->staState == SS_JOIN)
                RecvAuthentication (buf);
            break;

        case TYPE_DISASSOC:
            XPRINTF (TRACE_INFO, "Disassociation %d\n", GET16 (&buf->body[0]));
            if (pSta->staState == SS_ASSOC)
            {
                wln_send_event (pSta, WLN_EVT_DISASSOC, pSta->macStatus.bss_addr);

                if (GET16 (&buf->body[0]) == REAS_INACTIVITY)
                {
                    // Send authentication request
                    pSta->staState = SS_JOIN;
        XPRINTF(TRACE_INFO, "%d staState -> %s (TYPE_DISASSOC)\n\r", pSta->entryNdx, textStaState[pSta->staState]);
                    pSta->authState = SA_INIT;
        XPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
                    SendNextMgmtFrame(pSta);
                }
                else
                    NextBSS(pSta);
            }
            break;

        case TYPE_ASSOC_RESP:
            XPRINTF (TRACE_INFO, "Association response %d\n", GET16 (&buf->body[2]));
            if (pSta->staState == SS_AUTH)
            {
                // Check association response
                if (((GET16 (&buf->body[0]) & (CAP_ESS|CAP_IBSS)) == CAP_ESS) &&
                    (GET16 (&buf->body[2]) == STAT_SUCCESS))
                {
                    pSta->staState = SS_ASSOC;
                    if (pSta == pRoot)
                    {
                        /*
                         * Once we are sure we are associated with this BSS, update
                         * dot11SpectrumManagementRequired so that it is set if it is
                         * required by the BSS.
                         */
                        naDot11.spectrumManagementRequired = 
                                ((pRoot->macStatus.bss_caps & WLN_OPT_SPECTRUM_MGMT) != 0);
                    }
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
                    pSta->macStatus.state = WLN_ST_ASSOC_ESS;
        XPRINTF(TRACE_INFO, "%d macStatus.state -> %s\n\r", pSta->entryNdx, macStatusState[pSta->macStatus.state]);

#if (BSP_WPS_ENABLED == TRUE)
                    if (macParams.bss_type == WLN_BSS_WPS)
                    {
                        pSta->macStatus.state = WLN_ST_ASSOC_WPS;
                        naWpsEvent(pSta, WPS_EVENT_START, pSta->macStatus.bss_addr);
//                        wln_send_event (WLN_EVT_WPS_START, macStatus.bss_addr);
                    }
#endif
                    wln_send_event (pSta, WLN_EVT_ASSOC, pSta->macStatus.bss_addr);
#if     BSP_ETH_WIFI_BRIDGE
                                        /* if this is the root address achieving assoc state,
                                         * move all other end points to JOIN state */
                                        /*
                                         * When we reach here, the primary address has authenticated
                                         * and we can now start associating and authenticating the
                                         * secondary addresses.
                                         *
                                         */
                                        if (pSta->entryNdx == 0)
                                                MacLinkUpToSecondary(&pSta->macStatus);
#endif
                }
                else
                    NextBSS(pSta);
            }
            break;
        case TYPE_ACTION:
            handleActionMessage(pSta, buf);
            break;
    }

    MacFreeBuffers (buf);
    PUT_MUTEX();
}


/*
 * This function takes a transmit power value and applies the local power constraints
 * to it, if any.  If the transmit power value is too larger, then it is reduced to
 * comply with the local contraint set by the AP.
 *
 * The constraint values are stored in naWifiChannelConstraint by 
 * ParseMultiDomainInfo when this information is received in management messages.
 */
int wlnApplyTxPowerConstraint(int value, unsigned channelIndex)
{
    int constraint;
    int result = value;

    GET_MUTEX3();
    constraint = naWifiChannelConstraint[channelIndex];
    PUT_MUTEX3();
    
    if ((constraint != NA_TX_POWER_CONTRAINT_NONE) && (constraint < value))
    {
        result = constraint;
    }
    
    return result;
}
        
    
// parse country information elelment
// @param bp       country information data
// @param channel  current channel number
#define IS_CHANNEL_INDEX_VALID(x)   (x > 0)
static void ParseMultiDomainInfo(epStatus *pSta, uint8 *bp, int channel_indx)
{
    int idx, len = (int)bp[1], step=1, i = 0, j;
    int channel = wln_get_chan_info(channel_indx, NULL);
    
    static uint8 oldInfo[256];
    static int oldLen = 0;
    
    
    if ((len == oldLen) && (memcmp(bp, oldInfo, len) == 0) && (pSta->staState >= SS_JOIN/* pSta->macStatus.tx_power >=0*/))
    {
        return;  //don't parse this field if the info in the beacon is the same as before after we're joined
    }
    else
    {
        oldLen=len;
        if (len <= (int) sizeof oldInfo)
                memcpy(oldInfo, bp, len);
        else
                memcpy(oldInfo, bp, sizeof oldInfo);
        //printf("In ParseMultiDomainInfo(), New one\n");       
    }   
        
    //printf("ParseMultiDomainInfo()\n");       
    if (len >= COUNTRY_INFO_MIN_LEN)
    {
        // save the country string
        memcpy(&pSta->macStatus.country_info, &bp[2], WLN_COUNTRY_STRLEN);

        idx = 5; /* first channel info */
        len -= 3;
        
        GET_MUTEX3();
        memset(stationSupportedChannel, 0, sizeof stationSupportedChannel);
        memset(naWifiChannelConstraint, NA_TX_POWER_CONTRAINT_NONE, sizeof (naWifiChannelConstraint));
        while ((len-3) >= 0)
        {
            if(bp[idx] <= 200)
            {
                naDot11.regulatoryClassesRequired = FALSE;
                // check the channel
                if (CHAN_5G(channel_indx))
                {
                    BOOLEAN startedGroup = FALSE;
                    step=4;  //A channel numbers are increased by 4
                    
                    //build the stations supported channel list
                    for (j=0; j<bp[idx+1]; j++)
                    {
                        int channelContraintIndex = wln_is_channel_supported(macParams.chan_mask, (bp[idx]+step*j), TRUE);
                        if IS_CHANNEL_INDEX_VALID(channelContraintIndex)
                        {
                            if (stationSupportedChannel[i].startChannel == 0)
                                    stationSupportedChannel[i].startChannel = bp[idx]+step*j;
                                    
                            stationSupportedChannel[i].channelNum++;
                            stationSupportedChannel[i].txPwrLimit = (bp[idx+2] > WLN_TXPOWER_MAX)? WLN_TXPOWER_MAX : bp[idx+2];
                            naWifiChannelConstraint[channelContraintIndex] = stationSupportedChannel[i].txPwrLimit;
                            if (channel == (bp[idx]+step*j))
                            {
                                    pSta->macStatus.tx_power = stationSupportedChannel[i].txPwrLimit;
                                    MacSetTxPower((unsigned char)pSta->macStatus.tx_power);
                            }
                            startedGroup = TRUE;
                        }
                        else 
                        {
                            if (startedGroup)
                            {
                                /*
                                 * If we started a group of channels, but have now run
                                 * into an unsupported channel, then make sure we close
                                 * the current group so that the gap is handled correctly.
                                 */
                                i++;
                                startedGroup = FALSE;
                                if (i > (int)(sizeof(stationSupportedChannel)/sizeof(stationSupportedChannel_T)))
                                        break;
                            }
                        }
                    }
                    if (stationSupportedChannel[i].startChannel != 0)
                    {
                        i++;                              
                        startedGroup = FALSE;
                        if (i > (int)(sizeof(stationSupportedChannel)/sizeof(stationSupportedChannel_T)))
                                break;
                    }
                    
                }
                else
                {
                    for (j = 0; j < bp[idx+1]; j++)
                    {
                        int channelContraintIndex = wln_is_channel_supported(macParams.chan_mask, (bp[idx]+j), FALSE);

                        if IS_CHANNEL_INDEX_VALID(channelContraintIndex)
                        {
                            naWifiChannelConstraint[channelContraintIndex] = bp[idx+2];
                        }
                    }

                    if ((channel >= bp[idx]) && (channel <= (bp[idx]+(bp[idx+1]*step))))
                    {
                        // save the maximum tx power
                        if (bp[idx+2] > WLN_TXPOWER_MAX)
                            pSta->macStatus.tx_power = WLN_TXPOWER_MAX;
                        else
                            pSta->macStatus.tx_power = (int)bp[idx+2];
                        MacSetTxPower((unsigned char)pSta->macStatus.tx_power);
                        break;
                     }
                }
                
                len -= 3;
                idx += 3;
                
             }
            else
            {
                
                regulatoryClass_T * regulatoryClass;
                int maxRegClass;
                
                naDot11.regulatoryClassesRequired = TRUE;
                
                if(ParseRegulatoryInfo(pSta->macStatus.country_info, &regulatoryClass, &maxRegClass) == 0)
                {
                    if ((bp[idx+1] < maxRegClass) && (bp[idx+2] <= MAX_COVERAGE_CLASS_VALUE))
                    {
                        BOOLEAN startedGroup = FALSE;
                        
                        for( j=0; j < regulatoryClass[bp[idx+1]].chanNum; j++)
                        {
                            int channelContraintIndex = wln_is_channel_supported(macParams.chan_mask, regulatoryClass[bp[idx+1]].channel_set[j], TRUE);
                            if IS_CHANNEL_INDEX_VALID(channelContraintIndex)
                            {
                                if (stationSupportedChannel[i].startChannel == 0)
                                        stationSupportedChannel[i].startChannel = regulatoryClass[bp[idx+1]].channel_set[j];
                                        
                                stationSupportedChannel[i].channelNum++;
                                // our max tx power is less than the limits that all supported regulatory class require.
                                stationSupportedChannel[i].txPwrLimit = WLN_TXPOWER_MAX;
                                naWifiChannelConstraint[channelContraintIndex] = WLN_TXPOWER_MAX;
                                
                                if (channel == regulatoryClass[bp[idx+1]].channel_set[j])
                                {
                                        pSta->macStatus.tx_power = stationSupportedChannel[i].txPwrLimit;
                                        MacSetTxPower((unsigned char)pSta->macStatus.tx_power);                                                         
                                        // set the slot time in HW here or save the info when we are told to change channel?
                                        #if (PLATFORM == connectcorewi9p9215_a || PLATFORM == connectwiem9210)
                                        SetCoverageclass(bp[idx+2]);
                                        #endif  
                                }
                                startedGroup = TRUE;
                            } 
                            else
                            {
                                if (startedGroup)
                                {
                                    i++;
                                    startedGroup = FALSE;
                                    if (i > (int)(sizeof(stationSupportedChannel)/sizeof(stationSupportedChannel_T)))
                                            break;                                                                  
                                }
                            }
                        }
                        
                        if (stationSupportedChannel[i].startChannel != 0)
                        {
                            i++;                              
                            startedGroup = FALSE;
                            if (i > (int)(sizeof(stationSupportedChannel)/sizeof(stationSupportedChannel_T)))
                                    break;                                                                  
                        }
                    }
                }
                                       
                len -= 3;
                idx += 3;
                                        
            }
        }
        PUT_MUTEX3();
    }
}


//
// Parse a received beacon or probe response frame
// @param buf Received frame
// @param channel Expected channel
// @param sp BSS scan information returned
// @return TRUE if valid frame
//
static BOOL ParseBeacon (MacBuffer *buf, int channel, wln_scan_bss *sp)
{
    uint16 bssCaps = GET16 (&buf->body[10]);
    uint8 *bp;

	epStatus *pSta = rootEndPoint[0];
    
    memset (sp, 0, sizeof(wln_scan_bss));

    // Save fixed fields
    SET_ADDR (sp->bss_addr, buf->macHdr.addr3);
    //fixes from CAT code (__FIX_BEACON_RECEIVED_WHILE_ADVANCING_SCAN == TRUE)

	// save current hardware channel index setting
	//  this ensures scan channel is always set correctly,
	//  because incoming channel index will be mgmt scan chanel index
	//  that may be in process of advancing when this beacon was received
	//  (and therefore will not be the receive channel index)
    sp->channel = global_chan;
    //sp->channel = channel;
    sp->rx_signal = buf->rx_signal;
    sp->bss_caps = bssCaps;
    sp->tbttMilliseconds = GET16 (&buf->body[8]);

    // Set default ERP info
    if (!(bssCaps & CAP_SHORTPRE))
        sp->erp_info |= ERP_BARKER;
        
    // Process each info element
//    for (bp = &buf->body[12]; bp+bp[1]+2 <= buf->body+buf->length; bp += bp[1]+2)
    for (bp = &buf->body[12]; bp+bp[1]+2 <= ((uint8*)&buf->macHdr)+(buf->length-FCS_SIZE); bp += bp[1]+2)

    {
        switch (bp[0])
        {
            case ELEM_SSID:
                // Check for valid SSID
                if (bp[1] > WLN_SSID_SIZE)
                    return FALSE;

                // Save SSID
                //memcpy (sp->ssid, bp+2, bp[1]);
                //sp->ssid_len = bp[1];
                //__FIX_NULL_SSID_MEMORY_ACCESS
                sp->ssid_len = (bp[1] < WLN_SSID_SIZE) ? bp[1] : WLN_SSID_SIZE;
                memset(sp->ssid, 0, sizeof(sp->ssid));
                if (sp->ssid_len) 
                {
                        memcpy (sp->ssid, bp+2, sp->ssid_len);
                }
                break;

            case ELEM_SUPRATES:
            case ELEM_EXTSUPRATES:
                // Check supported rates
                if (!CheckRates (bp+2, bp[1], sp))
                    return FALSE;
                break;

            case ELEM_DSPARAM:
                // Check channel number
                if (!CHAN_5G (channel) &&
                    (bp[1] != 1 || bp[2] != channel))
                        return FALSE;
                break;

            case ELEM_IBSSPARAM:
                // Save ATIM window if IBSS
                if (bssCaps & CAP_IBSS)
                    sp->atim = GET16 (bp+2);
                break;

            case ELEM_ERPINFO:
                // Save extended rate PHY info
                sp->erp_info = bp[2];
                break;

            case ELEM_COUNTRY:
                if ((macParams.options & WLN_OPT_MULTI_DOMAIN) || (macParams.options & WLN_OPT_SPECTRUM_MGMT))
                    ParseMultiDomainInfo(pSta, bp, channel);
                break;

            case ELEM_VENDOR:
                // Save WPA info
                if ((bssCaps & (CAP_ESS|CAP_PRIVACY)) == (CAP_ESS|CAP_PRIVACY) &&
                    bp[1]+2 <= WLN_WPAIE_SIZE &&
                    memcmp (bp+2, wpaOUI, sizeof wpaOUI) == 0)
                {
                        memcpy (sp->wpa_info, bp, bp[1]+2);
                }
#if (BSP_WPS_ENABLED == TRUE)
                /* if we're in WPS, check to see this is the right registrar */
                else if (macParams.bss_type == WLN_BSS_WPS && buf->macHdr.fc.type != TYPE_BEACON && naWpsIsOui(bp+2))
                {
//                    printf("ParseBeacon: ssid %s channel %d wps length = %d\n", sp->ssid, sp->channel, bp[1]);

                    /* see whether this is a registrar that we want */
                    if (naWpsIsRegistrar(bp+2, bp[1]) == FALSE)
                    {
                        return FALSE;
                    }
                    /* add this registrar if it's ok */
                    else if (naWpsAddRegistrar(sp, bp+2, bp[1]) != 0)
                    {
                        return FALSE;
                    }
                }
#endif
                    break;

            case ELEM_RSN:
                // Save RSN info
                if ((bssCaps & (CAP_ESS|CAP_PRIVACY)) == (CAP_ESS|CAP_PRIVACY) &&
                    bp[1]+2 <= WLN_WPAIE_SIZE)
                        memcpy (sp->rsn_info, bp, bp[1]+2);
                break;
                
            case ELEM_QUIET:
                {
                    uint16 beaconInterval = GET16 (&buf->body[8]);
                    uint32 millisecondsToNextQuietPeriod = bp[2] * beaconInterval + (bp[6] << 8) + bp[7];
                    uint32 duration = (bp[4] << 8) + bp[5]; 
                    
                    if (   (sp->millisecondsToNextQuietPeriod > millisecondsToNextQuietPeriod)
                        || (sp->millisecondsToNextQuietPeriod == 0))
                    {
                        sp->millisecondsToNextQuietPeriod = millisecondsToNextQuietPeriod;
                        sp->lengthOfNextQuietPeriod = duration;
                    }
                }
                break;
                
            case ELEM_CHANNEL_SWITCH:
                {
                    sp->channelSwitchMode = bp[2];
                    sp->channelSwitchChannel = bp[3];
                    if (sp->tbttMilliseconds != 0)
                    {
                        sp->channelSwitchMilliseconds = bp[4] * sp->tbttMilliseconds;
                    }
                    else
                    {
                        sp->channelSwitchMilliseconds = bp[4] * DEFAULT_MILLS_PER_BEACON;
                    }
                }
                break;
                    
        }
    }

    if ((macParams.options & WLN_OPT_MULTI_DOMAIN) || (macParams.options & WLN_OPT_SPECTRUM_MGMT))
    {
        if (pSta->macStatus.tx_power < 0)
            return FALSE;
    }

    return TRUE;
}

//
// Check if BSS info matches current parameters
// @param sp BSS scan information to check
// @return TRUE if match, FALSE if not
//
static BOOL CheckBSS (wln_scan_bss *sp)
{
    uint16 bssCaps;

    // Check capabilities
    bssCaps = sp->bss_caps;
    // want ESS, but not an ESS

    if ((macParams.bss_type == WLN_BSS_ESS && !(bssCaps & CAP_ESS)) ||
        // want IBSS, but not an IBSS
        ((macParams.bss_type == WLN_BSS_IBSS || macParams.bss_type == WLN_BSS_IBSS_JOIN)
            && !(bssCaps & CAP_IBSS)) ||
        // unsecured BSS not allowed
        (!(macParams.encrypt & WLN_ENCR_OPEN) && !(bssCaps & CAP_PRIVACY)) ||
        // secured BSS not allowed
        (!(macParams.encrypt & ~WLN_ENCR_OPEN) && (bssCaps & CAP_PRIVACY)))
            return FALSE;

    if ((naDot11.spectrumManagementRequired) && ((bssCaps & CAP_SPECTRUM_MGMT) == 0))
    {
        /*
         * Don't associate with an AP that doesn't support 802.11h DFS if we have
         * been configured to require it. 802.11 paragraph 11.9.
         */
        return FALSE;
    }
    
    // Check SSID if looking for a specific one
    if (macParams.ssid_len > 0 &&
        !(sp->ssid_len == macParams.ssid_len &&
          memcmp (sp->ssid, macParams.ssid, macParams.ssid_len) == 0))
        return FALSE;

    // No rates supported
    if (sp->rates == 0)
        return FALSE;

    // If not WPA, check if WEP allowed
    if (sp->wpa_info_len == 0 &&
        sp->rsn_info_len == 0 &&
        !(macParams.encrypt & WLN_ENCR_WEP) &&
        (bssCaps & CAP_PRIVACY))
            return FALSE;

    return TRUE;
}

//
// Process a received beacon or probe response frame while scanning
// @param buf Received frame
//
static void RecvScan (MacBuffer *buf)
{
    wln_scan_bss *sp;
    int i;

    // Check if already have this BSS
    if (scan_data)
    for (i = scan_count-1; i >= 0; i--)
            if (scan_channel == scan_data->bss[i].channel &&
                EQUAL_ADDR (buf->macHdr.addr3, scan_data->bss[i].bss_addr))
                return;

    // Allocate more space for scan data if needed
    if (scan_count == scan_max)
    {
        wln_scan_data *sd = realloc (scan_data,
            sizeof(wln_scan_data) + (scan_count+SCAN_NUM)*sizeof(wln_scan_bss));
        if (sd == NULL)
            return;
        scan_data = sd;
        scan_max = scan_count+SCAN_NUM;
    }

    // Add new BSS to end of list
    sp = &scan_data->bss[scan_count];
    if (!ParseBeacon (buf, scan_channel, sp))
        return;
    scan_count++;
    
    XPRINTF (TRACE_WARNING, "Found %dth bssid " WLN_MACSTR
        " ssid=%s (len %d) chan=%d rssi=%d caps=0x%04x rates=0x%04x\n",
                scan_count,
                WLN_MAC2STR(sp->bss_addr), sp->ssid, sp->ssid_len, sp->channel,
        sp->rx_signal, sp->bss_caps, sp->rates);
}


static void naWifiSetNextQuietPeriod(uint32 millsToNextPeriod, uint32 duration)
{
    uint32 ticksToNextPeriod = ((millsToNextPeriod * NABspTicksPerSecond) + (NABspTicksPerSecond / 2)) / 1000;
    static TX_TIMER quietTimer;
    static unsigned initialized = FALSE;
    static uint32 savedDuration;
    
    if (!initialized)
    {
        /*
         * If first time, then create the timer and program it to start the quiet
         * period.
         */
        UINT ccode;
        
        memset(&quietTimer, 0, sizeof(quietTimer));
        savedDuration = duration;
        ccode = tx_timer_create(&quietTimer, "Wifi Quiet", naWifiStartQuietPeriod, 
                                (ULONG) &savedDuration, ticksToNextPeriod, 0, TX_AUTO_ACTIVATE);
        if (ccode != TX_SUCCESS)
        {
            panic("naWifiSetNextQuietPeriod: tx_timer_create failed.");
        }
        initialized = TRUE;
    }
    else
    {
        /*
         * If the timer has already been created, then find it its current state.
         */
        CHAR *name = NULL;
        UINT active = 0;
        ULONG ticksToGo = 0;
        ULONG rescheduleTicks = 0;
        TX_TIMER *next = NULL;
        
        UINT ccode = tx_timer_info_get(&quietTimer, &name, &active, &ticksToGo,
                                       &rescheduleTicks, &next);
        if (ccode != TX_SUCCESS)
        {
            panic("naWifiSetNextQuietPeriod: tx_timer_info_get failed");
        }
        
        if (active)
        {
            if (ticksToGo > ticksToNextPeriod)
            {
                /*
                 * If it is already running, but the new quiet period is sooner than
                 * the current one, then reprogram it for the new period.
                 */
                savedDuration = duration;
                tx_timer_deactivate(&quietTimer);
                tx_timer_change(&quietTimer, ticksToNextPeriod, 0);
                tx_timer_activate(&quietTimer);
            }
            /* else if current comes before the new, then stick with the current program */
        }
        else
        {
            /*
             * If the timer has already expired, then reprogram and reactivate it.
             */
            savedDuration = duration;
            tx_timer_change(&quietTimer, ticksToNextPeriod, 0);
            tx_timer_activate(&quietTimer);
        }
    }
}



void naWifiChannelSwitch(ULONG channelPtr)
{
    uint32 channel = *((ULONG *) channelPtr);
    int channelIndex = wln_is_channel_supported(macParams.chan_mask, channel, TRUE);
    
    /*
     * If we are switching away from this channel, then someone must have
     * detected radar on it.  Add it into our list of disabled channels.
     */
    wln_disable_channel(global_chan);   
    naDot11.receivedFrameOnCurrentChannel = FALSE;
    
    if (channelIndex != -1)
    {
        SetChannel(channelIndex);
    }
    else
    {
        /*
         * Reset secondary channels to scan state.
         */
        if (rootEndPoint[1] != NULL)
        {
            rootEndPoint[1]->staState = SS_SCAN;
        }
        if (rootEndPoint[2] != NULL)
        {
            rootEndPoint[2]->staState = SS_SCAN;
        }
        /*
         * Start scanning on primary channel.
         */
        if (macParams.bss_type != WLN_BSS_NONE)
            StartScan (rootEndPoint[0], macParams.ssid, macParams.ssid_len, macParams.channel, FALSE);
        else
        {
            SetChannel (macParams.channel);
            // Set BSSID and SSID to receive all frames
            SetBSS (0, bc_addr, NULL, 0, 0, 0);
        }
    }
}


void naWifiSetChannelSwitch(uint32 channelSwitchMilliseconds,
                            uint8 channelSwitchMode, uint8 channelSwitchChannel)
{
#define MIN_MILLS_TO_SWITCH_CHANNEL     (5)
    uint32 ticksToChannelSwitch = ((channelSwitchMilliseconds * NABspTicksPerSecond) + (NABspTicksPerSecond / 2)) / 1000;
    static TX_TIMER channelSwitchTimer;
    static unsigned initialized = FALSE;
    static uint32 newChannel;
    
    newChannel = channelSwitchChannel;
    
    if (channelSwitchMilliseconds < MIN_MILLS_TO_SWITCH_CHANNEL)
    {
        if (initialized)
        {
            tx_timer_deactivate(&channelSwitchTimer);
        }
        newChannel = channelSwitchChannel;
        naWifiChannelSwitch((ULONG) &newChannel);
    }
    else
    {            
        if (!initialized)
        {
            /*
             * If first time, then create the timer and program it to perform the channel
             * switch.
             */
            UINT ccode;
            
            memset(&channelSwitchTimer, 0, sizeof(channelSwitchTimer));
            ccode = tx_timer_create(&channelSwitchTimer, "Wifi Channel Switch", naWifiChannelSwitch, 
                                    (ULONG) &newChannel, ticksToChannelSwitch, 0, TX_AUTO_ACTIVATE);
            if (ccode != TX_SUCCESS)
            {
                panic("naWifiSetChannelSwitch: tx_timer_create failed.");
            }
            initialized = TRUE;
        }
        else
        {
            /*
             * If timer has already been used once, then just reprogram it.
             * The latest channel switch announcement always takes precedence
             * over a previous one.
             */
            tx_timer_deactivate(&channelSwitchTimer);
            tx_timer_change(&channelSwitchTimer, ticksToChannelSwitch, 0);
            tx_timer_activate(&channelSwitchTimer);
        }
        if (channelSwitchMode == CHANNEL_SWITCH_MODE_QUIET)
        {
            /*
             * If mode is set to quiet, then we cannot transmit until after the
             * switch.  I add 10 to the channel switch time just to make sure we
             * wait until after the switch to start transmitting.  The value is arbitrary.
             */
#define ARBITRARY_ADDITIONAL_AMOUNT_OF_TIME_TO_WAIT_BEFORE_TRANSMITTING     (10)
            ULONG quietTime = channelSwitchMilliseconds + ARBITRARY_ADDITIONAL_AMOUNT_OF_TIME_TO_WAIT_BEFORE_TRANSMITTING;
            
            naWifiStartQuietPeriod((ULONG) &quietTime);
        }
    }
}





//
// Process a received beacon or probe response frame
// while joined or trying to join a BSS
// @param buf Received frame
//
static void RecvBeacon (MacBuffer *buf)
{
    wln_scan_bss info;
    epStatus *pSta = rootEndPoint[0];

    // Beacon is from another BSS
    if (!EQUAL_ADDR (pSta->macStatus.bss_addr, buf->macHdr.addr3))
    {
        // Found another IBSS that matches our SSID, join it
        if (pSta->staState == SS_ASSOC &&
            (pSta->macStatus.bss_caps & CAP_IBSS) &&
            ParseBeacon (buf, pSta->macStatus.channel, &info) &&
            (info.bss_caps & CAP_IBSS) &&
            info.ssid_len == pSta->macStatus.ssid_len &&
            memcmp (info.ssid, pSta->macStatus.ssid, info.ssid_len) == 0)
        {
            if (pSta->staState == SS_ASSOC)
                wln_send_event (pSta, WLN_EVT_DISASSOC, pSta->macStatus.bss_addr);

            pSta->staState = SS_BEACON;
        XPRINTF(TRACE_INFO, "%d staState -> %s (RecvBeacon)\n\r", pSta->entryNdx, textStaState[pSta->staState]);
            SET_ADDR (pSta->macStatus.bss_addr, info.bss_addr);
        }
        else
            return;
    }

    // Parse beacon from current BSS
    else 
    {
        if (ParseBeacon (buf, pSta->macStatus.channel, &info))
        {
            pSta->tbttMilliseconds = info.tbttMilliseconds;
            GET_TSF(naDot11.tsfHigh, naDot11.tsfLow, buf->body);
            naDot11.ticksAtLastBeacon = tx_time_get();
            if (info.millisecondsToNextQuietPeriod != 0)
            {
                naWifiSetNextQuietPeriod(info.millisecondsToNextQuietPeriod, 
                                         info.lengthOfNextQuietPeriod);
            }
            if (info.channelSwitchMilliseconds != 0)
            {
                naWifiSetChannelSwitch(info.channelSwitchMilliseconds,
                                       info.channelSwitchMode,
                                       info.channelSwitchChannel);
            }
        }
        else
        {
            return;
        }
    }

    // Restore state after a scan
    if (pSta->scan_state >= SS_JOIN)
    {
        pSta->staState = pSta->scan_state;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
        pSta->scan_state = SS_WAIT;
        XPRINTF(TRACE_INFO, "scan_state -> %s\n\r", textStaState[pSta->scan_state]);
        bcnTime = 0;
    }

    // Waiting for first beacon from BSS
    if (pSta->staState == SS_BEACON)
    {
        // Set BSS info
        pSta->macStatus.authen = 0;
        pSta->macStatus.encrypt = 0;
                atimWindow = info.atim;
                basicRates = info.rates_basic;

        // Initialize rate info
        if (!InitRates (&info, TRUE))
            return;

        // Initialize non-WPA encryption
        SetDefaultKeys(pSta);

        // Initialize beacon timer
        bcnInt = GET16 (buf->body+8);
        bcnTime = 0;

        if (pSta->macStatus.bss_caps & CAP_ESS)
        {
            // Free any beacon or probe response frames
            MacFreeBuffers (bcnFrame);
            MacFreeBuffers (respFrame);
            bcnFrame = respFrame = NULL;
                }
        else
        {
            // Make IBSS beacon using current beacon interval
            if (!MakeBeacon (bcnInt))
                return;
                }

                // Update BSS info in hardware
                SetBSS (pSta->macStatus.bss_caps, pSta->macStatus.bss_addr,
                        pSta->macStatus.ssid, pSta->macStatus.ssid_len, info.rates_basic, info.atim);

        if (pSta->macStatus.bss_caps & CAP_ESS)
        {
            // Send authentication frame if ESS
            pSta->staState = SS_JOIN;
        XPRINTF(TRACE_INFO, "%d staState -> %s (RecvBeacon)\n\r", pSta->entryNdx, textStaState[pSta->staState]);
            pSta->authState = SA_INIT;
        XPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
            SendNextMgmtFrame(pSta);
        }
        else
        {
            pSta->staState = SS_ASSOC;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
            pSta->macStatus.state = WLN_ST_JOIN_IBSS;
        XPRINTF(TRACE_INFO, "%d macStatus.state -> %s\n\r", pSta->entryNdx, macStatusState[pSta->macStatus.state]);
            wln_send_event (pSta, WLN_EVT_ASSOC, pSta->macStatus.bss_addr);
        }
    }

    // Update rate info if protection state changes
    else if (erpInfo != info.erp_info)
    {
        if (!InitRates (&info, FALSE))
            MacDisassociate (pSta, pSta->macStatus.bss_addr, REAS_DEAUTH_LEAVING);
    }

    // Update ERP info which can change every beacon
    erpInfo = info.erp_info;
}

//
// Process a received authentication frame
// @param buf Received frame
//
static void RecvAuthentication (MacBuffer *buf)
{
    epStatus *pSta = MacGetStationPtr((char *) buf->macHdr.addr1);

        if (pSta == NULL)
                return;

        // Check authentication response
    if (GET16 (&buf->body[4]) != STAT_SUCCESS)
    {
                XPRINTF(TRACE_WARNING, "RecvAuthentication fail %d\n\r",
                                                GET16 (&buf->body[4]));
        // Try next method
        pSta->authState++;
        XPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
        SendNextMgmtFrame(pSta);
        return;
    }

    switch (pSta->authState)
    {
        case SA_OPEN:
            if (GET16 (&buf->body[0]) == AUTH_OPEN &&
                GET16 (&buf->body[2]) == 2)
            {
                pSta->staState = SS_AUTH;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
                pSta->macStatus.authen = WLN_AUTH_OPEN;
        XPRINTF(TRACE_INFO, "macStatus.authen = %d\n\r", pSta->macStatus.authen);
                SendNextMgmtFrame(pSta);
            }
            break;

        case SA_LEAP:
            if (GET16 (&buf->body[0]) == AUTH_LEAP &&
                GET16 (&buf->body[2]) == 2)
            {
                pSta->staState = SS_AUTH;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
                pSta->macStatus.authen = WLN_AUTH_LEAP;
        XPRINTF(TRACE_INFO, "macStatus.authen = %d\n\r", pSta->macStatus.authen);
                SendNextMgmtFrame(pSta);
            }
            break;

        case SA_SHARED:
            // Shared key challenge (2nd frame)
            if (!buf->macHdr.fc.protected &&
                GET16 (&buf->body[0]) == AUTH_SHAREDKEY &&
                GET16 (&buf->body[2]) == 2 &&
                buf->body[6] == ELEM_CHALLENGE &&
                buf->body[7] == CHAL_SIZE)
            {
                memcpy (pSta->challenge, &buf->body[8], CHAL_SIZE);
                pSta->authState = SA_SHARED2;
        XPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
                SendNextMgmtFrame(pSta);
            }
            break;

        case SA_SHARED2:
            // Shared key response (4th frame)
            if (GET16 (&buf->body[0]) == AUTH_SHAREDKEY &&
                GET16 (&buf->body[2]) == 4)
            {
                pSta->staState = SS_AUTH;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
                pSta->macStatus.authen = WLN_AUTH_SHAREDKEY;
        XPRINTF(TRACE_INFO, "macStatus.authen = %d\n\r", pSta->macStatus.authen);
                SendNextMgmtFrame(pSta);
            }
            break;

        default:
            break;
    }

    // Ignore other received authentication frames
}

//
// Compare BSS information
// Return > 0 if b is better than a
//
static int CompareBSS (wln_scan_bss *a, wln_scan_bss *b)
{
#ifdef BLACKBOX_BRAND_ENABLED
    // Black Box brand does not have a preferred SSID
#else
    // If searching for any SSID, check for preferred SSID
    if (macParams.ssid_len == 0)
    {
        BOOL pref_a = (a->ssid_len == PREF_SSID_LEN &&
                       memcmp (a->ssid, PREF_SSID, PREF_SSID_LEN) == 0);
        BOOL pref_b = (b->ssid_len == PREF_SSID_LEN &&
                       memcmp (b->ssid, PREF_SSID, PREF_SSID_LEN) == 0);

        if (pref_b && !pref_a)
            return 1;
        if (pref_a && !pref_b)
            return -1;
    }
#endif

    // Use strongest signal
    return b->rx_signal - a->rx_signal;
}

//
// Start scanning for BSS info
//
static void StartScan (epStatus *pSta, uint8 *ssid, int ssid_len, int channel_indx, BOOL requested)
{
#if (BSP_WPS_ENABLED == TRUE)
    if (macParams.bss_type == WLN_BSS_WPS)
    {
        scan_chan_mask[0] = macParams.chan_mask[0];
        scan_chan_mask[1] = macParams.chan_mask[1];
        memset(scan_ssid, 0, ssid_len);
        scan_ssid_len = 0;
    }
    else
#endif
    {
        // Save parameters
        memcpy (scan_ssid, ssid, ssid_len);
        scan_ssid_len = ssid_len;
        if (channel_indx == WLN_CHAN_SCAN)
        {
            scan_chan_mask[0] = macParams.chan_mask[0];
            scan_chan_mask[1] = macParams.chan_mask[1];
        }
        else
        {
            scan_chan_mask[0] = 0;
            scan_chan_mask[1] = 0;
            scan_chan_mask[(channel_indx-1) >> 5] = (1UL << ((channel_indx-1) & 0x1f));
        }
    }

    scan_requested = requested;

    // If not already scanning, save current state to restore after scan complete
    if (pSta->scan_state == SS_WAIT)
        {
                pSta->scan_state = pSta->staState;
        XPRINTF(TRACE_INFO, "scan_state -> %s\n\r", textStaState[pSta->scan_state]);
        }

    // Switch to scan state
    pSta->staState = SS_SCAN;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
    scan_channel = 0;
    scan_count = 0;
    scan_time = GET_TIME();
    pSta->retry = AFTER_PROBE_SCAN_LIMIT + BEFORE_PROBE_SCAN_LIMIT;

    if (!requested)
        {
        pSta->macStatus.state = WLN_ST_SCANNING;
        XPRINTF(TRACE_INFO, "%d macStatus.state -> %s\n\r", pSta->entryNdx, macStatusState[pSta->macStatus.state]);
        }
    ScanChannel();
}

//
// Scan a channel for BSS info
//
static void ScanChannel (void)
{
        epStatus *pSta = rootEndPoint[0];
    static BOOLEAN probeHasBeenSent = FALSE;

    pSta->retry++;

    // After SCAN_LIMIT (or first time), switch to next channel.
    // Receive frames on this channel until next timer tick.
    if (   ((probeHasBeenSent) && (pSta->retry >= AFTER_PROBE_SCAN_LIMIT))
        || ((!probeHasBeenSent) && (pSta->retry >= BEFORE_PROBE_SCAN_LIMIT)))
    {
        // Wait for transmit queue to empty
        if (txQueue.head != NULL || txState != ST_IDLE)
            return;

        probeHasBeenSent = FALSE;
        // Find next active channel
        
        /* fixes from CAT code __FIX_CHANNEL_SCAN_INDEX_LOGIC == TRUE
         * This issue causes a channel index one past the legal max to be made 
         * during scan. This is a pre/post increment bug.
         */
        while (++scan_channel <= WLN_CHAN_NUM)
        {
            //scan_channel++;
            if (scan_chan_mask[(scan_channel-1) >> 5] & (1UL << ((scan_channel-1) & 0x1f)))
                break;
        }

        // Switch to next channel
        if (scan_channel <= WLN_CHAN_NUM)
        {
            pSta->retry = 0;
            SetChannel (scan_channel);
            naDot11.receivedFrameOnCurrentChannel = FALSE;
            
            // Set BSSID and SSID to receive all frames
            SetBSS (0, bc_addr, NULL, 0, 0, 0);
        }

        // Finished with application requested scan
        else if (scan_requested)
        {
            // Restore state before scan
            if (pSta->scan_state >= SS_JOIN)
            {
                // Set BSS info in hardware
                SetChannel (pSta->macStatus.channel);
                SetBSS (pSta->macStatus.bss_caps, pSta->macStatus.bss_addr,
                        pSta->macStatus.ssid, pSta->macStatus.ssid_len, basicRates, atimWindow);

                // Wait to receive a beacon
                pSta->staState = SS_BEACON;
        XPRINTF(TRACE_INFO, "%d staState -> %s (ScanChannel)\n\r", pSta->entryNdx, textStaState[pSta->staState]);
                mgmtTime = GET_TIME();
            }
           else
                   {
                        pSta->staState = pSta->scan_state = SS_WAIT;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
        XPRINTF(TRACE_INFO, "scan_state -> %s\n\r", textStaState[pSta->scan_state]);
                   }

            // Send scan event with results to application
            scan_data->count = scan_count;
            wln_send_event (pSta, WLN_EVT_SCAN_RESULTS, scan_data);

            // Force new auto scan
            scan_data->count = 0;
            scan_time = 0;
        }

        // Finished with auto scan
        else
        {
            // Sort scan results by preference
            if (scan_count > 1)
                qsort (scan_data->bss, scan_count, sizeof (wln_scan_bss),
                       (int (*)(const void *, const void *)) CompareBSS);

            // Try to join first BSS
                    pSta->scan_state = SS_WAIT;
        XPRINTF(TRACE_INFO, "scan_state -> %s\n\r", textStaState[pSta->scan_state]);
            scan_index = 0;
            NextBSS(pSta);
        }
    }

    // On other timer ticks, send probe request
    else
    {
        uint8 *bp;
        MacBuffer *buf;

        /* wait for power level from AP if 802.11d is enabled */
        if ((macParams.options & WLN_OPT_MULTI_DOMAIN) || (macParams.options & WLN_OPT_SPECTRUM_MGMT))
        {
            if (pSta->macStatus.tx_power < 0)
            {
                return;
            }
        }
        
        /*
         * If 802.11h spectrum management flag is set, then don't send
         * anything on this channel until we have received a frame.
         */
        if (   (naDot11.spectrumManagementRequired) 
            && (naDot11.receivedFrameOnCurrentChannel == FALSE))
        {
            return;
        }

        buf = MacGetBuffer();
        if (buf == NULL)
            return;
        bp = buf->body;

        // Set SSID
        bp[0] = ELEM_SSID;
        bp[1] = scan_ssid_len;
        memcpy (&buf->body[2], scan_ssid, scan_ssid_len);
        bp += bp[1] + 2;

        // Set supported rates
        bp = SetRates (bp, 0, scan_channel);

#if (BSP_WPS_ENABLED == TRUE)
        if (macParams.bss_type == WLN_BSS_WPS)
        {
            uint16 size;

            size = naWpsBuildProbeReq(bp);
            bp += size;
        }
#endif

        buf->macHdr.fc.type = TYPE_PROBE_REQ;
        SET_ADDR (buf->macHdr.addr3, bc_addr);
        buf->length = DATA_SIZE + (bp - buf->body);

        if (CHAN_5G (scan_channel))
            rateMin = RATE_MIN_A;
        else
            rateMin = RATE_MIN_B;

        SendFrames (pSta, buf, bc_addr);
        if (!probeHasBeenSent) 
        {
            pSta->retry = 0;                /* restart timer after we send a probe */
            probeHasBeenSent = TRUE;
        }
    }
}

//
// Try to associate with next BSS
//
// Use state machine for primary (root) end point
//
void NextBSS (epStatus *pSta)
{
    wln_scan_bss *sp;

        XPRINTF(TRACE_DEBUG, "NextBSS: staState %d macState %d scan_index %d\n\r",
                                   pSta->staState, pSta->macStatus.state, scan_index);

        /* if not primary address, our only choice is the active BSS */
        if (pSta->entryNdx != 0)
        {
                /* Don't change BSS -- but why bother retrying? */
                pSta->staState = SS_JOIN;
                XPRINTF(TRACE_INFO, "%d staState -> %s (TYPE_DISASSOC)\n\r",
                                                pSta->entryNdx, textStaState[pSta->staState]);
                pSta->authState = SA_INIT;
                XPRINTF(TRACE_INFO, "%d authState -> %s\n\r",
                                                pSta->entryNdx, textAuthState[pSta->authState]);
            memcpy(pSta->macStatus.bss_addr, rootEndPoint[0]->macStatus.bss_addr, sizeof(pSta->macStatus.bss_addr));
                SendNextMgmtFrame(pSta);
                return;
        }

    // Exit if not joining a BSS
    if (macParams.bss_type == WLN_BSS_NONE)
    {
        // Clear BSSID so no more frames are received
        memset (pSta->macStatus.bss_addr, 0, WLN_ADDR_SIZE);
        SetBSS (0, pSta->macStatus.bss_addr, NULL, 0, 0, 0);

        pSta->staState = SS_WAIT;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
        pSta->macStatus.state = WLN_ST_STOPPED;
        XPRINTF(TRACE_INFO, "%d macStatus.state -> %s\n\r", pSta->entryNdx, macStatusState[pSta->macStatus.state]);

        return;
    }

#if (BSP_WPS_ENABLED == TRUE)
    else if (macParams.bss_type == WLN_BSS_WPS)
    {
        if (naWpsProcessRegistrar(pSta, scan_data, scan_count) == 0)
        {
            SendMgmtFrame(pSta);
        }
        return;
    }
#endif

    pSta->staState = SS_BEACON;
        XPRINTF(TRACE_INFO, "%d staState -> %s (NextBSS)\n\r", pSta->entryNdx, textStaState[pSta->staState]);
    pSta->macStatus.state = WLN_ST_SCANNING;
        XPRINTF(TRACE_INFO, "%d macStatus.state -> %s\n\r", pSta->entryNdx, macStatusState[pSta->macStatus.state]);

    // Scan data has expired, start a new scan
        /* only scan from the primary address */
        if (pSta->entryNdx == 0)
        {
        if (scan_time == 0 || GET_TIME()-scan_time >= SCAN_MAX_TIME)
        {
                StartScan (pSta, macParams.ssid, macParams.ssid_len, macParams.channel, FALSE);
                return;
        }
        }

    // Try next BSS in scan info
    while (scan_index < scan_count)
    {
        sp = &scan_data->bss[scan_index];
        scan_index++;

        // Check if BSS info matches current parameters
        if (!CheckBSS (sp))
                {
                        XPRINTF(TRACE_INFO, "CheckBSS failed "WLN_MACSTR"\n\r",
                                                   WLN_MAC2STR(sp->bss_addr));
            continue;
                }

        // Set BSS info so event handler can call wln_get_status()
        memcpy (pSta->macStatus.ssid, sp->ssid, sp->ssid_len);
        pSta->macStatus.ssid_len = sp->ssid_len;
        pSta->macStatus.channel = sp->channel;
        pSta->macStatus.bss_caps = sp->bss_caps;
        SET_ADDR (pSta->macStatus.bss_addr, sp->bss_addr);
        memcpy (pSta->macStatus.wpa_info, sp->wpa_info, sp->wpa_info_len + 2);
        memcpy (pSta->macStatus.rsn_info, sp->rsn_info, sp->rsn_info_len + 2);
        pSta->macStatus.authen = 0;
        pSta->macStatus.encrypt = 0;
        pSta->macStatus.tx_rate = sp->tx_rate;
        pSta->macStatus.rx_signal = sp->rx_signal*RSSI_SCALE;

        // If have event callback function, notify application.
        // Application (as in wpa) will call wln_associate() to associate
                // with this BSS.
        if (!wln_send_event (pSta, WLN_EVT_JOIN, pSta->macStatus.bss_addr))
        {
            // If wpa not set up, try to associate with BSS on our own.
            MacAssociate (pSta, pSta->macStatus.ssid, pSta->macStatus.ssid_len,
                                pSta->macStatus.channel, pSta->macStatus.bss_addr,
                                pSta->macStatus.bss_caps, pSta->macStatus.wpa_info);
        }
        return;
    }

    // No more scan info, try to start an IBSS
#if     BSP_WIFI_MULTIPLE_MAC
        /* only start an IBSS if we are the primary address */
        if (pSta->entryNdx == 0)
#endif
    if (StartIBSS(pSta))
        return;

    // Restart scan after a delay
    pSta->staState = SS_WAIT;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
    SendMgmtFrame(pSta);
}

//
// Send authentication request
//
static void SendAuthentication (epStatus *pSta)
{
    MacBuffer *buf = MacGetBuffer();

    if (buf == NULL)
        return;

    // Set common frame contents
    buf->macHdr.fc.type = TYPE_AUTH;
    SET16 (&buf->body[2], 1);
    SET16 (&buf->body[4], 0);
    buf->length = DATA_SIZE + 6;

    pSta->retry++;
    if (pSta->retry >= MGMT_RETRY_LIMIT)
    {
        // Try next method
        pSta->authState++;
        pSta->retry = 0;
    }

#if (BSP_WPS_ENABLED == TRUE)
    if (macParams.bss_type == WLN_BSS_WPS)
    {
        /* we always want OPEN */
        SET16 (&buf->body[0], AUTH_OPEN);
        pSta->authState = SA_OPEN;
    } 
    else
    {
#endif

        XPRINTF(TRACE_WARNING, "SendAuthentication authstate %d authen %x wpalen %d rsnlen %d\n\r",
                                        pSta->authState, macParams.authen, pSta->macStatus.wpa_info_len,
                                        pSta->macStatus.rsn_info_len);


    switch (pSta->authState)
    {
        case SA_LEAP:   /* same as SA_INIT */
            // LEAP allowed and not a WPA BSS
            if ((macParams.authen & WLN_AUTH_LEAP) && 
                pSta->macStatus.wpa_info_len == 0 &&
                pSta->macStatus.rsn_info_len == 0)
            {
                SET16 (&buf->body[0], AUTH_LEAP);
                pSta->authState = SA_LEAP;
        XPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
                break;
            }
            // Fall through to next method

        case SA_SHARED:
            // Shared key allowed and not a WPA BSS
            if ((macParams.authen & WLN_AUTH_SHAREDKEY) &&
                pSta->macStatus.wpa_info_len == 0 &&
                pSta->macStatus.rsn_info_len == 0)
            {
                SET16 (&buf->body[0], AUTH_SHAREDKEY);
                pSta->authState = SA_SHARED;
        XPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
                break;
            }
            // Fall through to next method

        case SA_OPEN:
        case SA_FAIL2:
            // Open system or WEP with 802.1X allowed, or a WPA BSS
            if ((macParams.authen & (WLN_AUTH_OPEN|WLN_AUTH_WEP_8021X)) ||
                pSta->macStatus.wpa_info_len > 0 ||
                pSta->macStatus.rsn_info_len > 0)
            {
                SET16 (&buf->body[0], AUTH_OPEN);
                pSta->authState = SA_OPEN;
        XPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
                break;
            }
            // Fall through to next method

        case SA_FAIL:
            // All methods have failed
            MacFreeBuffers (buf);
            NextBSS(pSta);
            return;

        case SA_SHARED2:
            // Shared key response (3rd frame)
            SET16 (&buf->body[IV_SIZE], AUTH_SHAREDKEY);
            SET16 (&buf->body[IV_SIZE+2], 3);
            SET16 (&buf->body[IV_SIZE+4], 0);
            buf->body[IV_SIZE+6] = ELEM_CHALLENGE;
            buf->body[IV_SIZE+7] = CHAL_SIZE;
            memcpy (&buf->body[IV_SIZE+8], pSta->challenge, CHAL_SIZE);
            buf->length = DATA_SIZE + CHAL_SIZE + 8;

            // Encrypt challenge with WEP
            if (!wepCipher.Encrypt (pSta, buf))
            {
                // No WEP keys available
                MacFreeBuffers (buf);
                pSta->authState = SA_FAIL2;
        XPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
                return;
            }

            pSta->authState = SA_SHARED2;
        XPRINTF(TRACE_INFO, "%d authState -> %s\n\r", pSta->entryNdx, textAuthState[pSta->authState]);
            break;
    }
#if (BSP_WPS_ENABLED == TRUE)
    }
#endif

    SendFrames (pSta, buf, pSta->macStatus.bss_addr);
}

//
// Send association request
//
static void SendAssociation (epStatus *pSta)
{
    uint8 *bp, i, j;
    uint16 caps;
#define WLN_TXPOWER_MIN_OPERATIONAL   6  //dbm
    MacBuffer *buf = MacGetBuffer();
    if (buf == NULL)
        return;
    bp = buf->body;

    // Set capabilities
    caps = CAP_ESS;
    if (naDot11.spectrumManagementRequired)
    {
        caps |= CAP_SPECTRUM_MGMT;
    }
    if (macParams.options & WLN_OPT_SHORTPRE)
        caps |= CAP_SHORTPRE;
    // Always support short preamble and short slot time for 802.11g
    if (!(macParams.options & WLN_OPT_BONLY))
        caps |= CAP_SHORTPRE|CAP_SHORTSLOT;
    SET16 (bp, caps);
    
    // Set listen interval if PS is enabled
    SET16 (bp+2, 0);
#ifdef MAC_PS_ENABLED
    {  // it is OK to set listen interval without duty cycle enabled
        wln_duty_cycle dutyCycle;
        
        wln_get_duty_cycle(&dutyCycle);
        if (dutyCycle.offtime <= WLN_PS_MAX_OFF_TIME)
            SET16 (bp+2, (dutyCycle.offtime+50)/100); 
    }
#endif        
    bp += 4;

    // Set SSID
    bp[0] = ELEM_SSID;
    bp[1] = pSta->macStatus.ssid_len;
    memcpy (bp+2, pSta->macStatus.ssid, pSta->macStatus.ssid_len);
    bp += bp[1] + 2;

    // Set supported rates
    bp = SetRates (bp, 0, pSta->macStatus.channel);

    if (naDot11.spectrumManagementRequired)
    {   
        bp[0] = ELEM_POWER_CAPABILITY;
        bp[1] = 2;
        bp[2] = WLN_TXPOWER_MIN_OPERATIONAL; //WLN_TXPOWER_MIN;    
        bp[3] = WLN_TXPOWER_MAX;
        bp += 4;
        
        GET_MUTEX3();
        // Set supported channels
        if (stationSupportedChannel[0].startChannel != 0)
        {
                bp[0] = ELEM_TPC_SUPPORTED_CHANNELS;
                j = 0;
                bp[1]=0;
                for (i=0; i<(sizeof (stationSupportedChannel)/sizeof (stationSupportedChannel_T)); i++)
                {
                        if (stationSupportedChannel[i].startChannel != 0)
                        {
                                bp[2+i*2] = stationSupportedChannel[i].startChannel;
                                bp[3+i*2] = stationSupportedChannel[i].channelNum;
                                bp[1] += 2;
                                j++;
                        }
                
                }
                bp += 2+2*j;
        }
        PUT_MUTEX3();
     }
        
        
    
#if(BSP_WANT_WPA == TRUE)              // This is to fix Vantive 29314
    // Set RSN or WPA info
    if (pSta->macStatus.rsn_info_len > 0)
    {
        memcpy (bp, pSta->macStatus.rsn_info, pSta->macStatus.rsn_info_len + 2);
        bp += bp[1] + 2;
    }
    else if (pSta->macStatus.wpa_info_len > 0)
    {
        memcpy (bp, pSta->macStatus.wpa_info, pSta->macStatus.wpa_info_len + 2);
        bp += bp[1] + 2;
    }
#endif

        {
        char tBuf[3*WLN_SSID_SIZE];
        hexToText((char *) &pSta->macStatus.ssid, tBuf, pSta->macStatus.ssid_len);
        XPRINTF(TRACE_DEBUG, "SendAssociation caps %x ssid %s\n\r", caps, tBuf);
        }

    buf->macHdr.fc.type = TYPE_ASSOC_REQ;
    buf->length = DATA_SIZE + (bp - buf->body);
    SendFrames (pSta, buf, pSta->macStatus.bss_addr);
}

//
// Clear retry count and send the next management frame
//
static void SendNextMgmtFrame (epStatus *pSta)
{
    pSta->retry = 0;
    mgmtTime = GET_TIME();
    SendMgmtFrame(pSta);
}

//
// Check retry count and send the next management frame
//
extern int monitorMode;

void SendMgmtFrame (epStatus *pSta)
{
#if     0
        if (pSta->entryNdx != 0)
                SMPRINTF(TRACE_INFO, "SendMgmt "WLN_MACSTR" %s (%s)\n\r",
                        WLN_MAC2STR(pSta->staAddr),
                        textStaState[pSta->staState],
                        macStatusState[pSta->macStatus.state]);
#endif
        /* This is a bit tricky, as we could be sending a frame for any of
         * the addresses. Must we cycle through all of them? */

    switch (pSta->staState)
    {
        case SS_WAIT:
            // If joining a BSS, restart scan after SCAN_MIN_TIME
            if (macParams.bss_type != WLN_BSS_NONE && monitorMode == 0 &&
                (scan_time == 0 || GET_TIME()-scan_time >= SCAN_MIN_TIME))
                        {
                                XPRINTF(TRACE_WARNING, "SendMgmtFrame StartScan %s chan %d\n\r",
                                                macParams.ssid, macParams.channel);
                StartScan (pSta, macParams.ssid, macParams.ssid_len, macParams.channel, FALSE);
                        }
            break;

        case SS_BEACON:
                // No beacon received
                        if (pSta->macStatus.bss_caps & CAP_ESS)
                        {
            // Can't restore connection prior to a scan, notify application
            if (pSta->scan_state == SS_ASSOC)
                        {
                                XPRINTF(TRACE_WARNING, "SendMgmtFrame send disassociate\n\r");
                wln_send_event (pSta, WLN_EVT_DISASSOC, pSta->macStatus.bss_addr);
                        }
            pSta->scan_state = SS_WAIT;
        XPRINTF(TRACE_INFO, "scan_state -> %s\n\r", textStaState[pSta->scan_state]);

                                // Continue scanning
                        XPRINTF(TRACE_WARNING, "SendMgmtFrame SS_BEACON NextBSS\n\r");
            NextBSS(pSta);
                        }

                        // If IBSS, there may not be another station sending beacons.
                        // Just assume we are connected after a timeout.
                        else
                        {
                                pSta->staState = SS_ASSOC;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
                                pSta->scan_state = SS_WAIT;
        XPRINTF(TRACE_INFO, "scan_state -> %s\n\r", textStaState[pSta->scan_state]);
            }
            break;

        case SS_JOIN:
                        XPRINTF(TRACE_WARNING, "SendMgmtFrame SendAuthentication\n\r");
            SendAuthentication(pSta);
            break;

        case SS_AUTH:
            pSta->retry++;
            if (pSta->retry >= MGMT_RETRY_LIMIT)
                        {
                        XPRINTF(TRACE_WARNING, "SendMgmtFrame SS_AUTH NextBSS\n\r");
                NextBSS(pSta);
                        }
            else
                        {
                        XPRINTF(TRACE_WARNING, "SendMgmtFrame SendAssociation\n\r");
                SendAssociation(pSta);
                        }
            break;

        default:
            break;
    }
}

//
// Start a new IBSS
//
static BOOL StartIBSS (epStatus *pSta)
{
    wln_scan_bss info;
    int i;

    // Must have specific type, channel, and SSID
    if (!(macParams.bss_type == WLN_BSS_IBSS &&
          macParams.channel != WLN_CHAN_SCAN &&
          macParams.ssid_len != 0))
        return FALSE;

    if (naDot11.spectrumManagementRequired)
    {
        /*
         * We cannot start an IBSS if 802.11h spectrum management has been turned on
         * since we do not support radar detection.
         */
        return FALSE;
    }
    
    // Init BSS info
    //memcpy (pSta->macStatus.ssid, macParams.ssid, macParams.ssid_len);
    //pSta->macStatus.ssid_len = macParams.ssid_len;
    //__FIX_NULL_SSID_MEMORY_ACCESS == TRUE
    pSta->macStatus.ssid_len = (macParams.ssid_len < WLN_SSID_SIZE) ? macParams.ssid_len : WLN_SSID_SIZE;
    memset(pSta->macStatus.ssid, 0, sizeof(pSta->macStatus.ssid));
    if (pSta->macStatus.ssid_len) 
    {
        memcpy (pSta->macStatus.ssid, macParams.ssid, macParams.ssid_len);
    }
    
    pSta->macStatus.channel = macParams.channel;
    pSta->macStatus.wpa_info_len = 0;
    pSta->macStatus.rsn_info_len = 0;
    pSta->macStatus.authen = 0;
    pSta->macStatus.encrypt = 0;
    atimWindow = 0;
    erpInfo = 0;

    // Set capabilities
    pSta->macStatus.bss_caps = CAP_IBSS;
    if (!(macParams.encrypt & WLN_ENCR_OPEN))
        pSta->macStatus.bss_caps |= CAP_PRIVACY;
    if (macParams.options & WLN_OPT_SHORTPRE)
        pSta->macStatus.bss_caps |= CAP_SHORTPRE;

    // Init rate info
    if (CHAN_5G (pSta->macStatus.channel))
        info.rates = RATE_MASK_A;
    else if (macParams.options & WLN_OPT_BONLY)
        info.rates = RATE_MASK_B;
    else
        info.rates = RATE_MASK_G;

    info.rates_basic = info.rates & RATE_MASK_BASIC;
        basicRates = info.rates_basic;
    info.erp_info = 0;
    InitRates (&info, TRUE);

    // Select a random BSSID
    for (i = 0; i < WLN_ADDR_SIZE; i++)
        pSta->macStatus.bss_addr[i] = (uint8) wln_rand();
    pSta->macStatus.bss_addr[0] &= ~MAC_GROUP;  // clear group bit
    pSta->macStatus.bss_addr[0] |=  MAC_LOCAL;  // set local bit

    if (!MakeBeacon (BEACON_INT))
        return FALSE;

    // Initialize non-WPA encryption
    SetDefaultKeys(pSta);

    // Set BSS info in hardware
    SetChannel (pSta->macStatus.channel);
    SetBSS (pSta->macStatus.bss_caps, pSta->macStatus.bss_addr,
            pSta->macStatus.ssid, pSta->macStatus.ssid_len, info.rates_basic, 0);

    DPRINTF ("Start bssid=" WLN_MACSTR "ssid=%.*s chan=%d caps=0x%04x\n",
                WLN_MAC2STR(pSta->macStatus.bss_addr), pSta->macStatus.ssid_len,
                pSta->macStatus.ssid, pSta->macStatus.channel, pSta->macStatus.bss_caps);

    pSta->staState = SS_ASSOC;
        XPRINTF(TRACE_INFO, "%d staState -> %s\n\r", pSta->entryNdx, textStaState[pSta->staState]);
    pSta->macStatus.state = WLN_ST_START_IBSS;
        XPRINTF(TRACE_INFO, "%d macStatus.state -> %s\n\r", pSta->entryNdx, macStatusState[pSta->macStatus.state]);
    wln_send_event (pSta, WLN_EVT_ASSOC, pSta->macStatus.bss_addr);

    return TRUE;
}

//
// Create beacon and probe response frames to send in an IBSS
// @param interval Beacon interval in TU
// @return TRUE if success, FALSE if error
//
static BOOL MakeBeacon (int interval)
{
    uint8 *bp;
    uint16 caps;
    int dur;
        epStatus *pSta = rootEndPoint[0];

    // Allocate or reuse current frames
    if (bcnFrame == NULL)
        bcnFrame = MacGetBuffer();
    if (respFrame == NULL)
        respFrame = MacGetBuffer();
    if (respFrame == NULL || bcnFrame == NULL)
        return FALSE;

    // Init beacon MAC header
    memset (&bcnFrame->macHdr, 0, sizeof (MacHeader));
    bcnFrame->macHdr.fc.type = TYPE_BEACON;
    memset (bcnFrame->macHdr.addr1, 0xff, WLN_ADDR_SIZE);
    SET_ADDR (bcnFrame->macHdr.addr2, macParams.sta_addr);
    SET_ADDR (bcnFrame->macHdr.addr3, pSta->macStatus.bss_addr);

    // Set fixed params
    // Timestamp is set by hardware
    bp = bcnFrame->body;
    SET16 (&bp[8], interval);

    // Set capabilities
    caps = pSta->macStatus.bss_caps & (CAP_ESS|CAP_IBSS|CAP_PRIVACY);
    if (macParams.options & WLN_OPT_SHORTPRE)
        caps |= CAP_SHORTPRE;
    SET16 (&bp[10], caps);
    bp += 12;

    // Set SSID
    bp[0] = ELEM_SSID;
    
    //__FIX_SSID_IN_IBSS_BEACONS == TRUE)

// we need a non-zero ssid in our IBSS beacon
// this temp patch ensures we have one if MakeBeacon() is called before one is defined as current
//   configured SSID: macParams.ssid
//   current SSID: pSta->macStatus.ssid
	{
		int i;

		if (pSta->macStatus.ssid_len)
		{
			// reject current SSID if ANY byte is zero within ssid_len bytes
			for (i=0;i<pSta->macStatus.ssid_len;i++)
			{
				if (pSta->macStatus.ssid[i]==0)
					break;
			}
		}
		
		if (!pSta->macStatus.ssid_len || i < pSta->macStatus.ssid_len)
		{
			// current SSID rejected, use configured SSID
			bp[1] = macParams.ssid_len;
			if (bp[1])
				memcpy(&bp[2], macParams.ssid, (int)bp[1]);
		}
		else
		{
			// always use current SSID if possible
			bp[1] = pSta->macStatus.ssid_len;
			if (bp[1])
				memcpy(&bp[2], pSta->macStatus.ssid, (int)bp[1]);
		}

		bp += bp[1] + 2;
	}
	
    //bp[1] = pSta->macStatus.ssid_len;
    //memcpy (&bp[2], pSta->macStatus.ssid, pSta->macStatus.ssid_len);
    //bp += bp[1] + 2;

    // Set supported rates
    bp = SetRates (bp, ELEM_SUPRATES, pSta->macStatus.channel);

    // Set channel number
    if (!CHAN_5G (pSta->macStatus.channel))
    {
        bp[0] = ELEM_DSPARAM;
        bp[1] = 1;
        bp[2] = pSta->macStatus.channel;
        bp += bp[1] + 2;
    }
        else
        {
                int ch;
                
                ch = (pSta->macStatus.channel < 0 || pSta->macStatus.channel > WLN_CHAN_NUM)
                                ? -1 : wln_get_chan_info(pSta->macStatus.channel, NULL);

                if (ch >= 0)
                {
                        bp[0] = ELEM_DSPARAM;
                        bp[1] = 1;
                        bp[2] = ch;
                        bp += bp[1] + 2;
                }
        }

    // Set IBSS ATIM window
    bp[0] = ELEM_IBSSPARAM;
    bp[1] = 2;
    SET16 (&bp[2], atimWindow);
    bp += bp[1] + 2;

    // Set TPC Report
    if (naDot11.spectrumManagementRequired)
    {
        bp = generateTpcElement(0, bp);
    }
    
    // Set ERP info.
    if (!CHAN_5G (pSta->macStatus.channel) && !(macParams.options & WLN_OPT_BONLY))
    {
        bp[0] = ELEM_ERPINFO;
        bp[1] = 1;
        bp[2] = 0;
        bp += bp[1] + 2;
    }

    // Set extended supported rates
    bp = SetRates (bp, ELEM_EXTSUPRATES, pSta->macStatus.channel);

    bcnFrame->length = DATA_SIZE + (bp - bcnFrame->body);
    SetPLCP (bcnFrame, rateMin);

    // Copy probe response from beacon
    memcpy (respFrame, bcnFrame,
        sizeof (MacBuffer) + bcnFrame->length - DATA_SIZE);

    // Set probe response type and duration
    respFrame->macHdr.fc.type = TYPE_PROBE_RESP;
    dur = GetAckDuration (respFrame->tx_rate);
    respFrame->macHdr.duration = HTON16(dur);

    return TRUE;
}

//
// Set link status LED based on management state
//
static void SetStatusLED (void)
{
    static int count = 0;
    epStatus *pSta = rootEndPoint[0];
    count++;

    switch (pSta->macStatus.state)
    {
        case WLN_ST_JOIN_IBSS:
        case WLN_ST_START_IBSS:
            SetLED (count & 7); // slow blink, 7/8 on
            break;

        case WLN_ST_AUTH_ESS:
            SetLED (TRUE);      // solid on
            break;

        case WLN_ST_ASSOC_ESS:
            if (pSta->macStatus.wpa_info_len == 0 ||
                pSta->macStatus.rsn_info_len == 0)
            {
                SetLED (TRUE);  // solid on if not WPA
                break;
            }
            // Fall through

        default:
            SetLED (count & 1); // fast blink
            break;
    }
}

//
// Update activity LED
//
void UpdateActivityLED (BOOL on)
{
    static int count = 0;
    
    // Turn on if not aleady on, turn off after a delay
    if (count == 0)
        SetActivityLED (on);
        
    if (on)
        count = 5;
    else if (count > 0)
        count--;
}

// Get scan information
// @param params area to store data
// scanq->scan_count is how many structs caller is prepared to accept
// return value is how many structs we copy into the data block
// Only called from the cli at present. wpa uses wln_get_status.

int wln_get_scan (wln_scan_query *scanq)
{
        wln_scan_bss *ps;
        int ndx, scans = 0;
        epStatus *pSta = rootEndPoint[0];

        /* we can copy up to scanq->scan_count scan structs back */
        if (scan_data != NULL)
        {
                ps = &scan_data->bss[0];
                for (ndx = 0; ndx < min(scan_count, scanq->scan_count); ndx++)
                {
                memcpy (&scanq->scan_data[ndx], ps, sizeof (wln_scan_bss));
                        ps++;
                        scans++;
                }
        }
        else
        memset (&scanq->scan_data, 0, sizeof (wln_scan_data));

        memcpy(scanq->scan_ssid, scan_ssid, WLN_SSID_SIZE);
        scanq->scan_ssid_len = scan_ssid_len;
        scanq->scan_chan_mask[0] = scan_chan_mask[0];
        scanq->scan_chan_mask[1] = scan_chan_mask[1];
        scanq->scan_requested = scan_requested;
        scanq->scan_count = scan_count;
        scanq->scan_max = scan_max;
        scanq->scan_channel = scan_channel;
        scanq->scan_index = scan_index;
        scanq->scan_state = pSta->scan_state;
        scanq->scan_time = scan_time;

        return scans;
}

/*
 * the beacon and response frames are in MacBuffer structures,
 * whose definitions are private to the wifi driver.
 */
int wln_get_oper_state (wln_oper_state *ostate)
{
        epStatus *pSta = rootEndPoint[0];

        ostate->authState = (int) pSta->authState;
        ostate->txState = (int) txState;
        ostate->bcnState = (int) bcnState;
        memcpy(ostate->challenge, pSta->challenge, CHAL_SIZE);
        if (bcnFrame != NULL)
        {
                ostate->bcnLen = min((int) &bcnFrame->macHdr - (int) &bcnFrame + 
                        bcnFrame->length, 80);
                memcpy(ostate->bcnFrame, bcnFrame, ostate->bcnLen);
        }
        else
                ostate->bcnLen = 0;
        if (respFrame != NULL)
        {
                ostate->rspLen = min((int) &respFrame->macHdr - (int) &respFrame + 
                        respFrame->length, 80);
                memcpy(ostate->respFrame, respFrame, ostate->rspLen);
        }
        else
                ostate->rspLen = 0;
        ostate->staState = (int) pSta->staState;
        ostate->bcnInt = bcnInt;
        ostate->atimWindow = atimWindow;
        ostate->basicRates = basicRates;
        ostate->erpInfo = erpInfo;
        ostate->macRandom = macRandom;
        /*
         * Todo: This function, wln_get_oper_state, is used by the CLI.  Do we
         *       need to report on more than the first MAC address?
         */
        wln_get_ciphers(rootEndPoint[0], &ostate->newPairCipher, &ostate->groupCipher);
        return 0;
}

