/**************************************************************************** * Ralink Tech Inc. * Taiwan, R.O.C. * * (c) Copyright 2002, Ralink Technology, Inc. * * All rights reserved. Ralink's source code is an unpublished work and the * use of a copyright notice does not imply otherwise. This source code * contains confidential trade secret material of Ralink Tech. Any attemp * or participation in deciphering, decoding, reverse engineering or in any * way altering the source code is stricitly prohibited, unless the prior * written consent of Ralink Technology, Inc. is obtained. ***************************************************************************/ /**************************************************************************** Abstract: All related WMM ACM EDCA function body. ***************************************************************************/ #define MODULE_WMM_ACM #include "rt_config.h" #ifdef WMM_ACM_SUPPORT /* IEEE802.11E related include files */ #include "acm_extr.h" /* used for other modules */ #include "acm_comm.h" /* used for edca/wmm */ #include "acm_edca.h" /* used for edca/wmm */ /* ----- EDCA External Variable (only used in ACM module) ----- */ extern UCHAR gTCLAS_Elm_Len[]; #ifdef ACM_MEMORY_TEST extern UINT32 gACM_MEM_Alloc_Num; extern UINT32 gACM_MEM_Free_Num; #endif /* ACM_MEMORY_TEST */ /* ----- EDCA Private Variable ----- */ /* AC0 (BE), AC1 (BK), AC2 (VI), AC3 (VO); AC3 > AC2 > AC0 > AC1 */ /* DSCP = (DSCP >> 3) & 0x07 */ UCHAR gEDCA_UP_AC[8] = { 0, 1, 1, 0, 2, 2, 3, 3 }; /* AC0 vs. UP0, AC1 vs. UP1, AC2 vs. UP4, AC3 vs. UP6 */ UCHAR gEDCA_AC_UP[4] = { 0, 1, 4, 6 }; /* DSCP(0) -> Priority 0 (AC_BE) DSCP(1) -> Priority 1 (AC_BK) DSCP(2) -> Priority 2 (AC_BK) DSCP(3) -> Priority 3 (AC_BE) DSCP(4) -> Priority 4 (AC_VI) DSCP(5) -> Priority 5 (AC_VI) DSCP(6) -> Priority 6 (AC_VO) DSCP(7) -> Priority 7 (AC_VO) */ UCHAR gEDCA_UP_DSCP[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; /* ====================== Public Function ============================= */ /* ======================================================================== Routine Description: Change current EDCA Information. Arguments: pAd - WLAN control block pointer CpNu - the numerator of Contention Period, if 0, no any update for CpNu CpDe - the denominator of Contention Period if 0, no any update for CpDe BeNu - the numerator of Best Effort percentage, if 0, no any update for BeNu BeDe - the denominator of Best Effort percentage if 0, no any update for BeDe Return Value: ACM_RTN_OK - change ok ACM_RTN_FAIL - change fail Note: 1. CpNu/CpDe is the percentage of EDCA in 1 second. 2. BeNu/BeDe is the percentage of Best Effort streams in 1 second. 3. The function will not delete any stream if residunt bandwidth is not enough for (CpNu/CpDe)*SI or (BeNu/BeDe). 4. New (CpNu/CpDe) or (BeNu/BeDe) will be valid after bandwidth is enough. 5. If the old ACM is enabled and the new ACM is disabled, the function will not deleted these streams use the AC. ======================================================================== */ ACM_FUNC_STATUS ACM_EDCA_InfomationChange( ACM_PARAM_IN PRTMP_ADAPTER pAd, ACM_PARAM_IN UINT16 CpNu, ACM_PARAM_IN UINT16 CpDe, ACM_PARAM_IN UINT16 BeNu, ACM_PARAM_IN UINT16 BeDe) { ACM_CTRL_PARAM *pEdcaParam; ULONG SplFlags; /* change EDCA parameters */ ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL); pEdcaParam = &(ACMR_CB->EdcaCtrlParam); /* copy new settings to new parameters */ if ((CpNu > 0) && (CpDe > 0)) { pEdcaParam->CP_MinNu = CpNu; pEdcaParam->CP_MinDe = CpDe; } /* End of if */ if ((BeNu > 0) && (BeDe > 0)) { pEdcaParam->BEK_MinNu = BeNu; pEdcaParam->BEK_MinDe = BeDe; } /* End of if */ ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr); /* delete all streams */ ACMP_TC_DeleteAll(pAd); return ACM_RTN_OK; LabelSemErr: return ACM_RTN_FAIL; } /* End of ACM_EDCA_InfomationChange */ /* ======================================================================== Routine Description: Check whether the element is WME Information. Arguments: *pElm - the element SubType - the sub type Return Value: ACM_RTN_OK - check ok ACM_RTN_FAIL - check fail Note: No semaphore protection is needed. ======================================================================== */ ACM_FUNC_STATUS ACM_WME_ELM_Check( ACM_PARAM_IN UCHAR *pElm, ACM_PARAM_IN UCHAR SubType) { UCHAR ElmLen[3] = { ACM_ELM_WME_INFO_LEN, ACM_ELM_WME_PARAM_LEN, ACM_ELM_WME_TSPEC_LEN }; if ((pElm[0] == ACM_ELM_WME_ID) && (pElm[1] == ElmLen[SubType]) && (pElm[2] == ACM_WME_OUI_0) && (pElm[3] == ACM_WME_OUI_1) && (pElm[4] == ACM_WME_OUI_2) && (pElm[5] == ACM_WME_OUI_TYPE) && (pElm[6] == SubType) && (pElm[7] == ACM_WME_OUI_VERSION)) { return ACM_RTN_OK; } /* End of if */ return ACM_RTN_FAIL; } /* End of ACM_WME_ELM_Check */ #ifdef CONFIG_STA_SUPPORT /* ======================================================================== Routine Description: Check a WME TSPEC element in a buffer. Arguments: *pBuffer - the buffer *pTid - the TID of the TSPEC element *pMediumTime - the medium time of the TSPEC element Return Value: TRUE - TSPEC element FALSE - not TSPEC element Note: ======================================================================== */ BOOLEAN ACMP_WME_TSPEC_ElementCheck( ACM_PARAM_IN UCHAR *pBuffer, ACM_PARAM_OUT UINT32 *pTid, ACM_PARAM_OUT UINT32 *pMediumTime) { ACM_ELM_WME_TSPEC *pElmTspec; if (pBuffer == NULL) return FALSE; /* End of if */ pElmTspec = (ACM_ELM_WME_TSPEC *)pBuffer; if ((pElmTspec->ElementId != ACM_ELM_WME_ID) || (pElmTspec->Length != ACM_ELM_WME_TSPEC_LEN) || (pElmTspec->OUI[0] != ACM_WME_OUI_0) || (pElmTspec->OUI[1] != ACM_WME_OUI_1) || (pElmTspec->OUI[2] != ACM_WME_OUI_2) || (pElmTspec->OUI_Type != ACM_WME_OUI_TYPE) || (pElmTspec->OUI_SubType != ACM_WME_OUI_SUBTYPE_TSPEC) || (pElmTspec->Version != ACM_WME_OUI_VERSION)) { /* not TSPEC element */ return FALSE; } /* End of if */ *pTid = (UINT32)pElmTspec->Tspec.TsInfo.TID; *pMediumTime = (UINT32)pElmTspec->Tspec.MediumTime; return TRUE; } /* End of ACMP_WME_TSPEC_ElementCheck */ #endif /* CONFIG_STA_SUPPORT */ #ifdef CONFIG_STA_SUPPORT_SIM /* ======================================================================== Routine Description: Fill a TSPEC element to a buffer. Arguments: pAd - WLAN control block pointer *pBuffer - the buffer *pTspec11e - the current TSPEC Return Value: filled element length Note: ======================================================================== */ UINT32 ACMP_WME_TSPEC_ElementFill( ACM_PARAM_IN PRTMP_ADAPTER pAd, ACM_PARAM_IN UCHAR *pBuffer, ACM_PARAM_IN ACM_TSPEC *pTspec11e) { ACM_ELM_WME_TSPEC *pElmTspec; ACM_WME_TSPEC *pTspecWme; ACM_WME_TS_INFO *pInfo; /* sanity check */ if ((pBuffer == NULL) || (pTspec11e == NULL)) return 0; /* End of if */ /* TSPEC element */ pElmTspec = (ACM_ELM_WME_TSPEC *)pBuffer; pElmTspec->ElementId = ACM_ELM_WME_ID; pElmTspec->Length = ACM_ELM_WME_TSPEC_LEN; /* init OUI field */ pElmTspec->OUI[0] = ACM_WME_OUI_0; pElmTspec->OUI[1] = ACM_WME_OUI_1; pElmTspec->OUI[2] = ACM_WME_OUI_2; pElmTspec->OUI_Type = ACM_WME_OUI_TYPE; pElmTspec->OUI_SubType = ACM_WME_OUI_SUBTYPE_TSPEC; pElmTspec->Version = ACM_WME_OUI_VERSION; /* init TS Info field */ pTspecWme = &pElmTspec->Tspec; ACMR_MEM_ZERO(pTspecWme, sizeof(ACM_WME_TSPEC)); pInfo = &pElmTspec->Tspec.TsInfo; pInfo->TID = pTspec11e->TsInfo.TSID; pInfo->Direction = pTspec11e->TsInfo.Direction; pInfo->UP = pTspec11e->TsInfo.UP; pInfo->PSB = pTspec11e->TsInfo.APSD; pInfo->One = 1; /* always 1 */ #ifdef ACM_CC_FUNC_11N_AGG pInfo->AckPolicy = pTspec11e->TsInfo.AckPolicy; #endif /* ACM_CC_FUNC_11N_AGG */ /* init TSPEC parameters */ pTspecWme->NominalMsduSize = pTspec11e->NominalMsduSize; pTspecWme->MaxMsduSize = pTspec11e->MaxMsduSize; pTspecWme->MinServInt = pTspec11e->MinServInt; pTspecWme->MaxServInt = pTspec11e->MaxServInt; pTspecWme->InactivityInt = pTspec11e->InactivityInt; pTspecWme->SuspensionInt = pTspec11e->SuspensionInt; pTspecWme->ServiceStartTime = pTspec11e->ServiceStartTime; pTspecWme->MinDataRate = pTspec11e->MinDataRate; pTspecWme->MeanDataRate = pTspec11e->MeanDataRate; pTspecWme->PeakDataRate = pTspec11e->PeakDataRate; pTspecWme->MaxBurstSize = pTspec11e->MaxBurstSize; pTspecWme->DelayBound = pTspec11e->DelayBound; pTspecWme->MinPhyRate = pTspec11e->MinPhyRate; pTspecWme->SurplusBandwidthAllowance = \ pTspec11e->SurplusBandwidthAllowance; return (sizeof(ACM_ELM_WME_TSPEC)); } /* End of ACMP_WME_TSPEC_ElementFill */ #endif /* CONFIG_STA_SUPPORT_SIM */ /* ====================== Private Function (EDCA) (AP) ====================== */ /* ======================================================================== Routine Description: Reclaim a EDCA used ACM time after a actived stream is deleted. Arguments: pAd - WLAN control block pointer *pStream - the EDCA stream Return Value: None Note: Protect in caller. ======================================================================== */ STATIC VOID ACM_EDCA_AllocatedTimeReturn( ACM_PARAM_IN PRTMP_ADAPTER pAd, ACM_PARAM_IN ACM_STREAM *pStream) { #define ACM_LMR_TIME_DECREASE(time, value) \ if (time >= value) time -= value; else time = 0; #define ACM_LMR_TIME_INCREASE(time, value) \ time += value; ACM_CTRL_PARAM *pEdca; ACM_TSPEC *pTspec; UINT32 TimeUsed, AcId; UINT32 Direction; /* init */ pEdca = &ACMR_CB->EdcaCtrlParam; pTspec = pStream->pTspec; /* check if the stream is EDCA */ if (pTspec->TsInfo.AccessPolicy != ACM_ACCESS_POLICY_EDCA) return; /* End of if */ /* get AC ID */ AcId = ACM_MR_EDCA_AC(pStream->UP); /* reclaim used time */ TimeUsed = pTspec->MediumTime << 5; /* unit: microsecond */ if (TimeUsed == 0) return; /* End of if */ ACM_LMR_TIME_DECREASE(pEdca->AcmTotalTime, TimeUsed); ACM_LMR_TIME_DECREASE(pEdca->AcmAcTime[AcId], TimeUsed); Direction = pTspec->TsInfo.Direction; #ifdef CONFIG_AP_SUPPORT if (ACMR_IS_AP_MODE(pAd)) { if (Direction == ACM_DIRECTION_BIDIREC_LINK) { /* for bidirectional link, used time = dnlink + uplink; so we need to decrease it twice */ ACM_LMR_TIME_DECREASE(pEdca->AcmTotalTime, TimeUsed); ACM_LMR_TIME_DECREASE(pEdca->AcmAcTime[AcId], TimeUsed); } /* End of if */ } /* End of if */ #endif /* CONFIG_AP_SUPPORT */ /* for main link or bidirectional link, we shall reset the CSR */ if ((pStream->FlgOutLink == 1) || (Direction == ACM_DIRECTION_BIDIREC_LINK)) { ACM_LMR_TIME_DECREASE(pEdca->AcmOutTime[AcId], TimeUsed); /* modify CSR setting */ ACM_ASIC_ACM_Reset(pAd, AcId, pEdca->AcmOutTime[AcId]); } /* End of if */ #ifdef RELEASE_EXCLUDE /* dynamic ATL: update DATL time */ if (pEdca->FlgDatl) ACM_DATL_Update(pAd, AcId, TimeUsed, 0, ACM_DATL_NO_BORROW, 0); /* End of if */ #endif /* RELEASE_EXCLUDE */ /* update available ACM time */ TimeUsed = pEdca->AcmTotalTime; #ifdef ACM_CC_FUNC_MBSS TimeUsed += ACMR_CB->MbssTotalUsedTime; #endif /* ACM_CC_FUNC_MBSS */ #ifdef CONFIG_AP_SUPPORT if (ACMR_IS_AP_MODE(pAd)) { ACMR_AVAIL_ACM_TIME_UPDATE(pAd, TimeUsed); } /* End of if */ #endif /* CONFIG_AP_SUPPORT */ } /* End of ACM_EDCA_AllocatedTimeReturn */ #ifdef CONFIG_AP_SUPPORT /* ======================================================================== Routine Description: Delete a lower priority ACM AC stream to free bandwidth. Arguments: pAd - WLAN control block pointer TimeOff - the needed free time (us) Return Value: ACM_RTN_OK - delete successfullly ACM_RTN_FAIL - no any AC stream is deleted Note: Used in QAP. ======================================================================== */ STATIC ACM_FUNC_STATUS ACM_EDCA_Lower_UP_Del( ACM_PARAM_IN PRTMP_ADAPTER pAd, ACM_PARAM_IN UINT32 TimeOff) { ACM_STREAM **ppAcmStmList, *pStream; ACM_STREAM *pStreamDel; UINT32 DevIndex; UCHAR MAC[6]; UINT32 MediumTime, MediumTimeMax; UINT32 IdTidNum; /* check whether no any ACM stream exists */ if (ACMR_CB->EdcaCtrlParam.AcmTotalTime == 0) return ACM_RTN_FAIL; /* no ACM AC stream can be deleted */ /* End of if */ /* init */ pStreamDel = NULL; DevIndex = 0; MediumTimeMax = 0; /* try to find a non-bidirectional ACM AC whose medium time > TimeOff */ /* first, search in client database */ TimeOff = (TimeOff >> 5) + 1; /* change to 32 miscroseconds */ /* 1 is for roundUp */ while(1) { /* get a associated STATION MAC from our database */ if (ACM_PeerDeviceMacGetNext(pAd, &DevIndex, MAC) != ACM_RTN_OK) break; /* End of if */ /* get its WMM TS list */ ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet( pAd, MAC, ACM_PEER_TSPEC_INPUT_GET); if (ppAcmStmList == NULL) break; /* End of if */ /* try to find a TS that its medium time >= TimeOff */ for(IdTidNum=0; IdTidNumpTspec->TsInfo.Direction != \ ACM_DIRECTION_BIDIREC_LINK)) { MediumTime = pStream->pTspec->MediumTime; if (MediumTime >= TimeOff) { /* find one stream so delete it */ pStream->Cause = TSPEC_CAUSE_BANDWIDTH; ACM_TC_Delete(pAd, pStream); return ACM_RTN_OK; } /* End of if */ if (MediumTime > MediumTimeMax) { /* backup the stream with max medium time */ MediumTimeMax = MediumTime; pStreamDel = pStream; } /* End of if */ } /* End of if */ } /* End of for */ } /* End of while */ if (pStreamDel == NULL) return ACM_RTN_FAIL; /* no stream can be deleted */ /* End of if */ /* find it and delete the stream */ pStreamDel->Cause = TSPEC_CAUSE_BANDWIDTH; ACM_TC_Delete(pAd, pStreamDel); return ACM_RTN_OK; } /* End of ACM_EDCA_Lower_UP_Del */ /* ======================================================================== Routine Description: Handle a EDCA TSPEC request from the QSTA. Arguments: pAd - WLAN control block pointer *pCdb - the source QSTA *pNewStream - the requested TSPEC *pOldStreamIn - the old same in TSPEC with same AC *pOldStreamOut - the old same out TSPEC with same AC *pOldStreamDiffAc - the old same TSPEC with different AC Return Value: Status Code Note: 1. Admission Control Mechanism for EDCA. ======================================================================== */ STATIC UCHAR ACM_EDCA_ReqHandle( ACM_PARAM_IN PRTMP_ADAPTER pAd, ACM_PARAM_IN ACMR_STA_DB *pCdb, ACM_PARAM_IN ACM_STREAM *pNewStream, ACM_PARAM_IN ACM_STREAM *pOldStreamIn, ACM_PARAM_IN ACM_STREAM *pOldStreamOut, ACM_PARAM_IN ACM_STREAM *pOldStreamDiffAc) { /* unit: 100K bps */ UINT16 RateMapping[ACM_RATE_MAX_NUM][2] = { { 540, ACM_RATE_54M }, { 480, ACM_RATE_48M }, { 360, ACM_RATE_36M }, { 240, ACM_RATE_24M }, { 180, ACM_RATE_18M }, { 120, ACM_RATE_12M }, { 90, ACM_RATE_9M }, { 60, ACM_RATE_6M }, { 110, ACM_RATE_11M }, { 55, ACM_RATE_5_5M }, { 20, ACM_RATE_2M }, { 10, ACM_RATE_1M } }; ACM_TSPEC *pTspec; ACM_FUNC_STATUS RtnCode; UINT32 SBA_Int, SBA_Dec; UINT32 PPS; /* unit: packet per sec */ UINT32 MpduExgTime; /* unit: microseconds */ UINT32 MediumTime; /* unit: 32 microseconds */ UINT32 MediumTimeOld; UINT32 AcmTimeOldBi; UINT32 AcId; UCHAR Direction; UINT16 NormSize, NormMpduAgg; UINT32 RateIndex, TxopLimit, TxopLimitDn, TxopLimitUp; UCHAR FlgIsSpreambleUsed, FlgIsCtsEnable, FlgIsRtsEnable, FlgIsGmode; UINT32 VbAcId, VbBandwidth; UINT32 MediumTimeTemp, IdPhyRateNum; /* check whether ACM is needed for the AC */ AcId = ACM_MR_EDCA_AC(pNewStream->UP); #ifndef ACM_CC_FUNC_SPEC_CHANGE_TG if (ACMR_CB->EdcaCtrlParam.FlgAcmStatus[AcId] == ACM_FLG_FUNC_DISABLED) return ACM_STATUS_CODE_PRIVATE_ACM_DISABLED; /* no ACM is needed */ /* End of if */ #endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */ pTspec = pNewStream->pTspec; Direction = pTspec->TsInfo.Direction; MediumTime = 0; MediumTimeOld = 0; AcmTimeOldBi = 0; /* get minimum TXOP, maybe a packet tx time will excess the TXOP */ /* currently we do not use the field, TxopLimit */ TxopLimitDn = ACMR_TXOP_AP_GET(pAd, AcId); TxopLimitUp = ACMR_TXOP_BSS_GET(pAd, AcId); #if 0 if (Direction == ACM_DIR_UP_OR_DL) TxopLimit = TxopLimitDn; else if (Direction == ACM_DIR_DL_OR_UP) TxopLimit = TxopLimitUp; else if (TxopLimitDn > TxopLimitUp) /* choice the smaller one for BI */ TxopLimit = TxopLimitUp; else TxopLimit = TxopLimitDn; /* End of if */ #else TxopLimit = 0; #endif #ifdef RELEASE_EXCLUDE /* ===================================================================== IEEE802.11e D8.0 H.2.2 Deriving Medium Time There are two requirements to consider: 1) the traffic requirements of the application, and 2) the expected error performance of the medium. The application requirements are captured by two TSPEC parameters: Nominal MSDU Size and Mean Data Rate. The medium requirements are captured by two TSPEC parameters: Surplus Bandwidth Allowance and Minimum PHY Rate. When RTS/CTS protection is not used: Medium Time = Surplus Bandwidth Allowance * pps * MPDUExchangeTime where pps = ceiling (Mean Data Rate / 8) / Nominal MSDU Size; the unit of Nominal MSDU Size is octet. ===================================================================== */ #endif /* RELEASE_EXCLUDE */ /* parse the surplus bandwidth allowance */ /* Surplus Bandwidth Allowance = SBA_Int(3-bit).SBA_Dec(13-bit) */ SBA_Int = pTspec->SurplusBandwidthAllowance >> ACM_SURPLUS_DEC_BIT_NUM; SBA_Dec = pTspec->SurplusBandwidthAllowance & ACM_SURPLUS_DEC_BITMAP; SBA_Dec = ACM_SurplusFactorDecimalBin2Dec(SBA_Dec); ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Surplus Bandwidth Allowance = %d.%02d!\n", SBA_Int, SBA_Dec)); /* In WMM ACM Test Plan, the range for SBA should exclude 1.0 and 8.0 */ if (SBA_Int > ACM_SURPLUS_INT_MAX) { ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Surplus Bandwidth Allowance = %d.%d > %d.0!\n", SBA_Int, SBA_Dec, ACM_SURPLUS_INT_MAX)); return ACM_STATUS_CODE_WMM_INVALID_PARAMETERS; } /* End of if */ /* calculate pps */ /* bit 15 = 1: means fix size; 0: means nominal size */ /* TODO: if NormSize >> MeanDataRate or maximum WLAN packet size (1548B) ? */ PPS = 1; NormSize = pTspec->NominalMsduSize; #ifdef ACM_CC_FUNC_11N_AGG if (pTspec->TsInfo.AckPolicy == ACM_ACK_POLICY_BLOCK) NormMpduAgg = pTspec->MaxBurstSize; /* AMPDU aggregation number */ else #endif /* ACM_CC_FUNC_11N_AGG */ NormMpduAgg = 1; /* End of if */ if (NormSize > 0) { NormSize = NormSize & 0x7FFF; PPS = (((pTspec->MeanDataRate>>3)/NormSize)/NormMpduAgg); PPS += 1; if (PPS <= 1) { #ifdef RELEASE_EXCLUDE /* Test Event 1 found: This is illegal because norminal size > data rate size, very strange, so fix the norminal data size. But for CISCO and Marvell AP, they will response the ADDTS Response frame and the medium time is "0". */ #endif /* RELEASE_EXCLUDE */ pTspec->NominalMsduSize = (pTspec->MeanDataRate)>>3; NormSize = pTspec->NominalMsduSize; } /* End of if */ } /* End of if */ /* calculate MPDU exchange time */ #ifdef RELEASE_EXCLUDE /* ===================================================================== MPDU exchange time maybe (p: preamble tx time, rts/cts: rts/cts tx time, cts-self: cts self tx time, h: WLAN header tx time, data: data tx time, a: ack tx time) 1) p + h + data 2) p + h + data + sifs + p + ack 3) p + cts-self + sifs + p + h + data 4) p + cts-self + sifs + p + h + data + sifs + p + ack 5) p + rts + sifs + p + cts + sifs + p + h + data 6) p + rts + sifs + p + cts + sifs + p + h + data + sifs + p + ack Note: Maybe cts-self + rts/cts are used. In HCCA, we need to calculate the TXOP in the worse case because we do not know if QSTA use cts-self and rts/cts simultaneouly. ===================================================================== */ /* ===================================================================== Data hardware fragment needs to be diabled when 1) a hardware fragment tx time > TXOP limit, ==> use software fragment, a fragment tx time = TXOP limit 2) sum of all hardware fragment tx time > TXOP limit, ==> use software fragment, a fragment tx time = TXOP limit ===================================================================== */ #endif /* RELEASE_EXCLUDE */ /* check if ACM is enabled, only allocate bandwidth for enabled ACM */ if (ACMR_CB->EdcaCtrlParam.FlgAcmStatus[AcId] != ACM_FLG_FUNC_DISABLED) { #ifdef ACM_CC_FUNC_11N if (pNewStream->PhyModeMin >= ACMR_PHY_HT) { UINT16 NumOfAgg = 0; /* for HT minimum physical rate, includes RTS/CTS time */ /* TODO: for AMSDU, if no legacy station connects, no RTS/CTS */ ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> minimal MCS = %d, 20/40MHz Flag = %d\n", pNewStream->McsMin, ACMR_IS_2040_STA(pCdb))); #ifdef ACM_CC_FUNC_11N_AGG if (pTspec->TsInfo.AckPolicy == ACM_ACK_POLICY_BLOCK) { /* Nominal MPDU Aggregation */ NumOfAgg = pTspec->MaxBurstSize; ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> number of aggregation = %d\n", pTspec->MaxBurstSize)); } #endif /* ACM_CC_FUNC_11N_AGG */ MpduExgTime = ACM_TX_TimeCalHT( pAd, pCdb, NormSize, pNewStream->McsMin, ACMR_IS_2040_STA(pCdb), 0, /* always use regular GI */ 1, /* always use RTS/CTS */ 0, /* always use BLOCK ACK */ 0, /* no AMPDU */ TxopLimit, FALSE, /* no AMSDU */ NumOfAgg, NULL, NULL, NULL, NULL, NULL); } else #endif /* ACM_CC_FUNC_11N */ { /* find the rate ID of the minimum physical rate */ RateIndex = pTspec->MinPhyRate/100000; for(IdPhyRateNum=0; IdPhyRateNum ACMR_RTS_THRESH(pAd)) && (FlgIsCtsEnable == 0)) FlgIsRtsEnable = 1; else FlgIsRtsEnable = 0; /* End of if */ if (ACMR_ERP_IS_BG_PROTECTION_ENABLED(pAd)) FlgIsRtsEnable = 1; /* End of if */ #ifdef RELEASE_EXCLUDE /* EX: data size = 208B, rate = 54Mbps, MpduExgTime = 104us in A band. */ #endif /* RELEASE_EXCLUDE */ MpduExgTime = ACM_TX_TimeCal( pAd, pCdb, NormSize, RateIndex, FlgIsGmode, FlgIsCtsEnable, FlgIsRtsEnable, FlgIsSpreambleUsed, pTspec->TsInfo.AckPolicy, TxopLimit, NULL, NULL, NULL, NULL, NULL); } /* End of if */ /* calculate the medium time, unit: microseconds */ MediumTime = SBA_Int * PPS * MpduExgTime; ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> norm_size = %d, pps = %d, MpduExgTime = %d, medium time = %d!\n", NormSize, PPS, MpduExgTime, MediumTime)); if (SBA_Dec > 0) { /* Avoid MediumTime * SBA_Dec > 0xFFFFFFFF because MediumTime is only unsigned (32-bit). */ MediumTimeTemp = 0xFFFFFFFF/MediumTime; if (SBA_Dec <= MediumTimeTemp) { /* medium time += 0.SBA_Dec * pps * MpduExgTime */ MediumTime += (SBA_Dec*PPS*MpduExgTime)/ACM_SURPLUS_DEC_BASE; } /* End of if */ } /* End of if */ MediumTime &= 0xFFFFFFE0; /* unit: microseconds */ MediumTime += 32; /* extra 32us for roundUp */ MediumTimeOld = 0; /* calculate old used ACM time */ if (pOldStreamIn != NULL) MediumTimeOld += pOldStreamIn->pTspec->MediumTime << 5; /* End of if */ if (pOldStreamOut != NULL) { MediumTimeOld += pOldStreamOut->pTspec->MediumTime << 5; if (Direction == ACM_DIRECTION_BIDIREC_LINK) AcmTimeOldBi = pOldStreamOut->pTspec->MediumTime << 5; /* End of if */ } /* End of if */ if (pOldStreamDiffAc != NULL) MediumTimeOld += pOldStreamDiffAc->pTspec->MediumTime << 5; /* End of if */ /* check whether current bandwidth is enough */ RtnCode = ACM_BandwidthCheck( pAd, AcId, 0, pTspec->TsInfo.AccessPolicy, Direction, MediumTimeOld, /* old used time */ MediumTime, /* new used time */ AcmTimeOldBi, /* old used time */ NULL, &VbAcId, &VbBandwidth); if (RtnCode != ACM_RTN_OK) return ACM_STATUS_CODE_ASSOC_DENIED_INSUFFICIENT_BANDWIDTH; /* End of if */ } /* End of if */ /* the new stream is allowed */ pTspec->MediumTime = MediumTime>>5; /* transfer to unit: 32us */ pNewStream->Status = TSPEC_STATUS_ACTIVE; pNewStream->InactivityCur = pTspec->InactivityInt; #ifdef ACM_CC_FUNC_HCCA pNewStream->SuspensionCur = pTspec->SuspensionInt; #else pNewStream->SuspensionCur = 0; #endif /* ACM_CC_FUNC_HCCA */ /* free old TSPEC */ if (pOldStreamIn != NULL) { ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Free in stream!\n")); ACM_TC_Discard(pAd, pOldStreamIn); } /* End of if */ if (pOldStreamOut != NULL) { ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Free out stream!\n")); ACM_TC_Discard(pAd, pOldStreamOut); } /* End of if */ if (pOldStreamDiffAc != NULL) { ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Free dif stream!\n")); ACM_TC_Discard(pAd, pOldStreamDiffAc); } /* End of if */ /* active/add the new TSPEC */ if (ACM_TC_Active(pAd, pNewStream) == ACM_RTN_OK) { if (MediumTime > 0) { #ifdef RELEASE_EXCLUDE /* dynamic ATL */ if (ACMR_CB->EdcaCtrlParam.FlgDatl != 0) { if (MediumTime <= MediumTimeOld) { /* Get the needed extra bandwidth for new medium time because we will not handle the case above, we need to handle the case here before ACM_EDCA_Param_ACM_Update() */ ACM_DATL_Handle(pAd, AcId, 0, MediumTime, &VbAcId, &VbBandwidth); } /* End of if */ } /* End of if */ #endif /* RELEASE_EXCLUDE */ /* update used time */ ACM_EDCA_Param_ACM_Update( pAd, pNewStream->AcmAcId, Direction, pNewStream->UP, MediumTime, 0, VbAcId, VbBandwidth); } /* End of if */ RtnCode = ACM_STATUS_CODE_SUCCESS; } else RtnCode = ACM_STATUS_CODE_UNSPECIFIED_FAILURE; /* End of if */ /* update to CSR */ if ((Direction == ACM_DIRECTION_DOWN_LINK) || (Direction == ACM_DIRECTION_BIDIREC_LINK)) { /* When the Direction is donwlink or bidirectional link, we maybe reset ASIC ACM control registers. Currently no any related register is needed to re-set. */ AcId = pNewStream->AcmAcId; ACM_ASIC_ACM_Reset( pAd, AcId, ACMR_CB->EdcaCtrlParam.AcmOutTime[AcId]); } /* End of if */ return RtnCode; } /* End of ACM_EDCA_ReqHandle */ #endif /* CONFIG_AP_SUPPORT */ /* ======================================================================== Routine Description: Update new ACM medium time for EDCA mechanism. Arguments: pAd - WLAN control block pointer AcmAcId - the AC for the stream (0 ~ 3) Direction - the Direction of the stream UP - the user priority AcmTimeNew - new medium time of the stream (unit: microseconds) AcmTimeOld - old medium time of the stream (unit: microseconds) VbAcId - the borrowed AC ID, 0 ~ 3 VbBandwidth - the borrowed bandwidth from a AC (unit: microsecond) Return Value: None Note: Protect in caller. ======================================================================== */ STATIC VOID ACM_EDCA_Param_ACM_Update( ACM_PARAM_IN PRTMP_ADAPTER pAd, ACM_PARAM_IN UINT32 AcmAcId, ACM_PARAM_IN UCHAR Direction, ACM_PARAM_IN UCHAR UP, ACM_PARAM_IN UINT32 AcmTimeNew, ACM_PARAM_IN UINT32 AcmTimeOld, ACM_PARAM_IN UINT32 VbAcId, ACM_PARAM_IN UINT32 VbBandwidth) { ACM_CTRL_PARAM *pEdcaParam; UINT32 TimeNewDn, TimeOldDn; UINT32 TimeNewUp, TimeOldUp; UINT32 TimeUsed; UINT32 AcId; /* init */ pEdcaParam = &(ACMR_CB->EdcaCtrlParam); TimeNewDn = 0; TimeOldDn = 0; TimeNewUp = 0; TimeOldUp = 0; AcId = AcmAcId; switch(Direction) { case ACM_DIRECTION_UP_LINK: /* uplink */ case ACM_DIRECTION_DIRECT_LINK: TimeNewUp = AcmTimeNew; TimeOldUp = AcmTimeOld; #ifdef CONFIG_AP_SUPPORT if (ACMR_IS_AP_MODE(pAd)) { /* for uplink in QAP, AcmAcId is not AC ID; it is minor link; so we need to re-get AC ID from UP of the uplink */ AcId = ACM_MR_EDCA_AC(UP); } /* End of if */ #endif /* CONFIG_AP_SUPPORT */ break; case ACM_DIRECTION_DOWN_LINK: /* dnlink */ TimeNewDn = AcmTimeNew; TimeOldDn = AcmTimeOld; #ifdef CONFIG_STA_SUPPORT if (ACMR_IS_STA_MODE(pAd)) { /* for dnlink in QSTA, AcmAcId is not AC ID; it is minor link; so we need to re-get AC ID from UP of the dnlink */ AcId = ACM_MR_EDCA_AC(UP); } /* End of if */ #endif /* CONFIG_STA_SUPPORT */ break; case ACM_DIRECTION_BIDIREC_LINK: /* dnlink + uplink */ TimeNewDn = TimeNewUp = AcmTimeNew; TimeOldDn = TimeOldUp = AcmTimeOld; break; } /* End of switch */ if ((TimeNewDn == TimeOldDn) && (TimeNewUp == TimeOldUp)) return; /* same time, do NOT need to update */ /* End of if */ /* accumulate new allocated time */ #ifdef CONFIG_AP_SUPPORT if (ACMR_IS_AP_MODE(pAd)) { pEdcaParam->AcmTotalTime += TimeNewDn + TimeNewUp; pEdcaParam->AcmOutTime[AcId] += TimeNewDn; pEdcaParam->AcmAcTime[AcId] += TimeNewDn + TimeNewUp; } /* End of if */ #endif /* CONFIG_AP_SUPPORT */ #ifdef CONFIG_STA_SUPPORT if (ACMR_IS_STA_MODE(pAd)) { pEdcaParam->AcmTotalTime += TimeNewUp; pEdcaParam->AcmOutTime[AcId] += TimeNewUp; pEdcaParam->AcmAcTime[AcId] += TimeNewUp; } /* End of if */ #endif /* CONFIG_STA_SUPPORT */ /* substract old medium time from total ACM time parameters */ #ifdef CONFIG_AP_SUPPORT if (ACMR_IS_AP_MODE(pAd)) { if (pEdcaParam->AcmTotalTime >= (TimeOldDn + TimeOldUp)) pEdcaParam->AcmTotalTime -= (TimeOldDn + TimeOldUp); /* End of if */ } /* End of if */ #endif /* CONFIG_AP_SUPPORT */ #ifdef CONFIG_STA_SUPPORT if (ACMR_IS_STA_MODE(pAd)) { if (pEdcaParam->AcmTotalTime >= TimeOldUp) pEdcaParam->AcmTotalTime -= TimeOldUp; /* End of if */ } /* End of if */ #endif /* CONFIG_STA_SUPPORT */ else { pEdcaParam->AcmTotalTime = 0; /* fatal error */ ACMR_DEBUG(ACMR_DEBUG_ERR, ("acm_err> Used total ACM time < stream medium time! " "EDCA_Param_ACM_Update()\n")); } /* End of if */ #ifdef CONFIG_AP_SUPPORT if (ACMR_IS_AP_MODE(pAd)) { if (pEdcaParam->AcmOutTime[AcId] >= TimeOldDn) pEdcaParam->AcmOutTime[AcId] -= TimeOldDn; /* End of if */ } /* End of if */ #endif /* CONFIG_AP_SUPPORT */ #ifdef CONFIG_STA_SUPPORT if (ACMR_IS_STA_MODE(pAd)) { if (pEdcaParam->AcmOutTime[AcId] >= TimeOldUp) pEdcaParam->AcmOutTime[AcId] -= TimeOldUp; /* End of if */ } /* End of if */ #endif /* CONFIG_STA_SUPPORT */ else { pEdcaParam->AcmOutTime[AcId] = 0; /* fatal error */ ACMR_DEBUG(ACMR_DEBUG_ERR, ("acm_err> Used ACM time < stream medium time! " "EDCA_Param_ACM_Update()\n")); } /* End of if */ /* update the ACM used time of the AC */ #ifdef CONFIG_AP_SUPPORT if (ACMR_IS_AP_MODE(pAd)) { if (pEdcaParam->AcmAcTime[AcId] >= (TimeOldDn + TimeOldUp)) pEdcaParam->AcmAcTime[AcId] -= (TimeOldDn + TimeOldUp); /* End of if */ } /* End of if */ #endif /* CONFIG_AP_SUPPORT */ #ifdef CONFIG_STA_SUPPORT if (ACMR_IS_STA_MODE(pAd)) { if (pEdcaParam->AcmAcTime[AcId] >= TimeOldUp) pEdcaParam->AcmAcTime[AcId] -= TimeOldUp; /* End of if */ } /* End of if */ #endif /* CONFIG_STA_SUPPORT */ else { pEdcaParam->AcmAcTime[AcId] = 0; /* fatal error */ ACMR_DEBUG(ACMR_DEBUG_ERR, ("acm_err> Used AC ACM time < stream medium time! " "EDCA_Param_ACM_Update()\n")); } /* End of if */ #ifdef RELEASE_EXCLUDE /* dynamic ATL: update DATL time only in QAP */ if (pEdcaParam->FlgDatl) { ACM_DATL_Update(pAd, AcId, AcmTimeOld, AcmTimeNew, VbAcId, VbBandwidth); } /* End of if */ #endif /* RELEASE_EXCLUDE */ /* update available ACM time */ TimeUsed = pEdcaParam->AcmTotalTime; #ifdef ACM_CC_FUNC_MBSS TimeUsed += ACMR_CB->MbssTotalUsedTime; #endif /* ACM_CC_FUNC_MBSS */ #ifdef CONFIG_AP_SUPPORT if (ACMR_IS_AP_MODE(pAd)) { ACMR_AVAIL_ACM_TIME_UPDATE(pAd, TimeUsed); } /* End of if */ #endif /* CONFIG_AP_SUPPORT */ } /* End of ACM_EDCA_Param_ACM_Update */ /* ====================== Private Function (WMM) =========================== */ #ifdef ACM_CC_FUNC_WMM /* ======================================================================== Routine Description: Translate 11e status code to WME status code. Arguments: StatusCode - 11e status code Return Value: WME status code Note: Only 3 status code for WMM ACM. WLAN_STATUS_CODE_WME_INVALID_PARAM - invalid TSPEC parameters WLAN_STATUS_CODE_WME_ACM_ACCEPTED - accept WLAN_STATUS_CODE_WME_REFUSED - refuse due to insufficient BW ======================================================================== */ STATIC UCHAR ACM_11E_WMM_StatusTranslate( ACM_PARAM_IN UCHAR StatusCode) { ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> 11e status code = %d\n", StatusCode)); if (StatusCode == ACM_STATUS_CODE_INVALID_PARAMETERS) return WLAN_STATUS_CODE_WME_INVALID_PARAM; /* End of if */ if (StatusCode == ACM_STATUS_CODE_SUCCESS) return WLAN_STATUS_CODE_WME_ACM_ACCEPTED; /* End of if */ return WLAN_STATUS_CODE_WME_REFUSED; } /* End of ACM_11E_WMM_StatusTranslate */ /* ======================================================================== Routine Description: Translate WME TSPEC & TCLAS to 11e TSPEC & TCLAS. Arguments: *pPktElm - the TSPEC related element in the packet buffer BodyLen - the action frame length *pETspec - the 11e TSPEC *pTclas - the 11e TCLAS *pTclasNum - the number of TCLAS *pTclasProcessing - the TCLAS PROCESSING value Return Value: ACM_RTN_OK - translate ok ACM_RTN_FAIL - translate fail Note: Internally we use 11e TSPEC, not WMM TSPEC. ======================================================================== */ STATIC ACM_FUNC_STATUS ACM_WME_11E_TSPEC_TCLAS_Translate( ACM_PARAM_IN UCHAR *pPktElm, ACM_PARAM_IN UINT32 BodyLen, ACM_PARAM_IN ACM_TSPEC *pETspec, ACM_PARAM_IN ACM_TCLAS **pTclas, ACM_PARAM_IN UINT32 *pTclasNum, ACM_PARAM_IN UCHAR *pTclasProcessing) { ACM_WME_TSPEC *pTspec; ACM_ELM_WME_TCLAS *pElmTclas; ACM_ELM_WME_TCLAS_PROCESSING *pElmTclasProcessing; UCHAR *pElm; UCHAR ElmID, ElmLen, ElmSubID; UCHAR TclasType; UINT32 IdTclasNum; /* init */ pTspec = NULL; for(IdTclasNum=0; IdTclasNum 0) { ElmID = *pElm; ElmLen = *(pElm+1); if (BodyLen < (UINT32)(ACM_ELM_ID_LEN_SIZE+ElmLen)) { /* fatal error, packet size is not enough */ ACMR_DEBUG(ACMR_DEBUG_ERR, ("acm_err> packet length %d is too small %d! " "WME_11E_TSPEC_TCLAS_Translate()\n", BodyLen, (ACM_ELM_ID_LEN_SIZE+ElmLen))); goto LabelParseErr; } /* End of if */ /* not check *(pElm+1) = element length and not check *(pElm+6) = WMM sub element ID */ if ((ElmID != ACM_ELM_WME_ID) || (*(pElm+2) != ACM_WME_OUI_0) || (*(pElm+3) != ACM_WME_OUI_1) || (*(pElm+4) != ACM_WME_OUI_2) || (*(pElm+5) != ACM_WME_OUI_TYPE) || (*(pElm+7) != ACM_WME_OUI_VERSION)) { /* not WMM element so check next element */ pElm += (ACM_ELM_ID_LEN_SIZE+ElmLen); BodyLen -= (ACM_ELM_ID_LEN_SIZE+ElmLen); continue; } /* End of if */ ElmSubID = *(pElm+ACM_WME_OUI_ID_OFFSET); switch(ElmSubID) { case ACM_WME_OUI_SUBTYPE_TSPEC: /* TSPEC element */ ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> find a WMM TSPEC element! " "WME_11E_TSPEC_TCLAS_Translate()\n")); pTspec = &((ACM_ELM_WME_TSPEC *)pElm)->Tspec; if (ACM_WME_11E_TSPEC_Translate(pTspec, pETspec) != ACM_RTN_OK) { goto LabelParseErr; } /* End of if */ break; case ACM_WSM_OUI_SUBTYPE_TCLAS: /* TCLASS element */ ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> find a WMM TCLAS element! " "WME_11E_TSPEC_TCLAS_Translate()\n")); /* sanity check for TCLAS number & element length */ if ((*pTclasNum) >= ACM_TCLAS_MAX_NUM) goto LabelParseErr; /* End of if */ /* skip element id/len, OUI header, user priority */ TclasType = *(pElm+2+ACM_WME_OUI_HDR_LEN+1); switch(TclasType) { case ACM_TCLAS_TYPE_ETHERNET: if (ElmLen != ACM_TCLAS_TYPE_WME_ETHERNET_LEN) goto LabelParseErr; /* End of if */ break; case ACM_TCLAS_TYPE_IP_V4: if (ElmLen != ACM_TCLAS_TYPE_WME_IP_V4_LEN) goto LabelParseErr; /* End of if */ break; case ACM_TCLAS_TYPE_8021DQ: if (ElmLen != ACM_TCLAS_TYPE_WME_8021DQ_LEN) goto LabelParseErr; /* End of if */ break; default: goto LabelParseErr; } /* End of switch */ pElmTclas = (ACM_ELM_WME_TCLAS *)pElm; pTclas[(*pTclasNum)++] = &pElmTclas->Tclas; break; case ACM_WSM_OUI_SUBTYPE_TCLAS_PROCESSING: /* TCLASS Processing */ if (ElmLen != ACM_ELM_WME_TCLAS_PROCESSING_LEN) goto LabelParseErr; /* End of if */ ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> find a WMM TCLAS PROCESSING element! " "WME_11E_TSPEC_TCLAS_Translate()\n")); pElmTclasProcessing = (ACM_ELM_WME_TCLAS_PROCESSING *)pElm; *pTclasProcessing = pElmTclasProcessing->Processing; break; } /* End of switch */ /* check next element */ pElm += (2+ElmLen); /* 2: Element ID & Element Length */ BodyLen -= (2+ElmLen); /* 2: Element ID & Element Length */ } /* End of while */ return ACM_RTN_OK; LabelParseErr: return ACM_RTN_FAIL; } /* End of ACM_WME_11E_TSPEC_TCLAS_Translate */ /* ======================================================================== Routine Description: Translate WME TSPEC to 11e TSPEC. Arguments: *pWTspec - the 'W'ME TSPEC *pETspec - the 11'e' TSPEC Return Value: ACM_RTN_OK - translate ok ACM_RTN_FAIL - translate fail Note: ======================================================================== */ STATIC ACM_FUNC_STATUS ACM_WME_11E_TSPEC_Translate( ACM_PARAM_IN ACM_WME_TSPEC *pWTspec, ACM_PARAM_IN ACM_TSPEC *pETspec) { ACM_TS_INFO *pInfo; /* init */ pInfo = &pETspec->TsInfo; /* translate WMM TSPEC to 11e TSPEC */ ACMR_MEM_ZERO((UCHAR *)pETspec, sizeof(ACM_TSPEC)); /* init TS Info field */ pInfo->TrafficType = pWTspec->TsInfo.Reserved4; pInfo->TSID = pWTspec->TsInfo.TID; pInfo->Direction = pWTspec->TsInfo.Direction; pInfo->AccessPolicy = pWTspec->TsInfo.One; pInfo->Aggregation = pWTspec->TsInfo.Zero1; pInfo->APSD = pWTspec->TsInfo.PSB; pInfo->UP = pWTspec->TsInfo.UP; #ifdef ACM_CC_FUNC_11N_AGG pInfo->AckPolicy = pWTspec->TsInfo.AckPolicy; #endif /* ACM_CC_FUNC_11N_AGG */ /* in WMM ACM TG, we need to check bit16 ~ 23 and bit8 == 0, and we will not use schedule field, so we set (bit16 ~ 23 | bit8) to the field */ pInfo->Schedule = pWTspec->TsInfo.Reserved1 | pWTspec->TsInfo.Reserved3; /* init TSPEC parameters */ pETspec->NominalMsduSize = pWTspec->NominalMsduSize; pETspec->MaxMsduSize = pWTspec->MaxMsduSize; pETspec->MinServInt = pWTspec->MinServInt; pETspec->MaxServInt = pWTspec->MaxServInt; if (pWTspec->InactivityInt == 0) { /* can not be 0 so use default timeout */ pETspec->InactivityInt = ACM_WME_TSPEC_INACTIVITY_DEFAULT; } else pETspec->InactivityInt = pWTspec->InactivityInt; /* End of if */ pETspec->SuspensionInt = pWTspec->SuspensionInt; pETspec->ServiceStartTime = pWTspec->ServiceStartTime; pETspec->MinDataRate = pWTspec->MinDataRate; pETspec->MeanDataRate = pWTspec->MeanDataRate; pETspec->PeakDataRate = pWTspec->PeakDataRate; /* if you want to issue NULL TSPEC, Min = Mean = Peak = 0 */ if (pETspec->MeanDataRate == 0) { if (pETspec->PeakDataRate != 0) pETspec->MeanDataRate = pETspec->PeakDataRate; else { if (pETspec->MinDataRate != 0) pETspec->MeanDataRate = pETspec->MinDataRate; /* End of if */ } /* End of if */ } /* End of if */ pETspec->MaxBurstSize = pWTspec->MaxBurstSize; pETspec->DelayBound = pWTspec->DelayBound; pETspec->MinPhyRate = pWTspec->MinPhyRate; pETspec->SurplusBandwidthAllowance = \ pWTspec->SurplusBandwidthAllowance; pETspec->MediumTime = pWTspec->MediumTime; return ACM_RTN_OK; } /* End of ACM_WME_11E_TSPEC_Translate */ /* ======================================================================== Routine Description: Make a WME action frame body. Arguments: pAd - WLAN control block pointer *pStream - the stream *pPkt - the frame body pointer Action - action StatusCode - status code, used when action = response Return Value: ACM_RTN_OK - insert ok ACM_RTN_FAIL - insert fail Note: ======================================================================== */ STATIC UINT32 ACM_WME_ActionFrameBodyMake( ACM_PARAM_IN PRTMP_ADAPTER pAd, ACM_PARAM_IN ACM_STREAM *pStream, ACM_PARAM_IN UCHAR *pPkt, ACM_PARAM_IN UCHAR Action, ACM_PARAM_IN UCHAR StatusCode) { ACM_WME_NOT_FRAME *pFrameBody; ACM_ELM_WME_TSPEC *pElmTspec; ACM_ELM_WME_TCLAS_PROCESSING *pElmTspecProcessing; ACM_WME_TS_INFO *pInfo; ACM_WME_TSPEC *pTspec; UCHAR *pElmTclas; UINT32 BodyLen, Len; UINT32 IdTclasNum; /* sanity check for type */ if (Action > ACM_ACTION_WME_TEAR_DOWN) return 0; /* End of if */ /* init frame body */ pFrameBody = (ACM_WME_NOT_FRAME *)pPkt; pFrameBody->Category = ACM_CATEGORY_WME; pFrameBody->Action = Action; if (Action != ACM_ACTION_WME_TEAR_DOWN) pFrameBody->DialogToken = pStream->DialogToken; else pFrameBody->DialogToken = 0; /* always 0 for DELTS */ /* End of if */ pFrameBody->StatusCode = StatusCode; BodyLen = 4; /* TSPEC element */ pElmTspec = &pFrameBody->ElmTspec; pElmTspec->ElementId = ACM_ELM_WME_ID; pElmTspec->Length = ACM_ELM_WME_TSPEC_LEN; /* init OUI field */ pElmTspec->OUI[0] = ACM_WME_OUI_0; pElmTspec->OUI[1] = ACM_WME_OUI_1; pElmTspec->OUI[2] = ACM_WME_OUI_2; pElmTspec->OUI_Type = ACM_WME_OUI_TYPE; pElmTspec->OUI_SubType = ACM_WME_OUI_SUBTYPE_TSPEC; pElmTspec->Version = ACM_WME_OUI_VERSION; /* init TS Info field */ pTspec = &pFrameBody->ElmTspec.Tspec; ACMR_MEM_ZERO(pTspec, sizeof(ACM_WME_TSPEC)); pInfo = &pFrameBody->ElmTspec.Tspec.TsInfo; pInfo->TID = pStream->pTspec->TsInfo.TSID; pInfo->Direction = pStream->pTspec->TsInfo.Direction; pInfo->UP = pStream->pTspec->TsInfo.UP; pInfo->PSB = pStream->pTspec->TsInfo.APSD; pInfo->One = 1; /* always 1 */ #ifdef ACM_CC_FUNC_11N_AGG pInfo->AckPolicy = pStream->pTspec->TsInfo.AckPolicy; #endif /* ACM_CC_FUNC_11N_AGG */ /* init TSPEC parameters */ pTspec->NominalMsduSize = pStream->pTspec->NominalMsduSize; pTspec->MaxMsduSize = pStream->pTspec->MaxMsduSize; pTspec->MinServInt = pStream->pTspec->MinServInt; pTspec->MaxServInt = pStream->pTspec->MaxServInt; pTspec->InactivityInt = pStream->pTspec->InactivityInt; pTspec->SuspensionInt = pStream->pTspec->SuspensionInt; pTspec->ServiceStartTime = pStream->pTspec->ServiceStartTime; pTspec->MinDataRate = pStream->pTspec->MinDataRate; pTspec->MeanDataRate = pStream->pTspec->MeanDataRate; pTspec->PeakDataRate = pStream->pTspec->PeakDataRate; pTspec->MaxBurstSize = pStream->pTspec->MaxBurstSize; pTspec->DelayBound = pStream->pTspec->DelayBound; pTspec->MinPhyRate = pStream->pTspec->MinPhyRate; pTspec->SurplusBandwidthAllowance = \ pStream->pTspec->SurplusBandwidthAllowance; if (pTspec->TsInfo.Direction != ACM_DIRECTION_DOWN_LINK) { /* we need to fill medium time if the link is not downlink-only */ pTspec->MediumTime = pStream->pTspec->MediumTime; } /* End of if */ BodyLen += (ACM_ELM_ID_LEN_SIZE+pElmTspec->Length); /* TCLASS element */ pElmTclas = pFrameBody->Tclas; for(IdTclasNum=0; IdTclasNumpTclas[IdTclasNum] != NULL) { *pElmTclas++ = ACM_ELM_WME_ID; Len = ACM_TCLAS_LEN_GET(pStream->pTclas[IdTclasNum]->ClassifierType); *pElmTclas++ = Len; *pElmTclas++ = ACM_WME_OUI_0; *pElmTclas++ = ACM_WME_OUI_1; *pElmTclas++ = ACM_WME_OUI_2; *pElmTclas++ = ACM_WME_OUI_TYPE; *pElmTclas++ = ACM_WSM_OUI_SUBTYPE_TCLAS; *pElmTclas++ = ACM_WME_OUI_VERSION; ACMR_MEM_COPY(pElmTclas, pStream->pTclas[IdTclasNum], Len-ACM_WME_OUI_HDR_LEN); pElmTclas += (Len-ACM_WME_OUI_HDR_LEN); BodyLen += (ACM_ELM_ID_LEN_SIZE+Len); continue; /* check next TCLAS */ } /* End of if */ break; /* no more TCLAS exists */ } /* End of for */ /* TCLASS Processing element */ if (pStream->pTclas[0] != NULL) { /* TCLAS PROCESSING element exists only when at least one TCLAS element exists. */ if (pStream->TclasProcessing != ACM_TCLAS_PROCESSING_NOT_EXIST) { pElmTspecProcessing = (ACM_ELM_WME_TCLAS_PROCESSING *)pElmTclas; BodyLen += (ACM_ELM_ID_LEN_SIZE+ACM_ELM_WME_TCLAS_PROCESSING_LEN); pElmTspecProcessing->ElementId = ACM_ELM_WME_ID; pElmTspecProcessing->Length = ACM_ELM_WME_TCLAS_PROCESSING_LEN; pElmTspecProcessing->OUI[0] = ACM_WME_OUI_0; pElmTspecProcessing->OUI[1] = ACM_WME_OUI_1; pElmTspecProcessing->OUI[2] = ACM_WME_OUI_2; pElmTspecProcessing->OUI_Type = ACM_WME_OUI_TYPE; pElmTspecProcessing->OUI_SubType = ACM_WSM_OUI_SUBTYPE_TCLAS_PROCESSING; pElmTspecProcessing->Version = ACM_WME_OUI_VERSION; pElmTspecProcessing->Processing = pStream->TclasProcessing; } /* End of if */ } /* End of if */ return BodyLen; } /* End of ACM_WME_ActionFrameBodyMake */ /* ======================================================================== Routine Description: Handle a WME action frame. Arguments: pAd - WLAN control block pointer *pCdb - the source QSTA *pFrameBody - the action frame body BodyLen - the length of action frame body PhyRate - the physical tx rate for the frame, bps Action - Setup request, response, or teardown *pStatusCode - response status code *pMediumTime - the allowed medium time Return Value: None Note: ======================================================================== */ STATIC VOID ACM_WME_ActionHandle( ACM_PARAM_IN PRTMP_ADAPTER pAd, ACM_PARAM_IN ACMR_STA_DB *pCdb, ACM_PARAM_IN UCHAR *pFrameBody, ACM_PARAM_IN UINT32 BodyLen, ACM_PARAM_IN UINT32 PhyRate, ACM_PARAM_IN UCHAR Action, ACM_PARAM_OUT UCHAR *pStatusCode, ACM_PARAM_OUT UINT16 *pMediumTime) { ACM_WME_NOT_FRAME *pNotFrame; ACM_TCLAS *pTclas[ACM_TSPEC_TCLAS_MAX_NUM]; ACM_TSPEC Tspec; UINT32 TclasNum; UCHAR TclasProcessing; UCHAR StatusCode; ACM_FUNC_STATUS RtnCode; /* init */ pNotFrame = (ACM_WME_NOT_FRAME *)pFrameBody; TclasNum = 0; TclasProcessing = ACM_TCLAS_PROCESSING_NOT_EXIST; StatusCode = ACM_STATUS_CODE_SUCCESS; /* sanity check for input parameters */ if (Action > ACM_ACTION_WME_TEAR_DOWN) { ACMR_DEBUG(ACMR_DEBUG_ERR, ("acm_err> Error action type = %d! " "WME_ActionHandle()\n", Action)); return; } /* End of if */ if (ACM_WME_ELM_Check((UCHAR *)&pNotFrame->ElmTspec, ACM_WME_OUI_SUBTYPE_TSPEC) != ACM_RTN_OK) { ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Element check error! " "WME_ActionHandle()\n")); return; /* TSPEC element error */ } /* End of if */ if (BodyLen < ACM_NOT_FRAME_BODY_LEN) { ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Frame length is not enough! " "WME_ActionHandle()\n")); return; /* error! < minimum action frame length */ } /* End of if */ /* translate WME TSPEC to 11e TSPEC */ if (ACM_WME_11E_TSPEC_TCLAS_Translate( (UCHAR *)&pNotFrame->ElmTspec, BodyLen, &Tspec, pTclas, &TclasNum, &TclasProcessing) != ACM_RTN_OK) { ACMR_DEBUG(ACMR_DEBUG_ERR, ("acm_err> Translate TSPEC fail! " "WME_ActionHandle()\n")); return; /* translate fail */ } /* End of if */ /* handle it by action */ switch(Action) { #ifdef CONFIG_AP_SUPPORT case ACM_ACTION_WME_SETUP_REQ: ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> A WME Request Frame is RCV! " "WME_ActionHandle()\n")); RtnCode = ACM_TC_ReqHandle( pAd, pCdb, ACM_STREAM_TYPE_WIFI, pNotFrame->DialogToken, &Tspec, TclasNum, pTclas, TclasProcessing, PhyRate, &StatusCode, pMediumTime); if (RtnCode != ACM_RTN_OK) { ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> A WME Setup request is not allowed %d! " "WME_ActionHandle()\n", RtnCode)); } /* End of if */ break; #endif /* CONFIG_AP_SUPPORT */ #ifdef CONFIG_STA_SUPPORT case ACM_ACTION_WME_SETUP_RSP: RtnCode = ACM_TC_RspHandle( pAd, pCdb, pNotFrame->DialogToken, pNotFrame->StatusCode, &Tspec, NULL, &StatusCode); if (RtnCode != ACM_RTN_OK) { ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> A WME Setup response is error %d! " "WME_ActionHandle()\n", RtnCode)); } /* End of if */ break; #endif /* CONFIG_STA_SUPPORT */ case ACM_ACTION_WME_TEAR_DOWN: ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> A WME Tear down is RCV! " "DEL the stream! WME_ActionHandle()\n")); ACMP_TC_DestroyBy_TS_Info( pAd, ACMR_CLIENT_MAC(pCdb), &Tspec.TsInfo, ACMR_IS_AP_MODE(pAd)); break; default: /* should not be here */ break; } /* End of switch */ /* upadte status code */ *pStatusCode = StatusCode; } /* End of ACM_WME_ActionHandle */ /* ====================== Private Function (WMM) (AP) ====================== */ #ifdef CONFIG_AP_SUPPORT #if 0 /* no use */ /* ======================================================================== Routine Description: Send a WME Setup Response frame to the QSTA. Arguments: pAd - WLAN control block pointer *pReqNew - the requested TSPEC pointer StatusCode - response status Return Value: ACM_RTN_OK - send ok ACM_RTN_FAIL - send fail ACM_RTN_ALLOC_ERR - allocate a frame fail Note: 1. Use high priority queue to send. 2. Only for QAP mode. ======================================================================== */ STATIC ACM_FUNC_STATUS EACM_WME_SETUP_RspSend( ACM_PARAM_IN PRTMP_ADAPTER pAd, ACM_PARAM_IN ACM_STREAM *pReqNew, ACM_PARAM_IN UCHAR StatusCode) { ACMR_WLAN_HEADER HdrAction; NDIS_STATUS Status; ULONG FrameLen; UCHAR *pBufFrame; /* init */ FrameLen = 0; pBufFrame = NULL; /* get an unused nonpaged memory */ Status = MlmeAllocateMemory(pAd, &pBufFrame); if (Status != NDIS_STATUS_SUCCESS) return ACM_RTN_ALLOC_ERR; /* End of if */ ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Make up a ADDTS response...\n")); /* make the frame header */ MgtMacHeaderInit( pAd, &HdrAction, SUBTYPE_ACTION, 0, ACMR_CLIENT_MAC(pReqNew->pCdb), pAd->ApCfg.MBSSID[BSS0].Bssid); MakeOutgoingFrame( pBufFrame, &FrameLen, sizeof(ACMR_WLAN_HEADER), &HdrAction, END_OF_ARGS); /* make the frame body */ FrameLen += ACM_WME_ActionFrameBodyMake( pAd, pReqNew, (UCHAR *)&pBufFrame[FrameLen], ACM_ACTION_WME_SETUP_RSP, StatusCode); /* send out the frame */ MiniportMMRequest(pAd, 0, pBufFrame, FrameLen); MlmeFreeMemory(pAd, pBufFrame); return ACM_RTN_OK; } /* End of EACM_WME_SETUP_RspSend */ #endif /* #if 0 */ #endif /* CONFIG_AP_SUPPORT */ #endif /* ACM_CC_FUNC_WMM */ #endif /* WMM_ACM_SUPPORT */ /* End of acm_edca.c */