//
// Embedded 802.11b wireless network interface
// MAC layer interrupt handler
// Copyright 2003 Digi International
//
#include "mac.h"

TxState txState;			// Transmitter state
static uint32 idleTime;		// Time last in idle state
static MacBuffer *txFrame;	// Frame being tranmitted
static MacBuffer rtsFrame;	// Pre-initialized RTS frame
MacBuffer ctsFrame;			// Pre-initialized CTS-to-self frame
static int cw;				// Contention window size
static int src;				// Short retry counter
static int lrc;				// Long retry counter
static int seq;				// Current sequence number
static int next_seq;		// Next sequence number

BcnState bcnState;			// Beacon/ATIM state
uint32 bcnTime;				// Time last beacon received
static BOOL sentBcn;		// TRUE if sent last beacon
static BOOL sendResp;		// TRUE to send probe response

// Map OFDM rate code to rate in 100kbps
static int ofdmRate[] = { 480, 240, 120, 60, 540, 360, 180, 90 };

static void Retry (void);
static void SendProbeResponse (MacBuffer *rxBuf);
static void SendRTS (MacBuffer *txBuf);
static void SendCTS (MacBuffer *txBuf);


//
// Initialize interrupt handler module
//
void InitIR (void)
{
	txFrame = NULL;
	txState = ST_IDLE;
	bcnState = SB_DATA;
	sentBcn = FALSE;
	sendResp = FALSE;
	bcnTime = 0;
	idleTime = 0;
	seq = 0;
	next_seq = 0;

	// Init fixed values in control frames
	memset (&rtsFrame, 0, sizeof rtsFrame);
	rtsFrame.length = RTS_SIZE;
	rtsFrame.macHdr.fc.type = TYPE_RTS;
	SET_ADDR (rtsFrame.macHdr.addr2, macParams.sta_addr);
	
	memset (&ctsFrame, 0, sizeof ctsFrame);
	ctsFrame.length = CTS_SIZE;
	ctsFrame.macHdr.fc.type = TYPE_CTS;
	SET_ADDR (ctsFrame.macHdr.addr1, macParams.sta_addr);
}

//
// Interrupt module timer
// Called periodically from MacTimer()
//
void IRTimer (void)
{
	uint32 now = GET_TIME();
	uint32 then;

#ifdef IR_TIMEOUT
	// Interrupt handler sets idleTime to 0 when idle.
	// Read time only once in case changed by interrupt.
	// Save actual time here instead of in interrupt handler.
	then = idleTime;
	if (then == 0)
		idleTime = now;
		
	// Check for transmit idle timeout
	else if (txState != ST_IDLE && now-then >= IR_TIMEOUT)
	{
		int level;
		DPRINTF ("Idle timeout state=%d\n", txState);
		level = DISABLE();
		HandleMacError (0);
		ENABLE (level);
	}
#endif

#ifdef BEACON_MISS
	// Check for missed beacons
	if (staState >= SS_JOIN && (macStatus.bss_caps & CAP_ESS))
	{
		// Interrupt handler sets bcnTime to 0 when beacon received.
		// Read time only once in case changed by interrupt.
		// Save actual time here instead of in interrupt handler.
		then = bcnTime;
		if (then == 0)
			bcnTime = now;

		// More than BEACON_MISS intervals since we last
		// received a beacon, resume search for BSS.
		// Note: times are in msecs, beacon intervals are in TU (1024 usecs).
		else if (1000L*(now-then) >= 1024UL*BEACON_MISS*bcnInt)
		{
			DPRINTF ("Beacon timeout\n");
			if (staState == SS_ASSOC)
				wln_send_event (WLN_EVT_DISASSOC, macStatus.bss_addr);
			NextBSS();
		}
	}
#endif
}

//
// Handle a receive interrupt
// @param rxBuf Frame being received, only header is valid
//
void HandleRxEnd (MacBuffer *rxBuf)
{
	BOOL txNext = FALSE;  // TRUE to transmit next frame
	uint16 length;
	
	//
	// Process transmit state transition
	//
	switch (txState)
	{
		case ST_CTS:
			// CTS received
			if (rxBuf->macHdr.fc.type == TYPE_CTS)
			{
				macStats.txRTS++;
				src = 0;
				txNext = TRUE;
			}
			break;

		case ST_ACK:
			// ACK received
			if (rxBuf->macHdr.fc.type == TYPE_ACK)
			{
			    if (txFrame != NULL)
			    {
    				//ASSERT (txFrame);
    				macStats.txBytes += txFrame->length-DATA_SIZE;
    				macStats.txFrames++;
    				rateTxCount++;
    
    				// No more fragments to send
    				if (!txFrame->macHdr.fc.moreFrag)
    				{
    					MacEvent (MAC_EVT_TX_INTR, txFrame);
    					txState = ST_IDLE;
    				}
    
    				// Free data frame
    				MacFreeBufferIR (txFrame);
    				txFrame = NULL;
    				txNext = TRUE;
			    }
			    else
			    {
			        txState = ST_IDLE;
			    }
			
			}
			break;

		case ST_BC:
		case ST_BUSY:
		case ST_IDLE:
			break;
	}

	//
	// Process received frame
	//

	// Calculate length in bytes.
	if (rxBuf->frameHdr.rx.modType == MOD_OFDM)
	{
		// Swap bytes of OFDM PLCP headers
		PlcpHeader plcp;
		plcp.plcp32 = SWAP32 (rxBuf->plcpHdr.plcp32);

		length = plcp.ofdm.length;
		macStatus.rx_rate = ofdmRate[plcp.ofdm.rate & 0x7];
	}
	else
	{
		// Use a switch to avoid doing a divide operation.
		length = NTOH16 (rxBuf->plcpHdr.pskcck.length);
		switch (rxBuf->plcpHdr.pskcck.signal)
		{
			case 10:
				length /= 8;
				break;
			case 20:
				length /= 4;
				break;
			case 55:
				length = (11*length)/16;
				break;
			case 110:
				length = (11*length)/8;
				if (rxBuf->plcpHdr.pskcck.service & SERVICE_LENEXT)
					length--;
				break;
		}
		macStatus.rx_rate = rxBuf->plcpHdr.pskcck.signal;
	}

    // Turn on activity LED
    if ((rxBuf->macHdr.fc.type != TYPE_BEACON) &&
        (rxBuf->macHdr.fc.type != TYPE_PROBE_REQ))
            UpdateActivityLED (TRUE);

    // in monitor mode, pass up the control frames too.
    if (monitorMode)
    {
        if (rxBuf->macHdr.fc.type == TYPE_RTS || rxBuf->macHdr.fc.type == TYPE_CTS || rxBuf->macHdr.fc.type == TYPE_ACK)
        {
            #if (USB_WIFI_ENABLED == 0)
            rxBuf = MacGetBufferIR();
            #endif
			if (rxBuf)
			{
				rxBuf->length = length;
				if (GetFrame (rxBuf))
				{
					// Add to receive queue
				    RXQ_PUT (rxBuf);
				    MacEvent (MAC_EVT_RX_INTR, rxBuf);
				}
				else
				    MacFreeBufferIR (rxBuf);
			}
			
			return;
		}
	}
            
	// Received RTS
	if (rxBuf->macHdr.fc.type == TYPE_RTS)
		macStats.rxRTS++;

	// Received data or management frame
	else if ((rxBuf->macHdr.fc.type & 0x7) == 0)
	{
		// Received beacon and already joined a BSS
		if (staState >= SS_BEACON && rxBuf->macHdr.fc.type == TYPE_BEACON)
		{
			// Receiving a beacon cancels beacon transmission
			if (bcnState == SB_BEACON)
				txState = ST_IDLE;
				
			// Update beacon state
			if (bcnState != SB_DATA)
			{
				if (atimWindow > 0)
					bcnState = SB_ATIM;
				else
					bcnState = SB_DATA;
			}
		}

		// Get a buffer for frame
		if (length <= MAX_FRAME_SIZE)
		{
#if (USB_WIFI_ENABLED == 0)
            // If USB, buffer is already allocated
			rxBuf = MacGetBufferIR();
#endif
			if (rxBuf)
			{
				// Get complete frame
				rxBuf->length = length;
				if (GetFrame (rxBuf))
				{
					// Received probe request.
					// Process in interrupt handler for fast response time.
					if (rxBuf->macHdr.fc.type == TYPE_PROBE_REQ)
					{
						if (monitorMode) // in monitor mode, pass up this frames as well.
						{
						    // Add to receive queue
						    RXQ_PUT (rxBuf);
						    MacEvent (MAC_EVT_RX_INTR, rxBuf);
						}
						else
						{
						    MacFreeBufferIR (rxBuf);
						    SendProbeResponse (rxBuf);
						}
					}
					else
					{
						// Add to receive queue
						RXQ_PUT (rxBuf);
						MacEvent (MAC_EVT_RX_INTR, rxBuf);
					}

					if (IS_GROUP_ADDR (rxBuf->macHdr.addr1))
						macStats.rxBCFrames++;
					else
						macStats.rxFrames++;

					if (rxBuf->macHdr.fc.retry)
						macStats.rxRetries++;

					macStats.rxBytes += length-DATA_SIZE;
					
                    // Frame has been completely read,
                    // buffer has been queued or freed
					length = 0;
				}
				else
					MacFreeBufferIR (rxBuf);
			}
			else
				macStats.rxDropBuffer++;
		}
		else
			macStats.rxDropSize++;
    }
	
	// Drop frame if not copied to buffer
	if (length > 0)
    {
		DropFrame (length);
#if (USB_WIFI_ENABLED == 1) 
        // Free pre-allocated USB buffer
        MacFreeBufferIR(rxBuf);
#endif
    }

	// Reset beacon timer
	bcnTime = 0;

	//
	// Transmit next data frame
	//
	if (txNext || txState == ST_IDLE)
#if (USB_WIFI_ENABLED == 1)
        MacEvent(MAC_EVT_TX_INTR, (void *)NULL);
#else    
		SendQueue();
#endif
}

//
// Handle a transmit complete interrupt.
//
void HandleTxEnd (void)
{
	// Broadcast frame transmitted
	if (txState == ST_BC)
	{
		ASSERT (txFrame);
		macStats.txBytes += txFrame->length-DATA_SIZE;
		macStats.txBCFrames++;
		MacEvent (MAC_EVT_TX_INTR, txFrame);
		
		// Free data frame
		MacFreeBufferIR (txFrame);
		txFrame = NULL;
		txState = ST_IDLE;
	}

	// Other frame transmitted
	else if (txState == ST_BUSY)
	{
		// Beacon transmitted
		if (bcnState == SB_BEACON)
		{
			sentBcn = TRUE;
			if (atimWindow > 0)
				bcnState = SB_ATIM;
			else
				bcnState = SB_DATA;
		}
		txState = ST_IDLE;
	}

	// Transmit next data frame
	if (txState == ST_IDLE)
#if (USB_WIFI_ENABLED == 1)
        MacEvent(MAC_EVT_TX_INTR, (void *)NULL);
#else    
		SendQueue();
#endif
}

//
// Handle an error interrupt
// @param error Error type (not used)
//
void HandleMacError (int error)
{
    NA_UNUSED_PARAM(error);

	// Retry data frame
	if (txState == ST_CTS || txState == ST_ACK)
		Retry();

	// Discard broadcast frame
	else if (txState == ST_BC)
	{
		ASSERT (txFrame);
		macStats.txDropBC++;
		MacEvent (MAC_EVT_TX_INTR, txFrame);
		MacFreeBufferIR (txFrame);
		txFrame = NULL;
	}

	txState = ST_IDLE;
	bcnState = SB_DATA;

	// Transmit next data frame
#if (USB_WIFI_ENABLED == 1)
    MacEvent(MAC_EVT_TX_INTR, (void *)NULL);
#else    
	SendQueue();
#endif
}

//
// Handle a beacon or ATIM interrupt
// @param tbtt TRUE if target beacon transmission time, FALSE if end of ATIM window
//
void HandleBeacon (BOOL tbtt)
{
	// Handle only if joined or started an IBSS
	if ((macStatus.bss_caps & CAP_IBSS) && staState == SS_ASSOC)
	{
		// Time to trasmit a beacon
		if (tbtt && bcnState != SB_BEACON)
		{
			sentBcn = FALSE;
			bcnState = SB_TBTT;
		}

		// End of ATIM interval
		else if (!tbtt)
		{
			// In case a received beacon cancels beacon transmission
			if (bcnState == SB_BEACON)
				txState = ST_IDLE;
			bcnState = SB_DATA;
		}

		// Transmit next frame
		if (txState == ST_IDLE)
#if (USB_WIFI_ENABLED == 1)
            MacEvent(MAC_EVT_TX_INTR, (void *)NULL);
#else    
			SendQueue();
#endif
	}
}

//
// Send the next frame in the transmit queue
//
// This function is called once per data fragment.
// It tries to keep at least 2 frames in the hardware transmit FIFO.
//
// When called for the first frame of a sequence, send one of:
//   Only data fragment
//   First 2 data fragments
//   RTS and first data fragment
//   CTS and only data fragment
//   CTS and first 2 data fragment
//
// When called after RTS or data fragment is complete, send only the
// next data fragment if any.  Previous fragment is already queued.
//
// This function is not called when CTS completes, so always send it
// with the first 2 data fragments.
//
void SendQueue (void)
{
	FrameHeader hdr;
	SeqControlU squ;
	BOOL firstFrame; // TRUE if first frame in sequence
	int next;

	ASSERT (txState == ST_IDLE || txState == ST_CTS || txState == ST_ACK);
	firstFrame = (txState == ST_IDLE);

	// Transmit a beacon.  This may interrupt the
	// current frame sequence after the CTS or ACK.
	if (bcnState == SB_TBTT)
	{
		if (bcnFrame)
		{
			bcnState = SB_BEACON;
			txState = ST_BUSY;
	
			squ.sq.frag = 0;
			squ.sq.seq = next_seq++;
			bcnFrame->macHdr.squ.sq16 = HTON16(squ.sq16);
			
			MacSendFrame (bcnFrame, 2*CW_MIN+1, SF_TXEND);
			return;
		}
		else
			bcnState = SB_DATA;
	}
	
	// Don't send other frames during ATIM window
	if (bcnState != SB_DATA)
		return;

	// Clear idle timer
	if (txState == ST_IDLE)
		idleTime = 0;

	// Transmit a probe response
	if (sendResp && respFrame)
	{
		sendResp = FALSE;
		txState = ST_ACK;  // let's wait for ACK
		
		squ.sq.frag = 0;
		squ.sq.seq = next_seq++;
		respFrame->macHdr.squ.sq16 = HTON16(squ.sq16);

		MacSendFrame (respFrame, CW_MIN, SF_RX);
		return;
	}

	// First time a data frame has been transmitted
	if (txFrame == NULL)
	{
		// Get next frame to send
		TXQ_GET (txFrame);
		if (txFrame == NULL)
		{
			ASSERT (txState == ST_IDLE);
			txState = ST_IDLE;
			return;
		}

		// Init counters
		cw = CW_MIN;
		src = lrc = 0;
		
		// Get new sequence number for first fragment, use it for all fragments
		squ.sq16 = NTOH16(txFrame->macHdr.squ.sq16);
		if (squ.sq.frag == 0)
			seq = next_seq++;
		squ.sq.seq = seq;
		txFrame->macHdr.squ.sq16 = HTON16(squ.sq16);
	}
	
	// Swap bytes of frame header
	hdr.hdr32 = SWAP32 (txFrame->frameHdr.hdr32);

	// Frame length exceeds RTS threashold
	if (firstFrame &&
		txFrame->length > macParams.rts_thresh &&
		!IS_GROUP_ADDR (txFrame->macHdr.addr1))
	{
		SendRTS (txFrame);
	}

	// OFDM protection mechanism enabled
	else if (firstFrame && 
		(erpInfo & ERP_USEPROTECT) &&
		hdr.tx.modType == MOD_OFDM)
	{
		if (macParams.options & WLN_OPT_RTSPROT)
			SendRTS (txFrame);
		else
			SendCTS (txFrame);
	}
	
	// Set next transmit action and state
	if (IS_GROUP_ADDR (txFrame->macHdr.addr1))
	{
		// Wait for txEnd
		txState = ST_BC;
		next = SF_TXEND;
	}
	else
	{
		// Wait for ACK, unless first frame is RTS
		if (!(firstFrame && txState == ST_CTS))
			txState = ST_ACK;
			
		if (txFrame->macHdr.fc.moreFrag)
			next = SF_RX_TX;
		else
			next = SF_RX;
	}

	// If starting a new frame sequence, send first data fragment
	if (firstFrame)
		MacSendFrame (txFrame, cw, next);
		
	// Send next data fragment, if any.
	// If sent RTS above, only send RTS and first data fragment.
	if (txState != ST_CTS && txFrame->macHdr.fc.moreFrag && txQueue.head)
	{
		squ.sq16 = NTOH16(txQueue.head->macHdr.squ.sq16);
		squ.sq.seq = seq;
		txQueue.head->macHdr.squ.sq16 = HTON16(squ.sq16);
	
		MacSendFrame (txQueue.head, cw, next);
	}
	
	// txFrame may need to be retransmitted, so don't free
	// until successfully ACKed (or completed or dropped)
    
    // Turn on activity LED
    UpdateActivityLED (TRUE);
}

//
// Prepare to retransmit the current frame, txFrame.
// Actual trasmission occurs next time SendFrame() is called.
//
static void Retry(void)
{
    if(txFrame != NULL) // we don't retry probe_response
    {
    	//ASSERT (txFrame);
    	macStats.txRetries++;
    	if (src+lrc == 0)
    		rateErrorCount++;
    
    	// Increase contention window
    	if (cw < CW_MAX)
    		cw = 2*cw + 1;
    
    	// Increment short or long retry counter
    	if (txState == ST_CTS || txFrame->length <= macParams.rts_thresh)
    		src++;
    	else
    		lrc++;
    
    	// Check if exceeded retry limit
    	if (src >= SHORT_RETRY_LIMIT || lrc >= LONG_RETRY_LIMIT)
    	{
    		macStats.txDropRetry++;
    		MacEvent (MAC_EVT_TX_INTR, txFrame);
    		
    		// Discard remaining fragments
     		while (txFrame && txFrame->macHdr.fc.moreFrag)
     		{
    			MacFreeBufferIR (txFrame);
    			TXQ_GET (txFrame);
    		}
    
    		// Discard last fragment
    		if (txFrame)
    			MacFreeBufferIR (txFrame);
    		txFrame = NULL;
    		return;
    	}
    
    	// If not a CTS frame, set retry bit
    	if (txState != ST_CTS)
    		txFrame->macHdr.fc.retry = TRUE;
    
    	// Reduce transmit rate
    	if (IS_DATA (txFrame))
    		ReduceRate (txFrame, 2*lrc+src);
    }
}

//
// Send a probe response frame
// @param rxBuf Probe request frame
//
static void SendProbeResponse (MacBuffer *rxBuf)
{
	// Respond only if we sent the last beacon
	if (sentBcn && respFrame)
	{
		// Check SSID
		if (rxBuf->body[0] == ELEM_SSID &&
			(rxBuf->body[1] == 0 ||
		 	 (rxBuf->body[1] == macStatus.ssid_len &&
		  	  memcmp (&rxBuf->body[2], macStatus.ssid, macStatus.ssid_len) == 0)))
		{
			// Send response to requester
			SET_ADDR (respFrame->macHdr.addr1, rxBuf->macHdr.addr2);
			
			// Set flag to send response when transmitter is idle.
			// Only one probe response can be pending.
			sendResp = TRUE;
		}
	}
}

//
// Send a request-to-send frame
// @param txBuf Data frame to transmit after RTS
//
static void SendRTS (MacBuffer *txBuf)
{
	int rate, dur;

	// Send RTS at basic rate.
// mlc trunk doesn't have.	hdr.hdr32 = SWAP32 (txBuf->frameHdr.hdr32);

	// This will be an 802.11b rate if protection is enabled.
#if 0
// MLC trunk doesn't have
	if ((erpInfo & ERP_USEPROTECT) && hdr.tx.modType == MOD_OFDM)
		rate = rateBC;
	else
#endif
	rate = ratesBasic[txBuf->tx_rate];
	SetPLCP (&rtsFrame, rate);

	// Duration of CTS-data-ACK
//MLC	dur = GetAckDuration (txBuf->rate) + GetDuration (txBuf);
	dur = GetAckDuration (rate) + GetDuration (txBuf);
	rtsFrame.macHdr.duration = HTON16 (dur);

	txState = ST_CTS;
	SET_ADDR (rtsFrame.macHdr.addr1, txBuf->macHdr.addr1);
	MacSendFrame (&rtsFrame, cw, SF_RX_TX);
}

//
// Send a CTS-to-self protection frame
// @param txBuf Data frame to transmit after CTS
//
static void SendCTS (MacBuffer *txBuf)
{
	int dur = GetDuration (txBuf);
	ctsFrame.macHdr.duration = HTON16 (dur);

	MacSendFrame (&ctsFrame, cw, SF_RX);
}

/*
int wln_send_raw_frame( uint8 * buf, int length )
{
    MacBuffer *txBuf;
    MacAddr dst;
    
    
    if (length > sizeof (MacBuffer))
        return -1;
      
      
    txBuf = GetBuffer();  
    if (txBuf == NULL)
    {
        return -1;
    }
    
    txBuf->next = NULL;
    txBuf->length = length;
    memcpy (&txBuf->macHdr, buf, length);
    
    dst[0]=0xff;
    dst[1]=0xff;
    dst[2]=0xff;
    dst[3]=0xff;
    dst[4]=0xff;
    dst[5]=0xff;
    
   // SendFrame (txBuf, 0, SF_NONE);
   SendFrames (txBuf, dst);
    //FreeBuffers (txBuf);
    return 0;
}
*/
