/******************************************************************************* ################################################################################ # Copyright (c) [2017-2019] [Radisys] # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http://www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ################################################################################ *******************************************************************************/ /************************************************************************ Name: LTE-MAC layer Type: C source file Desc: C source code for DRX realted functions File: rg_sch_drx.c **********************************************************************/ /** @file rg_sch_drx.c @brief This file implements the DRX processing . */ static const char* RLOG_MODULE_NAME="MAC"; static int RLOG_MODULE_ID=4096; static int RLOG_FILE_ID=163; /* header include files -- defines (.h) */ #include "common_def.h" #include "lrg.h" #include "rgr.h" #include "rgm.h" #include "tfu.h" #include "rg_env.h" #include "rg_sch_inf.h" #ifdef LTEMAC_PH3_HDFDD #include "rg_sch_hdfdd.h" #endif /*LTEMAC_PH3_HDFDD*/ #include "rg_sch.h" #include "rg_sch_err.h" #include "rg_sch_cmn.h" #include "rl_interface.h" #include "rl_common.h" /* header/extern include files (.x) */ #include "tfu.x" /* TFU types */ #include "lrg.x" /* layer management typedefs for MAC */ #include "rgr.x" /* layer management typedefs for MAC */ #include "rgm.x" /* layer management typedefs for MAC */ #include "rg_sch_inf.x" /* typedefs for Scheduler */ #ifdef LTEMAC_PH3_HDFDD #include "rg_sch_hdfdd.x" #endif /*LTEMAC_PH3_HDFDD*/ #include "rg_sch.x" /* typedefs for Scheduler */ #include "rg_sch_cmn.x" /** * @file rg_sch_drx.c This file gives the describes the design for DRX feature. * * DRX is Discontinuous Reception i.e. the UE in order to save some battery * would not monitor (decode) PDCCHs at all times. Instead the UE is configured * with a periodically occuring duration wherein it shall monitor PDCCHs. The UE * can be called ACTIVE at this time, this time is a minimum of a configurable * value - onDuration and a maximum of Infinity. * * ACTIVE time MIN (onDuration) MAX (infinity) * * A sample configuration is periodicity of 10 subframes (ms) and an offset * value of 3. This can be represented as the diagram given below. The portion * marked as ACTIVE is the onDuration and the UE monitors PDCCHs. * * @code * * <-ACTIVE-><---IN-ACTIVE------><-ACTIVE-><---IN-ACTIVE-- * * |__|__|__|--------|__|__|__|__|__|__|__|--------|__|__|__|__|__| * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 * * @endcode */ #ifdef LTE_TDD /****************************************************************************** * Structure definitions for TDD * ******************************************************************************/ /** @brief : No of DL subframes in a particular TDD configuration * * @details : Special subframes in TDD can carry PDCCH if configured * for DwPTS. For normal CP with subframe configruation (0-8) * & extended CP with subframe configuration (0-6), Special * Subframes can carry PDCCH and are represented in row 0. * Extended CP with subframe configuraton (7-8), which do * not carry PDCCH are represented in row 1. * rgSchDrxDlSfTddCfg[1][2] represent number of DL subframes * in TDD config 2 where DwPTS can carry PDCCH and * rgSchDrxDlSfTddCfg[0][2] represent number of DL subframes * in TDD config 2 where no DwPTS exits. */ PRIVATE uint8_t rgSchDrxDlSfTddCfg[RGSCH_MAX_SFCFG][RGSCH_MAX_TDD_CFG] = { {2,4,6,6,7,8,3}, {4,6,8,7,8,9,5} }; /** @brief : No of DL subframes till next SFN from a particular subframe * * @details :For a given Special subframe config * (refer rgSchDrxDlSfTddCfg for an explanation) and given * TDD config, how many DL subframes till Next SFN from this * subframe onwards. * */ PRIVATE uint8_t rgSchDrxDLSfTillNxtSFN[RGSCH_MAX_SFCFG][RGSCH_MAX_TDD_CFG] [RGSCH_NUM_SUB_FRAMES]= { { {2,1,1,1,1,1,0,0,0,0}, {4,3,3,3,3,2,1,1,1,1}, {6,5,5,5,4,3,2,2,2,1}, {6,5,5,5,5,5,4,3,2,1}, {7,6,6,6,6,5,4,3,2,1}, {8,7,7,7,6,5,4,3,2,1}, {3,2,2,2,2,2,1,1,1,1} }, { {4,3,2,2,2,2,1,0,0,0}, {6,5,4,4,4,3,2,1,1,1}, {8,7,6,6,5,4,3,2,1,1}, {7,6,5,5,5,5,4,3,2,1}, {8,7,6,6,6,5,4,3,2,1}, {9,8,7,7,6,5,4,3,2,1}, {5,4,3,3,3,3,2,1,1,1} } }; /*rgSchDrxDLSfTillNxtSFN*/ /** @brief : Lookup table for DL subframe given the number of subframes * * @details :For a given Special subframe config * (refer rgSchDrxDlSfTddCfg for an explanation) and given * TDD config, the DL subframe index given the number of subframes * */ PRIVATE uint8_t rgSchDrxDLSftoDLSfIdx[RGSCH_MAX_SFCFG][RGSCH_MAX_TDD_CFG] [RGSCH_NUM_SUB_FRAMES]= { { {5,0}, {9,0,4,5}, {9,0,3,4,5,8}, {9,0,5,6,7,8}, {9,0,4,5,6,7,8}, {9,0,3,4,5,6,7,8}, {9,0,5} }, { {6,0,1,5}, {9,0,1,4,5,6}, {9,0,1,3,4,5,6,8}, {9,0,1,5,6,7,8}, {9,0,1,4,5,6,7,8}, {9,0,1,3,4,5,6,7,8}, {9,0,1,5,6} } };/* rgSchdrxDLSftoDLSfIdx*/ /* ccpu00134196-[Add]-DRX retx timer changes */ /* The k+4 th subframe in TDD at which HARQ RTT expires may be an Uplink SF. In such case, the drx retx timer may start at the next pdcch sf instead of at k+4 itself */ uint8_t rgSchDrxHarqRetxFirstPsf[RGSCH_MAX_TDD_CFG][RGSCH_NUM_SUB_FRAMES] = { {0, 0, 4, 0, 6, 0, 0, 4, 0, 6}, {0, 0, 4, 6, 0, 0, 0, 4, 6, 0}, {0, 0, 4, 0, 0, 0, 0, 4, 0, 0}, {0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, {0, 0, 4, 4, 0, 0, 0, 0, 0, 0}, {0, 0, 4, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, 6, 5, 0, 0, 4, 7, 0}, }; #endif /* LTE_TDD */ /****************************************************************************** * Start of Function declarations * ******************************************************************************/ PRIVATE Void rgSCHDrxTtiHdlOnDurUl ARGS(( RgSchCellCb *cell, uint16_t ulIndex )); PRIVATE Void rgSCHDrxTtiHdlOnDurDl ARGS(( RgSchCellCb *cell, uint16_t dlIndex )); PRIVATE Void rgSCHDrxTtiHdlDlHarqRTT ARGS(( RgSchCellCb *cell, uint16_t dlIndex )); PRIVATE Void rgSCHDrxTtiHdlUlHarqRTT ARGS(( RgSchCellCb *cell, uint16_t ulIndex )); PRIVATE S16 rgSCHDrxTtiHdlOnDur ARGS((RgSchCellCb *cellCb, uint16_t dlIndex, uint16_t ulIndex)); PRIVATE S16 rgSCHDrxTtiHdlInActv ARGS((RgSchCellCb *cellCb, uint16_t dlIndex, uint16_t ulIndex)); PRIVATE S16 rgSCHDrxTtiHdlShortCycle ARGS((RgSchCellCb *cell, uint16_t dlIndex, uint16_t ulIndex)); PRIVATE S16 rgSCHDrxTtiHdlDlHarq ARGS((RgSchCellCb *cellCb, uint16_t dlIndex, uint16_t ulIndex)); PRIVATE S16 rgSCHDrxCpyUeCfg ARGS((RgSchDrxUeCb *drxCb, RgrUeDrxCfg* ueDrxCfg)); PRIVATE S16 rgSCHDrxGetNxtOnDur ARGS((RgSchCellCb* cell,RgSchDrxUeCb* drxCb, CmLteTimingInfo* nxtOnDur, uint8_t delta)); PRIVATE Void rgSCHDrxMvToNxtOnDurOcc ARGS((RgSchCellCb* cell, RgSchUeCb* ue, uint16_t idx, Bool calcFrmOffst)); #ifdef LTE_TDD PRIVATE Void rgSCHDrxCalcNxtTmrExpry ARGS((RgSchCellCb *cell, RgSchUeCb *ue, uint16_t delta, uint32_t tmrLen, S16 *distance, uint16_t *idx )); PRIVATE S16 rgSCHDrxGetNxtTmrExpry ARGS((RgSchCellCb *cell,uint16_t curTime, uint32_t duration, CmLteTimingInfo* tmrExpryIdx)); #endif /* LTE_TDD */ #ifdef EMTC_ENABLE EXTERN S16 rgSCHEmtcDrxCpyUeCfg ( RgSchUeCb *ueCb, RgrUeDrxCfg *drxCfg ); EXTERN S16 rgSCHDrxTtiHdlUlHarq ( RgSchCellCb *cell, uint16_t dlIndex, uint16_t ulIndex ); EXTERN Void rgSCHDrxUeUlHqReset ( RgSchCellCb *cell, RgSchUeCb *ue, RgUeUlHqCb *hqE ); #endif /** @brief This function handles the per TTI handling of the DRX module. * Invoked by rgSCHTti @sa rgSCHTti. * * @details This function goes through the drxQ and marks the UE as ACTIVE or * INACTIVE based on the timers and thier status. * * Function: rgSCHDrxTtiInd * * Processing steps: * - Processing is divided into respective timer handling. * - Calculate the DL and UL indices as follows * @code * dlIndex = (cell->crntTime.sfn * 10 + cell->crntTime.slot + * RG_SCH_DRX_DL_DELTA) % RG_SCH_MAX_DRXQ_SIZE; * * ulIndex = (cell->crntTime.sfn * 10 + cell->crntTime.slot + * RG_SCH_DRX_UL_DELTA) % RG_SCH_MAX_DRXQ_SIZE; * @endcode * - Call rgSCHDrxTtiHdlOnDur to handle onDurationTimer handling * - Call rgSCHDrxTtiHdlInActv to handle drx-InactivityTimer handling * - Call rgSCHDrxTtiHdlShortCycle to handle Short cycle timer. * - Call rgSCHDrxTtiHdlDlHarq to handle HARQ RTT and drx-retransmission * timers. * - Call rgSchDrxTtiHdlUlHarq to handle Uplink HARQ processing - * related to ACTIVITY when a UE is expecting a grant for an uplink * re-transmissions. * - Post this processing the ueCb->dlInactvMask's DRX Bit shall be set * or reset to denote ACTIVITY or INACTIVITY respectively. * - Post this processing the ueCb->ulInactvMask's DRX Bit shall be set * or reset to denote ACTIVITY or INACTIVITY respectively. * - Add the UE to the list of Active/inactive UEs. * * @param RgSchCellCb *cell * @param [out] CmLListCp *dlInactvLst List to which the DL in-active UEs are * added* * @return */ #ifdef ANSI Void rgSCHDrxTtiInd ( RgSchCellCb *cell ) #else Void rgSCHDrxTtiInd (cell) RgSchCellCb *cell; #endif { uint16_t dlIndex; uint16_t ulIndex; dlIndex = (cell->crntTime.sfn * RGSCH_NUM_SUB_FRAMES_5G + cell->crntTime.slot + RG_SCH_DRX_DL_DELTA) % RG_SCH_MAX_DRXQ_SIZE; ulIndex = (cell->crntTime.sfn * RGSCH_NUM_SUB_FRAMES_5G + cell->crntTime.slot + RG_SCH_DRX_UL_DELTA) % RG_SCH_MAX_DRXQ_SIZE; #ifdef LTEMAC_R9 rgSCHDrxTtiHdlDlHarq (cell, dlIndex, ulIndex); /* checks the Ul-Retransmission timer */ #ifdef EMTC_ENABLE if(cell->emtcEnable) { rgSCHDrxTtiHdlUlHarq (cell, dlIndex, ulIndex); } #endif rgSCHDrxTtiHdlInActv(cell, dlIndex, ulIndex); /*Process Short cycle expiry before On duration timer so that long cycles * On Duration can be processed if timer has expired*/ rgSCHDrxTtiHdlShortCycle (cell, dlIndex, ulIndex); rgSCHDrxTtiHdlOnDur(cell, dlIndex, ulIndex); #else /*LTEMAC_R9*/ rgSCHDrxTtiHdlOnDur(cell, dlIndex, ulIndex); rgSCHDrxTtiHdlDlHarq (cell, dlIndex, ulIndex); /* checks the Ul-Retransmission timer */ #ifdef EMTC_ENABLE if(cell->emtcEnable) { rgSCHDrxTtiHdlUlHarq (cell, dlIndex, ulIndex); } #endif rgSCHDrxTtiHdlInActv(cell, dlIndex, ulIndex); rgSCHDrxTtiHdlShortCycle (cell, dlIndex, ulIndex); #endif /*LTEMAC_R9*/ return; }/*rgSCHDrxTtiInd*/ /** @brief This function is called to handle onDurationTimer per TTI processing. * * @details * Invoked by - rgSCHDrxTtiInd * * Function: rgSCHDrxTtiHdlOnDur * * Processing steps: * * - OnDurationTimer is handled using the drxQ @sa RgSchDrxQ * * - For Downlink we shall look at an index that is * n + RG_SCH_DRX_DL_DELTA. * * * - For Uplink we shall look at an index that is * n + RG_SCH_DRX_UL_DELTA as * we are concerned with the PDCCH and not the actual PUSCH. * * * @param RgSchCellCb *cellCb * @param uint16_t dlIndex * @param uint16_t ulIndex * @return ROK/RFAILED */ #ifdef ANSI PRIVATE S16 rgSCHDrxTtiHdlOnDur ( RgSchCellCb* cell, uint16_t dlIndex, uint16_t ulIndex ) #else PRIVATE S16 rgSCHDrxTtiHdlOnDur(cell, dlIndex, ulIndex) RgSchCellCb* cell; uint16_t dlIndex; uint16_t ulIndex; #endif { #if ( ERRCLASS & ERRCLS_INT_PAR ) if ( cell == (RgSchCellCb* )NULLP ) { return RFAILED; } #endif rgSCHDrxTtiHdlOnDurDl(cell,dlIndex); rgSCHDrxTtiHdlOnDurUl(cell, ulIndex); return ROK; }/*rgSCHDrxTtiHdlOnDur*/ /** @brief This function handles the processing for drxInactityTimer per TTI * * @details * Invoked by - rgSCHDrxTtiInd * * Function: rgSCHDrxTtiHdlInActv * * Processing steps: * * - For Downlink we shall look at an index that is * n + RG_SCH_DRX_DL_DELTA of the drxQ. * * - MARK UE AS INACTIVE BASED ON DRX-INACTIVITY TIMER EXPIRY * * * - For Uplink we shall look at an index that is * n + RG_SCH_DRX_UL_DELTA as * we are concerned with the PDCCH and not the actual PUSCH. * * * @param RgSchCellCb *cellCb * @param uint16_t dlIndex * @param uint16_t ulIndex * @return ROK/RFAILED */ #ifdef ANSI S16 rgSCHDrxTtiHdlInActv ( RgSchCellCb *cell, uint16_t dlIndex, uint16_t ulIndex ) #else S16 rgSCHDrxTtiHdlInActv(cell, dlIndex, ulIndex) RgSchCellCb *cell; uint16_t dlIndex; uint16_t ulIndex; #endif { CmLList *node; RgSchDRXCellCb *drxCell=NULLP; RgSchUeCb *ue=NULLP; RgSchDrxUeCb *drxUe=NULLP; uint16_t shrtCycleExpIndx; CmLListCp dlInactvLst; /* list of UE's becoming DL-inactive */ CmLListCp ulInactvLst; /* list of UE's becoming UL-inactive */ RgSchCmnCell *cellSch = NULLP; Bool delInUlScan = FALSE; #if ( ERRCLASS & ERRCLS_INT_PAR ) if ( cell == (RgSchCellCb* )NULLP) { return RFAILED; } #endif drxCell = (cell->drxCb); delInUlScan = drxCell->delInUlScan; /*********************************************************** * Scanning inActvitiyQ in DL ***********************************************************/ /* The DL loop will scan for UE's whose inactivity timer has * expired. It will switch the cycle to short or long based * on the cycle configured. * Furhter,if !delInUlScan, then will remove the UE from the * inactivity q. */ node = drxCell->drxQ[dlIndex].inActvTmrQ.first; /* Initialize DL inactive list */ cmLListInit(&dlInactvLst); /* Initialize UL inactive list */ cmLListInit(&ulInactvLst); while (node) { ue = (RgSchUeCb*)node->node; node = node->next; drxUe = RG_SCH_DRX_GET_UE(ue); if ( delInUlScan == TRUE) { drxUe->drxInactDistance--; } if ( drxUe->drxInactDistance != DRX_TMR_EXPRD ) { continue; } /* UE is inactive as inactivity timer has expired */ drxUe->drxDlInactvMask |= RG_SCH_DRX_INACTVTMR_BITMASK; /* update the ue mask only if no condition in drx * is keeping ue active */ if ( !RG_SCH_DRX_DL_IS_UE_ACTIVE(drxUe)) { /* set the UE has DRX inactive */ ue->dl.dlInactvMask |= RG_DRX_INACTIVE; /* Add to DL inactive list */ cmLListAdd2Tail(&dlInactvLst,&(ue->dlDrxInactvLnk)); ue->dlDrxInactvLnk.node = (PTR)ue; } /*Remove from the queue if !delInUlScan */ if( delInUlScan == FALSE ) { cmLListDelFrm(&(drxCell->drxQ[dlIndex].inActvTmrQ), &(drxUe->inActvTmrEnt)); drxUe->drxInactvIndx = DRX_INVALID; }/* if (delInUlScan == FALSE) */ if (drxUe->isShortCycleCfgd) { /* add shorty cycle expirty */ drxUe->isLongCycle = FALSE; shrtCycleExpIndx = (dlIndex + (drxUe->shortCycleTmrLen * drxUe->shortDrxCycle)) % RG_SCH_MAX_DRXQ_SIZE; drxUe->drxShortCycleDistance = (drxUe->shortCycleTmrLen * drxUe->shortDrxCycle) / RG_SCH_MAX_DRXQ_SIZE; /*Remove the UE from existing index*/ if (drxUe->shortCycleIndx != DRX_INVALID) { cmLListDelFrm(&(drxCell->drxQ[drxUe->shortCycleIndx].shortCycleQ), &(drxUe->shortCycleEnt)); } cmLListAdd2Tail(&(drxCell->drxQ[shrtCycleExpIndx].shortCycleQ), &(drxUe->shortCycleEnt)); drxUe->shortCycleEnt.node = (PTR)ue; drxUe->shortCycleIndx = shrtCycleExpIndx; /*Calculate onDuration again & move the * ueCb to the next Onduration occurence */ /*we maybe at any position in the RF timeline, * need to calculate onDuration from the starting * offset */ rgSCHDrxMvToNxtOnDurOcc(cell,ue,RG_SCH_DRX_DL_DELTA,TRUE); }/*isShortCycleCfgd */ else { /* use the long cycle */ drxUe->isLongCycle = TRUE; }/*isLongCycle*/ }/*while(node) */ /*********************************************************** * Scanning inActvitiyQ in UL ***********************************************************/ /* The UL loop will scan for UE's whose inactivity timer has * expired and mark the UE's UL inactive. * Furhter,if delInUlScan, then will remove the UE from the * inactivity q. */ /* For Uplink we shall look at an index that is n + RG_SCH_DRX_UL_DELTA as we are concerned with the PDCCH and not the actual PUSCH.*/ node = drxCell->drxQ[ulIndex].inActvTmrQ.first; while (node) { ue = (RgSchUeCb*)node->node; node = node->next; drxUe = RG_SCH_DRX_GET_UE(ue); if ( delInUlScan == FALSE) { drxUe->drxInactDistance--; } if ( drxUe->drxInactDistance != DRX_TMR_EXPRD ) { continue; } /* Need to mark the UE as inactive due to expiry of * DRX inactivity timer */ /* UE is inactive as inactivity timer has expired */ drxUe->drxUlInactvMask |= RG_SCH_DRX_INACTVTMR_BITMASK; /* update the ue mask only if no other condition in * drx is keeping ue active */ if (!RG_SCH_DRX_UL_IS_UE_ACTIVE(drxUe)) { /* set the inactivity bit */ ue->ul.ulInactvMask |= RG_DRX_INACTIVE; /* Add to Ul inactive list */ cmLListAdd2Tail(&ulInactvLst,&(ue->ulDrxInactvLnk)); ue->ulDrxInactvLnk.node = (PTR)ue; } if ( delInUlScan == TRUE) { /* remove from queue */ cmLListDelFrm(&(drxCell->drxQ[ulIndex].inActvTmrQ), &(drxUe->inActvTmrEnt)); drxUe->drxInactvIndx = DRX_INVALID; }/* if ( delInUlScan == TRUE) */ }/*while(node) for uplink */ /* Send the list to the scheduler to mark UE as inactive in UL*/ cellSch = RG_SCH_CMN_GET_CELL(cell); cellSch->apisUl->rgSCHUlInactvtUes(cell, &ulInactvLst); /* Send the DL inactive list to the scheduler to mark UE as inactive */ cellSch = RG_SCH_CMN_GET_CELL(cell); cellSch->apisDl->rgSCHDlInactvtUes(cell,&dlInactvLst); return ROK; }/*rgSCHDrxTtiHdlInActv*/ /** @brief This function handles the per TTI processing for DRX short cycle * timer. * * @details * Invoked by - rgSCHDrxTtiInd * * Function: rgSCHDrxTtiHdlShortCycle * * Processing steps: * - For downlink * * in addition we have to mark the ues based on drx short cycle * expiry * * * @param RgSchCellCb *cell * @param uint16_t dlIndex * @param uint16_t ulIndex * @return ROK/RFAILED */ #ifdef ANSI S16 rgSCHDrxTtiHdlShortCycle ( RgSchCellCb *cell, uint16_t dlIndex, uint16_t ulIndex ) #else S16 rgSCHDrxTtiHdlShortCycle (cell, dlIndex, ulIndex) RgSchCellCb *cell; uint16_t dlIndex; uint16_t ulIndex; #endif { CmLList *node; RgSchDRXCellCb *drxCell=NULLP; RgSchUeCb *ue=NULLP; RgSchDrxUeCb *drxUe=NULLP; #if ( ERRCLASS & ERRCLS_INT_PAR ) if ( cell == (RgSchCellCb* )NULLP ) { return RFAILED; } #endif UNUSED(ulIndex); drxCell = RG_SCH_DRX_GET_CELL(cell); node = drxCell->drxQ[dlIndex].shortCycleQ.first; while (node) { ue = (RgSchUeCb*)node->node; node = node->next; drxUe = RG_SCH_DRX_GET_UE(ue); if ( --drxUe->drxShortCycleDistance != DRX_TMR_EXPRD) { continue; } /* mark the UE's current cycle to be long */ drxUe->isLongCycle = TRUE; /* remove from the shortCycleQ */ cmLListDelFrm(&(drxCell->drxQ[dlIndex].shortCycleQ), &(drxUe->shortCycleEnt)); drxUe->shortCycleIndx = DRX_INVALID; /* Remove from onDuration queue inserted based on short cycle * and calculate onDuration based on long cycle.*/ rgSCHDrxMvToNxtOnDurOcc(cell,ue,RG_SCH_DRX_DL_DELTA,TRUE); }/*while(node)...*/ return ROK; }/*rgSCHDrxTtiHdlShortCycle*/ /** @brief This function handles the HARQ timer's processing per TTI. * * @details * Invoked by - rgSCHDrxTtiInd * * Function: rgSCHDrxTtiHdlDlHarq * * Processing steps: * - In addition per TTI DRX module must look at Downlink HARQ queues * maintained to track HARQ RTT timer and drx-RetransmissionTimer. * Every TTI at the scheduling index we shall check these queues and * process accordingly. * * * - Though these timers are related to downlink HARQ processing, they * have an impact on uplink scheduling. The reason is that the UE, * if active for downlink scheduling implies that it is reading * PDCCHs i.e. we can still send uplink allocations to the UE. Hence * every TTI Uplink too would look at the harqRTTQ and harqRetxQ. * * * * @param RgSchCellCb *cellCb * @param uint16_t dlIndex * @param uint16_t ulIndex * @return ROK/RFAILED */ #ifdef ANSI PRIVATE S16 rgSCHDrxTtiHdlDlHarq ( RgSchCellCb *cell, uint16_t dlIndex, uint16_t ulIndex ) #else /* ANSI */ PRIVATE S16 rgSCHDrxTtiHdlDlHarq (cell, dlIndex, ulIndex) RgSchCellCb *cell; uint16_t dlIndex; uint16_t ulIndex; #endif /* ANSI */ { #if ( ERRCLASS & ERRCLS_INT_PAR) if ( cell == (RgSchCellCb *)NULLP ) { return RFAILED; } #endif /*ERRCLASS & ERRCLS_INT_PAR*/ rgSCHDrxTtiHdlDlHarqRTT(cell, dlIndex); rgSCHDrxTtiHdlUlHarqRTT(cell, ulIndex); return ROK; } /** @brief This function is called by the common scheduler as part of * finalization of allocations in downlink. * * @details * Invoked by - * * Function: rgSchDrxStrtInActvTmr * * This function is responsible to starting drx-InactivityTimer * * Processing steps: * * * @param RgSchCellCb *cell * @param CmLListCp *ueUlLst * @param uint8_t direction * @return Void */ #ifdef ANSI Void rgSCHDrxStrtInActvTmr ( RgSchCellCb *cell, CmLListCp *ueLst, uint8_t direction ) #else Void rgSCHDrxStrtInActvTmr(cell, ueLst, direction) RgSchCellCb *cell; CmLListCp *ueLst; uint8_t direction; #endif { CmLList *node; CmLList *delNode; RgSchUeCb *ueCb; RgSchDrxUeCb *ueDrxCb; #ifndef LTE_TDD uint16_t index1; #endif uint16_t inActvTmrExpIndx; #ifndef LTE_TDD uint16_t curTimeInSf; /* current time in number of subframes */ #endif #ifdef LTE_TDD uint16_t delta; #endif /*LTE_TDD*/ CmLListCp dlInactvLst; /* list of UE's becoming DL-inactive */ CmLListCp ulInactvLst; /* list of UE's becoming UL-inactive */ RgSchCmnCell *cellSch = NULLP; Bool delInUlScan = FALSE; if ( direction == RG_SCH_DRX_UL) { #ifndef LTE_TDD curTimeInSf = (cell->crntTime.sfn * RGSCH_NUM_SUB_FRAMES_5G) + (cell->crntTime.slot) +RG_SCH_DRX_UL_DELTA; #endif #ifdef LTE_TDD delta = RG_SCH_DRX_UL_DELTA; #endif /*LTE_TDD */ } else { #ifndef LTE_TDD curTimeInSf = (cell->crntTime.sfn * RGSCH_NUM_SUB_FRAMES_5G) + (cell->crntTime.slot) + RG_SCH_DRX_DL_DELTA; #endif #ifdef LTE_TDD delta = RG_SCH_DRX_DL_DELTA; #endif /*LTE_TDD */ } /* Initialize DL inactive list */ cmLListInit(&dlInactvLst); /* Initialize UL inactive list */ cmLListInit(&ulInactvLst); delInUlScan = cell->drxCb->delInUlScan; #ifndef LTE_TDD index1 = (curTimeInSf) % RG_SCH_MAX_DRXQ_SIZE; #endif node = ueLst->first; while(node) { ueCb = (RgSchUeCb *)node->node; ueDrxCb = ueCb->drxCb; /* Stop inactivity timer */ if ( ueDrxCb->drxInactvIndx != DRX_INVALID ) { cmLListDelFrm(&(cell->drxCb->drxQ[ueDrxCb->drxInactvIndx].inActvTmrQ), &(ueDrxCb->inActvTmrEnt)); } #ifdef LTE_TDD rgSCHDrxCalcNxtTmrExpry(cell, ueCb, delta, ueDrxCb->inactvtyTmrLen, &(ueDrxCb->drxInactDistance), &inActvTmrExpIndx ); #else /*LTE_TDD*/ inActvTmrExpIndx = (index1 + ueDrxCb->inactvtyTmrLen) % RG_SCH_MAX_DRXQ_SIZE; ueDrxCb->drxInactDistance = ueDrxCb->inactvtyTmrLen / RG_SCH_MAX_DRXQ_SIZE; #endif /*LTE_TDD*/ cmLListAdd2Tail(&(cell->drxCb->drxQ[inActvTmrExpIndx].inActvTmrQ), &(ueDrxCb->inActvTmrEnt)); ueDrxCb->inActvTmrEnt.node = (PTR)ueCb; ueDrxCb->drxInactvIndx = inActvTmrExpIndx; /* Update DRX InActive both masks */ ueDrxCb->drxUlInactvMask &= ~RG_SCH_DRX_INACTVTMR_BITMASK; ueDrxCb->drxDlInactvMask &= ~RG_SCH_DRX_INACTVTMR_BITMASK; /* Update UE's Inactive masks */ ueCb->ul.ulInactvMask &= ~RG_DRX_INACTIVE; ueCb->dl.dlInactvMask &= ~RG_DRX_INACTIVE; if ( delInUlScan == TRUE) { if ( ueDrxCb->inactvtyTmrLen == RGR_DRX_PRD_1PSF) { ueDrxCb->drxInactDistance = DRX_TMR_EXPRD; ueDrxCb->drxDlInactvMask |= RG_SCH_DRX_INACTVTMR_BITMASK; /* if no other condition is keeping ue inactive, * inactivate ue */ if ( !RG_SCH_DRX_DL_IS_UE_ACTIVE(ueDrxCb) ) { ueCb->dl.dlInactvMask |= RG_DRX_INACTIVE; /* Add to DL inactive list */ cmLListAdd2Tail(&dlInactvLst,&(ueCb->dlDrxInactvLnk)); ueCb->dlDrxInactvLnk.node = (PTR)ueCb; } }/*if ( ueDrxCb->inactvyTmrLen...*/ }/*delInUlScan==TRUE*/ else { if ( ueDrxCb->inactvtyTmrLen == RGR_DRX_PRD_1PSF ) { ueDrxCb->drxInactDistance = DRX_TMR_EXPRD; ueDrxCb->drxUlInactvMask |= RG_SCH_DRX_INACTVTMR_BITMASK; /* if no other condition is keeping ue inactive, * inactivate ue */ if ( !RG_SCH_DRX_DL_IS_UE_ACTIVE(ueDrxCb) ) { ueCb->ul.ulInactvMask |= RG_DRX_INACTIVE; if ( !RG_SCH_CMN_UL_IS_UE_ACTIVE(ueCb)) { /* Add to UL inactive list */ cmLListAdd2Tail(&ulInactvLst,&(ueCb->ulDrxInactvLnk)); ueCb->ulDrxInactvLnk.node = (PTR)ueCb; } }/*if ( !RG_SCH_DRX....)*/ }/*if (ueDrxCb->inactv...*/ } /* move the link list forward */ delNode = node; node = node->next; cmLListDelFrm(ueLst, delNode); delNode->node = NULLP; }/*node*/ /* Send the list to the scheduler to mark UE as inactive in UL*/ cellSch = RG_SCH_CMN_GET_CELL(cell); cellSch->apisUl->rgSCHUlInactvtUes(cell, &ulInactvLst); /* Send the DL inactive list to the scheduler to mark UE as inactive */ cellSch = RG_SCH_CMN_GET_CELL(cell); cellSch->apisDl->rgSCHDlInactvtUes(cell,&dlInactvLst); return; }/*rgSCHDrxStrtInActvTmr*/ /** @brief This function is called by the downlink HARQ module on receiving a * negative feedback from the UE for a PDSCH transmission. * * @details * Invoked by - rgSCHDhmHqTrnsFail * * Function: rgSCHDrxStartHarqRTTTmr * * Processing steps: * * * @param RgSchCellCb *cell * @param RgSchDlHqProcCb *dlHq * @param uint8_t tbCnt * @return Void */ #ifdef ANSI Void rgSCHDrxStartHarqRTTTmr ( RgSchCellCb *cell, RgSchDlHqProcCb *hqP, uint8_t tbCnt ) #else Void rgSCHDrxStartHarqRTTTmr(cell, hqP, tbCnt) RgSchCellCb *cell; RgSchDlHqProcCb *hqP; uint8_t tbCnt; #endif { RgSchDRXCellCb *drxCell =NULLP; RgSchDrxDlHqProcCb *drxHq =NULLP; uint16_t harqRTTExpIndx; uint8_t fdbkDelta; #ifdef LTE_TDD uint8_t firstDlTxOcassion; uint8_t drxRetxTmrStartSf; #endif drxCell = RG_SCH_DRX_GET_CELL(cell); drxHq = RG_SCH_DRX_GET_DL_HQ(hqP); #ifdef LTE_TDD /* ccpu00134196-[Add]-DRX retx timer changes */ firstDlTxOcassion = rgSchDrxHarqRetxFirstPsf[cell->ulDlCfgIdx] [hqP->tbInfo[tbCnt].fdbkTime.subframe]; harqRTTExpIndx = ((hqP->tbInfo[tbCnt].fdbkTime.sfn * 10) + hqP->tbInfo[tbCnt].fdbkTime.subframe + firstDlTxOcassion) % RG_SCH_MAX_DRXQ_SIZE; fdbkDelta = RGSCH_CALC_SF_DIFF(cell->crntTime, hqP->tbInfo[tbCnt].fdbkTime); #else /*LTE_TDD*/ /* For FDD HARQ RTT expiry index is 8 subframes from the time * corresponding PDSCH was scheduled. We are adding 1 subframe * so that UE is scheduled for retransmission in the next subframe*/ /* ccpu00134196-[Add]-DRX retx timer changes */ harqRTTExpIndx = ((hqP->tbInfo[tbCnt].timingInfo.sfn * RGSCH_NUM_SUB_FRAMES_5G) + hqP->tbInfo[tbCnt].timingInfo.slot + RG_SCH_MIN_HARQ_RTT) % RG_SCH_MAX_DRXQ_SIZE; fdbkDelta = RGSCH_CALC_SF_DIFF(cell->crntTime, hqP->tbInfo[tbCnt].timingInfo); #endif /*LTE_TDD*/ /* ccpu00134196-[Add]-DRX retx timer changes */ /* ensure that the insertion into the queue happens at an index greater than the dl index, ie, do +1 */ /* Instead of depending on TTI details of current time and HARQ RTT Expire * time, Handling this check with deltas, because with TTIs it is not possible * to handle wrap-around condition*/ #ifdef LTE_TDD if(fdbkDelta + RG_SCH_DRX_DL_DELTA >= firstDlTxOcassion) { /* The retx timer length should be reduced. This means based on the platforms delta between the DL HARQ processing and DL scheduling, drx retx timer lengths of 1ms/2ms may not be possible */ drxRetxTmrStartSf = (hqP->tbInfo[tbCnt].fdbkTime.subframe + firstDlTxOcassion) % RGSCH_NUM_SUB_FRAMES; uint8_t i; /* We are here because the Retx Timer start is moved by atleast one position. Hence the timer will be reduced by minimum one */ drxHq->retxTmrReduction = 1; /* Now check the consecutive subframes starting from the actual starting position of the retx tmr till the new position. Reduce the timer value only if the sf is a Pdcch sf */ for(i = 1; i <= fdbkDelta + RG_SCH_DRX_DL_DELTA-firstDlTxOcassion+ 1; i++) { if (rgSchTddUlDlSubfrmTbl[cell->ulDlCfgIdx] [(drxRetxTmrStartSf+i)%RGSCH_NUM_SUB_FRAMES] != RG_SCH_TDD_UL_SUBFRAME) { drxHq->retxTmrReduction++; } } #else if(fdbkDelta + RG_SCH_DRX_DL_DELTA >= RG_SCH_MIN_HARQ_RTT) { drxHq->retxTmrReduction = fdbkDelta + RG_SCH_DRX_DL_DELTA - RG_SCH_MIN_HARQ_RTT+ 1; #endif /* KW_FIX */ harqRTTExpIndx = (harqRTTExpIndx + drxHq->retxTmrReduction) % RG_SCH_MAX_DRXQ_SIZE; } else { drxHq->retxTmrReduction = 0; } cmLListAdd2Tail (&(drxCell->drxQ[harqRTTExpIndx].harqRTTQ), &(drxHq->harqRTTEnt)); drxHq->harqRTTEnt.node = (PTR)hqP; drxHq->rttIndx = harqRTTExpIndx; return; }/*rgSCHDrxStartHarqRTTTmr*/ /** @brief This function is called by the Configuration module to give a * trigger to DRX module for UE configuration. * * @details * * Function: rgSCHDrxUeCfg * * Processing steps: * - Copy configuration information into drxUe structure. * - Calculate the first occurance of onDuration based on values * provided in the configuration structure. * - Append the UE to the onDurationQ at that index. * - The UE must be appened to the list based on the timing calculated * above (nxtSfn, nxtSubframe). * * @param RgSchCellCb *cell Cell control block. * @param RgSchUeCb *ue UE control block. * @param RgrUeCfg *ueCfg RGR UE configuration information. * @return * -# ROK * -# RFAILED */ #ifdef ANSI S16 rgSCHDrxUeCfg ( RgSchCellCb *cell, RgSchUeCb *ue, RgrUeCfg *ueCfg ) #else S16 rgSCHDrxUeCfg (cell, ue, ueCfg) RgSchCellCb *cell; RgSchUeCb *ue; RgrUeCfg *ueCfg; #endif { S16 ret = ROK; RgSchDrxUeCb *ueDrxCb; CmLteTimingInfo nxtOnDur; uint16_t onDurIndx; uint16_t nxtOnDurTime; uint16_t curTime; uint8_t cellIdx; #if ( ERRCLASS & ERRCLS_INT_PAR ) if ( cell == (RgSchCellCb* )NULLP) { return RFAILED; } if ((ue == (RgSchUeCb* )NULLP) || (ueCfg == (RgrUeCfg* )NULLP)) { RLOG_ARG0(L_ERROR,DBG_CELLID,cell->cellId, "rgSCHDrxUeCfg():" "Invalid params.cell or ue or ueCfg is NULL "); return RFAILED; } #endif /* allocate and initialize drxCb */ ret = rgSCHUtlAllocSBuf(cell->instIdx, (Data**)&ue->drxCb, (sizeof(RgSchDrxUeCb))); if(ret != ROK) { RLOG_ARG1(L_ERROR,DBG_CELLID,cell->cellId, "Memory allocation FAILED for DRX UECB CRBTI:%d",ue->ueId); return (ret); } ueDrxCb = ue->drxCb; /* initialize the masks */ ueDrxCb->drxDlInactvMask = DRX_UE_INACTIVE; ueDrxCb->drxUlInactvMask = DRX_UE_INACTIVE; ue->dl.dlInactvMask |= RG_DRX_INACTIVE; ue->ul.ulInactvMask |= RG_DRX_INACTIVE; for(cellIdx = 0; cellIdx < CM_LTE_MAX_CELLS; cellIdx++) { ue->drxCb->drxDlInactvMaskPerCell[cellIdx] = DRX_UE_INACTIVE; ue->drxCb->drxUlInactvMaskPerCell[cellIdx] = DRX_UE_INACTIVE; } /* Copy the configuration values into the UE's DRX CB. */ rgSCHDrxCpyUeCfg (ueDrxCb, &ueCfg->ueDrxCfg); #ifdef EMTC_ENABLE if(ue->isEmtcUe) { rgSCHEmtcDrxCpyUeCfg(ue ,&ueCfg->ueDrxCfg); } #endif /* set all indexes to default values */ ueDrxCb->onDurIndx = DRX_INVALID; ueDrxCb->onDurExpIndx = DRX_INVALID; ueDrxCb->drxInactvIndx = DRX_INVALID; ueDrxCb->shortCycleIndx = DRX_INVALID; /* set all distances to timer expiry */ ueDrxCb->onDurExpDistance = DRX_TMR_EXPRD; ueDrxCb->drxInactDistance = DRX_TMR_EXPRD; ueDrxCb->drxShortCycleDistance = DRX_TMR_EXPRD; ueDrxCb->distance = DRX_TMR_EXPRD; /* Mark the UE in long/short cycle initially as per configuration*/ if(FALSE == ueDrxCb->isShortCycleCfgd) { ueDrxCb->isLongCycle = TRUE; } else { ueDrxCb->isLongCycle = FALSE; } /* Calculate the next occurance from this point */ rgSCHDrxGetNxtOnDur (cell, ueDrxCb, &nxtOnDur,RG_SCH_NO_DELTA); nxtOnDurTime = ((nxtOnDur.sfn * RGSCH_NUM_SUB_FRAMES_5G) + nxtOnDur.slot); curTime = ((cell->crntTime.sfn * RGSCH_NUM_SUB_FRAMES_5G) + cell->crntTime.slot); onDurIndx = nxtOnDurTime % RG_SCH_MAX_DRXQ_SIZE; ueDrxCb->distance = (nxtOnDurTime - curTime) / RG_SCH_MAX_DRXQ_SIZE; if (ueDrxCb->distance < 0) { RLOG_ARG2(L_ERROR,DBG_CELLID,cell->cellId, "DRXUE. Invalid " "value for distance, %d CRNTI:%d", ueDrxCb->distance,ue->ueId); } //printf("The onduartion index is: %d\n",(int)onDurIndx); cmLListAdd2Tail(&(cell->drxCb->drxQ[onDurIndx].onDurationQ), &(ueDrxCb->onDurationEnt)); ueDrxCb->onDurationEnt.node = (PTR)ue; ueDrxCb->onDurIndx = onDurIndx; /* Starting Short Cycle Timer */ if(TRUE == ueDrxCb->isShortCycleCfgd) { ueDrxCb->drxShortCycleDistance = (ueDrxCb->shortCycleTmrLen * ueDrxCb->shortDrxCycle) / RG_SCH_MAX_DRXQ_SIZE; ueDrxCb->shortCycleIndx = (curTime + (ueDrxCb->shortCycleTmrLen * ueDrxCb->shortDrxCycle)) % RG_SCH_MAX_DRXQ_SIZE; cmLListAdd2Tail(&(cell->drxCb->drxQ[ueDrxCb->shortCycleIndx].shortCycleQ), &(ueDrxCb->shortCycleEnt)); ueDrxCb->shortCycleEnt.node = (PTR)ue; } return (ret); } /* end of rgSCHDrxUeCfg */ /** @brief This function gets the next occurance of onDurationTimer from the * current time. * * @details rgSCHDrxGetNxtOnDur * * Function: rgSCHDrxGetNxtOnDur * * Processing steps: - * Calculation of first occurance of onDuration is to be done as * follows. * Assume DRX configuration came at subframe (x, y) the periodicity is * offset = (perd, offset). * The (sfn, subframe) satisfying the following condition is the first * occurance from this point. * * (sfn * 10 + subframe) mod perd = offset * - * * * @param RgSchCellCb *cell * @param RgSchDrxUeCb *drxCb * @param CmLteTimingInfo *nxtOnDur * @param uint8_t delta * @return ROK/RFAILED */ #ifdef ANSI PRIVATE S16 rgSCHDrxGetNxtOnDur ( RgSchCellCb *cell, RgSchDrxUeCb *drxCb, CmLteTimingInfo *nxtOnDur, uint8_t delta ) #else PRIVATE S16 rgSCHDrxGetNxtOnDur (cell, drxCb, nxtOnDur, delta) RgSchCellCb *cell; RgSchDrxUeCb *drxCb; CmLteTimingInfo *nxtOnDur; uint8_t delta; #endif { uint16_t curTime; uint16_t curDist; uint16_t cycleLen; uint32_t numOfCycles; uint32_t nxtDist; #if ( ERRCLASS & ERRCLS_INT_PAR ) if ( cell == (RgSchCellCb* )NULLP ) { return RFAILED; } if( (drxCb == (RgSchDrxUeCb* )NULLP) || (nxtOnDur == (CmLteTimingInfo* )NULLP) ) { RLOG_ARG0(L_ERROR,DBG_CELLID,cell->cellId, "rgSCHDrxGetNxOnDur():Invalid params." "cell/drxCb/nxtOnDur is NULL"); return RFAILED; } #endif if (TRUE == drxCb->isLongCycle) { cycleLen = drxCb->longDrxCycle; } else { cycleLen = drxCb->shortDrxCycle; } curTime = ((cell->crntTime.sfn * RGSCH_NUM_SUB_FRAMES_5G) + cell->crntTime.slot); curTime += delta; /*TODO: see if we need to take care of wrap arounds */ if ( curTime <= drxCb->drxStartOffset ) { /* offset is the nextOnDur */ nxtOnDur->sfn = drxCb->drxStartOffset / RGSCH_NUM_SUB_FRAMES_5G; nxtOnDur->slot = (uint8_t)(drxCb->drxStartOffset % RGSCH_NUM_SUB_FRAMES_5G); nxtDist = ((nxtOnDur->sfn * RGSCH_NUM_SUB_FRAMES_5G) + nxtOnDur->slot); } else { curDist = curTime - drxCb->drxStartOffset; numOfCycles = curDist / cycleLen; if (0 == (curDist % cycleLen)) { /* Perfect match pick up the current time */ /*nxtOnDur should be set to equal to currentTime + DELTA */ nxtOnDur->sfn = (uint16_t)curTime / RGSCH_NUM_SUB_FRAMES_5G; nxtOnDur->slot = (uint16_t)curTime % RGSCH_NUM_SUB_FRAMES_5G; nxtDist = ((nxtOnDur->sfn * RGSCH_NUM_SUB_FRAMES_5G) + nxtOnDur->slot); } else { nxtDist = drxCb->drxStartOffset + (numOfCycles + 1) * cycleLen; nxtOnDur->sfn = (uint16_t)nxtDist / RGSCH_NUM_SUB_FRAMES_5G; nxtOnDur->slot = (uint16_t)nxtDist % RGSCH_NUM_SUB_FRAMES_5G; } } /*If next On Duration is less than DL DELTA ahead, we will miss it and * hence need to move to the On-Duration after that.*/ if((nxtDist - (curTime - delta)) <= RG_SCH_DRX_MAX_DELTA) { nxtDist = nxtDist + cycleLen; nxtOnDur->sfn = (uint16_t)nxtDist / RGSCH_NUM_SUB_FRAMES_5G; nxtOnDur->slot = (uint16_t)nxtDist % RGSCH_NUM_SUB_FRAMES_5G; } return ROK; } /* end of rgSCHDrxGetNxtOnDur */ /** @brief This function is a utility function to copy the UE configuration from * the RGR structure to the UE control block. * * @details * -Invoked by rgSCHDrxUeCfg * * Function: rgSCHDrxCpyUeCfg * * Processing steps: * - Copies configuration values * * @param RgSchDrxUeCb *ueCb * @param RgrUeDrxCfg *drxCfg * @return ROK/RFAILED */ #ifdef ANSI PRIVATE S16 rgSCHDrxCpyUeCfg ( RgSchDrxUeCb *ueCb, RgrUeDrxCfg *drxCfg ) #else PRIVATE S16 rgSCHDrxCpyUeCfg (ueCb, drxCfg) RgSchDrxUeCb *ueCb; RgrUeDrxCfg *drxCfg; #endif { #if ( ERRCLASS & ERRCLS_INT_PAR ) if ( (ueCb == (RgSchDrxUeCb* )NULLP ) || (drxCfg == (RgrUeDrxCfg* )NULLP) ) { return RFAILED; } #endif /* Copy all values to UE control block */ #ifdef LTEMAC_R9 ueCb->cqiMask = drxCfg->cqiMask; #endif /*LTEMAC_R9*/ ueCb->onDurTmrLen = drxCfg->drxOnDurTmr; ueCb->inactvtyTmrLen = drxCfg->drxInactvTmr; ueCb->drxRetransTmrLen = drxCfg->drxRetxTmr; ueCb->longDrxCycle = drxCfg->drxLongCycleOffst.longDrxCycle; ueCb->drxStartOffset = drxCfg->drxLongCycleOffst.drxStartOffst; ueCb->isShortCycleCfgd = drxCfg->drxShortDrx.pres; ueCb->shortDrxCycle = drxCfg->drxShortDrx.shortDrxCycle; ueCb->shortCycleTmrLen = drxCfg->drxShortDrx.drxShortCycleTmr; return ROK; } /* end of rgSCHDrxCpyUeCfg */ #ifdef RGR_V2 /** @brief This function is called by the configuration module when a UE is * reconfigured for DRX parameters. * * @details * Invoked By - rgSCHCfgRgrUeCfg * * Function: rgSCHDrxUeReCfg * As per MAC specifications the new values of timers shall be applied only once * they are restarted, hence no processing is required for modified timer values. * * Processing steps: * - if offset and/or drxCycleLenght changes then recalculate the next * onDurationIndex * - remove the UE from current index * - add the UE to the new index. * - if short cycle is enabled * - set isShortCycleCfgd = TRUE * * @param RgSchCellCb *cell * @param RgSchUeCb *ue * @param RgrUeRecfg *ueReCfg * @return ROK/RFAILED */ #ifdef ANSI S16 rgSCHDrxUeReCfg ( RgSchCellCb *cell, RgSchUeCb *ue, RgrUeRecfg *ueReCfg ) #else S16 rgSCHDrxUeReCfg (cell, ue, ueReCfg) RgSchCellCb *cell; RgSchUeCb *ue; RgrUeRecfg *ueReCfg; #endif { /* DRX_INFI_LOOP */ RgSchCmnCell *cellSch = NULLP; CmLListCp dlInactvLst; /* list of UE's becoming DL-inactive */ S16 ret = ROK; Inst instIdx = cell->instIdx; RgSchDrxUeCb *ueDrxCb; CmLteTimingInfo nxtOnDur; uint16_t nxtOnDurTime; uint16_t onDurIndx; uint16_t curTime; uint16_t shrtCycleExpIndx; uint16_t onDurExpTime; uint16_t cycleLen; uint16_t curIndx; uint8_t cellIdx; /* drx was disabled but now enabled for this ue */ if ( (ue->isDrxEnabled == FALSE) && (ueReCfg->ueDrxRecfg.isDrxEnabled == TRUE) ) { /* allocated and initialize drxCb */ ret = rgSCHUtlAllocSBuf(instIdx, (Data**)&ue->drxCb, (sizeof(RgSchDrxUeCb))); if ( ret != ROK ) { RLOG_ARG1(L_ERROR,DBG_CELLID,cell->cellId, "rgSCHdrxUeReCfg():""Memory allocation FAILED for DRX UE Cb CRNTI:%d", ue->ueId); return (ret); } ue->isDrxEnabled = TRUE; /* sachin */ /* initialize the masks */ ue->drxCb->drxDlInactvMask = DRX_UE_INACTIVE; ue->drxCb->drxUlInactvMask = DRX_UE_INACTIVE; ue->dl.dlInactvMask |= RG_DRX_INACTIVE; ue->ul.ulInactvMask |= RG_DRX_INACTIVE; for(cellIdx = 0; cellIdx < CM_LTE_MAX_CELLS; cellIdx++) { ue->drxCb->drxDlInactvMaskPerCell[cellIdx] = DRX_UE_INACTIVE; ue->drxCb->drxUlInactvMaskPerCell[cellIdx] = DRX_UE_INACTIVE; } /* set all indexes to default values */ ue->drxCb->onDurIndx = DRX_INVALID; ue->drxCb->onDurExpIndx = DRX_INVALID; ue->drxCb->drxInactvIndx = DRX_INVALID; ue->drxCb->shortCycleIndx = DRX_INVALID; /* set all distances to timer expiry */ ue->drxCb->onDurExpDistance = DRX_TMR_EXPRD; ue->drxCb->drxInactDistance = DRX_TMR_EXPRD; ue->drxCb->drxShortCycleDistance = DRX_TMR_EXPRD; ue->drxCb->distance = DRX_TMR_EXPRD; } if( ue->drxCb == NULLP ) { return RFAILED; } ueDrxCb = ue->drxCb; /*drx was enabled but now disabled for this UE */ if ( (ue->isDrxEnabled == TRUE ) && (ueReCfg->ueDrxRecfg.isDrxEnabled == FALSE) ) { /* remove UE from all DRX Queues */ (Void)rgSCHDrxUeDel(cell,ue); /* free drxCb */ /* ccpu00117052 - MOD - Passing double pointer for proper NULLP assignment*/ rgSCHUtlFreeSBuf(instIdx,(Data **)(&((ue->drxCb))), sizeof(RgSchDrxUeCb)); /* Resetting the DRX Bit set in Inactv Mask */ ue->dl.dlInactvMask &= ~RG_DRX_INACTIVE; ue->ul.ulInactvMask &= ~RG_DRX_INACTIVE; ue->isDrxEnabled = FALSE; }/*isDrxEnabled == FALSE */ else { /* If Application is updating DRX params */ if (ueReCfg->ueRecfgTypes & RGR_UE_DRX_RECFG ) { rgSCHDrxCpyUeCfg (ueDrxCb, &ueReCfg->ueDrxRecfg); #ifdef EMTC_ENABLE if(ue->isEmtcUe) { rgSCHEmtcDrxCpyUeCfg(ue, &ueReCfg->ueDrxRecfg); } #endif } /* Removing the UE from existing index of shortcycle, if any*/ if(ueDrxCb->shortCycleIndx != DRX_INVALID) { cmLListDelFrm(&(cell->drxCb->drxQ[ueDrxCb->shortCycleIndx].shortCycleQ), &(ueDrxCb->shortCycleEnt)); ueDrxCb->shortCycleEnt.node = (PTR) NULLP; ueDrxCb->shortCycleIndx = DRX_INVALID; } /* Mark for intiating long/short cycle as per received config */ if(FALSE == ue->drxCb->isShortCycleCfgd) { ue->drxCb->isLongCycle = TRUE; } else { ue->drxCb->isLongCycle = FALSE; } /* Calculate the next occurance from this point */ rgSCHDrxGetNxtOnDur (cell, ueDrxCb, &nxtOnDur,RG_SCH_NO_DELTA); /* remove the UE from the current onDuration Queue */ if ( ueDrxCb->onDurIndx != DRX_INVALID ) { cmLListDelFrm(&(cell->drxCb->drxQ[ueDrxCb->onDurIndx].onDurationQ), &(ueDrxCb->onDurationEnt)); } nxtOnDurTime = (nxtOnDur.sfn * RGSCH_NUM_SUB_FRAMES_5G) + nxtOnDur.slot; curTime = ((cell->crntTime.sfn * RGSCH_NUM_SUB_FRAMES_5G) + cell->crntTime.slot); /* If Onduration timer of old configuration is already running then waiting till it expires */ if((ueDrxCb->onDurExpIndx != DRX_INVALID) && (ueDrxCb->onDurExpDistance != DRX_TMR_EXPRD)) { curIndx = (curTime + RG_SCH_DRX_DL_DELTA) % RG_SCH_MAX_DRXQ_SIZE; RLOG_ARG3(L_DEBUG,DBG_CELLID,cell->cellId, "OLD ONDUR RUNNING-EXPIRES at %d curIdx-%d nxtOnDurTime-%d", ueDrxCb->onDurExpIndx, curIndx, nxtOnDurTime); /* Manipulating the time when old onDuration timer can expire */ if(curIndx >= ueDrxCb->onDurExpIndx) { onDurExpTime = curTime + ((ueDrxCb->onDurExpDistance+1) * RG_SCH_MAX_DRXQ_SIZE)+ (ueDrxCb->onDurExpIndx - curIndx + RG_SCH_DRX_DL_DELTA); } else { onDurExpTime = curTime + (ueDrxCb->onDurExpDistance * RG_SCH_MAX_DRXQ_SIZE)+ (ueDrxCb->onDurExpIndx - curIndx + RG_SCH_DRX_DL_DELTA); } if(nxtOnDurTime <= onDurExpTime) { if(ueDrxCb->isLongCycle) { cycleLen = ueDrxCb->longDrxCycle; } else { cycleLen = ueDrxCb->shortDrxCycle; } /* Moving to the possible occassion of onduration after current onduration expiry: * If both are aligning then going for next cycle */ nxtOnDurTime += ((onDurExpTime - nxtOnDurTime)/cycleLen + 1 ) *cycleLen ; } } /* Add the UE to the index which corresponds to the next onduration start*/ onDurIndx = nxtOnDurTime % RG_SCH_MAX_DRXQ_SIZE; ueDrxCb->distance = (nxtOnDurTime - curTime) / RG_SCH_MAX_DRXQ_SIZE; if (ueDrxCb->distance < 0) { RLOG_ARG2(L_ERROR,DBG_CELLID,cell->cellId,"DRXUE. Invalid " "value for distance, %d CRNTI:%d", ueDrxCb->distance,ue->ueId); } cmLListAdd2Tail(&(cell->drxCb->drxQ[onDurIndx].onDurationQ), &(ueDrxCb->onDurationEnt)); ueDrxCb->onDurationEnt.node = (PTR)ue; ueDrxCb->onDurIndx = onDurIndx; /* DRX_INFI_LOOP */ cmLListInit(&dlInactvLst); /* Add to DL inactive list */ cmLListAdd2Tail(&dlInactvLst,&(ue->dlDrxInactvLnk)); ue->dlDrxInactvLnk.node = (PTR)ue; /* Send the list to the scheduler to mark UE as inactive */ cellSch = RG_SCH_CMN_GET_CELL(cell); cellSch->apisDl->rgSCHDlInactvtUes(cell, &dlInactvLst); /* DRX_INFI_LOOP */ /* Starting short cycle timer as per the existence of old onDuration timer */ if(TRUE == ueDrxCb->isShortCycleCfgd) { /* Expiring short DRX cycle Tmr once the number of short DRX cycles done */ ueDrxCb->drxShortCycleDistance = (nxtOnDurTime + ueDrxCb->onDurTmrLen + (ueDrxCb->shortCycleTmrLen -1 )* ueDrxCb->shortDrxCycle) / RG_SCH_MAX_DRXQ_SIZE; shrtCycleExpIndx = (nxtOnDurTime + ueDrxCb->onDurTmrLen + ((ueDrxCb->shortCycleTmrLen -1)* ueDrxCb->shortDrxCycle)) % RG_SCH_MAX_DRXQ_SIZE; cmLListAdd2Tail(&(cell->drxCb->drxQ[shrtCycleExpIndx].shortCycleQ), &(ueDrxCb->shortCycleEnt)); ueDrxCb->shortCycleEnt.node = (PTR)ue; ueDrxCb->shortCycleIndx = shrtCycleExpIndx; } } return ROK; } /* end of rgSCHDrxUeReCfg */ #endif /** @brief This function Loop through the list of HARQ processes for this * UE and delete from the harqRTTQ and harqRetxQ * * Function: rgSCHDrxUeHqReset * Invoked by rgSCHDrxUeDel * * Processing steps: Loop through the list of HARQ processes for this UE and delete from * the harqRTTQ and harqRetxQ. * * @param RgSchCellCb *cell * @return ROK/RFAILED */ #ifdef ANSI Void rgSCHDrxUeHqReset ( RgSchCellCb *cell, RgSchUeCb *ue, RgSchDlHqEnt *hqE, uint8_t cellIdx ) #else Void rgSCHDrxUeHqReset(cell, ue, hqE, cellIdx) RgSchCellCb *cell; RgSchUeCb *ue; RgSchDlHqEnt *hqE; uint8_t cellIdx; #endif { RgSchDlHqProcCb *hqP; RgSchDrxDlHqProcCb *drxHq =NULLP; uint8_t i; for(i=0; i < hqE->numHqPrcs; i++) { hqP = &hqE->procs[i]; drxHq = RG_SCH_DRX_GET_DL_HQ(hqP); if(drxHq->rttIndx != DRX_INVALID) { cmLListDelFrm (&(cell->drxCb->drxQ[drxHq->rttIndx].harqRTTQ), &(drxHq->harqRTTEnt)); drxHq->rttIndx = DRX_INVALID; } if(drxHq->reTxIndx != DRX_INVALID) { cmLListDelFrm (&(cell->drxCb->drxQ[drxHq->reTxIndx].harqRetxQ), &(drxHq->harqRetxEnt)); drxHq->reTxIndx = DRX_INVALID; } } ue->drxCb->drxDlInactvMaskPerCell[cellIdx] = DRX_UE_INACTIVE; ue->drxCb->drxUlInactvMaskPerCell[cellIdx] = DRX_UE_INACTIVE; } /** @brief This function Deletes DRX specific context for a UE. * * @details This funciton is invoked by the Configuration module on RGR UE Deletion. * This function removes the UE's context from all of the DRX Queues. * In addition it deletes context of UE's HARQ Processes present in the harqRTTQ * and harqRetxQ * * * Function: rgSCHDrxUeDel * Invoked by rgSCHCfgRgrUeDel * * Processing steps: * - Remove the UE from the following queues * - onDurationQ - using onDurIndx * - onDurationExpQ - using onDurExpIndx * - inActvTmrQ - using drxInactvIndx * - shortCycleQ - using shortCycleIndx * - Loop through the list of HARQ processes for this UE and delete from * the harqRTTQ and harqRetxQ. * * @param RgSchCellCb *cell * @param RgSchUeCb *ue * @return ROK/RFAILED */ #ifdef ANSI S16 rgSCHDrxUeDel ( RgSchCellCb *cell, RgSchUeCb *ue ) #else S16 rgSCHDrxUeDel (cell, ue) RgSchCellCb *cell; RgSchUeCb *ue; #endif { RgSchDrxUeCb *ueDrxCb; RgSchDlHqEnt *hqE = NULLP; uint8_t cellIdx; RgSchUeCellInfo *cellInfo = NULLP; #ifdef EMTC_ENABLE RgSchCmnUlUe *ueUl = RG_SCH_CMN_GET_UL_UE(ue, cell); #endif /* ccpu00129899: Moved the drx-enabled check to the caller */ ueDrxCb = ue->drxCb; /* Remove UE from all queues */ if ( ueDrxCb->onDurIndx != DRX_INVALID ) { cmLListDelFrm(&(cell->drxCb->drxQ[ueDrxCb->onDurIndx].onDurationQ), &(ueDrxCb->onDurationEnt)); ueDrxCb->onDurIndx = DRX_INVALID; } if ( ueDrxCb->onDurExpIndx != DRX_INVALID ) { cmLListDelFrm(&(cell->drxCb->drxQ[ueDrxCb->onDurExpIndx].onDurationExpQ), &(ueDrxCb->onDurationExpEnt)); ueDrxCb->onDurExpIndx = DRX_INVALID; } if ( ueDrxCb->drxInactvIndx != DRX_INVALID ) { cmLListDelFrm(&(cell->drxCb->drxQ[ueDrxCb->drxInactvIndx].inActvTmrQ), &(ueDrxCb->inActvTmrEnt)); ueDrxCb->drxInactvIndx = DRX_INVALID; } if ( ueDrxCb->shortCycleIndx != DRX_INVALID ) { cmLListDelFrm(&(cell->drxCb->drxQ[ueDrxCb->shortCycleIndx].shortCycleQ), &(ueDrxCb->shortCycleEnt)); ueDrxCb->shortCycleIndx = DRX_INVALID; } for(cellIdx = 0; cellIdx < CM_LTE_MAX_CELLS; cellIdx++) { cellInfo = ue->cellInfo[cellIdx]; if(cellInfo) { hqE = cellInfo->hqEnt; rgSCHDrxUeHqReset(cell, ue, hqE, cellIdx); } } #ifdef EMTC_ENABLE if(ue->isEmtcUe) { rgSCHDrxUeUlHqReset(cell, ue, &(ueUl->hqEnt)); } #endif /* Resetting the DRX Bit set in Inactv Mask */ ue->dl.dlInactvMask &= ~RG_DRX_INACTIVE; ue->ul.ulInactvMask &= ~RG_DRX_INACTIVE; ueDrxCb->drxDlInactvMask = DRX_UE_INACTIVE; ueDrxCb->drxUlInactvMask = DRX_UE_INACTIVE; return ROK; }/*rgSCHDrxUeDel*/ /** @brief This function is called at the time of RGR cell configuration. * * @details * Invoked by - rgSCHCfgRgrCellCfg * * Function: rgSCHDrxCellCfg * * Processing steps: * - Initializes the following drxQ (memset would do). * * * @param RgSchCellCb *cell * @param RgrCellCfg *cellCfg * @return ROK/RFAILED */ #ifdef ANSI S16 rgSCHDrxCellCfg ( RgSchCellCb *cell, RgrCellCfg *cellCfg ) #else S16 rgSCHDrxCellCfg (cell, cellCfg) RgSchCellCb *cell; RgrCellCfg *cellCfg; #endif { S16 ret = ROK; Inst instIdx = cell->instIdx; #if ( ERRCLASS & ERRCLS_INT_PAR ) /*KWORK_FIX :Removed check for cell being NULL*/ if( (cellCfg == (RgrCellCfg* )NULLP)) { RLOG_ARG0(L_ERROR,DBG_CELLID,cell->cellId, "rgSCHDrxCellCfg():Invalid Params. cell/cellCfg is NULL"); return RFAILED; } #endif /* allocate and initialize drxCb */ ret = rgSCHUtlAllocSBuf(instIdx, (Data**)&cell->drxCb, (sizeof(RgSchDRXCellCb))); if(ret != ROK) { RLOG_ARG0(L_ERROR,DBG_CELLID,cell->cellId,"rgSCHDrxCellCfg():" "Memory allocation FAILED for DRX cell Cb"); return (ret); } /* delInUlScan determines which index scans the queue last. * We look at onDurationQ both from ulIndex & dlIndex pov. * Consider, onDuration starts at index 5, and current index is 2, * while dlIndex is 2 & ulIndex is 3 i.e dl is looking 2 SF ahead * and ul is looking 3 SF ahead. In this case, dl will scan the queue * last and therefore DL will delete ueCb from onDuration q. * The reverse is true for the other case.*/ if ( RG_SCH_DRX_UL_DELTA > RG_SCH_DRX_DL_DELTA ) { cell->drxCb->delInUlScan = FALSE; } else { cell->drxCb->delInUlScan = TRUE; } return (ret); } /* end of rgSchDrxCellCfg */ /** @brief This function to delete DRX specific context in the cell control * block. * * @details * Invoked by - rgSCHCfgRgrCellDel * * Function: rgSCHDrxCellDel * * Processing steps: * - De-Inits RgSchDRXCellCb (Nothing to be done) * - Assumption: The API is invoked after deletion of UEs from the cell. * * @param RgSchCellCb *cell * @return Void */ #ifdef ANSI Void rgSCHDrxCellDel ( RgSchCellCb *cell ) #else Void rgSCHDrxCellDel (cell) RgSchCellCb *cell; #endif { Inst instIdx = cell->instIdx; if (cell->drxCb) { /* ccpu00117052 - MOD - Passing double pointer for proper NULLP assignment*/ rgSCHUtlFreeSBuf(instIdx, (Data **)(&(cell->drxCb)), sizeof(RgSchDRXCellCb)); } return; } /* end of rgSchDrxCellDel */ /** @brief This function is called when an SR is received from the UE. In this * case the UE should be marked as ACTIVE till we send a UL allocation to the * UE. * * @details * Invoked by - rgSCHCmnSrRcvd * Must be called after calling the specific scheduler. * * Function: rgSCHDrxSrInd * * Processing steps: * - Mark the UE as ACTIVE * ueCb->drxUlInactvMask |= (DRX_SR_ACTIVE); * - Optionally call schedulers to add this UE to their scheduling * queues. * - Set drxUe->srRcvd = TRUE * * Note : the specification state that the UE shall start be active * listening for UL grant, this implies we could potentially exploit * this to send DL transmissions as well. However we have currently * chosen not to do so. * * @param RgSchCellCb *cell * @param RgSchUeCb *ue * @return ROK/RFAILED */ #ifdef ANSI S16 rgSCHDrxSrInd ( RgSchCellCb *cell, RgSchUeCb *ue ) #else S16 rgSCHDrxSrInd (cell, ue) RgSchCellCb *cell; RgSchUeCb *ue; #endif { RgSchDrxUeCb *drxCb; #if ( ERRCLASS & ERRCLS_INT_PAR ) if ( cell == (RgSchCellCb* )NULLP) { return ROK; } if( (ue == (RgSchUeCb* )NULLP)) { RLOG_ARG0(L_ERROR,DBG_CELLID,cell->cellId, "rgSCHDrxSrInd():Invalid Params. cell/ue is NULL"); return RFAILED; } #endif /* KWork fix - shifted this down */ drxCb = RG_SCH_DRX_GET_UE(ue); /* Mark the UE as active for UL only */ drxCb->drxUlInactvMask &= ~RG_SCH_DRX_SR_BITMASK; drxCb->srRcvd = TRUE; /* Update UE's inactive mask and if required move UE to ACTIVE state */ RG_SCH_CMN_UL_UPDT_INACTV_MASK( cell, ue, RG_DRX_INACTIVE); return ROK; } /* rgSCHDrxSrInd */ /** @brief This function handles ACTIVITY due to RACH using a dedicated preamble * (PDCCH order) OR Handover. A UE shall remain marked as active from the time * we successfully send out a RAR upto the time it receives a PDCCH indicating a * new transmission. * * @details * Invoked by - rgSCHCmnHdlHoPo * * Function: rgSCHDrxDedRa * * Processing steps: * - MARK the UE as active * - set the raRcvd = TRUE for this UE. * * @code * ueCb->drxDlInactvMask |= (DRX_RA_ACTIVE); * ueCb->drxUlInactvMask |= (DRX_RA_ACTIVE); * ueCb->raRcvd = TRUE; * @endcode * * @param RgSchCellCb *cellCb * @param RgSchUeCb *ueCb * @return Void */ #ifdef ANSI Void rgSCHDrxDedRa ( RgSchCellCb *cellCb, RgSchUeCb *ueCb ) #else Void rgSCHDrxDedRa (cellCb, ueCb) RgSchCellCb *cellCb; RgSchUeCb *ueCb; #endif { RgSchDrxUeCb *drxCb; drxCb = RG_SCH_DRX_GET_UE(ueCb); /* Mark the UE as active for UL & DL */ drxCb->drxUlInactvMask &= ~RG_SCH_DRX_RA_BITMASK; /* Update UE's inactive mask and if required move UE to ACTIVE state */ RG_SCH_CMN_UL_UPDT_INACTV_MASK(cellCb, ueCb, RG_DRX_INACTIVE); drxCb->drxDlInactvMask &= ~RG_SCH_DRX_RA_BITMASK; /* Update UE's inactive mask and if required move UE to ACTIVE state */ RG_SCH_CMN_DL_UPDT_INACTV_MASK(cellCb, ueCb, RG_DRX_INACTIVE); drxCb->raRcvd = TRUE; return; } /* end of rgSCHDrxDedRa */ /** @brief This function calculates the next onDuration Occurence * and removes & queue it again in onDurationQ * * @details * Invoked by - * * Function: rgSCHDrxMvToNxtOnDurOcc * * Processing steps: * * * @param RgSchCellCb *cell * @param RgSchUeCb *ueCb * @param uint16_t idx - if calcFrmOffst is TRUE, * idx is delta to be added * @param Bool calcFrmOffst * @return Void */ #ifdef ANSI PRIVATE Void rgSCHDrxMvToNxtOnDurOcc ( RgSchCellCb *cell, RgSchUeCb *ueCb, uint16_t idx, Bool calcFrmOffst ) #else PRIVATE Void rgSCHDrxMvToNxtOnDurOcc (cell, ueCb, idx, calcFrmOffst) RgSchCellCb *cell; RgSchUeCb *ueCb; uint16_t idx; Bool calcFrmOffst; #endif { uint16_t nxtOnDurIndex; uint16_t curTime; RgSchDrxUeCb *drxUe; RgSchDRXCellCb *drxCell; CmLteTimingInfo nxtOnDur; /* to be used when calcFrmOffset is set */ uint16_t nxtOnDurInSf; /* next On Duration in no of subframes */ drxCell = cell->drxCb; drxUe = ueCb->drxCb; if(calcFrmOffst == FALSE) { if (drxUe->isLongCycle) { nxtOnDurIndex = ((idx + drxUe->longDrxCycle) % RG_SCH_MAX_DRXQ_SIZE ); drxUe->distance = drxUe->longDrxCycle/RG_SCH_MAX_DRXQ_SIZE; } else { nxtOnDurIndex = ((idx + drxUe->shortDrxCycle)% RG_SCH_MAX_DRXQ_SIZE); drxUe->distance = drxUe->shortDrxCycle / RG_SCH_MAX_DRXQ_SIZE; } } else { rgSCHDrxGetNxtOnDur(cell,drxUe,&nxtOnDur,(uint8_t)idx); nxtOnDurInSf = ((nxtOnDur.sfn * RGSCH_NUM_SUB_FRAMES_5G) + nxtOnDur.slot); curTime = ((cell->crntTime.sfn * RGSCH_NUM_SUB_FRAMES_5G) + cell->crntTime.slot); nxtOnDurIndex = nxtOnDurInSf % RG_SCH_MAX_DRXQ_SIZE; drxUe->distance = (nxtOnDurInSf-curTime) / RG_SCH_MAX_DRXQ_SIZE; if (drxUe->distance < 0) { RLOG_ARG2(L_ERROR,DBG_CELLID,cell->cellId,"DRXUE. Invalid " "value for distance, %d CRNTI:%d", drxUe->distance,ueCb->ueId); } } /* First remove from existing location */ if ( drxUe->onDurIndx != DRX_INVALID ) { cmLListDelFrm(&(drxCell->drxQ[drxUe->onDurIndx].onDurationQ), &(drxUe->onDurationEnt)); } /* Add at new location */ cmLListAdd2Tail(&(drxCell->drxQ[nxtOnDurIndex].onDurationQ), &(drxUe->onDurationEnt)); drxUe->onDurationEnt.node = (PTR)ueCb; drxUe->onDurIndx = nxtOnDurIndex; return; }/*rgSCHDrxMvToNxtOnDurOcc*/ #ifdef LTE_TDD /** @brief This function calculates the next SFN,subframe a given * timer is going to expire. Works for all TDD configurations. * * @details * * Function: rgSCHDrxGetNxtTmrExpry * We need to count only PDCCH frames in a given TDD * configuration. This is true for onDuration, inActivity * & drx-retransmission timers. * * Processing steps (assume timer starts at (12,2) and duration * is 23 DL subframes): * - based on TDD configuration, move to the next SFN and * count the number of DL subframes consumed. In our example, * moving to (12,9) will consume 6 DL subframes assuming TDD * config of 2. * - From this point on, determine how many exact Radio Frames * will be consumed for the remaining DL subframes. In our * example, remaining DL subframes are (23-6) are 17. * For config 2, the following holds true * 8 DLSF are in 1 RF * 1 DLSF in 1/8 DLSF * 17 DLSF in 17/8 i.e 2 RF + 1 subframe * In order to consume 17 DLSF, we need to move forward * 2 RFs + 1 subframe. Adding 2 RF's gives us (14,9) * - For the remaining subframe, we need to figure out the next * available DL subframe. For TDD_configuration, the first DL * subframe is at index 0. So, the timer will run till (15,0) * and will expire on (15,1) * * @param RgSchUeCb *ue Ue control block. * @param uint16_t curTime current Time * @param uint32_t duration Timer duration * @param CmLteTimingInfo *tmrExpryIdx Timer expry (SFN,sf) * @return ROK/RFAILED */ #ifdef ANSI PRIVATE S16 rgSCHDrxGetNxtTmrExpry ( RgSchCellCb *cell, uint16_t curTime, uint32_t duration, CmLteTimingInfo *tmrExpryIdx ) #else PRIVATE S16 rgSCHDrxGetNxtTmrExpry (cell,curTime,duration,tmrExpryIdx) RgSchCellCb *cell; uint16_t curTime; uint32_t duration; CmLteTimingInfo *tmrExpryIdx; #endif { uint32_t dlSfTillNxtSFN; /*!< DL subframes till next SFN */ uint8_t tddCfgMode; /*!< tdd config mode */ Bool isDwPtsCnted; /*!< is DwPts counted as PDCCH sf */ CmLteTimingInfo crntTime; /*!< current SFN & sf */ #if ( ERRCLASS & ERRCLS_INT_PAR ) if ( (cell == (RgSchCellCb* )NULLP) || (tmrExpryIdx == (CmLteTimingInfo* )NULLP) ) { return RFAILED; } #endif isDwPtsCnted = cell->isDwPtsCnted ; tddCfgMode = cell->ulDlCfgIdx; crntTime.sfn = curTime / RGSCH_NUM_SUB_FRAMES_5G; crntTime.slot = curTime % RGSCH_NUM_SUB_FRAMES_5G; /* First calculate the number of DL subframes till next SFN */ dlSfTillNxtSFN = rgSchDrxDLSfTillNxtSFN[isDwPtsCnted][tddCfgMode] [(crntTime.slot % RGSCH_NUM_SUB_FRAMES)]; if ( dlSfTillNxtSFN >= duration ) { /* the timer would expire on the same RF */ uint32_t diff = dlSfTillNxtSFN - duration; tmrExpryIdx->sfn = crntTime.sfn; if ( diff == 0 ) { tmrExpryIdx->subframe = rgSchDrxDLSftoDLSfIdx[isDwPtsCnted][tddCfgMode] [0]; } else { uint8_t arrayIdx; /* to know the DL sf index based on diff */ arrayIdx = rgSchDrxDlSfTddCfg[isDwPtsCnted][tddCfgMode]; tmrExpryIdx->subframe = rgSchDrxDLSftoDLSfIdx[isDwPtsCnted][tddCfgMode] [arrayIdx - diff]; } }/* if ( dlSfTillNxtSFN >= duration...*/ else { uint32_t remSf; /*!< remaining subframes */ uint32_t numRf; /*!< num of complete radio frames */ uint32_t numRemSfs; /*!< num of remaining subframes */ remSf = duration - dlSfTillNxtSFN; /* move to (currSFN,9) having consued dlSfTillNxtSFN DL subframes */ tmrExpryIdx->sfn = crntTime.sfn; tmrExpryIdx->subframe = (uint8_t)9; numRf = (1 * remSf)/rgSchDrxDlSfTddCfg[isDwPtsCnted][tddCfgMode]; numRemSfs = (1 * remSf)%rgSchDrxDlSfTddCfg[isDwPtsCnted][tddCfgMode]; tmrExpryIdx->sfn = tmrExpryIdx->sfn + numRf; /* we are now at (X,9) having consumed dlSfTillNxtSFN + numRf * num of DL * subframes in 1 RF */ if ( numRemSfs == 0 ) { /* we are at subframe 9 i.e. the timer is going to expire using exact * radio frames. However, not all TDD_configurations have 9 as their * last DL subframe. We might have passed the last DL subfrme. * Therefore, move back */ tmrExpryIdx->subframe = rgSchDrxDLSftoDLSfIdx[isDwPtsCnted][tddCfgMode] [numRemSfs]; } else { /* a reminder implies we have to move past this SFN as we are at the * last subframe on that SFN */ tmrExpryIdx->sfn++; tmrExpryIdx->subframe = rgSchDrxDLSftoDLSfIdx[isDwPtsCnted][tddCfgMode] [numRemSfs]; } }/*else if diff > duration */ /* timer will expire 1 + tmrExpryIdx */ if ( ( tmrExpryIdx->subframe + 1) == 10 ) { tmrExpryIdx->sfn++; tmrExpryIdx->subframe = 0; } else { tmrExpryIdx->subframe++; } /* check to see if it sfn has crossed its max */ if ( tmrExpryIdx->sfn > RG_SCH_CMN_MAX_SFN_NUM ) { tmrExpryIdx->sfn = tmrExpryIdx->sfn - (RG_SCH_CMN_MAX_SFN_NUM + 1); } return ROK; }/*rgSCHDrxGetNxtTmrExpry*/ /** @brief This function calculates the next onDuration Occurence * for TDD * @details * Invoked by - * * Function: rgSCHDrxCalcNxtTmrExpry * * Processing steps: a wrapper function to call * rgSCHDrxGetNxtTmrExpry * * @param RgSchCellCb *cell * @param RgSchUeCb *ue * @param uint16_t delta * @param uint32_t tmrLen * @param uint16_t *distance * @param S16 *idx * @return ROK/RFAILED */ #ifdef ANSI PRIVATE Void rgSCHDrxCalcNxtTmrExpry ( RgSchCellCb *cell, RgSchUeCb *ue, uint16_t delta, uint32_t tmrLen, S16 *distance, uint16_t *idx ) #else PRIVATE Void rgSCHDrxCalcNxtTmrExpry (cell,ue,delta,tmrLen,distance,idx) RgSchCellCb *cell; RgSchUeCb *ue; uint16_t delta; uint32_t tmrLen; S16 *distance; uint16_t *idx; #endif { uint16_t curTimeInSf; /*current time in no of subframes*/ CmLteTimingInfo tmrExpry; uint16_t tmrExpryInSf; /*timer expry in no of subframes*/ curTimeInSf = cell->crntTime.sfn * RGSCH_NUM_SUB_FRAMES_5G + cell->crntTime.slot; /* add delta to curTime */ curTimeInSf += delta; rgSCHDrxGetNxtTmrExpry(ue->cell,curTimeInSf,tmrLen,&tmrExpry); /* convert timre Expry in terms of subframes */ tmrExpryInSf = tmrExpry.sfn * RGSCH_NUM_SUB_FRAMES_5G + tmrExpry.subframe; *idx = (tmrExpryInSf) % RG_SCH_MAX_DRXQ_SIZE; if ( distance != NULLP ) /* hqReTx don't use the concept of distance. They can send NULLP for distance. */ { if ( tmrExpryInSf > curTimeInSf ) { *distance = (tmrExpryInSf - curTimeInSf) / RG_SCH_MAX_DRXQ_SIZE; } else { /* in case the RF is at its max and wraps around */ *distance = ((tmrExpryInSf + (RG_SCH_CMN_MAX_NUM_OF_SFN - 1)) - curTimeInSf) / RG_SCH_MAX_DRXQ_SIZE; } if (*distance < 0) { RLOG_ARG2(L_ERROR,DBG_CELLID,cell->cellId, "DRXUE. Invalid " "value for distance, %d CRNTI:%d", *distance,ue->ueId); } } return; }/*rgSCHDrxCalcNxtTmrExpry*/ /* ccpu00134670- Validating onduration timer versus DRX cycle*/ /*********************************************************** * * Func : rgSCHCfgVldtTddDrxCycCfg * * * Desc : Validates DRX Cycle Length configuration against received * onDuration timer from RRC. * * Ret : S16 * ROK - Success * * RFAILED - Failed * * Notes: * * File : * **********************************************************/ #ifdef ANSI S16 rgSCHCfgVldtTddDrxCycCfg ( RgSchCellCb *cell, uint16_t drxCycle, uint8_t onDurTmr, uint16_t offSet ) #else S16 rgSCHCfgVldtTddDrxCycCfg(cell, drxCycle, onDurTmr, offSet) RgSchCellCb *cell; uint16_t drxCycle; uint8_t onDurTmr; uint16_t offSet; #endif { uint16_t startTime; uint16_t endTimeInSf; CmLteTimingInfo endTime; startTime = offSet; do { rgSCHDrxGetNxtTmrExpry(cell, startTime, onDurTmr, &endTime); endTimeInSf = (endTime.sfn* RGSCH_NUM_SUB_FRAMES)+endTime.subframe; if(((RGSCH_MAX_SUBFRM_5G + endTimeInSf- startTime) % RGSCH_MAX_SUBFRM_5G) >= drxCycle) { return RFAILED; } startTime = (startTime + drxCycle); /* Going for next iteration if the DRX cycle is not multiple of 10. If it is multiple of 10(Number of Subframes in a SFN) then subframe, at which onduration timer can start, will be always same, Otherwise the possible subframe numbers, where the onDuration timer can start, is 5. Hence 4 more iterations are required. */ }while((drxCycle % RGSCH_NUM_SUB_FRAMES) && (startTime < (drxCycle * RGSCH_NUM_SUB_FRAMES/2))); return ROK; } #endif /*LTE_TDD */ /** @brief This function is called to handle onDurationTimer per TTI processing. * * @details * Invoked by - rgSCHDrxTtiInd * * Function: rgSCHDrxTtiHdlOnDurUl * * Processing steps: * * - OnDurationTimer is handled using the drxQ @sa RgSchDrxQ * * - For Uplink we shall look at an index that is * n + RG_SCH_DRX_UL_DELTA as * we are concerned with the PDCCH and not the actual PUSCH. * * * @param RgSchCellCb *cellCb * @param uint16_t ulIndex * @return Void */ #ifdef ANSI PRIVATE Void rgSCHDrxTtiHdlOnDurUl ( RgSchCellCb *cell, uint16_t ulIndex ) #else PRIVATE Void rgSCHDrxTtiHdlOnDurUl(cell, ulIndex) RgSchCellCb *cell; uint16_t ulIndex; #endif { CmLList *node; RgSchDRXCellCb *drxCell = NULLP; RgSchUeCb *ue = NULLP; RgSchDrxUeCb *drxUe = NULLP; CmLListCp ulInactvLst; /* list of UE's becoming DL-inactive */ RgSchCmnCell *cellSch = NULLP; Bool delInUlScan = FALSE; drxCell = (cell->drxCb); delInUlScan = drxCell->delInUlScan; /*********************************************************** * Scanning OnDurationQ in UL ***********************************************************/ /* For Uplink we shall look at an index that is n + RG_SCH_DRX_UL_DELTA as we are concerned with the PDCCH and not the actual PUSCH.*/ node = drxCell->drxQ[ulIndex].onDurationQ.first; while (node) { ue = (RgSchUeCb*)node->node; node = node->next; drxUe = RG_SCH_DRX_GET_UE(ue); if ( delInUlScan == FALSE) { drxUe->distance--; } if ( drxUe->distance != DRX_TMR_EXPRD ) { continue; } /* reset the bit mask to indicate that onduration has started */ drxUe->drxUlInactvMask &= ~RG_SCH_DRX_ONDUR_BITMASK; /* if no other condition is keeping UE as inactive, * activate UE */ RG_SCH_CMN_UL_UPDT_INACTV_MASK(cell, ue, RG_DRX_INACTIVE); if ( delInUlScan == TRUE ) { /*calculate next on duration occurence * and it to the onDuration Queue*/ rgSCHDrxMvToNxtOnDurOcc(cell,ue,ulIndex,FALSE); }/*delInUlScan == FALSE */ }/*while(node)*/ /*********************************************************** * Scanning OnDurationExpQ in UL ***********************************************************/ node = drxCell->drxQ[ulIndex].onDurationExpQ.first; /* Initialize UL inactive list */ cmLListInit(&ulInactvLst); while (node) { ue = (RgSchUeCb*)node->node; node = node->next; drxUe = RG_SCH_DRX_GET_UE(ue); if ( delInUlScan == FALSE ) { drxUe->onDurExpDistance--; } if ( drxUe->onDurExpDistance != DRX_TMR_EXPRD ) { continue; } /*UE is inactive as onduration has expired */ drxUe->drxUlInactvMask |= RG_SCH_DRX_ONDUR_BITMASK; if( !RG_SCH_DRX_UL_IS_UE_ACTIVE(drxUe)) { /* set the inactive bit to indicate UE has now become inactive */ ue->ul.ulInactvMask |= RG_DRX_INACTIVE; /* Add to DL inactive list */ cmLListAdd2Tail(&ulInactvLst,&(ue->ulDrxInactvLnk)); ue->ulDrxInactvLnk.node = (PTR)ue; } if ( delInUlScan == TRUE) { /*Remove from DRX queue*/ cmLListDelFrm(&(drxCell->drxQ[ulIndex].onDurationExpQ), &(drxUe->onDurationExpEnt)); drxUe->onDurExpIndx = DRX_INVALID; } }/*while(node)*/ /* Send the list to the scheduler to mark UE as inactive in UL*/ cellSch = RG_SCH_CMN_GET_CELL(cell); cellSch->apisUl->rgSCHUlInactvtUes(cell, &ulInactvLst); return; } /** @brief This function is called to handle onDurationTimer per TTI processing. * * @details * Invoked by - rgSCHDrxTtiInd * * Function: rgSCHDrxTtiHdlOnDurDl * * Processing steps: * * - OnDurationTimer is handled using the drxQ @sa RgSchDrxQ * * - For Downlink we shall look at an index that is * n + RG_SCH_DRX_DL_DELTA. * * * @param RgSchCellCb *cellCb * @param uint16_t dlIndex * @return Void */ #ifdef ANSI PRIVATE Void rgSCHDrxTtiHdlOnDurDl ( RgSchCellCb *cell, uint16_t dlIndex ) #else PRIVATE Void rgSCHDrxTtiHdlOnDurDl(cell, dlIndex) RgSchCellCb *cell; uint16_t dlIndex; #endif { CmLList *node; RgSchDRXCellCb *drxCell = NULLP; RgSchUeCb *ue = NULLP; RgSchDrxUeCb *drxUe = NULLP; RgSchCmnCell *cellSch = NULLP; uint16_t expiryIndex; CmLListCp dlInactvLst; /* list of UE's becoming DL-inactive */ Bool delInUlScan = FALSE; /* The DL loop, if onDurationTimer has started, will add the UeCb * in the onDurationTmrExprQ. If !delInUlScan, then calculate the next * OnDuration occurence, q it there and remove it from the current location. */ /*********************************************************** * Scanning OnDurationQ in DL ***********************************************************/ drxCell = (cell->drxCb); delInUlScan = drxCell->delInUlScan; //printf("CELL Timer [SFN : %d],[SF : %d]\n",cell->crntTime.sfn,cell->crntTime.slot); node = drxCell->drxQ[dlIndex].onDurationQ.first; while (node) { ue = (RgSchUeCb* )node->node; node = node->next; drxUe = RG_SCH_DRX_GET_UE(ue); if ( delInUlScan == TRUE) { drxUe->distance--; } if ( drxUe->distance != DRX_TMR_EXPRD ) { continue; } /* UE is active as onduration is to start */ drxUe->drxDlInactvMask &= ~RG_SCH_DRX_ONDUR_BITMASK; /* set the UE as DRX active*/ /* Update UE's inactive mask and if required move UE to ACTIVE state */ RG_SCH_CMN_DL_UPDT_INACTV_MASK(cell, ue, RG_DRX_INACTIVE); /*ACC-TDD */ /* Temporary fix to delete stale entry */ if (drxUe->onDurExpIndx != DRX_INVALID) { RLOG_ARG4(L_DEBUG,DBG_CELLID,cell->cellId, "UEID:%d PreExisted[%d:%d]in onDurExpQ new[%d]", ue->ueId, drxUe->onDurExpIndx, drxUe->onDurExpDistance, dlIndex); cmLListDelFrm(&(drxCell->drxQ[drxUe->onDurExpIndx].onDurationExpQ), &(drxUe->onDurationExpEnt)); drxUe->onDurExpIndx = DRX_INVALID; } /*start the onduration expiry timer*/ #ifdef LTE_TDD rgSCHDrxCalcNxtTmrExpry(cell, ue, RG_DL_DELTA, drxUe->onDurTmrLen, &(drxUe->onDurExpDistance), &(expiryIndex) ); #else /*LTE_TDD */ expiryIndex = ((dlIndex + drxUe->onDurTmrLen) % RG_SCH_MAX_DRXQ_SIZE); drxUe->onDurExpDistance = (drxUe->onDurTmrLen)/ RG_SCH_MAX_DRXQ_SIZE; #endif /*LTE_TDD */ cmLListAdd2Tail(&(drxCell->drxQ[expiryIndex].onDurationExpQ), &(drxUe->onDurationExpEnt)); //printf("DRXOnDuration Timer Started at [SFN : %d],[SF : %d]\n",cell->crntTime.sfn,cell->crntTime.slot); drxUe->onDurationExpEnt.node = (PTR)ue; drxUe->onDurExpIndx = expiryIndex; //printf("DRxOnDuration will Expire = [%d]\n",(cell->crntTime.sfn*10+cell->crntTime.slot+drxUe->onDurTmrLen)); if ( delInUlScan == FALSE ) { /*calculate next on duration occurence * and it to the onDuration Queue*/ rgSCHDrxMvToNxtOnDurOcc(cell,ue,dlIndex,FALSE); }/*delInUlScan == FALSE */ }/*while(node)*/ /*********************************************************** * Scanning OnDurationExpQ in DL ***********************************************************/ /* Mark UE as Inactive based on OnDuration Expiry */ node = drxCell->drxQ[dlIndex].onDurationExpQ.first; /* Initialize DL inactive list */ cmLListInit(&dlInactvLst); while (node) { ue = (RgSchUeCb*)node->node; node = node->next; drxUe = RG_SCH_DRX_GET_UE(ue); if ( delInUlScan == TRUE ) { drxUe->onDurExpDistance--; } if ( drxUe->onDurExpDistance != DRX_TMR_EXPRD ) { continue; } /* UE is inactive as onduration has expired */ drxUe->drxDlInactvMask |= (RG_SCH_DRX_ONDUR_BITMASK); if( !RG_SCH_DRX_DL_IS_UE_ACTIVE(drxUe)) { /* set the inactive bit to indicate UE has now become inactive */ ue->dl.dlInactvMask |= RG_DRX_INACTIVE; /* Add to DL inactive list */ cmLListAdd2Tail(&dlInactvLst,&(ue->dlDrxInactvLnk)); ue->dlDrxInactvLnk.node = (PTR)ue; } if ( delInUlScan == FALSE ) { /*Remove from DRX queue*/ cmLListDelFrm(&(drxCell->drxQ[dlIndex].onDurationExpQ), &(drxUe->onDurationExpEnt)); drxUe->onDurExpIndx = DRX_INVALID; } } /* Send the list to the scheduler to mark UE as inactive */ cellSch = RG_SCH_CMN_GET_CELL(cell); cellSch->apisDl->rgSCHDlInactvtUes(cell, &dlInactvLst); return; }/*rgSCHDrxTtiHdlOnDurDl*/ /** @brief This function handles the Dl HARQ timer's processing per TTI. * * @details * Invoked by - rgSCHDrxTtiHdlDlHarq * * Function: rgSCHDrxTtiHdlDlHarqRTT * * Processing steps: * - In addition per TTI DRX module must look at Downlink HARQ queues * maintained to track HARQ RTT timer and drx-RetransmissionTimer. * Every TTI at the scheduling index we shall check these queues and * process accordingly. * * @param RgSchCellCb *cellCb * @param uint16_t dlIndex * @return Void */ #ifdef ANSI PRIVATE Void rgSCHDrxTtiHdlDlHarqRTT ( RgSchCellCb *cell, uint16_t dlIndex ) #else /* ANSI */ PRIVATE Void rgSCHDrxTtiHdlDlHarqRTT(cell, dlIndex) RgSchCellCb *cell; uint16_t dlIndex; #endif /* ANSI */ { CmLList *node; RgSchDrxDlHqProcCb *drxHq; RgSchDlHqProcCb *dlHq; RgSchDRXCellCb *drxCell; RgSchDrxUeCb *drxUe; uint16_t reTxExpIndx; Bool delInUlScan; RgSchUeCb *ue; CmLListCp dlInactvLst; /* list of UE's becoming DL-inactive */ RgSchCmnCell *cellSch = RG_SCH_CMN_GET_CELL(cell); uint8_t cellIdx; uint32_t dlInactvMask; drxCell = cell->drxCb; delInUlScan = drxCell->delInUlScan; /*********************************************************** * Scanning harqRTTQ in DL ***********************************************************/ cmLListInit(&dlInactvLst); node = drxCell->drxQ[dlIndex].harqRTTQ.first; while (node) { dlHq = (RgSchDlHqProcCb*)node->node; node = node->next; ue = dlHq->hqE->ue; #ifdef EMTC_ENABLE if(TRUE == ue->isEmtcUe) { continue; } #endif drxHq = RG_SCH_DRX_GET_DL_HQ(dlHq); drxUe = RG_SCH_DRX_GET_UE(ue); cellIdx = ue->cellIdToCellIdxMap[RG_SCH_CELLINDEX(dlHq->hqE->cell)]; /* add the UE to the cell's retransmission queuee before starting * reTx timer, because this will not depend on retx timer trigger*/ rgSCHUtlDlProcAddToRetx(dlHq->hqE->cell, dlHq); if ( delInUlScan == FALSE) { cmLListDelFrm (&(drxCell->drxQ[dlIndex].harqRTTQ), &(drxHq->harqRTTEnt)); drxHq->rttIndx = DRX_INVALID; } /* ccpu00134565: Starting retransmission timer only if timerLen is * having non-zero value after reduction, Adding to Retx queue is * independent of starting retransmission timer. Hence if part will * take care of starting retx timer only*/ if (drxUe->drxRetransTmrLen > drxHq->retxTmrReduction) { /* add the harq proc to the re-tx queue--*/ #ifdef LTE_TDD /* ccpu00134196-[Add]-DRX retx timer changes */ rgSCHDrxCalcNxtTmrExpry(cell, ue, RG_DL_DELTA, drxUe->drxRetransTmrLen-drxHq->retxTmrReduction, NULLP, /*retransQ does not maintain distance*/ &reTxExpIndx ); #else /*LTE_TDD*/ /* ccpu00134196-[Add]-DRX retx timer changes */ reTxExpIndx = ((dlIndex + drxUe->drxRetransTmrLen-drxHq->retxTmrReduction) % RG_SCH_MAX_DRXQ_SIZE); #endif /*LTE_TDD*/ /* TODO. Workaround to avoid duplicate entry */ if(drxHq->reTxIndx == DRX_INVALID) { cmLListAdd2Tail (&(drxCell->drxQ[reTxExpIndx].harqRetxQ), &(drxHq->harqRetxEnt)); drxHq->harqRetxEnt.node = (PTR)dlHq; drxHq->reTxIndx = reTxExpIndx; } else { RLOG_ARG4(L_ERROR,DBG_CELLID,cell->cellId,"CRNTI:%d " "Adding Retx Node to expire at RetxIndx: %d at dlIndex %d " "drxHq->reTxIndx %d", ue->ueId, reTxExpIndx, dlIndex, drxHq->reTxIndx); continue; } /*mark the ue as active for downlink--*/ drxUe->drxDlInactvMask &= ~(RG_SCH_DRX_DLHQ_BITMASK << dlHq->procId); drxUe->drxDlInactvMaskPerCell[cellIdx] &= ~(RG_SCH_DRX_DLHQ_BITMASK << dlHq->procId); /* Update UE's inactive mask and if required move UE to ACTIVE state */ RG_SCH_CMN_DL_UPDT_INACTV_MASK(cell, ue, RG_DRX_INACTIVE); } }/*while(node)*/ /*********************************************************** * Scanning harqRetxQ in DL ***********************************************************/ node = drxCell->drxQ[dlIndex].harqRetxQ.first; while (node) { dlHq = (RgSchDlHqProcCb*)node->node; ue = dlHq->hqE->ue; drxUe = RG_SCH_DRX_GET_UE(ue); node = node->next; drxHq = RG_SCH_DRX_GET_DL_HQ(dlHq); cellIdx = ue->cellIdToCellIdxMap[RG_SCH_CELLINDEX(dlHq->hqE->cell)]; /*mark the ue as in-active for downlink*/ drxUe->drxDlInactvMaskPerCell[cellIdx] |= (RG_SCH_DRX_DLHQ_BITMASK << dlHq->procId); dlInactvMask = RG_SCH_DRX_DLHQ_BITMASK << dlHq->procId; for(cellIdx = 0; cellIdx < CM_LTE_MAX_CELLS; cellIdx++) { dlInactvMask &= drxUe->drxDlInactvMaskPerCell[cellIdx]; } drxUe->drxDlInactvMask |= dlInactvMask; /* if no other condition is keeping ue active, * inactivate the Ue */ if ( !RG_SCH_DRX_DL_IS_UE_ACTIVE(drxUe)) { ue->dl.dlInactvMask |= (RG_DRX_INACTIVE); /* Add to DL inactive list */ cmLListAdd2Tail(&dlInactvLst,&(ue->dlDrxInactvLnk)); ue->dlDrxInactvLnk.node = (PTR)ue; } /*remove the harq proc from this queue*/ if ( delInUlScan == FALSE) { cmLListDelFrm (&(drxCell->drxQ[dlIndex].harqRetxQ), &(drxHq->harqRetxEnt)); drxHq->reTxIndx = DRX_INVALID; } } /*Call schedulers to inactivate ue*/ cellSch->apisDl->rgSCHDlInactvtUes(cell, &dlInactvLst); return; } /** @brief This function handles the Ul HARQ timer's processing per TTI. * * @details * Invoked by - rgSCHDrxTtiHdlDlHarq * * Function: rgSCHDrxTtiHdlUlHarqRTT * * Processing steps: * - In addition per TTI DRX module must look at Downlink HARQ queues * maintained to track HARQ RTT timer and drx-RetransmissionTimer. * Every TTI at the scheduling index we shall check these queues and * process accordingly. * * - Though these timers are related to downlink HARQ processing, they * have an impact on uplink scheduling. The reason is that the UE, * if active for downlink scheduling implies that it is reading * PDCCHs i.e. we can still send uplink allocations to the UE. Hence * every TTI Uplink too would look at the harqRTTQ and harqRetxQ. * * * * @param RgSchCellCb *cellCb * @param uint16_t ulIndex * @return Void */ #ifdef ANSI PRIVATE Void rgSCHDrxTtiHdlUlHarqRTT ( RgSchCellCb *cell, uint16_t ulIndex ) #else /* ANSI */ PRIVATE Void rgSCHDrxTtiHdlUlHarqRTT(cell, ulIndex) RgSchCellCb *cell; uint16_t ulIndex; #endif /* ANSI */ { CmLList *node; RgSchDrxDlHqProcCb *drxHq; RgSchDlHqProcCb *dlHq; RgSchDRXCellCb *drxCell; RgSchDrxUeCb *drxUe; Bool delInUlScan; RgSchUeCb *ue; CmLListCp ulInactvLst; /* list of UE's becoming DL-inactive */ RgSchCmnCell *cellSch = RG_SCH_CMN_GET_CELL(cell); uint8_t cellIdx; uint32_t ulInactvMask; drxCell = cell->drxCb; delInUlScan = drxCell->delInUlScan; cmLListInit(&ulInactvLst); /*********************************************************** * Scanning harqRTTQ in UL ***********************************************************/ /* Though these timers are related to downlink HARQ processing, they have an impact on uplink scheduling. The reason is that the UE, if active for downlink scheduling implies that it is reading PDCCHs i.e. we can still send uplink allocations to the UE. Hence every TTI Uplink too would look at the harqRTTQ and harqRetxQ. */ node = drxCell->drxQ[ulIndex].harqRTTQ.first; while (node) { dlHq = (RgSchDlHqProcCb*)node->node; ue = dlHq->hqE->ue; drxUe = RG_SCH_DRX_GET_UE(ue); node = node->next; drxHq = RG_SCH_DRX_GET_DL_HQ(dlHq); cellIdx = ue->cellIdToCellIdxMap[RG_SCH_CELLINDEX(dlHq->hqE->cell)]; if ( delInUlScan == TRUE ) { /* remove the harq proc from this queue--*/ cmLListDelFrm (&(drxCell->drxQ[ulIndex].harqRTTQ), &(drxHq->harqRTTEnt)); drxHq->rttIndx = DRX_INVALID; } /* mark the ue as active for uplink--*/ drxUe->drxUlInactvMask &= ~(RG_SCH_DRX_DLHQ_BITMASK << dlHq->procId); drxUe->drxUlInactvMaskPerCell[cellIdx] &= ~(RG_SCH_DRX_DLHQ_BITMASK << dlHq->procId); /* Update UE's inactive mask and if required move UE to ACTIVE state */ RG_SCH_CMN_UL_UPDT_INACTV_MASK( cell, ue, RG_DRX_INACTIVE); } /*********************************************************** * Scanning harqRetxQ in UL ***********************************************************/ node = drxCell->drxQ[ulIndex].harqRetxQ.first; while (node) { dlHq = (RgSchDlHqProcCb*)node->node; ue = dlHq->hqE->ue; drxUe = RG_SCH_DRX_GET_UE(ue); drxHq = RG_SCH_DRX_GET_DL_HQ(dlHq); cellIdx = ue->cellIdToCellIdxMap[RG_SCH_CELLINDEX(dlHq->hqE->cell)]; /*mark the ue as in-active for uplink*/ drxUe->drxUlInactvMaskPerCell[cellIdx] |= (RG_SCH_DRX_DLHQ_BITMASK << dlHq->procId); ulInactvMask = RG_SCH_DRX_DLHQ_BITMASK << dlHq->procId; for(cellIdx = 0; cellIdx < CM_LTE_MAX_CELLS; cellIdx++) { ulInactvMask &= drxUe->drxUlInactvMaskPerCell[cellIdx]; } drxUe->drxUlInactvMask |= ulInactvMask; if(!RG_SCH_DRX_UL_IS_UE_ACTIVE(drxUe)) { ue->ul.ulInactvMask |= (RG_DRX_INACTIVE); cmLListAdd2Tail(&ulInactvLst,&(ue->ulDrxInactvLnk)); ue->ulDrxInactvLnk.node = (PTR)ue; } /* remove the harq proc from this queue*/ if ( delInUlScan == TRUE) { cmLListDelFrm (&(drxCell->drxQ[ulIndex].harqRetxQ), &(drxHq->harqRetxEnt)); drxHq->reTxIndx = DRX_INVALID; } node = node->next; } cellSch->apisUl->rgSCHUlInactvtUes(cell, &ulInactvLst); return; } /********************************************************************** End of file **********************************************************************/