//
// Embedded 802.11b wireless network interface
// MAC layer data processing functions
// Copyright 2003 Digi International
//
#include "mac.h"
#include "wln_api.h"
#include "wpa_api.h"

/* wrap this around the whole source file */
#if	BSP_WIFI_MULTIPLE_MAC

extern char *macStatusState[];

// LLC/SNAP (802.2) header on data frames
static const LLC_SNAP llc_snap = { SAP_SNAP, SAP_SNAP, XDLC_U, {0, 0, 0}, 0 };

// Receive cache entry
typedef struct {
	MacAddr	addr;		// Transmitter address
	uint16	seq;		// Frame sequence number
	uint16	frags;		// Number of fragments received
	uint32	time;		// Time first fragment received
	MacQueue bufs;		// List of fragments
} RecvCacheEntry;

// Receive cache.
// This collects fragments of MSDUs until all have been received.
// It also stores the most recent MSDUs received so that duplicate
// frames can be detected.
static RecvCacheEntry recvCache[RXCACHE_NUM];

static void RecvData (epStatus *pSta, MacBuffer *sbuf);
static MacBuffer *RecvCache (MacBuffer *buf);
static BOOL SearchRecvCache (MacAddr addr, uint16 seq, RecvCacheEntry **rce);
static const MacCipher *ciphers[] =
{
	&openCipher,
	&wepCipher,
	&tkipCipher,
#ifdef AES_ENABLED
	&ccmpCipher,
#else
	NULL,
#endif
};


MacStats macStats;		// Statistics counters
int maxFragSize;		// Max data fragment size

void naWlnmacInitializeCiphers(epStatus *pSta)
{
	pSta->pairCipher = NULL;
	pSta->newPairCipher = NULL;
	pSta->groupCipher = NULL;
}

//
// Initialize the MAC data module
//
void InitData(void)
{
	memset (recvCache, 0, sizeof recvCache);
	memset (&macStats, 0, sizeof macStats);

	naWlnmacInitializeCiphers(rootEndPoint[0]);

	maxFragSize = macParams.frag_thresh;
}

//
// Set an encryption key
// @param type key type, one of WLN_KEY_xxx
// @param addr peer station if pairwise key, broadcast address if group key
// @param id key index (0-3)
// @param tx TRUE if key is used to transmit
// @param key key data
// @param key_len key length in bytes
// @param seq packet sequence number, NULL if none
//
int wln_set_key (void *context, wln_key_type type, mac_addr addr, int id, BOOL tx,
				 uint8 *key, int key_len, uint8 *seq)
{
/*    epStatus *pSta = (epStatus *) wpa_get_usr_ptr(context); */
    epStatus *pSta = (epStatus *) context;

	if (pSta == NULL)
		return -1;

	XPRINTF(TRACE_INFO, "wln_set_key type %d " WLN_MACSTR " id %d\n\r",
			type, WLN_MAC2STR(addr), id);

	// Delete previous key
	if (IS_GROUP_ADDR (addr))
	{
		if (pSta->groupCipher)
			pSta->groupCipher->SetKey (pSta, id, FALSE, NULL, 0, NULL);
	}
	else
	{
		if (pSta)
		{
			if (pSta->pairCipher)
				pSta->pairCipher->SetKey (pSta, id, FALSE, NULL, 0, NULL);
			pSta->pairCipher = pSta->newPairCipher = NULL;
		}
	}

	if (type == WLN_KEY_NONE)
	{
		pSta->macStatus.encrypt = 0;
		return 0;
	}

	// Set new key
	else if (type <= WLN_KEY_CCMP)
	{
		if (ciphers[type] == NULL)
			return -1;

		if (!ciphers[type]->SetKey (pSta, id, tx, key, key_len, seq))
			return -1;

		// Setting a key to non-default value indicates sucessful authentication
		if (pSta->macStatus.state == WLN_ST_ASSOC_ESS &&
			(key_len != macParams.wep_key_len ||
			 memcmp (key, macParams.wep_key[id], key_len) != 0))
		{
			pSta->macStatus.state = WLN_ST_AUTH_ESS;
	XPRINTF(TRACE_INFO, "pSta->macStatus.state -> %s (wln_set_key)\n\r", macStatusState[pSta->macStatus.state]);

			// Guess at authentication type from WPA info
			if (pSta->macStatus.authen == WLN_AUTH_LEAP)
				;
            else if (pSta->macStatus.wpa_info_len == 0 && pSta->macStatus.rsn_info_len == 0)
				pSta->macStatus.authen = WLN_AUTH_WEP_8021X;
			else if ((pSta->macStatus.rsn_info_len != 0 && pSta->macStatus.rsn_info[19] == 2) ||
                     (pSta->macStatus.wpa_info_len != 0 && pSta->macStatus.wpa_info[23] == 2))
				pSta->macStatus.authen = WLN_AUTH_WPA_PSK;
			else
				pSta->macStatus.authen = WLN_AUTH_WPA_8021X;
	XPRINTF(TRACE_INFO, "pSta->macStatus.authen = %d\n\r", pSta->macStatus.authen);
		}

		if (IS_GROUP_ADDR (addr) || type == WLN_KEY_WEP)
			pSta->groupCipher = (MacCipher *) ciphers[type];
		else
			// WPA app queues an 802.1X frame, then sets the pairwise key.
			// Delay setting pairCipher until after sending the frame,
			// or before receiving a new frame.
			pSta->newPairCipher = (MacCipher *) ciphers[type];

		pSta->macStatus.encrypt |= 1 << type;
		XPRINTF(TRACE_INFO, "pSta->newPairCipher %x pSta->groupCipher %x"
			" pSta->macStatus.encrypt %x\n\r",
			   (int) pSta->newPairCipher, (int) pSta->groupCipher, pSta->macStatus.encrypt);
		return 0;
	}

	return -1;
}

//
// Initialize non-WPA encryption
//
// pairCipher and newPairCipher are specific to each endpoint.
// groupCipher can be shared across all endpoints. (confirm?)
// TODO Should macParams.wep_key be moved to macStatus so that the
// array can be endpoint-specific?
//
// TODO Call this for each secondary endpoint.
//
void SetDefaultKeys (epStatus *pSta)
{
	int i;

	pSta->pairCipher = pSta->newPairCipher = NULL;

	// Initialize WEP using default keys.
	// These may be used by shared key authentication or WEP encryption.
	if (macParams.wep_key_len > 0)
		for (i = 0; i < WLN_KEY_NUM; i++)
			wepCipher.SetKey (pSta, i, i == macParams.wep_key_id,
				macParams.wep_key[i], macParams.wep_key_len, NULL);

	// Select encryption method
	if (pSta->macStatus.bss_caps & CAP_PRIVACY)
	{
		pSta->groupCipher = &wepCipher;
		pSta->macStatus.encrypt = WLN_ENCR_WEP;
	}
	else
	{
		pSta->groupCipher = &openCipher;
		pSta->macStatus.encrypt = WLN_ENCR_OPEN;
	}
}

//
// Send a data MSDU, fragmenting and encrypting as needed.
// @param dst Destination MAC address
// @param epType Ethernet protocol type
// @param data Data to send
// @param size Size of data in bytes
// @return One of ERR_xxx
//

MacReturn MacSendData (MacAddr src, MacAddr dst, int epType, uint8 *data, int size)
{
	MacBuffer *sbuf;
	MacBuffer *buf;
	const MacCipher *cipher;
	int fragSize;
	int frag;
	int i;
	epStatus *pSta = MacGetStationPtr((char *) src);

	if (pSta == NULL)
		return ERR_ASSOC;

	// Drop data frame if not associated
	if (pSta->staState != SS_ASSOC)
	{
		macStats.txDropAssoc++;
		return ERR_ASSOC;
	}

    // Not an 802.1x (EAPOL) frame, set new pair cipher
    if (epType != EP_8021X)
        pSta->pairCipher = pSta->newPairCipher;

	// Decide which encryption cipher to use
	// Don't encrypt 802.1x (EAPOL) frames if no pairwise key
	if (epType == EP_8021X && pSta->pairCipher == NULL)
		cipher = &openCipher;
	// No pair key, or ad-hoc broadcast
	else if (pSta->pairCipher == NULL ||
		(IS_GROUP_ADDR (dst) && (pSta->macStatus.bss_caps & CAP_IBSS)))
		cipher = pSta->groupCipher;
	else
		cipher = pSta->pairCipher;

	if (cipher == NULL)
		return ERR_ENCRYPT;

	// Fragment directed frames, all frames sent to AP
	size += SNAP_SIZE;
	if (size > macParams.frag_thresh - DATA_SIZE &&
		(!IS_GROUP_ADDR (dst) || (pSta->macStatus.bss_caps & CAP_ESS)))
	{
		// More than one fragment of size maxFragSize
		fragSize = maxFragSize - DATA_SIZE;
		if (size > MAX_FRAGS * fragSize)
			return ERR_SIZE;
	}
	else
	{
		// One fragment of size up to BUF_SIZE
		fragSize = BUF_SIZE;
		if (size > fragSize)
			return ERR_SIZE;
	}

	// Allocate a buffer for each fragment
	sbuf = NULL;
	for (i = 0; i < size; i += fragSize)
	{
		buf = MacGetBuffer();
		if (buf == NULL)
		{
			MacFreeBuffers (sbuf);
			return ERR_MEMORY;
		}

		// Add to buffer list
		buf->next = sbuf;
		sbuf = buf;
	}

	/* TODO add logic here for the WDS configuration option. jhl */
	/* If WDS, addr3 is DA and addr4 is SA. addr1 is receiver and
	 * addr2 is transmitter.	*/
	// Set MAC header fields on first fragment
	sbuf->macHdr.fc.type = TYPE_DATA;
	// SET_ADDR (sbuf->macHdr.addr2, macParams.sta_addr);
	SET_ADDR (sbuf->macHdr.addr2, src);
	if (pSta->macStatus.bss_caps & CAP_ESS)
	{
		sbuf->macHdr.fc.toDS = TRUE;
		SET_ADDR (sbuf->macHdr.addr1, pSta->macStatus.bss_addr);
		SET_ADDR (sbuf->macHdr.addr3, dst);
	}
	else
	{
		SET_ADDR (sbuf->macHdr.addr1, dst);
		SET_ADDR (sbuf->macHdr.addr3, pSta->macStatus.bss_addr);
	}
	// Prepare each fragment
	frag = 0;
	for (buf = sbuf; buf; buf = buf->next)
	{
		uint8 *body = buf->body + cipher->ivSize;
		int length = size;
		SeqControlU squ;

		// Limit length to fragment size
		if (length > fragSize)
			length = fragSize;
		buf->length = length + DATA_SIZE;

		// Copy MAC header from first fragment
		if (frag > 0)
			buf->macHdr = sbuf->macHdr;

		// Set fragment fields
		squ.sq.seq = 0;
		squ.sq.frag = frag;
		buf->macHdr.squ.sq16 = HTON16 (squ.sq16);
		buf->macHdr.fc.moreFrag = (buf->next != NULL);

		// Copy LLC/SNAP header to first fragment
		if (frag == 0)
		{
			LLC_SNAP *ls = (LLC_SNAP *) body;
			*ls = llc_snap;
			ls->eptype = epType;

			body   += SNAP_SIZE;
			length -= SNAP_SIZE;
			size   -= SNAP_SIZE;
		}

		// Copy data to fragment
		memcpy (body, data, length);
		data += length;
		size -= length;
		frag++;
	}

	// Add message integrity check
	if (!cipher->AddMIC (pSta, sbuf, dst))
	{
		MacFreeBuffers (sbuf);
		return ERR_ENCRYPT;
	}

	// Encrypt each fragment after headers and MIC are set
	for (buf = sbuf; buf; buf = buf->next)
		if (!cipher->Encrypt (pSta, buf))
		{
			MacFreeBuffers (sbuf);
			return ERR_ENCRYPT;
		}

	// Done with frame, set new pair cipher
	pSta->pairCipher = pSta->newPairCipher;
	SendFrames (pSta, sbuf, dst);

	return ERR_OK;
}

extern int wifi_not_polling;

//
// Send a list of data MPDUs or a management MPDU
// @param sbuf Start of buffer list.
// @param dst Destination MAC address
//
void SendFrames (epStatus *pSta, MacBuffer *sbuf, MacAddr dst)
{
	MacBuffer *buf;
	int rate, rate2, dur;
#if (USB_WIFI_ENABLED == 0)
    int level;
#endif

	rate = rate2 = rateTx;

	// Set MAC address fields for management frames.
	// Send at lowest rate.
	if (IS_MGMT (sbuf))
	{
		SET_ADDR (sbuf->macHdr.addr1, dst);
		SET_ADDR (sbuf->macHdr.addr2, pSta->staAddr);
		if (pSta->staState >= SS_JOIN)
			SET_ADDR (sbuf->macHdr.addr3, pSta->macStatus.bss_addr);
		rate2 = rateMin;
	}

	// If broadcast, send at broadcast rate
	else if (IS_GROUP_ADDR (sbuf->macHdr.addr1))
	{
		rate2 = rateBC;

		XPRINTF(TRACE_XMIT, "SendFrames fc = %x addr3 "WLN_MACSTR"\n\r",
		*(unsigned short *) &sbuf->macHdr.fc, WLN_MAC2STR(sbuf->macHdr.addr3));
	}

	// If protection enabled and multiple fragments,
	// send all but last fragment at broadcast rate
	else if (erpInfo & ERP_USEPROTECT)
		rate = rateBC;

	// Set PLCP header for each fragment
	for (buf = sbuf; buf->next; buf = buf->next)
		SetPLCP (buf, rate);
	SetPLCP (buf, rate2);

	// Set duration for initial fragments
	for (buf = sbuf; buf->next; buf = buf->next)
	{
		// Set this fragment's duration to its ACK plus
		// next fragment's data and ACK
		dur = GetAckDuration (rate) + GetDuration (buf->next);
		buf->macHdr.duration = HTON16(dur);
	}

	// Set duration for final (or only) fragment
	if (IS_GROUP_ADDR (buf->macHdr.addr1))
		buf->macHdr.duration = 0;
	else
	{
		dur = GetAckDuration (rate2);
		buf->macHdr.duration = HTON16(dur);
	}

	// Add each fragment to transmit queue
#if (USB_WIFI_ENABLED == 1)
    tx_mutex_get (&wln_tx_mutex, TX_WAIT_FOREVER);
#else
	level = DISABLE();
#endif

	while (sbuf)
	{
		buf = sbuf->next;
		TXQ_PUT (sbuf);
		sbuf = buf;
	}

	// Start sending first fragment
	if (txState == ST_IDLE)
        {
#if (USB_WIFI_ENABLED == 1)
		if (wifi_not_polling)
		{
			// Don't transmit unless we are polling
			// the WiWave
			MacEvent(MAC_EVT_TX_INTR, (void *)NULL);
		}
		else
		{
			SendQueue();
		}
#else
		SendQueue();
#endif
	}

#if (USB_WIFI_ENABLED == 1)
    tx_mutex_put (&wln_tx_mutex);
#else
	ENABLE (level);
#endif
}

//
// Copy data from a list of MPDUs, defragmenting as needed.
// @param buf Start of buffer list
// @param data Destination to copy data from buffers
// @param size Size of data in bytes
// @return Number of bytes copied, may be less than size
// if end of buffer list is reached
//
int MacGetData (MacBuffer *buf, uint8 *data, int size)
{
	int total = 0;

	// Copy buffers until size is reached
	while (buf && size > 0)
	{
		uint8 *body = buf->body;
		int length = buf->length-DATA_SIZE;

		// Skip over IV
		if (buf->macHdr.fc.protected)
		{
			if (body[3] & EXT_IV)
				body += EXTIV_SIZE;
			else
				body += IV_SIZE;
		}

		// Skip over LLC/SNAP header if first fragment
		if (total == 0)
		{
			body   += SNAP_SIZE;
			length -= SNAP_SIZE;
		}

		if (length > size)
			length = size;
		memcpy (data, body, length);

		buf = buf->next;
		data += length;
		size -= length;
		total += length;
	}

	return total;
}

/*
 * Utility function to find the end-point status block for a given
 * mac address.
 *
 * The companion function wpa_get_usr_ptr asks wpa for the epStatus pointer
 * associated with the context.
 */

epStatus * MacGetStationPtr(char *macAddr)
{
	int ndx;
	epStatus *pSta = NULL;

	for (ndx = 0; ndx < WLN_MAX_ADDRS; ndx++)
	{
		pSta = rootEndPoint[ndx];
		// if (EQUAL_ADDR(pSta->staAddr, macAddr))
		if (EQUAL_ADDR(pSta->staAddr, macAddr))
			return pSta;
	}
	return NULL;
}

// static MacAddr bc_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

//
// Process a frame from receive queue
// @param buf Received frame
//
void RecvFrame (MacBuffer *buf)
{
    int i;
	epStatus *pSta;

	/* beware of broadcast and multicast frames */
    if (*((char *)(buf->macHdr.addr1)) & 0x1)
    {
		pSta = rootEndPoint[0];
	}
	else
	{
		pSta = MacGetStationPtr((char *) buf->macHdr.addr1);
	}

	if (pSta == NULL)
	{
		MacFreeBuffers (buf);
		return;
	}
    GET_MUTEX();
    if (monitorMode)
    {
	    for (i = 0; i < MAX_TYPES; i++)
	    {
		    if (pSta->types[i].ep_type == WLN_MONITOR_TYPE &&
						   	pSta->types[i].func != NULL)
		    {
		    	pSta->types[i].func (pSta->types[i].context, NULL,
						   	(uint8 *)&buf->macHdr, buf->length);
				MacFreeBuffers (buf);
				DPRINTF ("Passed the received frame to application\n");
				PUT_MUTEX();
				return;
		    }
	    }
	}
	PUT_MUTEX();

    naDot11.receivedFrameOnCurrentChannel = TRUE;
	LogRecvStats(buf->macHdr.fc);
	/* jhl turn these off to teste the bridge */
#if 0	 //these are the fixes for UNH test, vantive 23309, 23308, and 23298

	// Should send a deauthentication frame if
	//  1. not authenticated and not associated, and
	//  2. Received frame with unicast address in the address 1 field from
	//  sender, and
	//  3. The received frame is Class 2 -- association request/response;
	//  reassociation request/response;
	//      disassociation.

	// Should send a deauthentication frame if
	//  1. not authenticated and not associated, and
	//  2. Received frame with unicast address in the address 1 field
	//  from sender, and
	//  3. The received frame is Class 3 -- PS_POLL;
	//      any data frames, including those with either the ToDS or
	//      FromDS bits set.

	if ( (pSta) && (pSta->staState < SS_AUTH) &&
	        EQUAL_ADDR (buf->macHdr.addr1, pSta->staAddr) &&
	        ((IS_MGMT(buf) && ( buf->macHdr.fc.type == TYPE_ASSOC_REQ ||
	                            buf->macHdr.fc.type == TYPE_ASSOC_RESP ||
	                            buf->macHdr.fc.type == TYPE_REASSOC_REQ ||
	                            buf->macHdr.fc.type == TYPE_REASSOC_RESP ||
	                            buf->macHdr.fc.type == TYPE_DISASSOC )) ||
	        (((IS_DATA (buf) && (buf->macHdr.fc.toDS ||
								 buf->macHdr.fc.fromDS )) ||
	        buf->macHdr.fc.type == TYPE_PSPOLL ))))
	{
	    force_disassociate(pSta, buf, REAS_INCORRECT_FRAME_UNAUTH, 1);
	    MacFreeBuffers (buf);
        macStats.rxDropInvalid++;
		return;
	}

	//Should send a disassociation frame if
	//  1. is authenticated and not associated, and
	//  2. Received frame with unicast address in the address 1 field
	//  	 from sender, and
	//  3. The received frame is Class 3 -- PS_POLL;
	//      any data frames, including those with either the ToDS or
	//      FromDS bits set.
	if ( (pSta) && (pSta->staState == SS_AUTH) &&
	        EQUAL_ADDR (buf->macHdr.addr1, pSta->staAddr) &&
	        (((IS_DATA (buf) && (buf->macHdr.fc.toDS || buf->macHdr.fc.fromDS ))
			  || buf->macHdr.fc.type == TYPE_PSPOLL )))
	{
	    force_disassociate(pSta, buf, REAS_INCORRECT_FRAME_UNASSO, 0);
	    MacFreeBuffers (buf);
        macStats.rxDropInvalid++;
		return;
	}

#endif	/* UNH fixes */

    // Discard received frame if:
	//   To DS
	//   Broadcast originally from this station
	//   Data and not associated or from another BSS
	if (buf->macHdr.fc.toDS ||
		((pSta) && (IS_GROUP_ADDR (buf->macHdr.addr1) &&
				 EQUAL_ADDR (buf->macHdr.addr3, pSta->staAddr))) ||
		((pSta) && IS_DATA (buf) &&
		 (pSta->staState != SS_ASSOC ||
		  (buf->macHdr.fc.fromDS &&
		  		 !EQUAL_ADDR (buf->macHdr.addr2, pSta->macStatus.bss_addr)) ||
		  (!buf->macHdr.fc.fromDS &&
		  		 !EQUAL_ADDR (buf->macHdr.addr3, pSta->macStatus.bss_addr)))))
	{
		MacFreeBuffers (buf);
		macStats.rxDropInvalid++;
		return;
	}

	// Cache directed frames until all fragments received
	if (!IS_GROUP_ADDR (buf->macHdr.addr1))
	{
		buf = RecvCache (buf);
		if (buf == NULL)
			return;
	}

#if AIROHA_TRANSCEIVER
    adjustMaxAgc(pSta, buf);
#endif
#if defined(BSP_WANT_AUTO_ADJUST_MAC_AGC)
    adjustMaxAgc_wi9c(pSta, buf);
#endif

	// Have complete list of fragments
	if (IS_DATA (buf))
		RecvData (pSta, buf);
	else
		RecvMgmtFrame (pSta, buf);
}

//
// Process a data MSDU
// @param sbuf List of buffers
//

static void RecvData (epStatus *pSta, MacBuffer *sbuf)
{
	MacBuffer *buf;
	const MacCipher *cipher;
	LLC_SNAP *ls;
	int length;

	XPRINTF(TRACE_RECV, "RecvData sbuf->macHdr.addr1 "WLN_MACSTR"\n\r",
					WLN_MAC2STR(sbuf->macHdr.addr1));
	// Update pair cipher before trying to decrypt
	if (!IS_GROUP_ADDR (sbuf->macHdr.addr1))
		pSta->pairCipher = pSta->newPairCipher;

	// Decide which decryption cipher to use
	if (!sbuf->macHdr.fc.protected)
		cipher = &openCipher;
	else if (IS_GROUP_ADDR (sbuf->macHdr.addr1) || pSta->pairCipher == NULL)
		cipher = pSta->groupCipher;
	else
		cipher = pSta->pairCipher;

	if (cipher == NULL)
	{
		MacFreeBuffers (sbuf);
		macStats.rxDropDecrypt++;
		return;
	}

	// Decrypt each fragment
	for (buf = sbuf; buf; buf = buf->next)
		if (!cipher->Decrypt (pSta, buf))
		{
			MacFreeBuffers (sbuf);
			macStats.rxDropDecrypt++;
			return;
		}

	// Verify message integrity check
	if (!cipher->VerifyMIC (pSta, sbuf))
	{
		MacFreeBuffers (sbuf);
		macStats.rxDropDecrypt++;
		return;
	}

	// Calculate total length
	length = 0;
	for (buf = sbuf; buf; buf = buf->next)
    	length += buf->length - DATA_SIZE;

	// Verify LLC/SNAP header
	ls = (LLC_SNAP *) (sbuf->body + cipher->ivSize);
	if (length < SNAP_SIZE || memcmp (ls, &llc_snap, SNAP_SIZE-2) != 0)
	{
		MacFreeBuffers (sbuf);
		macStats.rxDropInvalid++;
		return;
	}

    // Drop if not encrypted when required.
    // Always allow 802.1x (EAPOL) frames.
	if (!sbuf->macHdr.fc.protected && (pSta->macStatus.bss_caps & CAP_PRIVACY) && ls->eptype != EP_8021X)
	{
		MacFreeBuffers (sbuf);
		macStats.rxDropDecrypt++;
		return;
	}

	/* TODO add logic here for the WDS configuration option. jhl */
	/* If from AP (distribution system) addr1 is DA and addr3 is SA.
	 * If not (IBSS = ad hoc) addr1 is DA but add2 is SA.
	 * If WDS, addr3 is DA and addr4 is SA.	*/
    // Send data frame to upper layer
	MacRecvData (sbuf, sbuf->macHdr.addr1,
		sbuf->macHdr.fc.fromDS ? sbuf->macHdr.addr3 : sbuf->macHdr.addr2,
		ls->eptype, length-SNAP_SIZE);
	MacFreeBuffers (sbuf);
}

//
// Add a frame to the receive cache.
// Discards duplicate frames and out of order fragments.
// @param Received frame
// @return List of fragments in MSDU if all fragments received, else NULL.
//
static MacBuffer *RecvCache (MacBuffer *buf)
{
	RecvCacheEntry *rce;
	BOOL found;
	SeqControlU squ;

	// Find <addr2,seq> in receive cache, or an entry to replace
	squ.sq16 = NTOH16 (buf->macHdr.squ.sq16);
	found = SearchRecvCache (buf->macHdr.addr2, squ.sq.seq, &rce);

	// First or only fragment
	if (squ.sq.frag == 0)
	{
		// Discard if duplicate and retry bit set
		if (found && buf->macHdr.fc.retry && rce->frags == 0)
		{
			MacFreeBuffers (buf);
			macStats.rxDropDup++;
			return NULL;
		}

		// Free any previous buffers
		if (rce->bufs.head)
		{
			MacFreeBuffers (rce->bufs.head);
			rce->bufs.head = NULL;
			macStats.rxDropAge++;
		}

		// Set new cache entry
		SET_ADDR (rce->addr, buf->macHdr.addr2);
		rce->seq = squ.sq.seq;
		rce->frags = 1;
		rce->time = GET_TIME();
		PutQueue (&rce->bufs, buf);
	}

	// Other fragments:
	// Add to cache entry if next expected fragment
	else if (found && squ.sq.frag == rce->frags)
	{
		rce->frags++;
		PutQueue (&rce->bufs, buf);
	}
	else
	{
		MacFreeBuffers (buf);
		macStats.rxDropDup++;
		return NULL;
	}

	// Return list of buffers if all fragments received
	if (!buf->macHdr.fc.moreFrag)
	{
		// Remove buffers from cache, but leave entry for duplicate detection
		buf = rce->bufs.head;
		rce->bufs.head = NULL;
		rce->frags = 0;
		return buf;
	}
	else
		return NULL;
}

//
// Find the entry with <addr,seq> in receive cache.
// If not found, select an entry to replace.
// @param addr Transmitter address
// @param seq Frame sequence number
// @param rce Returned pointer to cache entry
// @return TRUE if entry found, FALSE to replace the entry at rce.
//
static BOOL SearchRecvCache (MacAddr addr, uint16 seq, RecvCacheEntry **rce)
{
	int i;
	uint32 oldest = recvCache[0].time;  // Oldest entry to replace
	*rce = &recvCache[0];

	for (i = 0; i < RXCACHE_NUM; i++)
	{
		// Found a match
		if (recvCache[i].seq == seq && EQUAL_ADDR (recvCache[i].addr, addr))
		{
			*rce = &recvCache[i];
			return TRUE;
		}
		// Found an older entry
		else if (recvCache[i].time < oldest)
		{
			*rce = &recvCache[i];
			oldest = recvCache[i].time;
		}
	}

	return FALSE;
}

//
// Free buffers associated with old receive cache entries
//
void AgeRecvCache()
{
	int i;
	uint32 now = GET_TIME();

	for (i = 0; i < RXCACHE_NUM; i++)
		// Has buffers and time is old
		if (recvCache[i].bufs.head && now-recvCache[i].time >= MAX_RX_LIFETIME)
		{
			// Free buffers, but leave entry for duplicate detection
			MacFreeBuffers (recvCache[i].bufs.head);
			recvCache[i].bufs.head = NULL;
			recvCache[i].frags = 0;
			macStats.rxDropAge++;
		}
}


//
// Open cipher (no encryption) implementation
//
static BOOL OpenSetKey (epStatus *pSta, int id, BOOL tx, uint8 *key, int len, uint8 *seq)
{
    NA_UNUSED_PARAM(pSta);
    NA_UNUSED_PARAM(id);  /* added to remove GNU warning. */
    NA_UNUSED_PARAM(tx);  /* added to remove GNU warning. */
    NA_UNUSED_PARAM(key);  /* added to remove GNU warning. */
    NA_UNUSED_PARAM(len);  /* added to remove GNU warning. */
    NA_UNUSED_PARAM(seq);  /* added to remove GNU warning. */
	return FALSE;
}

static BOOL OpenEncrypt (epStatus *pSta, MacBuffer *buf)
{
    NA_UNUSED_PARAM(pSta);
    NA_UNUSED_PARAM(buf);  /* added to remove GNU warning. */
	return TRUE;
}

static BOOL OpenDecrypt (epStatus *pSta, MacBuffer *buf)
{
    NA_UNUSED_PARAM(pSta);
    NA_UNUSED_PARAM(buf);  /* added to remove GNU warning. */
	return !buf->macHdr.fc.protected;
}

BOOL OpenAddMIC (epStatus *pSta, MacBuffer *buf, MacAddr dst)
{
    NA_UNUSED_PARAM(pSta);
    NA_UNUSED_PARAM(buf);  /* added to remove GNU warning. */
    NA_UNUSED_PARAM(dst);  /* added to remove GNU warning. */
	return TRUE;
}

BOOL OpenVerifyMIC (epStatus *pSta, MacBuffer *buf)
{
    NA_UNUSED_PARAM(pSta);
    NA_UNUSED_PARAM(buf);  /* added to remove GNU warning. */
	return TRUE;
}

void wln_get_ciphers(epStatus *pSta, unsigned int *pPairCipher, unsigned int *pGroupCipher)
{
	*pPairCipher = (unsigned int) pSta->newPairCipher;
	*pGroupCipher = (unsigned int) pSta->groupCipher;
}

MacCipher openCipher = {
	0,
	OpenSetKey,
	OpenEncrypt,
	OpenDecrypt,
	OpenAddMIC,
	OpenVerifyMIC
};

#endif	/* BSP_WIFI_MULTIPLE_MAC around whole file */

