/* ************************************************************************* * 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; impdu_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) ) { // printk("timeout[%d] (%lx-%lx = %d > %d): %x, ", pBAEntry->list.qlen, Now32, (pBAEntry->LastIndSeqAtTimer), // (int)((long) Now32 - (long)(pBAEntry->LastIndSeqAtTimer)), REORDERING_PACKET_TIMEOUT, // pBAEntry->LastIndSeq); // // 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; } //printk("%x, flush one!\n", pBAEntry->LastIndSeq); } } /* * 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<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; DBGPRINT(RT_DEBUG_TRACE,("Send AddBA to %02x:%02x:%02x:%02x:%02x:%02x Tid:%d isForced:%d Wcid:%d\n" ,pEntry->Addr[0],pEntry->Addr[1],pEntry->Addr[2] ,pEntry->Addr[3],pEntry->Addr[4],pEntry->Addr[5] ,TID,isForced,pEntry->Aid)); if (!(pEntry->TXBAbitmap & (1<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<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<RECBATimer, &Cancelled); } else { RTMPInitTimer(pAd, &pBAEntry->RECBATimer, GET_TIMER_FUNCTION(BARecSessionIdleTimeout), pBAEntry, TRUE); } // Set Bitmap flag. pEntry->RXBAbitmap |= (1<BARecWcidArray[TID] = Idx; pEntry->BADeclineBitmap &= ~(1<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; iBATable.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; ipAdapter; #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)); DBGPRINT(RT_DEBUG_TRACE,("BA Ori Session Timeout(%d) to %02x:%02x:%02x:%02x:%02x:%02x Tid:%d Wcid:%d\n" ,pBAEntry->Token ,pEntry->Addr[0],pEntry->Addr[1],pEntry->Addr[2] ,pEntry->Addr[3],pEntry->Addr[4],pEntry->Addr[5] ,pBAEntry->TID,pEntry->Aid)); 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 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<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; #ifdef RT30xx UCHAR bbpdata=0; UINT32 macdata; #endif // RT30xx // 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: #ifdef RT30xx if (IS_RT3090(pAd)) { // disable MMPS BBP control register RTMP_BBP_IO_READ8_BY_REG_ID(pAd, BBP_R3, &bbpdata); bbpdata &= ~(0x04); //bit 2 RTMP_BBP_IO_WRITE8_BY_REG_ID(pAd, BBP_R3, bbpdata); // disable MMPS MAC control register RTMP_IO_READ32(pAd, 0x1210, &macdata); macdata &= ~(0x09); //bit 0, 3 RTMP_IO_WRITE32(pAd, 0x1210, macdata); } #endif // RT30xx // Frame.Psmp = 0; break; case MMPS_DYNAMIC: #ifdef RT30xx if (IS_RT3090(pAd)) { // enable MMPS BBP control register RTMP_BBP_IO_READ8_BY_REG_ID(pAd, BBP_R3, &bbpdata); bbpdata |= 0x04; //bit 2 RTMP_BBP_IO_WRITE8_BY_REG_ID(pAd, BBP_R3, bbpdata); // enable MMPS MAC control register RTMP_IO_READ32(pAd, 0x1210, &macdata); macdata |= 0x09; //bit 0, 3 RTMP_IO_WRITE32(pAd, 0x1210, macdata); } #endif // RT30xx // Frame.Psmp = 3; break; case MMPS_STATIC: #ifdef RT30xx if (IS_RT3090(pAd)) { // enable MMPS BBP control register RTMP_BBP_IO_READ8_BY_REG_ID(pAd, BBP_R3, &bbpdata); bbpdata |= 0x04; //bit 2 RTMP_BBP_IO_WRITE8_BY_REG_ID(pAd, BBP_R3, bbpdata); // enable MMPS MAC control register RTMP_IO_READ32(pAd, 0x1210, &macdata); macdata |= 0x09; //bit 0, 3 RTMP_IO_WRITE32(pAd, 0x1210, macdata); } #endif // RT30xx // 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 { DBGPRINT(RT_DEBUG_ERROR, ("!!! (%d) Can't allocate reordering mpdu blk\n", pBAEntry->list.qlen)); /* * 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)) { // release packet RELEASE_NDIS_PACKET(pAd, pRxBlk->pRxPacket, NDIS_STATUS_FAILURE); return; } 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 { 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 // DOT11_N_SUPPORT //