//
// 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;
    epStatus *pSta = rootEndPoint[0];

#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);
#if AIROHA_TRANSCEIVER
        resetTxFifo();
#endif
        ENABLE (level);
    }
#endif

#ifdef BEACON_MISS
    // Check for missed beacons
    if (pSta->staState >= SS_JOIN && (pSta->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 (pSta->staState == SS_ASSOC)
                wln_send_event (pSta, WLN_EVT_DISASSOC, pSta->macStatus.bss_addr);
            NextBSS(pSta);
        }
    }
#endif
}

char hexAsciiMap[] = "0123456789abcdef";

void hexToText(char *pHex, char *pText, int len)
{
    while (len-- > 0)
    {
        *pText++ = hexAsciiMap[((*pHex) & 0xf0) >> 4];
        *pText++ = hexAsciiMap[(*pHex) & 0x0f];
        *pText++ = ' ';
        pHex++;
    }
    *(--pText) = '\0';
}

//
// Handle a receive interrupt
// @param rxBuf Frame being received, only header is valid
// and that only up to the end of address2 (size of smallest frame).
//
void HandleRxEnd (MacBuffer *rxBuf)
{
    BOOL txNext = FALSE;  // TRUE to transmit next frame
    uint16 length;
    epStatus *pSta = rootEndPoint[0];
    
    //
    // 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;
                    }
    
#ifdef MAC_PS_ENABLED
                    if (txFrame == wlnNullFramePtr) 
                    {
                        if ((wln_ps_mode == WLN_PSMODE_NF_CLR) || 
                                (wln_ps_mode == WLN_PSMODE_NF_SET))
                        {
                            if (wln_ps_mode == WLN_PSMODE_NF_CLR)
                                wln_ps_mode = WLN_PSMODE_NF_CLR_ACK;    
                            else
                                wln_ps_mode = WLN_PSMODE_NF_SET_ACK;
                            tx_event_flags_set(&wln_ps_event_flags, WLN_PSEVENT_ACK, TX_OR);
                        }
                    }
#endif
    
                    // 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;
        pSta->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;
        }
        pSta->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 (pSta->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
                    {
#ifdef  WIFI_VERBOSE
                        char tBuf[3*sizeof(MacHeader)];
                        hexToText((char *) &rxBuf->macHdr, tBuf,
                                        sizeof(MacHeader));
                        XPRINTF(TRACE_DEBUG, "RX: %s\n\r", tBuf);
#endif
                        // 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)
{
    epStatus *pSta = rootEndPoint[0];

    // Handle only if joined or started an IBSS
    if ((pSta->macStatus.bss_caps & CAP_IBSS) && pSta->staState == SS_ASSOC)
    {
        // Time to transmit 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.
//
int sendingCTSRTS = 0;
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);

    #ifdef MAC_PS_ENABLED
    /* no, we are sleeping cannot process the request */
    if ((wln_ps_mode == WLN_PSMODE_SLEEP) || 
        (wln_ps_mode == WLN_PSMODE_NF_SET_ACK) || 
        (wln_ps_mode == WLN_PSMODE_NF_CLR_ACK))
    {
        if (txFrame == wlnNullFramePtr)
            txFrame = NULL;
        return;
    }
    else if ((wlnNullFramePtr != NULL) && (txFrame == wlnNullFramePtr))
    {  
        // we ignored the ack, so drop the packet
        if (wlnClearTxFrame || ((wln_ps_mode != WLN_PSMODE_NF_SET) && (wln_ps_mode != WLN_PSMODE_NF_CLR)))
        {
            txFrame = NULL;
            wlnClearTxFrame = FALSE;
        }
        else if (txFrame->macHdr.fc.retry == 0)  // allow only retry at this point
            return;                
    }
    #endif

    if (naDot11.quiet.transmitterStopped)       /* if transmitter stopped for quiet period */
    {
        uint32 ticks = tx_time_get();
        
        if ((naDot11.quiet.periodEnd + naDot11.quiet.overflowCorrection) < (ticks + naDot11.quiet.overflowCorrection))
        {                                       /* if time to end quiet period */
            naDot11.quiet.transmitterStopped = FALSE; 
        }                                       /* clear flag and fall thru to code that sends stuff */
        else
        {
            return;                             /* exit if not time to end quiet period */
        }
    }
    
    // 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);
            
            sendingCTSRTS = 0;
            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);
    
        sendingCTSRTS = 0;
        MacSendFrame (respFrame, CW_MIN, SF_RX);
        XPRINTF(TRACE_XMIT, "SendQueue: resp sendResp %d respFrame %x\n\r",
                        (int) sendResp, (int) respFrame);
        return;
    }

    // First time a data frame has been transmitted
    if (txFrame == NULL)
    {
        // Get next frame to send
#ifdef MAC_PS_ENABLED
        if (wln_ps_mode == WLN_PSMODE_NF_SET)
            txFrame = wln_make_null_frame(TRUE);
        else if (wln_ps_mode == WLN_PSMODE_NF_CLR)
            txFrame = wln_make_null_frame(FALSE);
        else
            TXQ_GET (txFrame);
#else
        TXQ_GET (txFrame);
#endif
        
        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);
    }
    
#ifdef MAC_PS_ENABLED
    if (txFrame == wlnNullFramePtr)
    {
        txState = ST_ACK;
        sendingCTSRTS = 0;
        MacSendFrame (txFrame, cw, SF_RX);
        return;
    }
#endif

    // Swap bytes of frame header
    hdr.hdr32 = SWAP32 (txFrame->frameHdr.hdr32);

    // Frame length exceeds RTS threshold
    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)
    {
#ifdef  WIFI_VERBOSE
        char tBuf[3*sizeof(MacHeader)];
        hexToText((char *) &txFrame->macHdr, tBuf, sizeof(MacHeader));
        XPRINTF(TRACE_XMIT, "MacSendFrame first %s\n\r", tBuf);
#endif
        sendingCTSRTS = 0;
        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);
#ifdef  WIFI_VERBOSE
        XPRINTF(TRACE_XMIT, "MacSendFrame subsequent %x\n\r",
                        (int) txQueue.head);
#endif
        sendingCTSRTS = 0;
        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);
            
#ifdef MAC_PS_ENABLED
            // gave up
            if (txFrame == wlnNullFramePtr) 
            {
                if ((wln_ps_mode == WLN_PSMODE_NF_CLR) || 
                    (wln_ps_mode == WLN_PSMODE_NF_SET))
                {
                    tx_event_flags_set(&wln_ps_event_flags, WLN_PSEVENT_ACK, TX_OR);
                }
                txFrame = NULL;
                return;
            }
#endif
                    
            // 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);
#ifdef MAC_PS_ENABLED
        else if (txFrame->macHdr.fc.type == TYPE_NULL_DATA)
            ReduceRate (txFrame, 2*lrc+src);
#endif
    }
}

//
// Send a probe response frame
// @param rxBuf Probe request frame
//
static void SendProbeResponse (MacBuffer *rxBuf)
{
    epStatus *pSta = rootEndPoint[0];

    // 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] == pSta->macStatus.ssid_len &&
              memcmp (&rxBuf->body[2], pSta->macStatus.ssid, pSta->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)
{
    FrameHeader hdr;
    int rate, dur;

    // Swap bytes of frame header
    hdr.hdr32 = SWAP32 (txBuf->frameHdr.hdr32);

    // Send RTS at broadcast rate if protection enabled, else at basic rate
    if ((erpInfo & ERP_USEPROTECT) && hdr.tx.modType == MOD_OFDM)
        rate = rateBC;
    else
    rate = ratesBasic[txBuf->tx_rate];
        
    SetPLCP (&rtsFrame, rate);

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

    txState = ST_CTS;
    SET_ADDR (rtsFrame.macHdr.addr1, txBuf->macHdr.addr1);
    sendingCTSRTS = 1;
    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);
    
    sendingCTSRTS = 1;
    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); this is not used
    //FreeBuffers (txBuf);
    return 0;
}
*/
