/* ************************************************************************* * 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. * * * ************************************************************************* */ #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 * OS_HZ)/1000) /* system ticks -- 100 ms */ #define MAX_REORDERING_PACKET_TIMEOUT ((3000 * OS_HZ)/1000) /* system ticks -- 100 ms */ #define RESET_RCV_SEQ (0xFFFF) static void ba_mpdu_blk_free(struct rt_rtmp_adapter *pAd, struct reordering_mpdu *mpdu_blk); struct rt_ba_ori_entry *BATableAllocOriEntry(struct rt_rtmp_adapter *pAd, u16 * Idx); struct rt_ba_rec_entry *BATableAllocRecEntry(struct rt_rtmp_adapter *pAd, u16 * Idx); void BAOriSessionSetupTimeout(void *SystemSpecific1, void *FunctionContext, void *SystemSpecific2, void *SystemSpecific3); void BARecSessionIdleTimeout(void *SystemSpecific1, void *FunctionContext, void *SystemSpecific2, void *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(struct rt_rtmp_adapter *pAd, struct rt_mac_table_entry *pEntryPeer, u8 * pWinSize) { u8 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(struct rt_rtmp_adapter *pAd, IN struct reordering_mpdu *mpdu) { void *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 */ /* */ ANNOUNCE_OR_FORWARD_802_3_PACKET(pAd, pPacket, RTMP_GET_PACKET_IF(pPacket)); } } /* * 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(struct rt_rtmp_adapter *pAd) { struct rt_ba_table *Tab; struct rt_ba_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(struct rt_rtmp_adapter *pAd, int num) { int i; u8 *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", (u32)(num * sizeof(struct reordering_mpdu)))); /* allocate number of mpdu_blk memory */ os_alloc_mem(pAd, (u8 **) & 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(struct rt_rtmp_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(struct rt_rtmp_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 u16 ba_indicate_reordering_mpdus_in_order(struct rt_rtmp_adapter *pAd, struct rt_ba_rec_entry *pBAEntry, u16 StartSeq) { struct reordering_mpdu *mpdu_blk; u16 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(struct rt_rtmp_adapter *pAd, struct rt_ba_rec_entry *pBAEntry, u16 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(struct rt_rtmp_adapter *pAd, struct rt_ba_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(struct rt_rtmp_adapter *pAd, struct rt_ba_rec_entry *pBAEntry, unsigned long Now32) { u16 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; } DBGPRINT(RT_DEBUG_OFF, ("%x, flush one!\n", pBAEntry->LastIndSeq)); } } /* * generate ADDBA request to * set up BA agreement */ void BAOriSessionSetUp(struct rt_rtmp_adapter *pAd, struct rt_mac_table_entry *pEntry, u8 TID, u16 TimeOut, unsigned long DelayTime, IN BOOLEAN isForced) { /*struct rt_mlme_addba_req AddbaReq; */ struct rt_ba_ori_entry *pBAEntry = NULL; u16 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; /* DBGPRINT(RT_DEBUG_TRACE, ("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(struct rt_rtmp_adapter *pAd, struct rt_mac_table_entry *pEntry, struct rt_frame_addba_rsp * pFrame) { struct rt_ba_ori_entry *pBAEntry = NULL; BOOLEAN Cancelled; u8 TID; u16 Idx; u8 *pOutBuffer2 = NULL; int NStatus; unsigned long FrameLen; struct rt_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, ((u8)pFrame->BaParm.BufSize)); BA_MaxWinSizeReasign(pAd, pEntry, &pBAEntry->BAWinSize); pBAEntry->TimeOutValue = pFrame->TimeOutValue; pBAEntry->ORI_BA_Status = Originator_Done; pAd->BATable.numDoneOriginator++; /* 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", __func__, 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; } BarHeaderInit(pAd, &FrameBar, pAd->MacTab.Content[pBAEntry->Wcid].Addr, pAd->CurrentAddress); 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(struct rt_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(struct rt_rtmp_adapter *pAd, struct rt_mac_table_entry *pEntry, struct rt_frame_addba_req * pFrame) { struct rt_ba_rec_entry *pBAEntry = NULL; BOOLEAN Status = TRUE; BOOLEAN Cancelled; u16 Idx; u8 TID; u8 BAWinSize; /*u32 Value; */ /*u32 offset; */ ASSERT(pEntry); /* find TID */ TID = pFrame->BaParm.TID; BAWinSize = min(((u8)pFrame->BaParm.BufSize), (u8)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", __func__, 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; */ DBGPRINT(RT_DEBUG_OFF, ("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); } /* Set Bitmap flag. */ pEntry->RXBAbitmap |= (1 << TID); pEntry->BARecWcidArray[TID] = Idx; pEntry->BADeclineBitmap &= ~(1 << TID); /* Set BA session mask in WCID table. */ RTMP_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); } struct rt_ba_rec_entry *BATableAllocRecEntry(struct rt_rtmp_adapter *pAd, u16 * Idx) { int i; struct rt_ba_rec_entry *pBAEntry = NULL; NdisAcquireSpinLock(&pAd->BATabLock); if (pAd->BATable.numAsRecipient >= MAX_BARECI_SESSION) { DBGPRINT(RT_DEBUG_OFF, ("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; } struct rt_ba_ori_entry *BATableAllocOriEntry(struct rt_rtmp_adapter *pAd, u16 * Idx) { int i; struct rt_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(struct rt_rtmp_adapter *pAd, unsigned long Idx) { struct rt_ba_ori_entry *pBAEntry = NULL; struct rt_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) { pAd->BATable.numDoneOriginator -= 1; 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(struct rt_rtmp_adapter *pAd, unsigned long Idx) { struct rt_ba_rec_entry *pBAEntry = NULL; struct rt_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(struct rt_rtmp_adapter *pAd, u8 Wcid, u8 TID, IN BOOLEAN bPassive, IN BOOLEAN bForceSend) { unsigned long Idx = 0; struct rt_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 */ struct rt_mlme_delba_req DelbaReq; struct rt_mlme_queue_elem *Elem = (struct rt_mlme_queue_elem *)kmalloc(sizeof(struct rt_mlme_queue_elem), MEM_ALLOC_FLAG); if (Elem != NULL) { NdisZeroMemory(&DelbaReq, sizeof(DelbaReq)); NdisZeroMemory(Elem, sizeof(struct rt_mlme_queue_elem)); COPY_MAC_ADDR(DelbaReq.Addr, pAd->MacTab.Content[Wcid].Addr); DelbaReq.Wcid = Wcid; DelbaReq.TID = TID; DelbaReq.Initiator = ORIGINATOR; Elem->MsgLen = sizeof(DelbaReq); NdisMoveMemory(Elem->Msg, &DelbaReq, sizeof(DelbaReq)); MlmeDELBAAction(pAd, Elem); kfree(Elem); } else { DBGPRINT(RT_DEBUG_ERROR, ("%s(bForceSend):alloc memory failed!\n", __func__)); } } return; } DBGPRINT(RT_DEBUG_TRACE, ("%s===>Wcid=%d.TID=%d \n", __func__, 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)) { struct rt_mlme_delba_req DelbaReq; struct rt_mlme_queue_elem *Elem = (struct rt_mlme_queue_elem *)kmalloc(sizeof(struct rt_mlme_queue_elem), MEM_ALLOC_FLAG); if (Elem != NULL) { NdisZeroMemory(&DelbaReq, sizeof(DelbaReq)); NdisZeroMemory(Elem, sizeof(struct rt_mlme_queue_elem)); COPY_MAC_ADDR(DelbaReq.Addr, pAd->MacTab.Content[Wcid].Addr); DelbaReq.Wcid = Wcid; DelbaReq.TID = pBAEntry->TID; DelbaReq.Initiator = ORIGINATOR; Elem->MsgLen = sizeof(DelbaReq); NdisMoveMemory(Elem->Msg, &DelbaReq, sizeof(DelbaReq)); MlmeDELBAAction(pAd, Elem); kfree(Elem); } else { DBGPRINT(RT_DEBUG_ERROR, ("%s():alloc memory failed!\n", __func__)); return; } } RTMPCancelTimer(&pBAEntry->ORIBATimer, &Cancelled); BATableFreeOriEntry(pAd, Idx); if (bPassive) { /*BAOriSessionSetUp(pAd, &pAd->MacTab.Content[Wcid], TID, 0, 10000, TRUE); */ } } void BARecSessionTearDown(struct rt_rtmp_adapter *pAd, u8 Wcid, u8 TID, IN BOOLEAN bPassive) { unsigned long Idx = 0; struct rt_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", __func__, 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)) { struct rt_mlme_delba_req DelbaReq; BOOLEAN Cancelled; /*unsigned long offset; */ /*u32 VALUE; */ RTMPCancelTimer(&pBAEntry->RECBATimer, &Cancelled); /* */ /* 1. Send DELBA Action Frame */ /* */ if (bPassive == FALSE) { struct rt_mlme_queue_elem *Elem = (struct rt_mlme_queue_elem *)kmalloc(sizeof(struct rt_mlme_queue_elem), MEM_ALLOC_FLAG); if (Elem != NULL) { NdisZeroMemory(&DelbaReq, sizeof(DelbaReq)); NdisZeroMemory(Elem, sizeof(struct rt_mlme_queue_elem)); COPY_MAC_ADDR(DelbaReq.Addr, pAd->MacTab.Content[Wcid].Addr); DelbaReq.Wcid = Wcid; DelbaReq.TID = TID; DelbaReq.Initiator = RECIPIENT; Elem->MsgLen = sizeof(DelbaReq); NdisMoveMemory(Elem->Msg, &DelbaReq, sizeof(DelbaReq)); MlmeDELBAAction(pAd, Elem); kfree(Elem); } else { DBGPRINT(RT_DEBUG_ERROR, ("%s():alloc memory failed!\n", __func__)); return; } } /* */ /* 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; RTMP_DEL_BA_SESSION_FROM_ASIC(pAd, Wcid, TID); NdisReleaseSpinLock(&pAd->BATabLock); } BATableFreeRecEntry(pAd, Idx); } void BASessionTearDownALL(struct rt_rtmp_adapter *pAd, u8 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(void *SystemSpecific1, void *FunctionContext, void *SystemSpecific2, void *SystemSpecific3) { struct rt_ba_ori_entry *pBAEntry = (struct rt_ba_ori_entry *)FunctionContext; struct rt_mac_table_entry *pEntry; struct rt_rtmp_adapter *pAd; if (pBAEntry == NULL) return; pAd = pBAEntry->pAdapter; { /* Do nothing if monitor mode is on */ if (MONITOR_ON(pAd)) return; } pEntry = &pAd->MacTab.Content[pBAEntry->Wcid]; if ((pBAEntry->ORI_BA_Status == Originator_WaitRes) && (pBAEntry->Token < ORI_SESSION_MAX_RETRY)) { struct rt_mlme_addba_req AddbaReq; NdisZeroMemory(&AddbaReq, sizeof(AddbaReq)); COPY_MAC_ADDR(AddbaReq.pAddr, pEntry->Addr); AddbaReq.Wcid = (u8)(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(struct rt_mlme_addba_req), (void *)& AddbaReq); RTMP_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(void *SystemSpecific1, void *FunctionContext, void *SystemSpecific2, void *SystemSpecific3) { struct rt_ba_rec_entry *pBAEntry = (struct rt_ba_rec_entry *)FunctionContext; struct rt_rtmp_adapter *pAd; unsigned long 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); DBGPRINT(RT_DEBUG_OFF, ("%ld: REC BA session Timeout\n", Now32)); } } } void PeerAddBAReqAction(struct rt_rtmp_adapter *pAd, struct rt_mlme_queue_elem *Elem) { /* 7.4.4.1 */ /*unsigned long Idx; */ u8 Status = 1; u8 pAddr[6]; struct rt_frame_addba_rsp ADDframe; u8 *pOutBuffer = NULL; int NStatus; struct rt_frame_addba_req * pAddreqFrame = NULL; /*u8 BufSize; */ unsigned long FrameLen; unsigned long *ptemp; struct rt_mac_table_entry *pMacEntry; DBGPRINT(RT_DEBUG_TRACE, ("%s ==> (Wcid = %d)\n", __func__, 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 = (unsigned long *)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 = (struct rt_frame_addba_req *) (&Elem->Msg[0]); DBGPRINT(RT_DEBUG_OFF, ("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 = (struct rt_frame_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(struct rt_frame_addba_rsp)); /* 2-1. Prepare ADDBA Response frame. */ { 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); } 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(((u8)pAddreqFrame->BaParm.BufSize), (u8)pAd->CommonCfg.BACapability.field.RxBAWinLimit); if (ADDframe.BaParm.BufSize == 0) { ADDframe.BaParm.BufSize = 64; } ADDframe.TimeOutValue = 0; /*pAddreqFrame->TimeOutValue; */ *(u16 *) (&ADDframe.BaParm) = cpu2le16(*(u16 *) (&ADDframe.BaParm)); ADDframe.StatusCode = cpu2le16(ADDframe.StatusCode); ADDframe.TimeOutValue = cpu2le16(ADDframe.TimeOutValue); MakeOutgoingFrame(pOutBuffer, &FrameLen, sizeof(struct rt_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", __func__, Elem->Wcid, ADDframe.BaParm.TID, ADDframe.BaParm.BufSize)); } void PeerAddBARspAction(struct rt_rtmp_adapter *pAd, struct rt_mlme_queue_elem *Elem) { /*u8 Idx, i; */ /*u8 * pOutBuffer = NULL; */ struct rt_frame_addba_rsp * pFrame = NULL; /*struct rt_ba_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", __func__, Elem->Wcid)); /*hex_dump("PeerAddBARspAction()", Elem->Msg, Elem->MsgLen); */ if (PeerAddBARspActionSanity(pAd, Elem->Msg, Elem->MsgLen)) { pFrame = (struct rt_frame_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) || ((pAd->OpMode == OPMODE_STA) && STA_TGN_WIFI_ON(pAd) && (pFrame->StatusCode != 0)) ) { pAd->MacTab.Content[Elem->Wcid].BADeclineBitmap |= 1 << pFrame->BaParm.TID; } } } void PeerDelBAAction(struct rt_rtmp_adapter *pAd, struct rt_mlme_queue_elem *Elem) { /*u8 Idx; */ /*u8 * pOutBuffer = NULL; */ struct rt_frame_delba_req * pDelFrame = NULL; DBGPRINT(RT_DEBUG_TRACE, ("%s ==>\n", __func__)); /*DELBA Request from unknown peer, ignore this. */ if (PeerDelBAActionSanity(pAd, Elem->Wcid, Elem->Msg, Elem->MsgLen)) { pDelFrame = (struct rt_frame_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(struct rt_rtmp_adapter *pAd, unsigned long Wcid, unsigned long MsgLen, struct rt_frame_ba_req * pMsg) { struct rt_frame_ba_req * pFrame = pMsg; /*PRTMP_REORDERBUF pBuffer; */ /*PRTMP_REORDERBUF pDmaBuf; */ struct rt_ba_rec_entry *pBAEntry; /*BOOLEAN Result; */ unsigned long Idx; /*u8 NumRxPkt; */ u8 TID; /*, i; */ TID = (u8)pFrame->BARControl.TID; DBGPRINT(RT_DEBUG_TRACE, ("%s(): BAR-Wcid(%ld), Tid (%d)\n", __func__, Wcid, TID)); /*hex_dump("BAR", (char *)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(struct rt_frame_ba_req)) { DBGPRINT_ERR(("CntlEnqueueForRecv: BlockAck Request frame length size = %ld incorrect\n", MsgLen)); return FALSE; } else if (MsgLen != sizeof(struct rt_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)) { /*DBGPRINT(RT_DEBUG_TRACE, ("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(struct rt_rtmp_adapter *pAd, u8 Wcid, u8 Psmp) { u8 *pOutBuffer = NULL; int NStatus; /*unsigned long Idx; */ struct rt_frame_psmp_action Frame; unsigned long 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; } ActHeaderInit(pAd, &Frame.Hdr, pAd->CommonCfg.Bssid, pAd->CurrentAddress, pAd->MacTab.Content[Wcid].Addr); Frame.Category = CATEGORY_HT; Frame.Action = SMPS_ACTION; switch (Psmp) { case MMPS_ENABLE: #ifdef RT30xx if (IS_RT30xx(pAd) && (pAd->Antenna.field.RxPath > 1 || pAd->Antenna.field.TxPath > 1)) { RTMP_ASIC_MMPS_DISABLE(pAd); } #endif /* RT30xx // */ Frame.Psmp = 0; break; case MMPS_DYNAMIC: Frame.Psmp = 3; break; case MMPS_STATIC: #ifdef RT30xx if (IS_RT30xx(pAd) && (pAd->Antenna.field.RxPath > 1 || pAd->Antenna.field.TxPath > 1)) { RTMP_ASIC_MMPS_ENABLE(pAd); } #endif /* RT30xx // */ Frame.Psmp = 1; break; } MakeOutgoingFrame(pOutBuffer, &FrameLen, sizeof(struct rt_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 struct PACKED rt_beacon_request { u8 RegulatoryClass; u8 ChannelNumber; u16 RandomInterval; u16 MeasurementDuration; u8 MeasurementMode; u8 BSSID[MAC_ADDR_LEN]; u8 ReportingCondition; u8 Threshold; u8 SSIDIE[2]; /* 2 byte */ }; struct PACKED rt_measurement_req { u8 ID; u8 Length; u8 Token; u8 RequestMode; u8 Type; }; void convert_reordering_packet_to_preAMSDU_or_802_3_packet(struct rt_rtmp_adapter *pAd, struct rt_rx_blk *pRxBlk, u8 FromWhichBSSID) { void *pRxPkt; u8 Header802_3[LENGTH_802_3]; /* 1. get 802.3 Header */ /* 2. remove LLC */ /* a. pointer pRxBlk->pData to payload */ /* b. modify pRxBlk->DataSize */ RTMP_802_11_REMOVE_LLC_AND_CONVERT_TO_802_3(pRxBlk, Header802_3); ASSERT(pRxBlk->pRxPacket); pRxPkt = RTPKT_TO_OSPKT(pRxBlk->pRxPacket); SET_OS_PKT_NETDEV(pRxPkt, get_netdev_from_bssid(pAd, FromWhichBSSID)); SET_OS_PKT_DATAPTR(pRxPkt, pRxBlk->pData); SET_OS_PKT_LEN(pRxPkt, pRxBlk->DataSize); SET_OS_PKT_DATATAIL(pRxPkt, pRxBlk->pData, pRxBlk->DataSize); /* */ /* copy 802.3 header, if necessary */ /* */ if (!RX_BLK_TEST_FLAG(pRxBlk, fRX_AMSDU)) { { #ifdef LINUX NdisMoveMemory(skb_push(pRxPkt, LENGTH_802_3), Header802_3, LENGTH_802_3); #endif } } } #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(struct rt_rtmp_adapter *pAd, struct rt_ba_rec_entry *pBAEntry, struct rt_rx_blk *pRxBlk, u8 FromWhichBSSID) { struct reordering_mpdu *mpdu_blk; u16 Sequence = (u16)pRxBlk->pHeader->Sequence; mpdu_blk = ba_mpdu_blk_alloc(pAd); if ((mpdu_blk != NULL) && (!RX_BLK_TEST_FLAG(pRxBlk, fRX_EAP))) { /* 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(struct rt_rtmp_adapter *pAd, struct rt_rx_blk *pRxBlk, u8 FromWhichBSSID) { u16 Idx; struct rt_ba_rec_entry *pBAEntry = NULL; u16 Sequence = pRxBlk->pHeader->Sequence; unsigned long Now32; u8 Wcid = pRxBlk->pRxWI->WirelessCliID; u8 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)) { u16 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; } } }