diff options
Diffstat (limited to 'drivers/staging/rt2870/common/ba_action.c')
-rw-r--r-- | drivers/staging/rt2870/common/ba_action.c | 1798 |
1 files changed, 1798 insertions, 0 deletions
diff --git a/drivers/staging/rt2870/common/ba_action.c b/drivers/staging/rt2870/common/ba_action.c new file mode 100644 index 000000000000..95addb20bced --- /dev/null +++ b/drivers/staging/rt2870/common/ba_action.c @@ -0,0 +1,1798 @@ +/* + ************************************************************************* + * Ralink Tech Inc. + * 5F., No.36, Taiyuan St., Jhubei City, + * Hsinchu County 302, + * Taiwan, R.O.C. + * + * (c) Copyright 2002-2007, Ralink Technology, Inc. + * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * * + ************************************************************************* + */ + + +#ifdef DOT11_N_SUPPORT + +#include "../rt_config.h" + + + +#define BA_ORI_INIT_SEQ (pEntry->TxSeq[TID]) //1 // inital sequence number of BA session + +#define ORI_SESSION_MAX_RETRY 8 +#define ORI_BA_SESSION_TIMEOUT (2000) // ms +#define REC_BA_SESSION_IDLE_TIMEOUT (1000) // ms + +#define REORDERING_PACKET_TIMEOUT ((100 * HZ)/1000) // system ticks -- 100 ms +#define MAX_REORDERING_PACKET_TIMEOUT ((3000 * HZ)/1000) // system ticks -- 100 ms + +#define RESET_RCV_SEQ (0xFFFF) + +static void ba_mpdu_blk_free(PRTMP_ADAPTER pAd, struct reordering_mpdu *mpdu_blk); + + +BA_ORI_ENTRY *BATableAllocOriEntry( + IN PRTMP_ADAPTER pAd, + OUT USHORT *Idx); + +BA_REC_ENTRY *BATableAllocRecEntry( + IN PRTMP_ADAPTER pAd, + OUT USHORT *Idx); + +VOID BAOriSessionSetupTimeout( + IN PVOID SystemSpecific1, + IN PVOID FunctionContext, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3); + +VOID BARecSessionIdleTimeout( + IN PVOID SystemSpecific1, + IN PVOID FunctionContext, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3); + + +BUILD_TIMER_FUNCTION(BAOriSessionSetupTimeout); +BUILD_TIMER_FUNCTION(BARecSessionIdleTimeout); + +#define ANNOUNCE_REORDERING_PACKET(_pAd, _mpdu_blk) \ + Announce_Reordering_Packet(_pAd, _mpdu_blk); + +VOID BA_MaxWinSizeReasign( + IN PRTMP_ADAPTER pAd, + IN MAC_TABLE_ENTRY *pEntryPeer, + OUT UCHAR *pWinSize) +{ + UCHAR MaxSize; + + + if (pAd->MACVersion >= RALINK_2883_VERSION) // 3*3 + { + if (pAd->MACVersion >= RALINK_3070_VERSION) + { + if (pEntryPeer->WepStatus != Ndis802_11EncryptionDisabled) + MaxSize = 7; // for non-open mode + else + MaxSize = 13; + } + else + MaxSize = 31; + } + else if (pAd->MACVersion >= RALINK_2880E_VERSION) // 2880 e + { + if (pEntryPeer->WepStatus != Ndis802_11EncryptionDisabled) + MaxSize = 7; // for non-open mode + else + MaxSize = 13; + } + else + MaxSize = 7; + + DBGPRINT(RT_DEBUG_TRACE, ("ba> Win Size = %d, Max Size = %d\n", + *pWinSize, MaxSize)); + + if ((*pWinSize) > MaxSize) + { + DBGPRINT(RT_DEBUG_TRACE, ("ba> reassign max win size from %d to %d\n", + *pWinSize, MaxSize)); + + *pWinSize = MaxSize; + } +} + +void Announce_Reordering_Packet(IN PRTMP_ADAPTER pAd, + IN struct reordering_mpdu *mpdu) +{ + PNDIS_PACKET pPacket; + + pPacket = mpdu->pPacket; + + if (mpdu->bAMSDU) + { + ASSERT(0); + BA_Reorder_AMSDU_Annnounce(pAd, pPacket); + } + else + { + // + // pass this 802.3 packet to upper layer or forward this packet to WM directly + // + +#ifdef CONFIG_STA_SUPPORT + IF_DEV_CONFIG_OPMODE_ON_STA(pAd) + ANNOUNCE_OR_FORWARD_802_3_PACKET(pAd, pPacket, RTMP_GET_PACKET_IF(pPacket)); +#endif // CONFIG_STA_SUPPORT // + } +} + +/* + * Insert a reordering mpdu into sorted linked list by sequence no. + */ +BOOLEAN ba_reordering_mpdu_insertsorted(struct reordering_list *list, struct reordering_mpdu *mpdu) +{ + + struct reordering_mpdu **ppScan = &list->next; + + while (*ppScan != NULL) + { + if (SEQ_SMALLER((*ppScan)->Sequence, mpdu->Sequence, MAXSEQ)) + { + ppScan = &(*ppScan)->next; + } + else if ((*ppScan)->Sequence == mpdu->Sequence) + { + /* give up this duplicated frame */ + return(FALSE); + } + else + { + /* find position */ + break; + } + } + + mpdu->next = *ppScan; + *ppScan = mpdu; + list->qlen++; + return TRUE; +} + + +/* + * caller lock critical section if necessary + */ +static inline void ba_enqueue(struct reordering_list *list, struct reordering_mpdu *mpdu_blk) +{ + list->qlen++; + mpdu_blk->next = list->next; + list->next = mpdu_blk; +} + +/* + * caller lock critical section if necessary + */ +static inline struct reordering_mpdu * ba_dequeue(struct reordering_list *list) +{ + struct reordering_mpdu *mpdu_blk = NULL; + + ASSERT(list); + + if (list->qlen) + { + list->qlen--; + mpdu_blk = list->next; + if (mpdu_blk) + { + list->next = mpdu_blk->next; + mpdu_blk->next = NULL; + } + } + return mpdu_blk; +} + + +static inline struct reordering_mpdu *ba_reordering_mpdu_dequeue(struct reordering_list *list) +{ + return(ba_dequeue(list)); +} + + +static inline struct reordering_mpdu *ba_reordering_mpdu_probe(struct reordering_list *list) + { + ASSERT(list); + + return(list->next); + } + + +/* + * free all resource for reordering mechanism + */ +void ba_reordering_resource_release(PRTMP_ADAPTER pAd) +{ + BA_TABLE *Tab; + PBA_REC_ENTRY pBAEntry; + struct reordering_mpdu *mpdu_blk; + int i; + + Tab = &pAd->BATable; + + /* I. release all pending reordering packet */ + NdisAcquireSpinLock(&pAd->BATabLock); + for (i = 0; i < MAX_LEN_OF_BA_REC_TABLE; i++) + { + pBAEntry = &Tab->BARecEntry[i]; + if (pBAEntry->REC_BA_Status != Recipient_NONE) + { + while ((mpdu_blk = ba_reordering_mpdu_dequeue(&pBAEntry->list))) + { + ASSERT(mpdu_blk->pPacket); + RELEASE_NDIS_PACKET(pAd, mpdu_blk->pPacket, NDIS_STATUS_FAILURE); + ba_mpdu_blk_free(pAd, mpdu_blk); + } + } + } + NdisReleaseSpinLock(&pAd->BATabLock); + + ASSERT(pBAEntry->list.qlen == 0); + /* II. free memory of reordering mpdu table */ + NdisAcquireSpinLock(&pAd->mpdu_blk_pool.lock); + os_free_mem(pAd, pAd->mpdu_blk_pool.mem); + NdisReleaseSpinLock(&pAd->mpdu_blk_pool.lock); +} + + + +/* + * Allocate all resource for reordering mechanism + */ +BOOLEAN ba_reordering_resource_init(PRTMP_ADAPTER pAd, int num) +{ + int i; + PUCHAR mem; + struct reordering_mpdu *mpdu_blk; + struct reordering_list *freelist; + + /* allocate spinlock */ + NdisAllocateSpinLock(&pAd->mpdu_blk_pool.lock); + + /* initialize freelist */ + freelist = &pAd->mpdu_blk_pool.freelist; + freelist->next = NULL; + freelist->qlen = 0; + + DBGPRINT(RT_DEBUG_TRACE, ("Allocate %d memory for BA reordering\n", (UINT32)(num*sizeof(struct reordering_mpdu)))); + + /* allocate number of mpdu_blk memory */ + os_alloc_mem(pAd, (PUCHAR *)&mem, (num*sizeof(struct reordering_mpdu))); + + pAd->mpdu_blk_pool.mem = mem; + + if (mem == NULL) + { + DBGPRINT(RT_DEBUG_ERROR, ("Can't Allocate Memory for BA Reordering\n")); + return(FALSE); + } + + /* build mpdu_blk free list */ + for (i=0; i<num; i++) + { + /* get mpdu_blk */ + mpdu_blk = (struct reordering_mpdu *) mem; + /* initial mpdu_blk */ + NdisZeroMemory(mpdu_blk, sizeof(struct reordering_mpdu)); + /* next mpdu_blk */ + mem += sizeof(struct reordering_mpdu); + /* insert mpdu_blk into freelist */ + ba_enqueue(freelist, mpdu_blk); + } + + return(TRUE); +} + +//static int blk_count=0; // sample take off, no use + +static struct reordering_mpdu *ba_mpdu_blk_alloc(PRTMP_ADAPTER pAd) +{ + struct reordering_mpdu *mpdu_blk; + + NdisAcquireSpinLock(&pAd->mpdu_blk_pool.lock); + mpdu_blk = ba_dequeue(&pAd->mpdu_blk_pool.freelist); + if (mpdu_blk) + { +// blk_count++; + /* reset mpdu_blk */ + NdisZeroMemory(mpdu_blk, sizeof(struct reordering_mpdu)); + } + NdisReleaseSpinLock(&pAd->mpdu_blk_pool.lock); + return mpdu_blk; +} + +static void ba_mpdu_blk_free(PRTMP_ADAPTER pAd, struct reordering_mpdu *mpdu_blk) +{ + ASSERT(mpdu_blk); + + NdisAcquireSpinLock(&pAd->mpdu_blk_pool.lock); +// blk_count--; + ba_enqueue(&pAd->mpdu_blk_pool.freelist, mpdu_blk); + NdisReleaseSpinLock(&pAd->mpdu_blk_pool.lock); +} + + +static USHORT ba_indicate_reordering_mpdus_in_order( + IN PRTMP_ADAPTER pAd, + IN PBA_REC_ENTRY pBAEntry, + IN USHORT StartSeq) +{ + struct reordering_mpdu *mpdu_blk; + USHORT LastIndSeq = RESET_RCV_SEQ; + + NdisAcquireSpinLock(&pBAEntry->RxReRingLock); + + while ((mpdu_blk = ba_reordering_mpdu_probe(&pBAEntry->list))) + { + /* find in-order frame */ + if (!SEQ_STEPONE(mpdu_blk->Sequence, StartSeq, MAXSEQ)) + { + break; + } + /* dequeue in-order frame from reodering list */ + mpdu_blk = ba_reordering_mpdu_dequeue(&pBAEntry->list); + /* pass this frame up */ + ANNOUNCE_REORDERING_PACKET(pAd, mpdu_blk); + /* move to next sequence */ + StartSeq = mpdu_blk->Sequence; + LastIndSeq = StartSeq; + /* free mpdu_blk */ + ba_mpdu_blk_free(pAd, mpdu_blk); + } + + NdisReleaseSpinLock(&pBAEntry->RxReRingLock); + + /* update last indicated sequence */ + return LastIndSeq; +} + +static void ba_indicate_reordering_mpdus_le_seq( + IN PRTMP_ADAPTER pAd, + IN PBA_REC_ENTRY pBAEntry, + IN USHORT Sequence) +{ + struct reordering_mpdu *mpdu_blk; + + NdisAcquireSpinLock(&pBAEntry->RxReRingLock); + while ((mpdu_blk = ba_reordering_mpdu_probe(&pBAEntry->list))) + { + /* find in-order frame */ + if ((mpdu_blk->Sequence == Sequence) || SEQ_SMALLER(mpdu_blk->Sequence, Sequence, MAXSEQ)) + { + /* dequeue in-order frame from reodering list */ + mpdu_blk = ba_reordering_mpdu_dequeue(&pBAEntry->list); + /* pass this frame up */ + ANNOUNCE_REORDERING_PACKET(pAd, mpdu_blk); + /* free mpdu_blk */ + ba_mpdu_blk_free(pAd, mpdu_blk); + } + else + { + break; + } + } + NdisReleaseSpinLock(&pBAEntry->RxReRingLock); +} + + +static void ba_refresh_reordering_mpdus( + IN PRTMP_ADAPTER pAd, + PBA_REC_ENTRY pBAEntry) +{ + struct reordering_mpdu *mpdu_blk; + + NdisAcquireSpinLock(&pBAEntry->RxReRingLock); + + /* dequeue in-order frame from reodering list */ + while ((mpdu_blk = ba_reordering_mpdu_dequeue(&pBAEntry->list))) + { + /* pass this frame up */ + ANNOUNCE_REORDERING_PACKET(pAd, mpdu_blk); + + pBAEntry->LastIndSeq = mpdu_blk->Sequence; + ba_mpdu_blk_free(pAd, mpdu_blk); + + /* update last indicated sequence */ + } + ASSERT(pBAEntry->list.qlen == 0); + pBAEntry->LastIndSeq = RESET_RCV_SEQ; + NdisReleaseSpinLock(&pBAEntry->RxReRingLock); +} + + +//static +void ba_flush_reordering_timeout_mpdus( + IN PRTMP_ADAPTER pAd, + IN PBA_REC_ENTRY pBAEntry, + IN ULONG Now32) + +{ + USHORT Sequence; + +// if ((RTMP_TIME_AFTER((unsigned long)Now32, (unsigned long)(pBAEntry->LastIndSeqAtTimer+REORDERING_PACKET_TIMEOUT)) && +// (pBAEntry->list.qlen > ((pBAEntry->BAWinSize*7)/8))) //|| +// (RTMP_TIME_AFTER((unsigned long)Now32, (unsigned long)(pBAEntry->LastIndSeqAtTimer+(10*REORDERING_PACKET_TIMEOUT))) && +// (pBAEntry->list.qlen > (pBAEntry->BAWinSize/8))) + if (RTMP_TIME_AFTER((unsigned long)Now32, (unsigned long)(pBAEntry->LastIndSeqAtTimer+(MAX_REORDERING_PACKET_TIMEOUT/6))) + &&(pBAEntry->list.qlen > 1) + ) + { + DBGPRINT(RT_DEBUG_TRACE,("timeout[%d] (%08lx-%08lx = %d > %d): %x, flush all!\n ", pBAEntry->list.qlen, Now32, (pBAEntry->LastIndSeqAtTimer), + (int)((long) Now32 - (long)(pBAEntry->LastIndSeqAtTimer)), MAX_REORDERING_PACKET_TIMEOUT, + pBAEntry->LastIndSeq)); + ba_refresh_reordering_mpdus(pAd, pBAEntry); + pBAEntry->LastIndSeqAtTimer = Now32; + } + else + if (RTMP_TIME_AFTER((unsigned long)Now32, (unsigned long)(pBAEntry->LastIndSeqAtTimer+(REORDERING_PACKET_TIMEOUT))) + && (pBAEntry->list.qlen > 0) + ) + { + // + // force LastIndSeq to shift to LastIndSeq+1 + // + Sequence = (pBAEntry->LastIndSeq+1) & MAXSEQ; + ba_indicate_reordering_mpdus_le_seq(pAd, pBAEntry, Sequence); + pBAEntry->LastIndSeqAtTimer = Now32; + pBAEntry->LastIndSeq = Sequence; + // + // indicate in-order mpdus + // + Sequence = ba_indicate_reordering_mpdus_in_order(pAd, pBAEntry, Sequence); + if (Sequence != RESET_RCV_SEQ) + { + pBAEntry->LastIndSeq = Sequence; + } + + } +#if 0 + else if ( + (RTMP_TIME_AFTER((unsigned long)Now32, (unsigned long)(pBAEntry->LastIndSeqAtTimer+(MAX_REORDERING_PACKET_TIMEOUT))) && + (pBAEntry->list.qlen > 1)) + ) + { + DBGPRINT(RT_DEBUG_TRACE,("timeout[%d] (%lx-%lx = %d > %d): %x\n ", pBAEntry->list.qlen, Now32, (pBAEntry->LastIndSeqAtTimer), + (int)((long) Now32 - (long)(pBAEntry->LastIndSeqAtTimer)), MAX_REORDERING_PACKET_TIMEOUT, + pBAEntry->LastIndSeq)); + ba_refresh_reordering_mpdus(pAd, pBAEntry); + pBAEntry->LastIndSeqAtTimer = Now32; + } +#endif +} + + +/* + * generate ADDBA request to + * set up BA agreement + */ +VOID BAOriSessionSetUp( + IN PRTMP_ADAPTER pAd, + IN MAC_TABLE_ENTRY *pEntry, + IN UCHAR TID, + IN USHORT TimeOut, + IN ULONG DelayTime, + IN BOOLEAN isForced) + +{ + //MLME_ADDBA_REQ_STRUCT AddbaReq; + BA_ORI_ENTRY *pBAEntry = NULL; + USHORT Idx; + BOOLEAN Cancelled; + + if ((pAd->CommonCfg.BACapability.field.AutoBA != TRUE) && (isForced == FALSE)) + return; + + // if this entry is limited to use legacy tx mode, it doesn't generate BA. + if (RTMPStaFixedTxMode(pAd, pEntry) != FIXED_TXMODE_HT) + return; + + if ((pEntry->BADeclineBitmap & (1<<TID)) && (isForced == FALSE)) + { + // try again after 3 secs + DelayTime = 3000; +// printk("DeCline BA from Peer\n"); +// return; + } + + + Idx = pEntry->BAOriWcidArray[TID]; + if (Idx == 0) + { + // allocate a BA session + pBAEntry = BATableAllocOriEntry(pAd, &Idx); + if (pBAEntry == NULL) + { + DBGPRINT(RT_DEBUG_TRACE,("ADDBA - MlmeADDBAAction() allocate BA session failed \n")); + return; + } + } + else + { + pBAEntry =&pAd->BATable.BAOriEntry[Idx]; + } + + if (pBAEntry->ORI_BA_Status >= Originator_WaitRes) + { + return; + } + + pEntry->BAOriWcidArray[TID] = Idx; + + // Initialize BA session + pBAEntry->ORI_BA_Status = Originator_WaitRes; + pBAEntry->Wcid = pEntry->Aid; + pBAEntry->BAWinSize = pAd->CommonCfg.BACapability.field.RxBAWinLimit; + pBAEntry->Sequence = BA_ORI_INIT_SEQ; + pBAEntry->Token = 1; // (2008-01-21) Jan Lee recommends it - this token can't be 0 + pBAEntry->TID = TID; + pBAEntry->TimeOutValue = TimeOut; + pBAEntry->pAdapter = pAd; + + if (!(pEntry->TXBAbitmap & (1<<TID))) + { + RTMPInitTimer(pAd, &pBAEntry->ORIBATimer, GET_TIMER_FUNCTION(BAOriSessionSetupTimeout), pBAEntry, FALSE); + } + else + RTMPCancelTimer(&pBAEntry->ORIBATimer, &Cancelled); + + // set timer to send ADDBA request + RTMPSetTimer(&pBAEntry->ORIBATimer, DelayTime); +} + +VOID BAOriSessionAdd( + IN PRTMP_ADAPTER pAd, + IN MAC_TABLE_ENTRY *pEntry, + IN PFRAME_ADDBA_RSP pFrame) +{ + BA_ORI_ENTRY *pBAEntry = NULL; + BOOLEAN Cancelled; + UCHAR TID; + USHORT Idx; + PUCHAR pOutBuffer2 = NULL; + NDIS_STATUS NStatus; + ULONG FrameLen; + FRAME_BAR FrameBar; + + TID = pFrame->BaParm.TID; + Idx = pEntry->BAOriWcidArray[TID]; + pBAEntry =&pAd->BATable.BAOriEntry[Idx]; + + // Start fill in parameters. + if ((Idx !=0) && (pBAEntry->TID == TID) && (pBAEntry->ORI_BA_Status == Originator_WaitRes)) + { + pBAEntry->BAWinSize = min(pBAEntry->BAWinSize, ((UCHAR)pFrame->BaParm.BufSize)); + BA_MaxWinSizeReasign(pAd, pEntry, &pBAEntry->BAWinSize); + + pBAEntry->TimeOutValue = pFrame->TimeOutValue; + pBAEntry->ORI_BA_Status = Originator_Done; + // reset sequence number + pBAEntry->Sequence = BA_ORI_INIT_SEQ; + // Set Bitmap flag. + pEntry->TXBAbitmap |= (1<<TID); + RTMPCancelTimer(&pBAEntry->ORIBATimer, &Cancelled); + + pBAEntry->ORIBATimer.TimerValue = 0; //pFrame->TimeOutValue; + + DBGPRINT(RT_DEBUG_TRACE,("%s : TXBAbitmap = %x, BAWinSize = %d, TimeOut = %ld\n", __FUNCTION__, pEntry->TXBAbitmap, + pBAEntry->BAWinSize, pBAEntry->ORIBATimer.TimerValue)); + + // SEND BAR ; + NStatus = MlmeAllocateMemory(pAd, &pOutBuffer2); //Get an unused nonpaged memory + if (NStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(RT_DEBUG_TRACE,("BA - BAOriSessionAdd() allocate memory failed \n")); + return; + } + + +#ifdef CONFIG_STA_SUPPORT + IF_DEV_CONFIG_OPMODE_ON_STA(pAd) + BarHeaderInit(pAd, &FrameBar, pAd->MacTab.Content[pBAEntry->Wcid].Addr, pAd->CurrentAddress); +#endif // CONFIG_STA_SUPPORT // + + FrameBar.StartingSeq.field.FragNum = 0; // make sure sequence not clear in DEL function. + FrameBar.StartingSeq.field.StartSeq = pBAEntry->Sequence; // make sure sequence not clear in DEL funciton. + FrameBar.BarControl.TID = pBAEntry->TID; // make sure sequence not clear in DEL funciton. + MakeOutgoingFrame(pOutBuffer2, &FrameLen, + sizeof(FRAME_BAR), &FrameBar, + END_OF_ARGS); + MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer2, FrameLen); + MlmeFreeMemory(pAd, pOutBuffer2); + + + if (pBAEntry->ORIBATimer.TimerValue) + RTMPSetTimer(&pBAEntry->ORIBATimer, pBAEntry->ORIBATimer.TimerValue); // in mSec + } +} + +BOOLEAN BARecSessionAdd( + IN PRTMP_ADAPTER pAd, + IN MAC_TABLE_ENTRY *pEntry, + IN PFRAME_ADDBA_REQ pFrame) +{ + BA_REC_ENTRY *pBAEntry = NULL; + BOOLEAN Status = TRUE; + BOOLEAN Cancelled; + USHORT Idx; + UCHAR TID; + UCHAR BAWinSize; + //UINT32 Value; + //UINT offset; + + + ASSERT(pEntry); + + // find TID + TID = pFrame->BaParm.TID; + + BAWinSize = min(((UCHAR)pFrame->BaParm.BufSize), (UCHAR)pAd->CommonCfg.BACapability.field.RxBAWinLimit); + + // Intel patch + if (BAWinSize == 0) + { + BAWinSize = 64; + } + + Idx = pEntry->BARecWcidArray[TID]; + + + if (Idx == 0) + { + pBAEntry = BATableAllocRecEntry(pAd, &Idx); + } + else + { + pBAEntry = &pAd->BATable.BARecEntry[Idx]; + // flush all pending reordering mpdus + ba_refresh_reordering_mpdus(pAd, pBAEntry); + } + + DBGPRINT(RT_DEBUG_TRACE,("%s(%ld): Idx = %d, BAWinSize(req %d) = %d\n", __FUNCTION__, pAd->BATable.numAsRecipient, Idx, + pFrame->BaParm.BufSize, BAWinSize)); + + // Start fill in parameters. + if (pBAEntry != NULL) + { + ASSERT(pBAEntry->list.qlen == 0); + + pBAEntry->REC_BA_Status = Recipient_HandleRes; + pBAEntry->BAWinSize = BAWinSize; + pBAEntry->Wcid = pEntry->Aid; + pBAEntry->TID = TID; + pBAEntry->TimeOutValue = pFrame->TimeOutValue; + pBAEntry->REC_BA_Status = Recipient_Accept; + // initial sequence number + pBAEntry->LastIndSeq = RESET_RCV_SEQ; //pFrame->BaStartSeq.field.StartSeq; + + printk("Start Seq = %08x\n", pFrame->BaStartSeq.field.StartSeq); + + if (pEntry->RXBAbitmap & (1<<TID)) + { + RTMPCancelTimer(&pBAEntry->RECBATimer, &Cancelled); + } + else + { + RTMPInitTimer(pAd, &pBAEntry->RECBATimer, GET_TIMER_FUNCTION(BARecSessionIdleTimeout), pBAEntry, TRUE); + } + +#if 0 // for debugging + RTMPSetTimer(&pBAEntry->RECBATimer, REC_BA_SESSION_IDLE_TIMEOUT); +#endif + + // Set Bitmap flag. + pEntry->RXBAbitmap |= (1<<TID); + pEntry->BARecWcidArray[TID] = Idx; + + pEntry->BADeclineBitmap &= ~(1<<TID); + + // Set BA session mask in WCID table. + RT28XX_ADD_BA_SESSION_TO_ASIC(pAd, pEntry->Aid, TID); + + DBGPRINT(RT_DEBUG_TRACE,("MACEntry[%d]RXBAbitmap = 0x%x. BARecWcidArray=%d\n", + pEntry->Aid, pEntry->RXBAbitmap, pEntry->BARecWcidArray[TID])); + } + else + { + Status = FALSE; + DBGPRINT(RT_DEBUG_TRACE,("Can't Accept ADDBA for %02x:%02x:%02x:%02x:%02x:%02x TID = %d\n", + PRINT_MAC(pEntry->Addr), TID)); + } + return(Status); +} + + +BA_REC_ENTRY *BATableAllocRecEntry( + IN PRTMP_ADAPTER pAd, + OUT USHORT *Idx) +{ + int i; + BA_REC_ENTRY *pBAEntry = NULL; + + + NdisAcquireSpinLock(&pAd->BATabLock); + + if (pAd->BATable.numAsRecipient >= MAX_BARECI_SESSION) + { + printk("BA Recipeint Session (%ld) > %d\n", pAd->BATable.numAsRecipient, + MAX_BARECI_SESSION); + goto done; + } + + // reserve idx 0 to identify BAWcidArray[TID] as empty + for (i=1; i < MAX_LEN_OF_BA_REC_TABLE; i++) + { + pBAEntry =&pAd->BATable.BARecEntry[i]; + if ((pBAEntry->REC_BA_Status == Recipient_NONE)) + { + // get one + pAd->BATable.numAsRecipient++; + pBAEntry->REC_BA_Status = Recipient_USED; + *Idx = i; + break; + } + } + +done: + NdisReleaseSpinLock(&pAd->BATabLock); + return pBAEntry; +} + +BA_ORI_ENTRY *BATableAllocOriEntry( + IN PRTMP_ADAPTER pAd, + OUT USHORT *Idx) +{ + int i; + BA_ORI_ENTRY *pBAEntry = NULL; + + NdisAcquireSpinLock(&pAd->BATabLock); + + if (pAd->BATable.numAsOriginator >= (MAX_LEN_OF_BA_ORI_TABLE)) + { + goto done; + } + + // reserve idx 0 to identify BAWcidArray[TID] as empty + for (i=1; i<MAX_LEN_OF_BA_ORI_TABLE; i++) + { + pBAEntry =&pAd->BATable.BAOriEntry[i]; + if ((pBAEntry->ORI_BA_Status == Originator_NONE)) + { + // get one + pAd->BATable.numAsOriginator++; + pBAEntry->ORI_BA_Status = Originator_USED; + pBAEntry->pAdapter = pAd; + *Idx = i; + break; + } + } + +done: + NdisReleaseSpinLock(&pAd->BATabLock); + return pBAEntry; +} + + +VOID BATableFreeOriEntry( + IN PRTMP_ADAPTER pAd, + IN ULONG Idx) +{ + BA_ORI_ENTRY *pBAEntry = NULL; + MAC_TABLE_ENTRY *pEntry; + + + if ((Idx == 0) || (Idx >= MAX_LEN_OF_BA_ORI_TABLE)) + return; + + pBAEntry =&pAd->BATable.BAOriEntry[Idx]; + + if (pBAEntry->ORI_BA_Status != Originator_NONE) + { + pEntry = &pAd->MacTab.Content[pBAEntry->Wcid]; + pEntry->BAOriWcidArray[pBAEntry->TID] = 0; + + + NdisAcquireSpinLock(&pAd->BATabLock); + if (pBAEntry->ORI_BA_Status == Originator_Done) + { + pEntry->TXBAbitmap &= (~(1<<(pBAEntry->TID) )); + DBGPRINT(RT_DEBUG_TRACE, ("BATableFreeOriEntry numAsOriginator= %ld\n", pAd->BATable.numAsRecipient)); + // Erase Bitmap flag. + } + + ASSERT(pAd->BATable.numAsOriginator != 0); + + pAd->BATable.numAsOriginator -= 1; + + pBAEntry->ORI_BA_Status = Originator_NONE; + pBAEntry->Token = 0; + NdisReleaseSpinLock(&pAd->BATabLock); + } +} + + +VOID BATableFreeRecEntry( + IN PRTMP_ADAPTER pAd, + IN ULONG Idx) +{ + BA_REC_ENTRY *pBAEntry = NULL; + MAC_TABLE_ENTRY *pEntry; + + + if ((Idx == 0) || (Idx >= MAX_LEN_OF_BA_REC_TABLE)) + return; + + pBAEntry =&pAd->BATable.BARecEntry[Idx]; + + if (pBAEntry->REC_BA_Status != Recipient_NONE) + { + pEntry = &pAd->MacTab.Content[pBAEntry->Wcid]; + pEntry->BARecWcidArray[pBAEntry->TID] = 0; + + NdisAcquireSpinLock(&pAd->BATabLock); + + ASSERT(pAd->BATable.numAsRecipient != 0); + + pAd->BATable.numAsRecipient -= 1; + + pBAEntry->REC_BA_Status = Recipient_NONE; + NdisReleaseSpinLock(&pAd->BATabLock); + } +} + + +VOID BAOriSessionTearDown( + IN OUT PRTMP_ADAPTER pAd, + IN UCHAR Wcid, + IN UCHAR TID, + IN BOOLEAN bPassive, + IN BOOLEAN bForceSend) +{ + ULONG Idx = 0; + BA_ORI_ENTRY *pBAEntry; + BOOLEAN Cancelled; + + if (Wcid >= MAX_LEN_OF_MAC_TABLE) + { + return; + } + + // + // Locate corresponding BA Originator Entry in BA Table with the (pAddr,TID). + // + Idx = pAd->MacTab.Content[Wcid].BAOriWcidArray[TID]; + if ((Idx == 0) || (Idx >= MAX_LEN_OF_BA_ORI_TABLE)) + { + if (bForceSend == TRUE) + { + // force send specified TID DelBA + MLME_DELBA_REQ_STRUCT DelbaReq; + MLME_QUEUE_ELEM *Elem = (MLME_QUEUE_ELEM *) kmalloc(sizeof(MLME_QUEUE_ELEM), MEM_ALLOC_FLAG); + + NdisZeroMemory(&DelbaReq, sizeof(DelbaReq)); + NdisZeroMemory(Elem, sizeof(MLME_QUEUE_ELEM)); + + COPY_MAC_ADDR(DelbaReq.Addr, pAd->MacTab.Content[Wcid].Addr); + DelbaReq.Wcid = Wcid; + DelbaReq.TID = TID; + DelbaReq.Initiator = ORIGINATOR; +#if 1 + Elem->MsgLen = sizeof(DelbaReq); + NdisMoveMemory(Elem->Msg, &DelbaReq, sizeof(DelbaReq)); + MlmeDELBAAction(pAd, Elem); + kfree(Elem); +#else + MlmeEnqueue(pAd, ACTION_STATE_MACHINE, MT2_MLME_ORI_DELBA_CATE, sizeof(MLME_DELBA_REQ_STRUCT), (PVOID)&DelbaReq); + RT28XX_MLME_HANDLER(pAd); +#endif + } + + return; + } + + DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __FUNCTION__, Wcid, TID)); + + pBAEntry = &pAd->BATable.BAOriEntry[Idx]; + DBGPRINT(RT_DEBUG_TRACE,("\t===>Idx = %ld, Wcid=%d.TID=%d, ORI_BA_Status = %d \n", Idx, Wcid, TID, pBAEntry->ORI_BA_Status)); + // + // Prepare DelBA action frame and send to the peer. + // + if ((bPassive == FALSE) && (TID == pBAEntry->TID) && (pBAEntry->ORI_BA_Status == Originator_Done)) + { + MLME_DELBA_REQ_STRUCT DelbaReq; + MLME_QUEUE_ELEM *Elem = (MLME_QUEUE_ELEM *) kmalloc(sizeof(MLME_QUEUE_ELEM), MEM_ALLOC_FLAG); + + NdisZeroMemory(&DelbaReq, sizeof(DelbaReq)); + NdisZeroMemory(Elem, sizeof(MLME_QUEUE_ELEM)); + + COPY_MAC_ADDR(DelbaReq.Addr, pAd->MacTab.Content[Wcid].Addr); + DelbaReq.Wcid = Wcid; + DelbaReq.TID = pBAEntry->TID; + DelbaReq.Initiator = ORIGINATOR; +#if 1 + Elem->MsgLen = sizeof(DelbaReq); + NdisMoveMemory(Elem->Msg, &DelbaReq, sizeof(DelbaReq)); + MlmeDELBAAction(pAd, Elem); + kfree(Elem); +#else + MlmeEnqueue(pAd, ACTION_STATE_MACHINE, MT2_MLME_ORI_DELBA_CATE, sizeof(MLME_DELBA_REQ_STRUCT), (PVOID)&DelbaReq); + RT28XX_MLME_HANDLER(pAd); +#endif + } + RTMPCancelTimer(&pBAEntry->ORIBATimer, &Cancelled); + BATableFreeOriEntry(pAd, Idx); + + if (bPassive) + { + //BAOriSessionSetUp(pAd, &pAd->MacTab.Content[Wcid], TID, 0, 10000, TRUE); + } +} + +VOID BARecSessionTearDown( + IN OUT PRTMP_ADAPTER pAd, + IN UCHAR Wcid, + IN UCHAR TID, + IN BOOLEAN bPassive) +{ + ULONG Idx = 0; + BA_REC_ENTRY *pBAEntry; + + if (Wcid >= MAX_LEN_OF_MAC_TABLE) + { + return; + } + + // + // Locate corresponding BA Originator Entry in BA Table with the (pAddr,TID). + // + Idx = pAd->MacTab.Content[Wcid].BARecWcidArray[TID]; + if (Idx == 0) + return; + + DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __FUNCTION__, Wcid, TID)); + + + pBAEntry = &pAd->BATable.BARecEntry[Idx]; + DBGPRINT(RT_DEBUG_TRACE,("\t===>Idx = %ld, Wcid=%d.TID=%d, REC_BA_Status = %d \n", Idx, Wcid, TID, pBAEntry->REC_BA_Status)); + // + // Prepare DelBA action frame and send to the peer. + // + if ((TID == pBAEntry->TID) && (pBAEntry->REC_BA_Status == Recipient_Accept)) + { + MLME_DELBA_REQ_STRUCT DelbaReq; + BOOLEAN Cancelled; + MLME_QUEUE_ELEM *Elem = (MLME_QUEUE_ELEM *) kmalloc(sizeof(MLME_QUEUE_ELEM), MEM_ALLOC_FLAG); + //ULONG offset; + //UINT32 VALUE; + + RTMPCancelTimer(&pBAEntry->RECBATimer, &Cancelled); + + // + // 1. Send DELBA Action Frame + // + if (bPassive == FALSE) + { + NdisZeroMemory(&DelbaReq, sizeof(DelbaReq)); + NdisZeroMemory(Elem, sizeof(MLME_QUEUE_ELEM)); + + COPY_MAC_ADDR(DelbaReq.Addr, pAd->MacTab.Content[Wcid].Addr); + DelbaReq.Wcid = Wcid; + DelbaReq.TID = TID; + DelbaReq.Initiator = RECIPIENT; +#if 1 + Elem->MsgLen = sizeof(DelbaReq); + NdisMoveMemory(Elem->Msg, &DelbaReq, sizeof(DelbaReq)); + MlmeDELBAAction(pAd, Elem); + kfree(Elem); +#else + MlmeEnqueue(pAd, ACTION_STATE_MACHINE, MT2_MLME_ORI_DELBA_CATE, sizeof(MLME_DELBA_REQ_STRUCT), (PVOID)&DelbaReq); + RT28XX_MLME_HANDLER(pAd); +#endif + } + + + // + // 2. Free resource of BA session + // + // flush all pending reordering mpdus + ba_refresh_reordering_mpdus(pAd, pBAEntry); + + NdisAcquireSpinLock(&pAd->BATabLock); + + // Erase Bitmap flag. + pBAEntry->LastIndSeq = RESET_RCV_SEQ; + pBAEntry->BAWinSize = 0; + // Erase Bitmap flag at software mactable + pAd->MacTab.Content[Wcid].RXBAbitmap &= (~(1<<(pBAEntry->TID))); + pAd->MacTab.Content[Wcid].BARecWcidArray[TID] = 0; + + RT28XX_DEL_BA_SESSION_FROM_ASIC(pAd, Wcid, TID); + + NdisReleaseSpinLock(&pAd->BATabLock); + + } + + BATableFreeRecEntry(pAd, Idx); +} + +VOID BASessionTearDownALL( + IN OUT PRTMP_ADAPTER pAd, + IN UCHAR Wcid) +{ + int i; + + for (i=0; i<NUM_OF_TID; i++) + { + BAOriSessionTearDown(pAd, Wcid, i, FALSE, FALSE); + BARecSessionTearDown(pAd, Wcid, i, FALSE); + } +} + + +/* + ========================================================================== + Description: + Retry sending ADDBA Reqest. + + IRQL = DISPATCH_LEVEL + + Parametrs: + p8023Header: if this is already 802.3 format, p8023Header is NULL + + Return : TRUE if put into rx reordering buffer, shouldn't indicaterxhere. + FALSE , then continue indicaterx at this moment. + ========================================================================== + */ +VOID BAOriSessionSetupTimeout( + IN PVOID SystemSpecific1, + IN PVOID FunctionContext, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3) +{ + BA_ORI_ENTRY *pBAEntry = (BA_ORI_ENTRY *)FunctionContext; + MAC_TABLE_ENTRY *pEntry; + PRTMP_ADAPTER pAd; + + if (pBAEntry == NULL) + return; + + pAd = pBAEntry->pAdapter; + +#ifdef CONFIG_STA_SUPPORT + IF_DEV_CONFIG_OPMODE_ON_STA(pAd) + { + // Do nothing if monitor mode is on + if (MONITOR_ON(pAd)) + return; + } +#endif // CONFIG_STA_SUPPORT // + +#ifdef RALINK_ATE + // Nothing to do in ATE mode. + if (ATE_ON(pAd)) + return; +#endif // RALINK_ATE // + + pEntry = &pAd->MacTab.Content[pBAEntry->Wcid]; + + if ((pBAEntry->ORI_BA_Status == Originator_WaitRes) && (pBAEntry->Token < ORI_SESSION_MAX_RETRY)) + { + MLME_ADDBA_REQ_STRUCT AddbaReq; + + NdisZeroMemory(&AddbaReq, sizeof(AddbaReq)); + COPY_MAC_ADDR(AddbaReq.pAddr, pEntry->Addr); + AddbaReq.Wcid = (UCHAR)(pEntry->Aid); + AddbaReq.TID = pBAEntry->TID; + AddbaReq.BaBufSize = pAd->CommonCfg.BACapability.field.RxBAWinLimit; + AddbaReq.TimeOutValue = 0; + AddbaReq.Token = pBAEntry->Token; + MlmeEnqueue(pAd, ACTION_STATE_MACHINE, MT2_MLME_ADD_BA_CATE, sizeof(MLME_ADDBA_REQ_STRUCT), (PVOID)&AddbaReq); + RT28XX_MLME_HANDLER(pAd); + DBGPRINT(RT_DEBUG_TRACE,("BA Ori Session Timeout(%d) : Send ADD BA again\n", pBAEntry->Token)); + + pBAEntry->Token++; + RTMPSetTimer(&pBAEntry->ORIBATimer, ORI_BA_SESSION_TIMEOUT); + } + else + { + BATableFreeOriEntry(pAd, pEntry->BAOriWcidArray[pBAEntry->TID]); + } +} + +/* + ========================================================================== + Description: + Retry sending ADDBA Reqest. + + IRQL = DISPATCH_LEVEL + + Parametrs: + p8023Header: if this is already 802.3 format, p8023Header is NULL + + Return : TRUE if put into rx reordering buffer, shouldn't indicaterxhere. + FALSE , then continue indicaterx at this moment. + ========================================================================== + */ +VOID BARecSessionIdleTimeout( + IN PVOID SystemSpecific1, + IN PVOID FunctionContext, + IN PVOID SystemSpecific2, + IN PVOID SystemSpecific3) +{ + + BA_REC_ENTRY *pBAEntry = (BA_REC_ENTRY *)FunctionContext; + PRTMP_ADAPTER pAd; + ULONG Now32; + + if (pBAEntry == NULL) + return; + + if ((pBAEntry->REC_BA_Status == Recipient_Accept)) + { + NdisGetSystemUpTime(&Now32); + + if (RTMP_TIME_AFTER((unsigned long)Now32, (unsigned long)(pBAEntry->LastIndSeqAtTimer + REC_BA_SESSION_IDLE_TIMEOUT))) + { + pAd = pBAEntry->pAdapter; + // flush all pending reordering mpdus + ba_refresh_reordering_mpdus(pAd, pBAEntry); + printk("%ld: REC BA session Timeout\n", Now32); + } + } +} + + +VOID PeerAddBAReqAction( + IN PRTMP_ADAPTER pAd, + IN MLME_QUEUE_ELEM *Elem) + +{ + // 7.4.4.1 + //ULONG Idx; + UCHAR Status = 1; + UCHAR pAddr[6]; + FRAME_ADDBA_RSP ADDframe; + PUCHAR pOutBuffer = NULL; + NDIS_STATUS NStatus; + PFRAME_ADDBA_REQ pAddreqFrame = NULL; + //UCHAR BufSize; + ULONG FrameLen; + PULONG ptemp; + PMAC_TABLE_ENTRY pMacEntry; + + DBGPRINT(RT_DEBUG_TRACE, ("%s ==> (Wcid = %d)\n", __FUNCTION__, Elem->Wcid)); + + //hex_dump("AddBAReq", Elem->Msg, Elem->MsgLen); + + //ADDBA Request from unknown peer, ignore this. + if (Elem->Wcid >= MAX_LEN_OF_MAC_TABLE) + return; + + pMacEntry = &pAd->MacTab.Content[Elem->Wcid]; + DBGPRINT(RT_DEBUG_TRACE,("BA - PeerAddBAReqAction----> \n")); + ptemp = (PULONG)Elem->Msg; + //DBGPRINT_RAW(RT_DEBUG_EMU, ("%08x:: %08x:: %08x:: %08x:: %08x:: %08x:: %08x:: %08x:: %08x\n", *(ptemp), *(ptemp+1), *(ptemp+2), *(ptemp+3), *(ptemp+4), *(ptemp+5), *(ptemp+6), *(ptemp+7), *(ptemp+8))); + + if (PeerAddBAReqActionSanity(pAd, Elem->Msg, Elem->MsgLen, pAddr)) + { + + if ((pAd->CommonCfg.bBADecline == FALSE) && IS_HT_STA(pMacEntry)) + { + pAddreqFrame = (PFRAME_ADDBA_REQ)(&Elem->Msg[0]); + printk("Rcv Wcid(%d) AddBAReq\n", Elem->Wcid); + if (BARecSessionAdd(pAd, &pAd->MacTab.Content[Elem->Wcid], pAddreqFrame)) + Status = 0; + else + Status = 38; // more parameters have invalid values + } + else + { + Status = 37; // the request has been declined. + } + } + + if (pAd->MacTab.Content[Elem->Wcid].ValidAsCLI) + ASSERT(pAd->MacTab.Content[Elem->Wcid].Sst == SST_ASSOC); + + pAddreqFrame = (PFRAME_ADDBA_REQ)(&Elem->Msg[0]); + // 2. Always send back ADDBA Response + NStatus = MlmeAllocateMemory(pAd, &pOutBuffer); //Get an unused nonpaged memory + if (NStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(RT_DEBUG_TRACE,("ACTION - PeerBAAction() allocate memory failed \n")); + return; + } + + NdisZeroMemory(&ADDframe, sizeof(FRAME_ADDBA_RSP)); + // 2-1. Prepare ADDBA Response frame. +#ifdef CONFIG_STA_SUPPORT + IF_DEV_CONFIG_OPMODE_ON_STA(pAd) + { + if (ADHOC_ON(pAd)) + ActHeaderInit(pAd, &ADDframe.Hdr, pAddr, pAd->CurrentAddress, pAd->CommonCfg.Bssid); + else +#ifdef QOS_DLS_SUPPORT + if (pAd->MacTab.Content[Elem->Wcid].ValidAsDls) + ActHeaderInit(pAd, &ADDframe.Hdr, pAddr, pAd->CurrentAddress, pAd->CommonCfg.Bssid); + else +#endif // QOS_DLS_SUPPORT // + ActHeaderInit(pAd, &ADDframe.Hdr, pAd->CommonCfg.Bssid, pAd->CurrentAddress, pAddr); + } +#endif // CONFIG_STA_SUPPORT // + ADDframe.Category = CATEGORY_BA; + ADDframe.Action = ADDBA_RESP; + ADDframe.Token = pAddreqFrame->Token; + // What is the Status code?? need to check. + ADDframe.StatusCode = Status; + ADDframe.BaParm.BAPolicy = IMMED_BA; + ADDframe.BaParm.AMSDUSupported = 0; + ADDframe.BaParm.TID = pAddreqFrame->BaParm.TID; + ADDframe.BaParm.BufSize = min(((UCHAR)pAddreqFrame->BaParm.BufSize), (UCHAR)pAd->CommonCfg.BACapability.field.RxBAWinLimit); + if (ADDframe.BaParm.BufSize == 0) + { + ADDframe.BaParm.BufSize = 64; + } + ADDframe.TimeOutValue = 0; //pAddreqFrame->TimeOutValue; + + *(USHORT *)(&ADDframe.BaParm) = cpu2le16(*(USHORT *)(&ADDframe.BaParm)); + ADDframe.StatusCode = cpu2le16(ADDframe.StatusCode); + ADDframe.TimeOutValue = cpu2le16(ADDframe.TimeOutValue); + + MakeOutgoingFrame(pOutBuffer, &FrameLen, + sizeof(FRAME_ADDBA_RSP), &ADDframe, + END_OF_ARGS); + MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen); + MlmeFreeMemory(pAd, pOutBuffer); + + DBGPRINT(RT_DEBUG_TRACE, ("%s(%d): TID(%d), BufSize(%d) <== \n", __FUNCTION__, Elem->Wcid, ADDframe.BaParm.TID, + ADDframe.BaParm.BufSize)); +} + + +VOID PeerAddBARspAction( + IN PRTMP_ADAPTER pAd, + IN MLME_QUEUE_ELEM *Elem) + +{ + //UCHAR Idx, i; + //PUCHAR pOutBuffer = NULL; + PFRAME_ADDBA_RSP pFrame = NULL; + //PBA_ORI_ENTRY pBAEntry; + + //ADDBA Response from unknown peer, ignore this. + if (Elem->Wcid >= MAX_LEN_OF_MAC_TABLE) + return; + + DBGPRINT(RT_DEBUG_TRACE, ("%s ==> Wcid(%d)\n", __FUNCTION__, Elem->Wcid)); + + //hex_dump("PeerAddBARspAction()", Elem->Msg, Elem->MsgLen); + + if (PeerAddBARspActionSanity(pAd, Elem->Msg, Elem->MsgLen)) + { + pFrame = (PFRAME_ADDBA_RSP)(&Elem->Msg[0]); + + DBGPRINT(RT_DEBUG_TRACE, ("\t\t StatusCode = %d\n", pFrame->StatusCode)); + switch (pFrame->StatusCode) + { + case 0: + // I want a BAsession with this peer as an originator. + BAOriSessionAdd(pAd, &pAd->MacTab.Content[Elem->Wcid], pFrame); + break; + default: + // check status == USED ??? + BAOriSessionTearDown(pAd, Elem->Wcid, pFrame->BaParm.TID, TRUE, FALSE); + break; + } + // Rcv Decline StatusCode + if ((pFrame->StatusCode == 37) +#ifdef CONFIG_STA_SUPPORT + || ((pAd->OpMode == OPMODE_STA) && STA_TGN_WIFI_ON(pAd) && (pFrame->StatusCode != 0)) +#endif // CONFIG_STA_SUPPORT // + ) + { + pAd->MacTab.Content[Elem->Wcid].BADeclineBitmap |= 1<<pFrame->BaParm.TID; + } + } +} + +VOID PeerDelBAAction( + IN PRTMP_ADAPTER pAd, + IN MLME_QUEUE_ELEM *Elem) + +{ + //UCHAR Idx; + //PUCHAR pOutBuffer = NULL; + PFRAME_DELBA_REQ pDelFrame = NULL; + + DBGPRINT(RT_DEBUG_TRACE,("%s ==>\n", __FUNCTION__)); + //DELBA Request from unknown peer, ignore this. + if (PeerDelBAActionSanity(pAd, Elem->Wcid, Elem->Msg, Elem->MsgLen)) + { + pDelFrame = (PFRAME_DELBA_REQ)(&Elem->Msg[0]); + if (pDelFrame->DelbaParm.Initiator == ORIGINATOR) + { + DBGPRINT(RT_DEBUG_TRACE,("BA - PeerDelBAAction----> ORIGINATOR\n")); + BARecSessionTearDown(pAd, Elem->Wcid, pDelFrame->DelbaParm.TID, TRUE); + } + else + { + DBGPRINT(RT_DEBUG_TRACE,("BA - PeerDelBAAction----> RECIPIENT, Reason = %d\n", pDelFrame->ReasonCode)); + //hex_dump("DelBA Frame", pDelFrame, Elem->MsgLen); + BAOriSessionTearDown(pAd, Elem->Wcid, pDelFrame->DelbaParm.TID, TRUE, FALSE); + } + } +} + + +BOOLEAN CntlEnqueueForRecv( + IN PRTMP_ADAPTER pAd, + IN ULONG Wcid, + IN ULONG MsgLen, + IN PFRAME_BA_REQ pMsg) +{ + PFRAME_BA_REQ pFrame = pMsg; + //PRTMP_REORDERBUF pBuffer; + //PRTMP_REORDERBUF pDmaBuf; + PBA_REC_ENTRY pBAEntry; + //BOOLEAN Result; + ULONG Idx; + //UCHAR NumRxPkt; + UCHAR TID;//, i; + + TID = (UCHAR)pFrame->BARControl.TID; + + DBGPRINT(RT_DEBUG_TRACE, ("%s(): BAR-Wcid(%ld), Tid (%d)\n", __FUNCTION__, Wcid, TID)); + //hex_dump("BAR", (PCHAR) pFrame, MsgLen); + // Do nothing if the driver is starting halt state. + // This might happen when timer already been fired before cancel timer with mlmehalt + if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS | fRTMP_ADAPTER_NIC_NOT_EXIST)) + return FALSE; + + // First check the size, it MUST not exceed the mlme queue size + if (MsgLen > MGMT_DMA_BUFFER_SIZE) + { + DBGPRINT_ERR(("CntlEnqueueForRecv: frame too large, size = %ld \n", MsgLen)); + return FALSE; + } + else if (MsgLen != sizeof(FRAME_BA_REQ)) + { + DBGPRINT_ERR(("CntlEnqueueForRecv: BlockAck Request frame length size = %ld incorrect\n", MsgLen)); + return FALSE; + } + else if (MsgLen != sizeof(FRAME_BA_REQ)) + { + DBGPRINT_ERR(("CntlEnqueueForRecv: BlockAck Request frame length size = %ld incorrect\n", MsgLen)); + return FALSE; + } + + if ((Wcid < MAX_LEN_OF_MAC_TABLE) && (TID < 8)) + { + // if this receiving packet is from SA that is in our OriEntry. Since WCID <9 has direct mapping. no need search. + Idx = pAd->MacTab.Content[Wcid].BARecWcidArray[TID]; + pBAEntry = &pAd->BATable.BARecEntry[Idx]; + } + else + { + return FALSE; + } + + DBGPRINT(RT_DEBUG_TRACE, ("BAR(%ld) : Tid (%d) - %04x:%04x\n", Wcid, TID, pFrame->BAStartingSeq.field.StartSeq, pBAEntry->LastIndSeq )); + + if (SEQ_SMALLER(pBAEntry->LastIndSeq, pFrame->BAStartingSeq.field.StartSeq, MAXSEQ)) + { + //printk("BAR Seq = %x, LastIndSeq = %x\n", pFrame->BAStartingSeq.field.StartSeq, pBAEntry->LastIndSeq); + ba_indicate_reordering_mpdus_le_seq(pAd, pBAEntry, pFrame->BAStartingSeq.field.StartSeq); + pBAEntry->LastIndSeq = (pFrame->BAStartingSeq.field.StartSeq == 0) ? MAXSEQ :(pFrame->BAStartingSeq.field.StartSeq -1); + } + //ba_refresh_reordering_mpdus(pAd, pBAEntry); + return TRUE; +} + +/* +Description : Send PSMP Action frame If PSMP mode switches. +*/ +VOID SendPSMPAction( + IN PRTMP_ADAPTER pAd, + IN UCHAR Wcid, + IN UCHAR Psmp) +{ + PUCHAR pOutBuffer = NULL; + NDIS_STATUS NStatus; + //ULONG Idx; + FRAME_PSMP_ACTION Frame; + ULONG FrameLen; + + NStatus = MlmeAllocateMemory(pAd, &pOutBuffer); //Get an unused nonpaged memory + if (NStatus != NDIS_STATUS_SUCCESS) + { + DBGPRINT(RT_DEBUG_ERROR,("BA - MlmeADDBAAction() allocate memory failed \n")); + return; + } +#ifdef CONFIG_STA_SUPPORT + IF_DEV_CONFIG_OPMODE_ON_STA(pAd) + ActHeaderInit(pAd, &Frame.Hdr, pAd->CommonCfg.Bssid, pAd->CurrentAddress, pAd->MacTab.Content[Wcid].Addr); +#endif // CONFIG_STA_SUPPORT // + + Frame.Category = CATEGORY_HT; + Frame.Action = SMPS_ACTION; + switch (Psmp) + { + case MMPS_ENABLE: + Frame.Psmp = 0; + break; + case MMPS_DYNAMIC: + Frame.Psmp = 3; + break; + case MMPS_STATIC: + Frame.Psmp = 1; + break; + } + MakeOutgoingFrame(pOutBuffer, &FrameLen, + sizeof(FRAME_PSMP_ACTION), &Frame, + END_OF_ARGS); + MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen); + MlmeFreeMemory(pAd, pOutBuffer); + DBGPRINT(RT_DEBUG_ERROR,("HT - SendPSMPAction( %d ) \n", Frame.Psmp)); +} + + +#define RADIO_MEASUREMENT_REQUEST_ACTION 0 + +typedef struct PACKED +{ + UCHAR RegulatoryClass; + UCHAR ChannelNumber; + USHORT RandomInterval; + USHORT MeasurementDuration; + UCHAR MeasurementMode; + UCHAR BSSID[MAC_ADDR_LEN]; + UCHAR ReportingCondition; + UCHAR Threshold; + UCHAR SSIDIE[2]; // 2 byte +} BEACON_REQUEST; + +typedef struct PACKED +{ + UCHAR ID; + UCHAR Length; + UCHAR Token; + UCHAR RequestMode; + UCHAR Type; +} MEASUREMENT_REQ; + + + + +void convert_reordering_packet_to_preAMSDU_or_802_3_packet( + IN PRTMP_ADAPTER pAd, + IN RX_BLK *pRxBlk, + IN UCHAR FromWhichBSSID) +{ + PNDIS_PACKET pRxPkt; + UCHAR Header802_3[LENGTH_802_3]; + + // 1. get 802.3 Header + // 2. remove LLC + // a. pointer pRxBlk->pData to payload + // b. modify pRxBlk->DataSize + +#ifdef CONFIG_STA_SUPPORT + IF_DEV_CONFIG_OPMODE_ON_STA(pAd) + RTMP_802_11_REMOVE_LLC_AND_CONVERT_TO_802_3(pRxBlk, Header802_3); +#endif // CONFIG_STA_SUPPORT // + + ASSERT(pRxBlk->pRxPacket); + pRxPkt = RTPKT_TO_OSPKT(pRxBlk->pRxPacket); + + RTPKT_TO_OSPKT(pRxPkt)->dev = get_netdev_from_bssid(pAd, FromWhichBSSID); + RTPKT_TO_OSPKT(pRxPkt)->data = pRxBlk->pData; + RTPKT_TO_OSPKT(pRxPkt)->len = pRxBlk->DataSize; + RTPKT_TO_OSPKT(pRxPkt)->tail = RTPKT_TO_OSPKT(pRxPkt)->data + RTPKT_TO_OSPKT(pRxPkt)->len; + + // + // copy 802.3 header, if necessary + // + if (!RX_BLK_TEST_FLAG(pRxBlk, fRX_AMSDU)) + { + +#ifdef CONFIG_STA_SUPPORT + IF_DEV_CONFIG_OPMODE_ON_STA(pAd) + { +#ifdef LINUX + NdisMoveMemory(skb_push(pRxPkt, LENGTH_802_3), Header802_3, LENGTH_802_3); +#endif +#ifdef UCOS + NdisMoveMemory(net_pkt_push(pRxPkt, LENGTH_802_3), Header802_3, LENGTH_802_3); +#endif + } +#endif // CONFIG_STA_SUPPORT // + } +} + + +#define INDICATE_LEGACY_OR_AMSDU(_pAd, _pRxBlk, _fromWhichBSSID) \ + do \ + { \ + if (RX_BLK_TEST_FLAG(_pRxBlk, fRX_AMSDU)) \ + { \ + Indicate_AMSDU_Packet(_pAd, _pRxBlk, _fromWhichBSSID); \ + } \ + else if (RX_BLK_TEST_FLAG(_pRxBlk, fRX_EAP)) \ + { \ + Indicate_EAPOL_Packet(_pAd, _pRxBlk, _fromWhichBSSID); \ + } \ + else \ + { \ + Indicate_Legacy_Packet(_pAd, _pRxBlk, _fromWhichBSSID); \ + } \ + } while (0); + + + +static VOID ba_enqueue_reordering_packet( + IN PRTMP_ADAPTER pAd, + IN PBA_REC_ENTRY pBAEntry, + IN RX_BLK *pRxBlk, + IN UCHAR FromWhichBSSID) +{ + struct reordering_mpdu *mpdu_blk; + UINT16 Sequence = (UINT16) pRxBlk->pHeader->Sequence; + + mpdu_blk = ba_mpdu_blk_alloc(pAd); + if (mpdu_blk != NULL) + { + // Write RxD buffer address & allocated buffer length + NdisAcquireSpinLock(&pBAEntry->RxReRingLock); + + mpdu_blk->Sequence = Sequence; + + mpdu_blk->bAMSDU = RX_BLK_TEST_FLAG(pRxBlk, fRX_AMSDU); + + convert_reordering_packet_to_preAMSDU_or_802_3_packet(pAd, pRxBlk, FromWhichBSSID); + + STATS_INC_RX_PACKETS(pAd, FromWhichBSSID); + + // + // it is necessary for reordering packet to record + // which BSS it come from + // + RTMP_SET_PACKET_IF(pRxBlk->pRxPacket, FromWhichBSSID); + + mpdu_blk->pPacket = pRxBlk->pRxPacket; + + if (ba_reordering_mpdu_insertsorted(&pBAEntry->list, mpdu_blk) == FALSE) + { + // had been already within reordering list + // don't indicate + RELEASE_NDIS_PACKET(pAd, pRxBlk->pRxPacket, NDIS_STATUS_SUCCESS); + ba_mpdu_blk_free(pAd, mpdu_blk); + } + + ASSERT((0<= pBAEntry->list.qlen) && (pBAEntry->list.qlen <= pBAEntry->BAWinSize)); + NdisReleaseSpinLock(&pBAEntry->RxReRingLock); + } + else + { +#if 0 + DBGPRINT(RT_DEBUG_ERROR, ("!!! (%d:%d) Can't allocate reordering mpdu blk\n", + blk_count, pBAEntry->list.qlen)); +#else + DBGPRINT(RT_DEBUG_ERROR, ("!!! (%d) Can't allocate reordering mpdu blk\n", + pBAEntry->list.qlen)); +#endif + /* + * flush all pending reordering mpdus + * and receving mpdu to upper layer + * make tcp/ip to take care reordering mechanism + */ + //ba_refresh_reordering_mpdus(pAd, pBAEntry); + ba_indicate_reordering_mpdus_le_seq(pAd, pBAEntry, Sequence); + + pBAEntry->LastIndSeq = Sequence; + INDICATE_LEGACY_OR_AMSDU(pAd, pRxBlk, FromWhichBSSID); + } +} + + +/* + ========================================================================== + Description: + Indicate this packet to upper layer or put it into reordering buffer + + Parametrs: + pRxBlk : carry necessary packet info 802.11 format + FromWhichBSSID : the packet received from which BSS + + Return : + none + + Note : + the packet queued into reordering buffer need to cover to 802.3 format + or pre_AMSDU format + ========================================================================== + */ + +VOID Indicate_AMPDU_Packet( + IN PRTMP_ADAPTER pAd, + IN RX_BLK *pRxBlk, + IN UCHAR FromWhichBSSID) +{ + USHORT Idx; + PBA_REC_ENTRY pBAEntry = NULL; + UINT16 Sequence = pRxBlk->pHeader->Sequence; + ULONG Now32; + UCHAR Wcid = pRxBlk->pRxWI->WirelessCliID; + UCHAR TID = pRxBlk->pRxWI->TID; + + + if (!RX_BLK_TEST_FLAG(pRxBlk, fRX_AMSDU) && (pRxBlk->DataSize > MAX_RX_PKT_LEN)) + { +#if 0 // sample take off, no use + static int err_size; + + err_size++; + if (err_size > 20) { + printk("AMPDU DataSize = %d\n", pRxBlk->DataSize); + hex_dump("802.11 Header", (UCHAR *)pRxBlk->pHeader, 24); + hex_dump("Payload", pRxBlk->pData, 64); + err_size = 0; + } +#endif + // release packet + RELEASE_NDIS_PACKET(pAd, pRxBlk->pRxPacket, NDIS_STATUS_FAILURE); + return; + } + + +#if 0 // test + /* Rec BA Session had been torn down */ + INDICATE_LEGACY_OR_AMSDU(pAd, pRxBlk, FromWhichBSSID); + return; +#endif + + if (Wcid < MAX_LEN_OF_MAC_TABLE) + { + Idx = pAd->MacTab.Content[Wcid].BARecWcidArray[TID]; + if (Idx == 0) + { + /* Rec BA Session had been torn down */ + INDICATE_LEGACY_OR_AMSDU(pAd, pRxBlk, FromWhichBSSID); + return; + } + pBAEntry = &pAd->BATable.BARecEntry[Idx]; + } + else + { + // impossible !!! + ASSERT(0); + // release packet + RELEASE_NDIS_PACKET(pAd, pRxBlk->pRxPacket, NDIS_STATUS_FAILURE); + return; + } + + ASSERT(pBAEntry); + + // update last rx time + NdisGetSystemUpTime(&Now32); + + pBAEntry->rcvSeq = Sequence; + + + ba_flush_reordering_timeout_mpdus(pAd, pBAEntry, Now32); + pBAEntry->LastIndSeqAtTimer = Now32; + + // + // Reset Last Indicate Sequence + // + if (pBAEntry->LastIndSeq == RESET_RCV_SEQ) + { + ASSERT((pBAEntry->list.qlen == 0) && (pBAEntry->list.next == NULL)); + + // reset rcv sequence of BA session + pBAEntry->LastIndSeq = Sequence; + pBAEntry->LastIndSeqAtTimer = Now32; + INDICATE_LEGACY_OR_AMSDU(pAd, pRxBlk, FromWhichBSSID); + return; + } + + + // + // I. Check if in order. + // + if (SEQ_STEPONE(Sequence, pBAEntry->LastIndSeq, MAXSEQ)) + { + USHORT LastIndSeq; + + pBAEntry->LastIndSeq = Sequence; + INDICATE_LEGACY_OR_AMSDU(pAd, pRxBlk, FromWhichBSSID); + LastIndSeq = ba_indicate_reordering_mpdus_in_order(pAd, pBAEntry, pBAEntry->LastIndSeq); + if (LastIndSeq != RESET_RCV_SEQ) + { + pBAEntry->LastIndSeq = LastIndSeq; + } + pBAEntry->LastIndSeqAtTimer = Now32; + } + // + // II. Drop Duplicated Packet + // + else if (Sequence == pBAEntry->LastIndSeq) + { + + // drop and release packet + pBAEntry->nDropPacket++; + RELEASE_NDIS_PACKET(pAd, pRxBlk->pRxPacket, NDIS_STATUS_FAILURE); + } + // + // III. Drop Old Received Packet + // + else if (SEQ_SMALLER(Sequence, pBAEntry->LastIndSeq, MAXSEQ)) + { + + // drop and release packet + pBAEntry->nDropPacket++; + RELEASE_NDIS_PACKET(pAd, pRxBlk->pRxPacket, NDIS_STATUS_FAILURE); + } + // + // IV. Receive Sequence within Window Size + // + else if (SEQ_SMALLER(Sequence, (((pBAEntry->LastIndSeq+pBAEntry->BAWinSize+1)) & MAXSEQ), MAXSEQ)) + { + ba_enqueue_reordering_packet(pAd, pBAEntry, pRxBlk, FromWhichBSSID); + } + // + // V. Receive seq surpasses Win(lastseq + nMSDU). So refresh all reorder buffer + // + else + { +#if 0 + ba_refresh_reordering_mpdus(pAd, pBAEntry); + INDICATE_LEGACY_OR_AMSDU(pAd, pRxBlk, FromWhichBSSID); +#else + LONG WinStartSeq, TmpSeq; + + + TmpSeq = Sequence - (pBAEntry->BAWinSize) -1; + if (TmpSeq < 0) + { + TmpSeq = (MAXSEQ+1) + TmpSeq; + } + WinStartSeq = (TmpSeq+1) & MAXSEQ; + ba_indicate_reordering_mpdus_le_seq(pAd, pBAEntry, WinStartSeq); + pBAEntry->LastIndSeq = WinStartSeq; //TmpSeq; + + pBAEntry->LastIndSeqAtTimer = Now32; + + ba_enqueue_reordering_packet(pAd, pBAEntry, pRxBlk, FromWhichBSSID); + + TmpSeq = ba_indicate_reordering_mpdus_in_order(pAd, pBAEntry, pBAEntry->LastIndSeq); + if (TmpSeq != RESET_RCV_SEQ) + { + pBAEntry->LastIndSeq = TmpSeq; + } +#endif + } +} + +#endif // DOT11_N_SUPPORT // + |