X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=src%2F5gnrrlc%2Fkw_amm_dl.c;fp=src%2F5gnrrlc%2Fkw_amm_dl.c;h=7b309f828a848cbde84b198e0fbdc99da74d44ce;hb=5625a52ad68f6ad93684e68bbbdbaef0d462cf9a;hp=0000000000000000000000000000000000000000;hpb=59f84608ec15c016958a6e0e0ddd813f376c0925;p=o-du%2Fl2.git diff --git a/src/5gnrrlc/kw_amm_dl.c b/src/5gnrrlc/kw_amm_dl.c new file mode 100755 index 000000000..7b309f828 --- /dev/null +++ b/src/5gnrrlc/kw_amm_dl.c @@ -0,0 +1,4184 @@ +/******************************************************************************* +################################################################################ +# 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. # +################################################################################ +*******************************************************************************/ + +/********************************************************************20** + + Name: RLC - AM DL module file + + Type: C source file + + Desc: Source code for Acknowledged Mode Module functions such as, + + Transmission of data/control PDUs + Retransmission (Feedback in terms of status) + Polling + Assemble SDUs + Reception - reordering + Duplicate detection for byte segments + Reassemble SDUs + + File: kw_amm_dl.c + +*********************************************************************21*/ +static const char* RLOG_MODULE_NAME="AMM"; +static int RLOG_MODULE_ID=2048; +static int RLOG_FILE_ID=189; +/* header include files (.h) */ +#include "envopt.h" /* environment options */ +#include "envdep.h" /* environment dependent */ +#include "envind.h" /* environment independent */ + +#include "gen.h" /* general */ +#include "ssi.h" /* system services */ +#include "cm5.h" /* common timer defines */ +#include "cm_tkns.h" /* common tokens defines */ +#include "cm_mblk.h" /* common memory allocation library defines */ +#include "cm_llist.h" /* common link list defines */ +#include "cm_hash.h" /* common hash list defines */ +#include "cm_lte.h" /* common LTE defines */ +#include "lkw.h" /* LKW defines */ +#include "ckw.h" /* CKW defines */ +#include "kwu.h" /* KWU defines */ +#include "rgu.h" /* RGU defines */ +#include "kw_err.h" /* Err defines */ +#include "kw_env.h" /* RLC environment options */ + +#include "kw.h" /* RLC defines */ +#include "kw_udx.h" +#include "kw_dl.h" + + +/* extern (.x) include files */ +#include "gen.x" /* general */ +#include "ssi.x" /* system services */ +#include "cm5.x" /* common timer library */ +#include "cm_tkns.x" /* common tokens */ +#include "cm_mblk.x" /* common memory allocation */ +#include "cm_llist.x" /* common link list */ +#include "cm_hash.x" /* common hash list */ +#include "cm_lte.x" /* common LTE includes */ +#include "cm_lib.x" /* common memory allocation library */ +#include "lkw.x" /* LKW */ +#include "ckw.x" /* CKW */ +#include "kwu.x" /* KWU */ +#include "rgu.x" /* RGU */ + +#include "kw.x" +#include "kw_udx.x" +#include "kw_dl.x" + +//UDAY +#ifdef L2_OPTMZ +extern U32 kwAmmStaPduList[512]; + U32 kwAmmStaPduListCnt = 0; +#endif + + +/** @file gp_amm_dl.c +@brief RLC Acknowledged Mode Downlink Module +**/ +#define KW_MODULE (KW_DBGMASK_AM | KW_DBGMASK_DL) + +U32 kwStatusPduCnt, kwStatusAckCnt, kwStatusNcnt, kwSduSndCnt; + +/* local defines */ + +/* local externs */ + +/* forward references */ +EXTERN Void kwAmmDlHndlStatusPdu ARGS ((KwCb *gCb, + KwDlRbCb *rbCb, + KwUdxStaPdu *pStaPdu)); + +/* public variable declarations */ + +/* This structure holds all the global structs we need. */ + +/* private variable declarations */ + +#define KW_AM_RMV_HDR(_gCb, _rbCb, _retx) do { \ + if ((_retx)->yetToConst == FALSE) \ + {\ + Buffer *_pduInfo; \ + SSegMsg((_retx)->seg, (_retx)->hdrSz, &_pduInfo); \ + KW_FREE_BUF((_retx)->seg); \ + (_retx)->seg = _pduInfo; \ + }\ + (_rbCb)->m.amDl.estHdrSz -= retx->hdrSz;\ +} while(0) + +/* private function declarations */ + +PRIVATE Void kwResegRetxPdus ARGS ((KwCb *gCb, + KwDlRbCb *rbCb, + KwDatReq *kwDatReq)); + +PRIVATE Void kwRemRetxPdu ARGS ((KwCb *gCb, + KwDlRbCb *rbCb, + KwRetx *retx)); + +PRIVATE Void kwAmmCreateStatusPdu ARGS ((KwCb *gCb, + KwDlRbCb *rbCb, + KwDatReq *kwDatReq)); + +PRIVATE Void kwAmmDlMarkPduForReTx ARGS ((KwCb *gCb, + KwDlRbCb *rbCb, + KwRetx *retx)); + +PRIVATE Void kwAmmDlProcessSuccessfulTxPdu ARGS((KwCb *gCb, + KwDlRbCb *rbCb, + KwSn sn, + KwuDatCfmInfo **datCfm)); + +PRIVATE Void kwAmmDlSetTxNextAck ARGS((KwAmDl *amDl, KwSn sn)); + +PRIVATE Void kwAmmDlCheckAndStopPollTmr ARGS((KwCb *gCb, + KwDlRbCb *rbCb, + KwSn mAckSn)); + +PRIVATE Void kwAssembleSdus ARGS ((KwCb *gCb, + KwDlRbCb *rbCb, + KwDatReq *kwDatReq)); + +PRIVATE Bool kwAmmDlCheckAndSetPoll ARGS ((KwCb *gCb, + KwDlRbCb *rbCb, + Bool newPdu, + MsgLen bufSz)); + +PRIVATE Void kwAmmCreatePdu ARGS ((KwCb *gCb, + KwDlRbCb *rbCb, + KwAmHdr *amHdr, + KwDlPduInfo *pduInfo, + Buffer *pdu)); + +PRIVATE Void kwAmmSndStaInd ARGS ((KwCb *gCb,KwDlRbCb *rbCb, KwRetx *retx)); + +PRIVATE Void kwGetNxtRetx ARGS ((KwCb *gCb, KwRetx **retx)); + +PRIVATE Void kwConstructAmHdr ARGS ((KwAmHdr *amHdr, + U8 *hdr, + U8 snLen, + U16 *idx)); + +PRIVATE Void kwAmmDlUpdateTxAndReTxBufForAckSn ARGS ((KwCb *gCb, + KwDlRbCb *rbCb, + KwSn mAckSn, + CmLList *retx, + KwuDatCfmInfo **datCfm)); + +PRIVATE Void kwAmmDlMoveFrmTxtoRetxBuffer ARGS ((KwCb *gCb, + KwAmDl *amDl, + KwRetx **retx, + KwSn sn)); + +PRIVATE Void kwAmmDlCheckIsSDUDelivered ARGS((KwCb *gCb, + KwDlRbCb *rbCb, + KwSduMap *sduMap, + KwuDatCfmInfo **datCfm)); + +PRIVATE Void kwAmmAddPduToRetxLst ARGS((KwAmDl *amDl, + KwRetx *retx)); + +PRIVATE Void kwAmmDlMoveSduByteSegFrmTxtoRetxBuffer ARGS( +( +KwCb *gCb, +KwAmDl *amDl, +KwRetx **retx, +KwDlPduInfo *pduInfo +)); + +PRIVATE Void kwAmmDlHndlStatus4SduByteSegInTxBuf ARGS( +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwNackInfo *nackSnInfo, +KwRetx **retx, +KwuDatCfmInfo **datCfm +)); + +PRIVATE Void kwAmmDlUpdateTxAndReTxBufForNackSn ARGS( +( + KwCb *gCb, + KwDlRbCb *rbCb, + KwNackInfo *nackSnInfo, + CmLList **retxNode, + KwuDatCfmInfo **datCfm + )); + +PRIVATE Void KwDlAmmGetNackSnInfoFrmNackRangeIdx ARGS( +( +KwAmDl *amDl, +KwNackInfo *nackInfo, +CmLList *retxNode, +KwNackInfo *nackSnInfo, +U8 idx +)); + +PRIVATE Void kwAmmDlUpdTxAndReTxBufForLessThanNackSn ARGS( +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwSn sn, +KwSn mNackSn, +CmLList **retxNode, +KwuDatCfmInfo **datCfm +)); +/***************************************************************************** + + AM Module contains the following funcitons: + + - kwAmmQSdu + - kwAmmProcessSdus + - kwAmmDlAssembleCntrlInfo + - kwResegRetxPdus + - kwAssembleSdus + - kwAmmDlCheckAndSetPoll + - kwAmmProcessPdus + - kwDlmHndlStaRsp + - kwTriggerStatus + - kwReassembleSdus + +*******************************************************************************/ +/** @addtogroup ammode */ +/*@{*/ + +/** + * @brief Function to send a Status Response to MAC for a dedicated logical + * channel + * + * @details + * Function calculates the current bo and send a Status response for the + * dedicated logical channel if the bo is non zero + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb Radio Bearer control block + * @param[in] amDl AM downlink control block + * + * @return Void +*/ +#ifdef ANSI +PUBLIC Void kwAmmSendDStaRsp +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwAmDl *amDl +) +#else +PUBLIC Void kwAmmSendDStaRsp(gCb, rbCb, amDl) +KwCb *gCb; +KwDlRbCb *rbCb; +KwAmDl *amDl; +#endif +{ + S32 bo = kwAmmCalculateBo(amDl); + + if(bo) + { + kwUtlSndDStaRsp(gCb, rbCb, bo, amDl->estHdrSz, amDl->cntrlBo ?TRUE:FALSE,amDl->cntrlBo); + } + + RETVOID; +} + +/** + * @brief Function to check if the pollSn is acked and stop the poll timer + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb Radio Bearer control block + * @param[in] mAckSn The last received ACKSN. The base modulus value should + * be passed + * + * @return Void +*/ +#ifdef ANSI +PRIVATE Void kwAmmDlCheckAndStopPollTmr +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwSn mAckSn +) +#else +PRIVATE Void kwAmmDlCheckAndStopPollTmr(gCb, rbCb, mAckSn) +KwCb *gCb; +KwDlRbCb *rbCb; +KwSn mAckSn; +#endif +{ + KwSn mPollSn; + + MODAMT(rbCb->m.amDl.pollSn, mPollSn, rbCb->m.amDl.txNextAck,rbCb->m.amDl.snModMask); + + if (mPollSn <= mAckSn) + { + if (kwChkTmr(gCb, (PTR)rbCb, KW_EVT_AMDL_POLL_RETX_TMR)) + { + kwStopTmr(gCb, (PTR)rbCb, KW_EVT_AMDL_POLL_RETX_TMR); + } + } + + RETVOID; +} + +/** + * @brief Function to set VT(A) and VT(MS). Calculates the VT(MS) from VT(A) + * + * @param[in,out] amDl AM downlink control block + * @param[in]sn Sequence number to be set as VT(A) + * + * @return Void +*/ +#ifdef ANSI +PRIVATE Void kwAmmDlSetTxNextAck +( +KwAmDl *amDl, +KwSn sn +) +#else +PRIVATE Void kwAmmDlSetTxNextAck(amDl, sn) +KwAmDl *amDl; +KwSn sn +#endif +{ + amDl->txNextAck = sn; + + RETVOID; +} + +/** + * @brief Function to process a successfully re-transmitted PDU/segment + * + * @details + * Checks if the SDU has been completely delivered or not. Removes the PDU + * from the re-transmission buffer + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb Downlink Radio Bearer control block + * @param[in] retx The PDU/segment which was successfully re-transmitted + * + * @return Void +*/ +#ifdef ANSI +PRIVATE Void kwAmmDlProcessSuccessfulReTx +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwRetx *retx, +KwuDatCfmInfo **datCfm +) +#else +PRIVATE Void kwAmmDlProcessSuccessfulReTx(gCb, rbCb, retx, datCfm) +KwCb *gCb; +KwDlRbCb *rbCb; +KwRetx *retx; +KwuDatCfmInfo **datCfm; +#endif +{ + kwAmmDlCheckIsSDUDelivered(gCb, rbCb, &(retx->sduMap), datCfm); + + kwRemRetxPdu(gCb, rbCb, retx); + + RETVOID; +} + +/** + * @brief Handler to Move the PDU from txBuf to re-transmission buffer + * + * @details + * This function is used to move the PDU from the txBuf to re-transmit buffer + * + * @param[in]KwCb *gCb RLC instance control block + * @param[in]KwAmDl *amDl AM Downlink Control Block + * @param[in]KwRetx **retx node in the reTx buffer to be moved to, allocated by + * this function + * @param[in]KwDlPduInfo *pduInfo TX PDU which needs to be moved + * + * @return Void + * + */ + +#ifdef ANSI +PRIVATE Void kwAmmDlMoveSduByteSegFrmTxtoRetxBuffer +( +KwCb *gCb, +KwAmDl *amDl, +KwRetx **retx, +KwDlPduInfo *pduInfo +) +#else +PRIVATE Void kwAmmDlMoveSduByteSegFrmTxtoRetxBuffer(gCb, amDl, retx, pduInfo) +KwCb *gCb; +KwAmDl *amDl; +KwRetx **retx; +KwDlPduInfo *pduInfo; +#endif +{ + TRC2(kwAmmDlMoveSduByteSegFrmTxtoRetxBuffer); + + + KW_ALLOC_WC(gCb,*retx, sizeof(KwRetx)); + +#if (ERRCLASS & ERRCLS_ADD_RES) + if (*retx == NULLP) + { + RLOG0(L_FATAL, "Memory allocation failed"); + RETVOID; + } +#endif /* ERRCLASS & ERRCLS_RES */ + + (*retx)->seg = pduInfo->pdu; + (*retx)->segSz = pduInfo->pduSz; + /* MS_FIX for DL stall */ + (*retx)->soEnd = (pduInfo->amHdr.so + pduInfo->pduSz - 1); + + (*retx)->hdrSz = pduInfo->hdrSz; + (*retx)->retxCnt = 1; + (*retx)->yetToConst = 0; + (*retx)->pendingReTrans = TRUE; + + /* initialize the list pointer to 0 instead of memset */ + (*retx)->lstEnt.next = 0; + (*retx)->lstEnt.prev = 0; + /* copy the sdu maps */ + KW_MEM_CPY(&((*retx)->sduMap), + &pduInfo->sduMap, + sizeof(KwSduMap)); + + KW_MEM_CPY(&((*retx)->amHdr), &pduInfo->amHdr, sizeof(KwAmHdr)); + kwAmmAddPduToRetxLst(amDl, (*retx)); + + /* Update the BO appropriately */ + amDl->retxBo += (*retx)->segSz; + amDl->estHdrSz += (*retx)->hdrSz; + + gRlcStats.amRlcStats.numDLRetransPdus++; + + RETVOID; +} /*kwAmmDlMoveSduByteSegFrmTxtoRetxBuffer */ + +/** + * @brief Function to handle Status of Sdu byte segment for a nackSn + * + * @details + * This function is used to move the PDU from the txBuf to re-transmit buffer + * + * @param[in]KwCb *gCb RLC instance control block + * @param[in]KwDlRbCb *rbCb AM Downlink Control Block + * @param[in]KwNackInfo *nackSnInfo Nack Information of a NACK_SN + * @param[in]KwRetx **retx node in the reTx buffer to be moved to, allocated by + * this function + * @param[in]KwuDatCfmInfo **datCfm Ptr to datCfm + * + * @return Void + * + */ + +#ifdef ANSI +PRIVATE Void kwAmmDlHndlStatus4SduByteSegInTxBuf +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwNackInfo *nackSnInfo, +KwRetx **retx, +KwuDatCfmInfo ** datCfm +) +#else +PRIVATE Void kwAmmDlHndlStatus4SduByteSegInTxBuf(gCb, rbCb, nackSnInfo, retx, datCfm) +( +KwCb *gCb; +KwDlRbCb *rbCb; +KwNackInfo *nackSnInfo; +KwRetx **retx; +KwuDatCfmInfo **datCfm; +) +#endif +{ + KwTx *txBuf=NULLP; + CmLList *lnk; + CmLList *nextLnk; + + TRC2(kwAmmDlHndlStatus4SduByteSegInTxBuf) + + txBuf = kwUtlGetTxBuf(AMDL.txBufLst, nackSnInfo->sn); + if (txBuf == NULLP) + { + RETVOID; + } + lnk = txBuf->pduLst.first; + while(lnk) + { + KwDlPduInfo *pduInfo = (KwDlPduInfo *)(lnk->node); + KwSn pduSoEnd = (pduInfo->amHdr.so + pduInfo->sduMap.sduSz - 1); + + /* If So of Sdu byte segment(pduInfo/seg) is < status pdu + soStart that means it's ACKED*/ + if(pduSoEnd < nackSnInfo->soStart) + { + kwAmmDlCheckIsSDUDelivered(gCb, + rbCb, + &(pduInfo->sduMap), + datCfm); + + } + else if (pduSoEnd <= nackSnInfo->soEnd) + { + /* Move Sdu byte segment from TX buf to retx buf*/ + kwAmmDlMoveSduByteSegFrmTxtoRetxBuffer(gCb, + &rbCb->m.amDl, + retx, + pduInfo); + } + else + { + lnk = lnk->next; + continue; + } + nextLnk = lnk->next; + /* Delete node from the txBuf Pdu lst */ + cmLListDelFrm(&txBuf->pduLst, lnk); + KW_FREE_WC(gCb, pduInfo, sizeof(KwDlPduInfo)); + lnk = nextLnk; + } + if(!txBuf->pduLst.count) + { + /*No more Sdu byte segment are left. Hence delete txBuf*/ + kwUtlDelTxBuf(AMDL.txBufLst, txBuf,gCb); + } + + RETVOID; +} + +/** + * @brief Function to handle Status of Sdu byte segment for a nackSn + * + * @details + * This function is used to move the PDU from the txBuf to re-transmit buffer + * + * @param[in]KwCb *gCb RLC instance control block + * @param[in]KwDlRbCb *rbCb AM Downlink Control Block + * @param[in]KwNackInfo *nackSnInfo Nack Information of a NACK_SN + * @param[in]KwRetx **retx node in the reTx buffer to be moved to, allocated by + * this function + * @param[in]KwuDatCfmInfo **datCfm Ptr to datCfm + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAmmDlUpdateTxAndReTxBufForNackSn +( + KwCb *gCb, + KwDlRbCb *rbCb, + KwNackInfo *nackSnInfo, + CmLList **retxNode, + KwuDatCfmInfo **datCfm + ) +#else +PRIVATE Void kwAmmDlUpdateTxAndReTxBufForNackSn(gCb, rbCb, nackSnInfo, retxNode, datCfm) +( + KwCb *gCb; + KwDlRbCb *rbCb; + KwNackInfo *nackSnInfo; + CmLList **retxNode; + KwuDatCfmInfo **datCfm; + ) +#endif +{ + KwTx *txBuf; + KwRetx *retx; + + TRC2(kwAmmDlUpdateTxAndReTxBufForNackSn) + + /* Now process the NACK_SN received. Now the NACK_SN is */ + /* either the first element of RETX or is in TX array */ + /* To remove the remaining acks from the pdu byte segments */ + + /* if the NACK_SN is in the transmit buffer, move it to the re- + transmit buffer */ + txBuf = kwUtlGetTxBuf(AMDL.txBufLst, nackSnInfo->sn); + if (txBuf != NULLP) + { + if(nackSnInfo->isSegment) + { + /* Go through all the AMD PDUs of a particular SN + and check if segment is ACKED if yes then mark succesfully sent, + if segment is NACKed then move it to to retx lst */ + kwAmmDlHndlStatus4SduByteSegInTxBuf(gCb, rbCb, nackSnInfo, &retx, datCfm); + } + else + { + /*e2= 0 and e3= 0: Move complete PDU from TX buf to retx buf*/ + kwAmmDlMoveFrmTxtoRetxBuffer(gCb, + &rbCb->m.amDl, + &retx, + nackSnInfo->sn); + } + +#if (ERRCLASS & ERRCLS_ADD_RES) + if(retx) +#endif + { + (*retxNode) = retx->lstEnt.next; + } + + RETVOID; + } + + /* process the pdus/segments in the re-transmit buffer with + this NACK_SN */ + while (*retxNode) + { + retx = (KwRetx *)((*retxNode)->node); + if (retx->amHdr.sn != nackSnInfo->sn) + { + break; + } + if ((nackSnInfo->isSegment) && + ((retx->soEnd < nackSnInfo->soStart) /*|| (retx->amHdr.so > soEnd)*/)) + { + RLOG_ARG3(L_DEBUG, DBG_RBID, rbCb->rlcId.rbId, + "kwHndlStaRsp: Handle ACK for byte segment, Its " + "sn = %d UEID:%d CELLID:%d", + nackSnInfo->sn, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + RLOG_ARG4(L_DEBUG, DBG_RBID, rbCb->rlcId.rbId, + "soStart and soEnd = %d, %d, UEID:%d CELLID:%d", + retx->amHdr.so, retx->soEnd, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + + (*retxNode) = (*retxNode)->next; + kwAmmDlProcessSuccessfulReTx(gCb,rbCb, retx, datCfm); + } + else if((!nackSnInfo->isSegment) || (retx->soEnd <= nackSnInfo->soEnd)) + { + /* This case covers the NACKED segments and also the case */ + /* when there are segments and the entire SN is nacked. */ + /* This case also covers the case of nonsegmented retx PDU*/ + + (*retxNode) = (*retxNode)->next; + /* Mark the retx PDU we found for further retransmission */ + kwAmmDlMarkPduForReTx(gCb, rbCb, retx); + } + else + { + /* If we are here that means this segment and segments after this are ACKed*/ + break; + } + } /* end of retxNode while loop*/ + RETVOID; +} + +/** + * @brief Function to get nack Sn information from nackRange index + * + * @details + * This function is used to get nack Sn information from nackRange index + * +* @param[in]KwAmDl *amDl, +* @param[in]KwUdxStaPdu *StaPdu, +* @param[in]KwNackInfo *nackSnInfo, +* @param[in]KwRetx *retx; +* @param[in]KwSn sn, +* @param[in]U8 idx + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void KwDlAmmGetNackSnInfoFrmNackRangeIdx +( +KwAmDl *amDl, +KwNackInfo *nackInfo, +CmLList *retxNode, +KwNackInfo *nackSnInfo, +U8 idx +) +#else +PRIVATE Void KwDlAmmGetNackSnInfoFrmNackRangeIdx(amDl, nackInfo, retxNode, nackSnInfo, idx) +( +KwAmDl *amDl; +KwNackInfo *nackInfo; +CmLList *retxNode; +KwNackInfo *nackSnInfo; +U8 idx; +) +#endif +{ + KwTx *txBuf; + KwRetx *retx; + CmLList *node; + + TRC2(KwDlAmmGetNackSnInfoFrmNackRangeIdx) + + nackSnInfo->isSegment = FALSE; + + if((!nackInfo->isSegment) || (!idx && nackSnInfo->nackRange && (!nackInfo->soStart))) + { + nackSnInfo->soStart = 0; + nackSnInfo->soEnd = 0; + RETVOID; + } + txBuf = kwUtlGetTxBuf(amDl->txBufLst, nackSnInfo->sn); + if(txBuf != NULLP) + { + node = txBuf->pduLst.first; + while(node) + { + KwDlPduInfo *pduInfo = (KwDlPduInfo *)(node->node); + U16 pduSoEnd = pduInfo->amHdr.so + pduInfo->sduMap.sduSz - 1; + if((!idx) && (pduInfo->amHdr.so == nackInfo->soStart)) + { + nackSnInfo->isSegment = TRUE; + nackSnInfo->soStart = pduInfo->amHdr.so; + nackSnInfo->soEnd = pduSoEnd; + break; + } + else if((idx == nackSnInfo->nackRange - 1) && \ + (pduSoEnd == nackInfo->soEnd)) + { + nackSnInfo->isSegment = TRUE; + nackSnInfo->soStart = pduInfo->amHdr.so; + nackSnInfo->soEnd = pduSoEnd; + break; + } + node = node->next; + } + } + if(!nackSnInfo->isSegment) + { + while (retxNode) + { + retx = (KwRetx *)(retxNode->node); + if(retx->amHdr.sn != nackSnInfo->sn) + { + break; + } + if((!idx) && (retx->amHdr.so == nackInfo->soStart)) + { + nackSnInfo->isSegment = TRUE; + nackSnInfo->soStart = retx->amHdr.so; + nackSnInfo->soEnd = retx->soEnd; + break; + } + else if((idx == nackSnInfo->nackRange - 1) && \ + (retx->soEnd == nackInfo->soEnd)) + { + nackSnInfo->isSegment = TRUE; + nackSnInfo->soStart = retx->amHdr.so; + nackSnInfo->soEnd = retx->soEnd; + break; + } + retxNode = retxNode->next; + } + } +} + +/** + * @brief Function to update transmission buffers and send confimations to + * PDCP on the reception of Status PDU + * + * @details + * First processes the NACKs received + * -# Removes the pdus which are acked by each of the NACK SN from the + * transmission and re-transmission buffer + * -# If NACKed SN in in the transmisson buffer, moves it to re-transmission + * buffer + * -# Removes PDU segments of the NACKed SN which have been successfully + * received by the other end. For the un-successful ones, marks them for + * re-transmission + * -# When PDUs/segments are removed from the buffer, indicates to upper + * layer if the SDU is completely delivered + * -# Removes the PDUs/segments which are acked by the ACK_SN but not by the + * NACK_SNs + * + * @param[in] gCb RLC Instance control block + * @param[in] rbCb Downlink Radio Bearer control block + * @param[in] pStaPdu The decoded Status Pdu + * + * @return Void +*/ +#ifdef ANSI +PUBLIC Void kwAmmDlHndlStatusPdu +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwUdxStaPdu *pStaPdu +) +#else +PUBLIC Void kwAmmDlHndlStatusPdu(gCb, rbCb, pStaPdu) +KwCb *gCb; +KwDlRbCb *rbCb; +KwUdxStaPdu *pStaPdu; +#endif +{ + KwSn mAckSn; + S32 oldRetxBo; + CmLList *retxNode; + KwuDatCfmInfo* datCfm; + KwKwuSapCb *kwuSap; + KwSn mTxNext; + + TRC2(kwAmmDlHndlStatusPdu) + kwStatusPduCnt++; + + kwuSap = gCb->u.dlCb->kwuDlSap + KW_UI_PDCP; + /* store the re-transmission bo, to check if it changes due to the + processing of the status pdu */ + oldRetxBo = AMDL.retxBo; + + /* Allocate memory for datCfm Info */ + KW_SHRABL_STATIC_BUF_ALLOC(kwuSap->pst.region, kwuSap->pst.pool, datCfm, sizeof(KwuDatCfmInfo)); + +#if (ERRCLASS & ERRCLS_ADD_RES) + if (datCfm == NULLP) + { + RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId, + "Memory allocation failed UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + KW_SHRABL_STATIC_BUF_FREE(kwuSap->pst.region, kwuSap->pst.pool, datCfm, sizeof(KwuDatCfmInfo)); + RETVOID; + } +#endif /* ERRCLASS & ERRCLS_RES */ + + datCfm->numSduIds = 0; + datCfm->rlcId = rbCb->rlcId; + + MODAMT(pStaPdu->ackSn, mAckSn, AMDL.txNextAck,AMDL.snModMask); + MODAMT(AMDL.txNext,mTxNext, AMDL.txNextAck,AMDL.snModMask); + + if(mAckSn > mTxNext) + { + RLOG_ARG4(L_WARNING,DBG_RBID, rbCb->rlcId.rbId, + "Invalid ACK SN = %d received. Current Vta =%d" + "UEID:%d CELLID:%d", + pStaPdu->ackSn, + AMDL.txNextAck, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); +/* KW_SHRABL_STATIC_BUF_ALLOC(kwuSap->pst.region, kwuSap->pst.pool, datCfm, sizeof(KwuDatCfmInfo)); */ + KW_SHRABL_STATIC_BUF_FREE(kwuSap->pst.region, kwuSap->pst.pool, datCfm, sizeof(KwuDatCfmInfo)); + RETVOID; + } + + /* Venki - stopping the poll retx timer */ + /*Stop PollRetx Tmr */ + kwAmmDlCheckAndStopPollTmr(gCb, rbCb, mAckSn); + + /* Set the first node in retx list to retxNode */ + retxNode = AMDL.retxLst.first; + + /* If NACK exists in control PDU */ + if (pStaPdu->nackCnt) + { + KwSn sn; + KwNackInfo nackSnInfo; + KwSn mNackSn; + KwSn txNextAck; + KwSn transWinStartSn = AMDL.txNextAck; /*used to track the SN from which + to start processing the transmission + buffer */ + U32 idx = 0; + + /* if any NACKs then txNextAck should be equal to the first NACK_SN*/ + txNextAck = pStaPdu->nackInfo[0].sn; + + kwStatusNcnt += pStaPdu->nackCnt; + + /* For NACKs */ + while (idx < pStaPdu->nackCnt) + { + nackSnInfo.isSegment = pStaPdu->nackInfo[idx].isSegment; + nackSnInfo.nackRange = pStaPdu->nackInfo[idx].nackRange; + nackSnInfo.sn = pStaPdu->nackInfo[idx].sn; + + RLOG_ARG3(L_DEBUG,DBG_RBID, rbCb->rlcId.rbId, + "kwHndlStaRsp: NACK SN = %d UEID:%d CELLID:%d", + nackSnInfo.sn, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + + nackSnInfo.soStart = pStaPdu->nackInfo[idx].soStart; + nackSnInfo.soEnd = pStaPdu->nackInfo[idx].soEnd; + + /* e2 is used as a boolean indicating presence of SOStart or SOEnd */ + + sn = transWinStartSn; + + /* move transWinStartSn to nackSnInfo.sn + 1, as the pdu's before that + will be removed from the buffer */ + transWinStartSn = (nackSnInfo.sn + (nackSnInfo.nackRange ?\ + (nackSnInfo.nackRange - 1) : 0) + 1) & AMDL.snModMask; + + /* Clear the acked SNs from the retx list */ + MODAMT(nackSnInfo.sn, mNackSn, AMDL.txNextAck,AMDL.snModMask); + + if ((mNackSn > mAckSn) || (mNackSn >= mTxNext)) + { + /* Erroneous NACK_SN, we should raise an error towards L3 */ + RLOG_ARG2(L_ERROR,DBG_RBID, rbCb->rlcId.rbId, + "Status Pdu is not correct UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + KW_SHRABL_STATIC_BUF_FREE(kwuSap->pst.region, kwuSap->pst.pool, datCfm, sizeof(KwuDatCfmInfo)); + RETVOID; + } + + /* clear all the SNs < NACK_SN from re-transmission list */ + kwAmmDlUpdTxAndReTxBufForLessThanNackSn(gCb, rbCb, sn, mNackSn, + &retxNode, &datCfm); + + if(!nackSnInfo.nackRange) + { + kwAmmDlUpdateTxAndReTxBufForNackSn(gCb, rbCb, &nackSnInfo, &retxNode, &datCfm); + gRlcStats.amRlcStats.numRlcAmCellNackRx++; + } + else + { + U8 idx1 = 0; + /* Update issegment, soStart, soEnd ,sn in nackSnInfo and handle + * nack sn*/ + do + { + KwDlAmmGetNackSnInfoFrmNackRangeIdx(&AMDL, &pStaPdu->nackInfo[idx], + retxNode, &nackSnInfo, idx1); + + kwAmmDlUpdateTxAndReTxBufForNackSn(gCb, rbCb, &nackSnInfo, + &retxNode, &datCfm); + nackSnInfo.sn = ((nackSnInfo.sn + 1) & (AMDL.snModMask)); + gRlcStats.amRlcStats.numRlcAmCellNackRx++; + + }while((++idx1) < (nackSnInfo.nackRange)); + } + + idx++; + } /* End of nackCnt while loop */ + + /* Remove the PDUs with are further acked by the ACK_SN after taking + care of all the NACK_SN related acknowledgments*/ + kwAmmDlUpdateTxAndReTxBufForAckSn(gCb,rbCb, mAckSn, retxNode, &datCfm); + + /* Update txNextAck */ + kwAmmDlSetTxNextAck(&AMDL,txNextAck); + } + else + { + kwStatusAckCnt++; + /* For All ACKs */ + RLOG_ARG2(L_UNUSED,DBG_RBID, rbCb->rlcId.rbId, + "kwHndlStaRsp: Received All ACKS UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + + /* For the remaining ACKs after last nackSn */ + kwAmmDlUpdateTxAndReTxBufForAckSn(gCb,rbCb, mAckSn, retxNode, &datCfm); + + /* update txNextAck */ + kwAmmDlSetTxNextAck(&AMDL, pStaPdu->ackSn); + } + + if(datCfm->numSduIds != 0) + { + if(datCfm->numSduIds > 1024) + { + RLOG_ARG4(L_DEBUG,DBG_RBID,datCfm->rlcId.rbId, + "Sending [%lu] SDU Cfms to PDCP & [%lu] lost for" + "UEID:%d CELLID:%d", + datCfm->numSduIds, + datCfm->numSduIds-1024, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + datCfm->numSduIds = 1024; + } + kwSduSndCnt += datCfm->numSduIds; + /* Sap control block */ + KwUiKwuDatCfm(&kwuSap->pst, kwuSap->suId, datCfm); + } + else + { + KW_SHRABL_STATIC_BUF_FREE(kwuSap->pst.region, kwuSap->pst.pool, datCfm, sizeof(KwuDatCfmInfo)); + } + + /* Fix for memory corruption */ + KW_LLIST_FIRST_RETX(AMDL.retxLst, AMDL.nxtRetx); + /* BO update, if retransmission BO has changed. AMDL.retxBo would have + canged inside the above called functions */ + if (oldRetxBo != AMDL.retxBo) + { + kwAmmSendDStaRsp(gCb, rbCb, &AMDL); + } + + RETVOID; +} + +/** + * @brief Function to calculate the current buffer occupancy + * + * @details + * Function to calculate the current bo depending on the control, + * re-transmit, transmit bo's and the state of the transmit window. + * If the transmit window is stalled, then the transmit bo is not + * taken into account + * + * @param[in] amDl AM mode donwlink control block + * + * @return S16 + * Calculated bo +*/ +#ifdef ANSI +PUBLIC S32 kwAmmCalculateBo +( +KwAmDl *amDl +) +#else +PUBLIC S32 kwAmmCalculateBo(amDl) +KwAmDl *amDl; +#endif +{ + S32 bo; + + /* Make sure non of the bo's are negative */ + if (amDl->bo < 0) + { + amDl->bo = 0; + } + + if (amDl->cntrlBo < 0) + { + amDl->cntrlBo = 0; + } + + if (amDl->retxBo < 0) + { + amDl->retxBo = 0; + } + + bo = amDl->cntrlBo + amDl->retxBo; + + /* if window is not stalled then add the transmit bo also */ + if (! KW_AM_IS_TRANS_WIN_STALLED(amDl)) + { + bo += amDl->bo; + } + + return bo; +} +U32 kwRxSdu; + +/** + * @brief Handler to queue the SDUs received from PDCP + * + * @details + * This function is invoked by UIM to queue the SDU received from PDCP in the + * SDU queue of the corresponding RbCb. It also updates the BO and report the + * same to MAC. + * - Allocate memory for and assign received buffer to the SDU + * - Add SDU in the sduQ of KwAmDl + * - Calculate bo with the buffer received + * - Accumulate bo with retransmission bo and control pdu's bo if available + * - Estimate the header size for the bo; Fill in StaRspInfo and send it + * to MAC + * + * @param[in] gCb RLC Instance control block + * @param[in] rbCb RB control block + * @param[in] mBuf Sdu to be queued + * @param[in] datReq Ptr to the datReq sent from PDCP + * + * @return Void + * -# RETVOID +*/ +#ifdef ANSI +PUBLIC Void kwAmmQSdu +( +KwCb *gCb, +KwDlRbCb *rbCb, +Buffer *mBuf, +KwuDatReqInfo *datReq +) +#else +PUBLIC Void kwAmmQSdu(gCb, rbCb, mBuf, datReq) +KwCb *gCb; +KwDlRbCb *rbCb; +Buffer *mBuf; +KwuDatReqInfo *datReq; +#endif +{ + KwSdu *sdu; +#ifdef LTE_L2_MEAS +#ifndef L2_L3_SPLIT +#ifdef TENB_STATS + U32 kwWinSz; +#endif +#endif +#endif + TRC2(kwAmmQSdu) + + /* Allocate sdu */ + KW_ALLOC_WC(gCb,sdu, sizeof(KwSdu)); + +#if (ERRCLASS & ERRCLS_ADD_RES) + if (sdu == NULLP) + { + RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId, + "Memory allocation failed UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + RETVOID; + } +#endif /* ERRCLASS & ERRCLS_RES */ + + KW_UPD_L2_DL_TOT_SDU_STS(gCb,rbCb); + /* Discard new changes starts */ + kwUtlGetCurrTime(&sdu->arrTime); + /* Discard new changes ends */ + /* Assign values to sdu */ + SFndLenMsg(mBuf, &sdu->sduSz); + + sdu->mBuf = mBuf; + sdu->actSz = sdu->sduSz; + sdu->mode.am.sduId = datReq->sduId; + /* initialize values for AM mode to 0 */ + sdu->mode.am.rcvdSz = 0; + sdu->mode.am.isSegmented = 0; +#ifndef RGL_SPECIFIC_CHANGES +#ifdef MSPD +{ +extern U32 dlrate_kwu; +dlrate_kwu += sdu->sduSz; +} +#endif +#endif + /* Update nxtTx to point to the added sdu if this is the first SDU in the + * queue */ + if (AMDL.nxtTx == NULLP) + { + RLOG_ARG2(L_UNUSED,DBG_RBID, rbCb->rlcId.rbId, + "kwAmmQSdu: Received SDU will be transmitted next" + "UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + AMDL.nxtTx = sdu; + } + + /* Add sdu to the sdu list */ + cmLListAdd2Tail(&AMDL.sduQ, &sdu->lstEnt); + sdu->lstEnt.node = (PTR)sdu; +#ifdef LTE_L2_MEAS +#ifndef L2_L3_SPLIT +#ifdef TENB_STATS + if (rbCb->ueCb->tenbStats) + { + if (AMDL.sduQ.count > rbCb->ueCb->tenbStats->stats.nonPersistent.rlc.dlMaxPktsInSduQ) + { + rbCb->ueCb->tenbStats->stats.nonPersistent.rlc.dlMaxPktsInSduQ = AMDL.sduQ.count; + } + kwWinSz = KW_AM_TRANS_WIN_SIZE(&AMDL); + if (kwWinSz > rbCb->ueCb->tenbStats->stats.nonPersistent.rlc.dlMaxWindowSz) + { + rbCb->ueCb->tenbStats->stats.nonPersistent.rlc.dlMaxWindowSz = kwWinSz; + } + } +#endif +#endif +#endif + /* Update BO and estimate header size for the current BO */ + AMDL.bo = AMDL.bo + sdu->sduSz; + if(AMDL.snLen == KW_AM_CFG_12BIT_SN_LEN) + { + AMDL.estHdrSz += 2; + } + else + { + AMDL.estHdrSz += 3; + } +#ifdef LTE_L2_MEAS_RLC + /* Update numActUe if it is not active */ + if((rbCb->rbL2Cb.measOn & LKW_L2MEAS_ACT_UE) && + (rbCb->ueCb->numActRb[rbCb->qci] == 0)) + { + rbCb->ueCb->numActRb[rbCb->qci]++; + gCb.kwL2Cb.numActUe[rbCb->qci]++; + } +#endif + + if(!kwDlUtlIsReestInProgress(rbCb)) + { + kwAmmSendDStaRsp(gCb, rbCb, &AMDL); + } + + RETVOID; +} + +/** + * + * @brief Private handler to construct control PDU + * + * @details + * This function sets the pduSz correctly after eliminating the fixed + * header sizes and the MAC header size. It copies the already prepared + * STATUS PDU to the data to be sent to MAC. + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb Downlink RB control block + * @param[in] kwdatReq DatReq to be sent to MAC + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAmmDlAssembleCntrlInfo +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwDatReq *kwDatReq +) +#else +PRIVATE Void kwAmmDlAssembleCntrlInfo(gCb, rbCb, kwDatReq) +KwCb *gCb; +KwDlRbCb *rbCb; +KwDatReq *kwDatReq; +#endif +{ + KwUdxDlSapCb *sapCb; + MsgLen macHdrEstmt; + + TRC2(kwAmmDlAssembleCntrlInfo) + + macHdrEstmt = (rbCb->m.amDl.cntrlBo < 256) ? + KW_MAC_HDR_SZ2 : KW_MAC_HDR_SZ3; + /* Eliminate fixed hdr size (14bits including ACK_SN) */ + if (kwDatReq->pduSz >= (KW_CNTRL_PDU_FIXED_HDRSZ + macHdrEstmt)) + { + /* Check the TB size whether it is sufficcient enough to fit the + status Pdu into it otherwise make arrangement such that it can fit + into in a way of possible NACks*/ + /* ccpu00135743 : fix for MAC Hdr size calc */ + kwDatReq->pduSz -= macHdrEstmt; + + /* Create the status Pdu with the required NACKs */ + kwAmmCreateStatusPdu(gCb,rbCb,kwDatReq); + + sapCb = KW_GET_DL_SAPCB(gCb, rbCb); + KwDlUdxStaProhTmrStart(&(gCb->u.dlCb->udxDlSap->pst), + sapCb->suId, &(rbCb->rlcId)); + + /* Update number of pdus in pduInfo */ + kwDatReq->pduInfo.mBuf[kwDatReq->pduInfo.numPdu] = AMDL.mBuf; + kwDatReq->pduInfo.numPdu++; + gRlcStats.amRlcStats.numDLStaPduSent++; + + KW_FREE_SHRABL_BUF(gCb->u.dlCb->udxDlSap->pst.region, + gCb->u.dlCb->udxDlSap->pst.pool, + AMDL.pStaPdu, + sizeof(KwUdxDlStaPdu)); + + AMDL.pStaPdu = NULLP; + AMDL.mBuf = NULLP; + gRlcStats.amRlcStats.numDLStaPduSent++; + } + + RETVOID; +} + +/** + * @brief Handler to form the PDUs with the size indicated by MAC + * + * @details + * This function is invoked by UTL with the PDU size indicated by + * MAC (after eliminating MAC header size). It assembles control + * Info / data (New SDUs / Retx PDUs), check if polling needs to be + * set for the data PDU and returns PDU(s) and updated BO with + * estimated header size to be sent to MAC. + * + * - Check if the control BO is available and call kwAssembleCntrlInfo + * to assemble control Information + * - Check if the pdu size is available to form PDUs from retransmission + * buffer and call kwResegRetxPdus + * - Check if the pdu size is available and assemble SDUs from sduQ + * if exist, using kwAssembleSdus + * - PDU Info and bo are filled in and then sent to MAC from the + * utility function + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb RB control block + * @param[in] kwdatReq DatReq to be sent to MAC + * @param[in] fillCtrlPdu Indicates whether cntrl PDU to be filled or not + * + * @return Void + * + */ +#ifdef ANSI +PUBLIC Void kwAmmProcessSdus +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwDatReq *kwDatReq, +Bool fillCtrlPdu +) +#else +PUBLIC Void kwAmmProcessSdus(gCb, rbCb, kwDatReq,fillCtrlPdu) +KwCb *gCb; +KwDlRbCb *rbCb; +KwDatReq *kwDatReq; +Bool fillCtrlPdu; +#endif +{ + TRC2(kwAmmProcessSdus) + + + /* Assemble control information. fillCtrlPdu parameter check is added for CA + * It is used to force cntrl Pdu scheduling on PCell. for Non CA case this + * flag will always be TRUE. In CA case, for PCELL it is TRUE and for SCEll + * it is FALSE */ + + if ((AMDL.cntrlBo != 0) +#ifdef LTE_ADV + && (fillCtrlPdu) +#endif + ) + { + kwDatReq->boRep.staPduPrsnt = TRUE; + kwDatReq->boRep.staPduBo = AMDL.cntrlBo; + + if (AMDL.pStaPdu != NULLP) + { + kwAmmDlAssembleCntrlInfo (gCb, rbCb, kwDatReq); + } + else + { + RLOG_ARG2(L_ERROR, DBG_RBID, rbCb->rlcId.rbId, + "Miscomputation of control Bo. UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + } + AMDL.cntrlBo = 0; + } + + /* Retransmit PDUs /portions of PDUs available in retxLst */ + if ((kwDatReq->pduSz > 0) && (AMDL.nxtRetx != NULLP)) + { + kwResegRetxPdus (gCb,rbCb, kwDatReq); + } + + /* Assemble SDUs to form new PDUs */ + if ((kwDatReq->pduSz > 0) && (AMDL.nxtTx != 0)) + { + kwAssembleSdus(gCb,rbCb, kwDatReq); + } + + if (AMDL.nxtRetx != NULLP) + { + kwDatReq->boRep.oldestSduArrTime = AMDL.nxtRetx->sduMap.sdu->arrTime; + } + else if (AMDL.nxtTx != NULLP) + { + kwDatReq->boRep.oldestSduArrTime = AMDL.nxtTx->arrTime; + } + /* Accumulate bo */ + kwDatReq->boRep.bo = kwAmmCalculateBo(&AMDL); + kwDatReq->boRep.staPduBo = AMDL.cntrlBo; + + /* Hdr estimation is moved to kwAmmCreatePDu */ + kwDatReq->boRep.estHdrSz = AMDL.estHdrSz; + + if(kwDatReq->pduSz > 0) + { + gRlcStats.amRlcStats.numDLBytesUnused += kwDatReq->pduSz; + } + RETVOID; +} + +/** + * @brief Private handler split a PDU/segment into two + * + * @details + * Its a private function called by kwResegRetxPdu to split a segment + * or a retransmit PDU into two segments splitting at the passed size. + * This function is called only for those PDUs that dont have any LIs. + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb RB control block + * @param[in,out] crnt The PDU to be split, first part of split pdu remians + * in this + * @param[out] next The second part of the split pdu + * @param[in] size The size witin crnt, at which to split + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwSplitPdu +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwRetx *crnt, +KwRetx *next, +U16 size +) +#else +PRIVATE Void kwSplitPdu(gCb, rbCb, crnt, next, size) +KwCb *gCb; +KwDlRbCb *rbCb; +KwRetx *crnt; +KwRetx *next; +U16 size; +#endif +{ + U8 si; + KwAmDl *amDl = &AMDL; + + TRC2(kwSplitPdu) + /* Set the SN for the new segment */ + next->amHdr.sn = crnt->amHdr.sn; + + /* Set the protocol specific fields appropriately */ + si = crnt->amHdr.si; + crnt->amHdr.si = si | KW_SI_FIRST_SEG; + next->amHdr.si = si | KW_SI_LAST_SEG; + + crnt->amHdr.p = 0; + + /* Update seg size */ + next->segSz = crnt->segSz - size; + crnt->segSz = size; + + /* Set the SO fields appropriately */ + /* MS_FIX for DL stall */ + next->soEnd = crnt->soEnd; + + /* Set the SO fields appropriately */ + /* SO of next will be after the end of current */ + next->amHdr.so = crnt->amHdr.so + crnt->segSz; + /* SO End of current will be one less than the start of next */ + crnt->soEnd = next->amHdr.so - 1; + + /* intialize the other fields in the amHdr of next to 0 */ + next->amHdr.p = 0; + next->amHdr.dc = 0; + + /* This macro is called for No LI case - one SDU */ + /* Update the size of SDU in each node's sduMap */ + next->sduMap.sdu = crnt->sduMap.sdu; + crnt->sduMap.sduSz = crnt->segSz; + next->sduMap.sduSz = next->segSz; + + /* Segment the payload into two parts based on the size passed */ + SSegMsg(crnt->seg, size, &next->seg); + next->retxCnt = crnt->retxCnt; + next->yetToConst = TRUE; + next->pendingReTrans = crnt->pendingReTrans; + + /* Compute the header size and update the BO appropriately */ + if(amDl->snLen == KW_AM_CFG_12BIT_SN_LEN) + { + next->hdrSz = KW_AM_SEG_12BIT_SN_WITH_SO_HDRSZ; + if(crnt->amHdr.si == KW_SI_FIRST_SEG) + { + crnt->hdrSz = KW_AM_SEG_12BIT_SN_WITHOUT_SO_HDRSZ; + } + else + { + crnt->hdrSz = KW_AM_SEG_12BIT_SN_WITH_SO_HDRSZ; + } + } + else + { + next->hdrSz = KW_AM_SEG_18BIT_SN_WITH_SO_HDRSZ; + if(crnt->amHdr.si == KW_SI_FIRST_SEG) + { + crnt->hdrSz = KW_AM_SEG_18BIT_SN_WITHOUT_SO_HDRSZ; + } + else + { + crnt->hdrSz = KW_AM_SEG_18BIT_SN_WITH_SO_HDRSZ; + } + } + + /* Add the next to the retx list */ + AMDL.retxLst.crnt = &crnt->lstEnt; + CM_LLIST_INS_AFT_CRNT(AMDL.retxLst, next); + AMDL.nxtRetx = next; + amDl->estHdrSz += next->hdrSz; + + RETVOID; +} + +/** + * @brief Private handler to retransmit PDUs or PDU segments + * + * @details + * Its a private function called by kwProcessSdus, to create the + * PDUs / its segments from the retransmission buffer available in RbCb. + * + * - Eliminate the fixed header size and MAC header size while + * forming PDUs/segments + * - While pdusize is available and retxBuf has data (pdu or portion + * of pdu) to be sent, form the pdu as it is if it matches with the + * pdusize else segment the PDUs/portion of PDUs + * - Call kwAmmDlCheckAndSetPoll function to check and set the poll bit as + * required + * - Concatenate data and header info and fill pduInfo + * - Update retxCnt and send indication to PDCP if it reaches maxRetx + * threshold + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb RB control block + * @param[in] kwdatReq DatReq to be sent to MAC + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwResegRetxPdus +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwDatReq *kwDatReq +) +#else +PRIVATE Void kwResegRetxPdus(gCb, rbCb, kwDatReq) +KwCb *gCb; +KwDlRbCb *rbCb; +KwDatReq *kwDatReq; +#endif +{ + KwAmDl *amDl; + KwRetx *retx; + U8 hdr[KW_MAX_HDRSZ]; + U16 idx; + Buffer *pdu; + MsgLen pduSz; +#ifdef LTE_L2_MEAS + U16 sduIdx; + KwL2MeasTb *l2MeasTb; + KwlchInfo *lchInfo; + U8 numSdus; +#endif + + TRC2(kwResegRetxPdus) + + + amDl = &AMDL; +#ifdef LTE_L2_MEAS + /* TODO : This shoould be taken care in new Trasmissions */ + /* This lchInfo should be retrieved there */ + l2MeasTb = kwUtlGetCurMeasTb(gCb, rbCb); + if (l2MeasTb == NULLP) + { + RETVOID; + } + /* TODO : This lcid needs to be searched in case of normal Tx */ + /* In retx here, its fine as this will be higher priority */ + lchInfo = &l2MeasTb->lchInfo[l2MeasTb->numLchInfo]; + if (l2MeasTb->numLchInfo >= KW_MAX_ACTV_DRB) + { + RETVOID; + } + l2MeasTb->numLchInfo++; + lchInfo->lcId = rbCb->lch.lChId; + lchInfo->numSdus = 0; +#endif + + while ((kwDatReq->pduSz > 0) && (amDl->nxtRetx != NULLP)&& + (kwDatReq->pduInfo.numPdu < KW_MAX_PDU)) + { + U16 tmpSz; + + retx = amDl->nxtRetx; + /* kw003.201 : Add header size to seg size to determine if the */ + /* the segment can be completed within the allocation */ + /* kw003.201 - Eliminate MAC Header Size based on bites needed */ + tmpSz = KW_MIN((retx->segSz + retx->hdrSz), kwDatReq->pduSz); + pduSz = (retx->segSz + retx->hdrSz); + /* 5GNR_RLC: length field in 5GNR MAC Hdr is 8/16 btis*/ + kwDatReq->pduSz -= (tmpSz < 255) ? KW_MAC_HDR_SZ2 : KW_MAC_HDR_SZ3; + + /* kw003.201 - We should have at least one more than basic header */ + if (kwDatReq->pduSz <= retx->hdrSz) + { + RETVOID; + } + kwGetNxtRetx(gCb, &(amDl->nxtRetx)); + + /* Send retx buf without segmentation */ + if (kwDatReq->pduSz >= pduSz) + { + U8 pollBit; + + RLOG_ARG2(L_DEBUG,DBG_RBID,rbCb->rlcId.rbId, + "kwResegRetxPdus: Send retx buf without segmentation " + "UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + + if (retx->yetToConst) + { + /* Construct hdr with the available hdr values */ + kwConstructAmHdr(&retx->amHdr, hdr, amDl->snLen, &idx); + /* Add header to the pdu/segment */ + SAddPreMsgMultInOrder(hdr, idx + 1, retx->seg); + retx->yetToConst = FALSE; + } + + /* kw003.201 - Check if poll bit needs to be set. Retx size does */ + /* not affect the poll bit so it is being passed as zero */ + pollBit = kwAmmDlCheckAndSetPoll(gCb,rbCb, FALSE, 0); + KW_UPD_POLL_BIT(gCb, retx, pollBit); + + kwDatReq->pduSz -= pduSz; + AMDL.estHdrSz -= retx->hdrSz; +#ifdef LTE_L2_MEAS + + if (rbCb->rlcId.rbType == CM_LTE_DRB) + { + numSdus = 0; + for (sduIdx = lchInfo->numSdus ; + ((numSdus < retx->numSdu) && (sduIdx < KW_L2MEAS_SDUIDX)) ; + sduIdx++, numSdus++) + { + lchInfo->sduInfo[sduIdx].arvlTime = retx->sduMap[numSdus].sdu->arrTime; + lchInfo->sduInfo[sduIdx].isRetxPdu = TRUE; /* TODO : for later use */ + } + lchInfo->numSdus += numSdus; + } +#endif + } + else + { + KwRetx *tNode; + + /* Segment this pdu / portion of pdu. Insert this segment into */ + /* retxLst and update offset */ + RLOG_ARG2(L_DEBUG,DBG_RBID,rbCb->rlcId.rbId, + "kwResegRetxPdus: Segment retx buf UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + + /* Eliminate fixed header size if the pdu is segmented for the */ + /* first time */ + if(amDl->snLen == KW_AM_CFG_12BIT_SN_LEN) + { + if(retx->amHdr.si < KW_SI_LAST_SEG) + { + kwDatReq->pduSz -= KW_AM_SEG_12BIT_SN_WITHOUT_SO_HDRSZ; + } + else + { + kwDatReq->pduSz -= KW_AM_SEG_12BIT_SN_WITH_SO_HDRSZ; + } + } + else + { + if(retx->amHdr.si < KW_SI_LAST_SEG) + { + kwDatReq->pduSz -= KW_AM_SEG_18BIT_SN_WITHOUT_SO_HDRSZ; + } + else + { + kwDatReq->pduSz -= KW_AM_SEG_18BIT_SN_WITH_SO_HDRSZ; + } + } + if (kwDatReq->pduSz <= 0) + { + RETVOID; + } + + /* Allocate memory for tracking a new segment */ + KW_ALLOC_WC(gCb,tNode, sizeof(KwRetx)); +#if (ERRCLASS & ERRCLS_ADD_RES) + if (tNode == NULLP) + { + RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId, + "Memory allocation failed UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + RETVOID; + } +#endif /* ERRCLASS & ERRCLS_RES */ + + /* initialize the list pointer to 0 instead of memset */ + tNode->lstEnt.next = 0; + tNode->lstEnt.prev = 0; + + /* Segment header and data */ + KW_AM_RMV_HDR(gCb, rbCb, retx); + + /* kw003.201 - Split the payload and update other fields */ + kwSplitPdu(gCb,rbCb, retx, tNode, kwDatReq->pduSz); + +#ifdef LTE_L2_MEAS + numSdus = 0; + /* ccpu00143043 */ + sduIdx = lchInfo->numSdus; + for (numSdus = 0, sduIdx = lchInfo->numSdus; + ((numSdus < retx->numSdu) && (sduIdx < KW_L2MEAS_SDUIDX)); + numSdus++, sduIdx++) + { + lchInfo->sduInfo[sduIdx].arvlTime = + retx->sduMap[numSdus].sdu->arrTime; + lchInfo->sduInfo[sduIdx].isRetxPdu = TRUE; + } + lchInfo->numSdus = sduIdx; + if ((retx->amHdr.lsf == 0) && (lchInfo->numSdus > 0)) + { + lchInfo->numSdus--; + } +#endif + /* Construct hdr with the available hdr values */ + kwConstructAmHdr(&retx->amHdr, hdr, amDl->snLen, &idx); + SAddPreMsgMultInOrder(hdr, idx + 1, retx->seg); + + retx->hdrSz = idx + 1; + + /* Poll bit need not be set for this seg, since its second */ + /* half remains in retxLst */ + KW_UPD_POLL_BIT(gCb, retx, FALSE); + retx->yetToConst = FALSE; + kwDatReq->pduSz = 0; + } + + kwCpyMsg(gCb,retx->seg, &pdu); + + /* Update pduInfo */ + kwDatReq->pduInfo.mBuf[kwDatReq->pduInfo.numPdu] = pdu; + kwDatReq->pduInfo.numPdu++; + /* kw005.201 ccpu00117318, updating the statistics */ + gCb->genSts.pdusRetx += 1; + gRlcStats.amRlcStats.numRlcAmCellRetxPdu++; + retx->soEnd = retx->amHdr.so + retx->segSz - 1; + retx->pendingReTrans = FALSE; + amDl->retxBo -= retx->segSz; + } +#ifndef ALIGN_64BIT + RLOG_ARG3(L_DEBUG,DBG_RBID,rbCb->rlcId.rbId, + "kwResegRetxPdus: retxBo after resegmentation = %ld" + "UEID:%d CELLID:%d", + amDl->retxBo, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); +#else + RLOG_ARG3(L_DEBUG,DBG_RBID,rbCb->rlcId.rbId, + "kwResegRetxPdus: retxBo after resegmentation = %d " + "UEID:%d CELLID:%d", + amDl->retxBo, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); +#endif + + RETVOID; +} + + +/** + * @brief Private handler to assemble SDUs to form new data PDU(s) + * + * @details + * Its a private function called by kwProcessSdus, to create the new data + * PDUs from the SDU queue of RbCb. + * + * - While pdusize is available, segment/concatenate SDUs or else if it + * matches the pdu size form PDUs accordingly. + * - RLC header and MAC header size are eliminated while forming the PDUs + * - Call kwAmmDlCheckAndSetPoll function to check and set the poll bit + * as required + * - Concatenate data and header info and fill pduInfo + * + * @param[in] rbCb RB control block + * @param[in] kwdatReq DatReq to be sent to MAC + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAssembleSdus +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwDatReq *kwDatReq +) +#else +PRIVATE Void kwAssembleSdus (gCb, rbCb, kwDatReq) +KwCb *gCb; +KwDlRbCb *rbCb; +KwDatReq *kwDatReq; +#endif +{ + Buffer *pdu = NULLP; + MsgLen macGrntSz = kwDatReq->pduSz; + KwAmDl *amDl = &AMDL; + KwSdu *sdu = amDl->nxtTx; + KwSduMap sduMap; + Bool nxtTxUpd = FALSE; + KwuDiscSduInfo *discSduInfo = NULLP; + KwKwuSapCb* kwuSap = gCb->u.dlCb->kwuDlSap + KW_UI_PDCP; +#ifdef LTE_L2_MEAS + KwContSduLst contSduLst; /*Contained sduLst */ + S32 dataVol = amDl->bo; + U32 *totMacGrant = &kwDatReq->totMacGrant; + KwL2MeasDlIpTh *dlIpThPut = &rbCb->l2MeasIpThruput.dlIpTh; + U8 *sduIdx = &dlIpThPut->lastSduIdx; + Bool newIdx; + Bool isSduSegmented; + S32 oldBo; + KwlchInfo lchInfo; + KwlchInfo *dstLchInfo; + U32 segSduCnt = 0; + U32 lchIdx; + U32 numSdus = 0; + U32 currSduIdx = 0; + KwL2MeasTb *l2MeasTb; +#endif + /* Discard new changes starts */ + Ticks timeDiff = 0; + Ticks curTime = 0; + U8 numNewPdu = 0; + KwTx *txBuf = NULLP; + /* Discard new changes ends */ + VOLATILE U32 startTime = 0; + U32 hdrEstmt; + U32 fixedHdrSz; + U32 pduSz; + KwAmHdr *amHdr = NULLP; + KwDlPduInfo *pduInfo = NULLP; + + TRC2(kwAssembleSdus) + + +#ifdef LTE_L2_MEAS + contSduLst.numSdus = 0; + contSduLst.lcId = rbCb->lch.lChId; + oldBo = amDl->bo; + lchInfo.lcId = rbCb->lch.lChId; + lchInfo.numSdus = 0; +#endif + /* Discard new changes starts */ + /* Allocate memory for discSdu Info */ + KW_SHRABL_STATIC_BUF_ALLOC(kwuSap->pst.region, + kwuSap->pst.pool, + discSduInfo, + sizeof(KwuDiscSduInfo)); + +#if (ERRCLASS & ERRCLS_ADD_RES) + if (discSduInfo == NULLP) + { + RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId, + "Memory allocation failed UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + RETVOID; + } +#endif /* ERRCLASS & ERRCLS_RES */ + + discSduInfo->numSduIds = 0; + discSduInfo->rlcId = rbCb->rlcId; + + kwUtlGetCurrTime(&curTime); + amDl->sduQ.crnt = &sdu->lstEnt; + /* Eliminate fixed header size */ + /*5GNR: value of KW_AM_PDU_FIXED_HDRSZ will be 2 or 3 depending on SN Size*/ + if(amDl->snLen == KW_AM_CFG_12BIT_SN_LEN) + { + fixedHdrSz = KW_AM_PDU_12BIT_SN_HDRSZ; + } + else + { + fixedHdrSz = KW_AM_PDU_18BIT_SN_HDRSZ; + } + + while ((macGrntSz > fixedHdrSz) && (sdu != NULLP) && + (kwDatReq->pduInfo.numPdu < KW_MAX_PDU) && + (numNewPdu < KW_MAX_NEW_DL_PDU)) + { +#ifdef LTE_L2_MEAS + isSduSegmented = sdu->mode.am.isSegmented; +#endif + /* Discard new changes starts */ + if ((sdu->mode.am.isSegmented == FALSE) && (rbCb->discTmrInt > 0) && \ + (rbCb->rlcId.rbType == CM_LTE_DRB)) + { + //leftAmSdus[rbCb->qci]--; + timeDiff = KW_TIME_DIFF(curTime,sdu->arrTime); + if (timeDiff > rbCb->discTmrInt) + { + CmLList* nxtNode; + /*starting Task*/ + SStartTask(&startTime, PID_RLC_AMM_DISC_SDUS); +#ifdef LTE_L2_MEAS + KW_UPD_L2_DL_DISC_SDU_STS(gCb,rbCb); + /* TODO need to send disc cfm to pdcp */ +#endif + /* Update bo for boReport */ + amDl->bo -= sdu->sduSz; + + /* Get next sdu for assembly */ + nxtNode = sdu->lstEnt.next; + + /* store the info for sending it to PDCP */ + if(discSduInfo->numSduIds > 500) + { + RLOG_ARG2(L_ERROR,DBG_RBID, rbCb->rlcId.rbId, + "This is a big error, we shouldn't be here" + "UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + break; + } + + discSduInfo->sduIds[discSduInfo->numSduIds] = sdu->mode.am.sduId; + discSduInfo->numSduIds++; + + cmLListDelFrm(&amDl->sduQ, &sdu->lstEnt); + + kwUtlAddSduToBeFreedQueue(gCb, sdu); + kwUtlRaiseDlCleanupEvent(gCb); + + /* We need to restore the crnt in the linked list which + * would have become NULL in the DelFrm above */ + amDl->sduQ.crnt = nxtNode; + + if(nxtNode) + sdu = (KwSdu*)nxtNode->node; + else + sdu = NULLP; + + /*stopping Task*/ + SStopTask(startTime, PID_RLC_AMM_DISC_SDUS); + continue; + } + } + nxtTxUpd = FALSE; + +#ifdef LTE_L2_MEAS + newIdx = FALSE; +#endif + /** kw003.201 - Check for window stall when you are + * creating a new PDU + */ + if (KW_AM_IS_TRANS_WIN_STALLED(amDl)) + { + //int *a = NULLP; + printf("\n Window stalled \n"); + gRlcStats.amRlcStats.numRlcAmCellWinStall++; + //*a = 10; + break; + } + + hdrEstmt = fixedHdrSz; + + if (sdu->mode.am.isSegmented) + { + /* Adding two byte for SO */ + hdrEstmt += 2; + } + /* Eliminate MAC header */ + /* ccpu00135743 : Fix for MAC Hdr size calculation */ + /*5GNR: value of mac hdr length field changed to 8/16bits */ + pduSz = KW_MIN(macGrntSz, (sdu->sduSz + hdrEstmt)); + hdrEstmt += (pduSz < 255) ? KW_MAC_HDR_SZ2 : KW_MAC_HDR_SZ3; + + macGrntSz -= hdrEstmt; + /* kw005.201 Check for PDU Size is large enough. + * Fix for ccpu00118973 + * */ + if(macGrntSz <= 0) + { + break; + } + + /* Dont create new txBuf for segmented SDU */ + if (!sdu->mode.am.isSegmented) + { + /* Update txBuf */ + KW_ALLOC_WC(gCb,txBuf, sizeof(KwTx)); + + cmLListInit(&txBuf->pduLst); + +#if (ERRCLASS & ERRCLS_ADD_RES) + if (txBuf == NULLP) + { + U32 avblMem = 0; + SRegInfoShow(gCb->init.region, &avblMem); + RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId, + "Memory allocation failed UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + RETVOID; + } +#endif /* ERRCLASS & ERRCLS_RES */ + + kwUtlStoreTxBuf(amDl->txBufLst, txBuf, amDl->txNext); + } + else + { + txBuf = kwUtlGetTxBuf(amDl->txBufLst, amDl->txNext); + } + + KW_ALLOC_WC(gCb,pduInfo, sizeof(KwDlPduInfo)); +#if (ERRCLASS & ERRCLS_ADD_RES) + if (pduInfo == NULLP) + { + U32 avblMem = 0; + SRegInfoShow(gCb->init.region, &avblMem); + RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId, + "Memory allocation failed UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + RETVOID; + } +#endif /* ERRCLASS & ERRCLS_RES */ + + /*Initialize DL segment structure */ + pduInfo->lstEnt.next = NULLP; + pduInfo->lstEnt.prev = NULLP; + pduInfo->lstEnt.node = NULLP; + + pduInfo->pdu = NULLP; + pduInfo->amHdr.dc = 0; + pduInfo->amHdr.p = 0; + pduInfo->amHdr.si = 0; + pduInfo->amHdr.so = 0; + + pduInfo->amHdr.sn = amDl->txNext; + + if (macGrntSz >= sdu->sduSz) + { + pdu = sdu->mBuf; + sdu->mBuf = NULLP; + /* Update Framing Info */ + if (sdu->mode.am.isSegmented) + { + /*5GNR RLC: SN should be same for all segment of a SDU*/ + pduInfo->amHdr.sn = sdu->mode.am.sn; + pduInfo->amHdr.si = KW_SI_LAST_SEG; /* binary 10 */ + pduInfo->amHdr.so = sdu->actSz - sdu->sduSz; + sdu->mode.am.isSegmented = FALSE; + + + gRlcStats.amRlcStats.numRlcAmCellSduTx++; + //printf("\n 5GNRLOG: last segment of lcId %d SduId %u So %u macGrntSz %u sduActSz %u sdu->sduSz %u\n", + // rbCb->lch.lChId, sdu->mode.am.sduId, pduInfo->amHdr.so, macGrntSz, sdu->actSz, sdu->sduSz); + } + else + { + gRlcStats.amRlcStats.numRlcAmCellSduTx++; + } + amHdr = &pduInfo->amHdr; + /* Create PDU with hdr and data */ + kwAmmCreatePdu(gCb,rbCb, amHdr, pduInfo, pdu); + + //printf("\n Segmentation not required case: numPdu %d pdu %p \n",kwDatReq->pduInfo.numPdu, pdu); + +#ifdef LTE_L2_MEAS_RLC + kwUtlUpdSduSnMap(rbCb, sdu, kwDatReq, TRUE); +#endif /* LTE_L2_MEAS */ + + /* kw005.201 ccpu00117318, updating the statistics */ + kwUtlIncrementKwuStsSduTx(gCb->u.dlCb->kwuDlSap + rbCb->kwuSapId); +#ifdef LTE_L2_MEAS + if(KW_MEAS_IS_DL_ANY_MEAS_ON_FOR_RB(gCb,rbCb)) + { + if(isSduSegmented) + { + *sduIdx = dlIpThPut->lastSduIdx; + } + else + { + KW_GETSDUIDX(*sduIdx); + newIdx = TRUE; + } + kwUtlUpdateContainedSduLst(*sduIdx, &contSduLst); + kwUtlUpdateOutStandingSduLst(dlIpThPut, *sduIdx, sdu->actSz, + sdu->mode.am.sduId, newIdx); + /* Update the arrival time for each SDU */ + /* ccpu00143043 */ + if ( lchInfo.numSdus < KW_L2MEAS_SDUIDX) + { + lchInfo.sduInfo[lchInfo.numSdus].arvlTime = sdu->arrTime; + lchInfo.numSdus++; + } + } +#endif + sduMap.sduSz = sdu->sduSz; + } + else + { + /* Segmentation + * Allocate buffer for next PDU + * Remove the segmented portion from SDUQ + * Calculate the hdr with LI for SDU */ + + Buffer *remSeg = NULLP; + + //printf("\n SDU segmentation case: numPdu %d pdu %p \n", kwDatReq->pduInfo.numPdu, pdu); +#ifdef LTE_L2_MEAS + if(KW_MEAS_IS_DL_IP_MEAS_ON_FOR_RB(gCb,rbCb) || + KW_MEAS_IS_DL_DELAY_MEAS_ON_FOR_RB(gCb,rbCb) || + KW_MEAS_IS_DL_UU_LOSS_MEAS_ON_FOR_RB(gCb,rbCb) ) + { + /* If actual size of the sdu is equal to msgLen + * then it is first segment of the SDU */ + if(sdu->actSz == sdu->sduSz) + { + KW_GETSDUIDX(*sduIdx); + newIdx = TRUE; + } + else + { + *sduIdx = dlIpThPut->lastSduIdx; + } + kwUtlUpdateContainedSduLst(*sduIdx, &contSduLst); + kwUtlUpdateOutStandingSduLst(dlIpThPut, *sduIdx, sdu->actSz, + sdu->mode.am.sduId, newIdx); + if(KW_MEAS_IS_DL_UU_LOSS_MEAS_ON_FOR_RB(gCb,rbCb)) + { + /* If actual size of the sdu is equal to msgLen + * then it is first segment of the SDU */ + if(sdu->actSz == sdu->sduSz) + { + segSduCnt++; + } + } + } +#endif + + /* Segment the SDU to the size of the PDU and update header Info */ + SSegMsg(sdu->mBuf, macGrntSz, &remSeg); + pdu = sdu->mBuf; + sdu->mBuf = remSeg; + + /* Update SI and SN */ + if (sdu->mode.am.isSegmented) + { + /*5GNR RLC: SN should be same for all segment of a SDU. + * Sdu was already segmented and segmenting again*/ + pduInfo->amHdr.sn = sdu->mode.am.sn; + pduInfo->amHdr.si = KW_SI_MID_SEG; /* binary 11 */ + pduInfo->amHdr.so = sdu->actSz - sdu->sduSz; + + //printf("\n 5GNRLOG: mid segment of lcId %d SduId %u So %u macGrntSz %u sduActSz %u sdu->sduSz %u\n", + // rbCb->lch.lChId, sdu->mode.am.sduId, txBuf->amHdr.so, macGrntSz, sdu->actSz, sdu->sduSz); + } + else + { + /*5GNR RLC: This means it is the first*/ + pduInfo->amHdr.si = KW_SI_FIRST_SEG; /* binary 01 */ + /*5GNR_RLC: Store SN so that in sub-seqent SDU segments will use this SN*/ + sdu->mode.am.sn = pduInfo->amHdr.sn; + pduInfo->amHdr.so = 0; + + //printf("\n 5GNRLOG: First segment of lcId %d SduId %u So %u macGrntSz %u sduActSz %u sdu->sduSz %u\n", + // rbCb->lch.lChId, sdu->mode.am.sduId, txBuf->amHdr.so, macGrntSz, sdu->actSz, sdu->sduSz); + } + + amHdr = &pduInfo->amHdr; + /* Create PDU with hdr and data */ + kwAmmCreatePdu(gCb,rbCb, amHdr, pduInfo, pdu); + + sdu->mode.am.isSegmented = TRUE; + sdu->sduSz -= macGrntSz; + sduMap.sduSz = macGrntSz; + +#ifdef LTE_L2_MEAS_RLC + kwUtlUpdSduSnMap(rbCb, sdu, kwDatReq, FALSE); +#endif /* LTE_L2_MEAS */ + + amDl->nxtTx = sdu; + nxtTxUpd = TRUE; + } + + /* Update bo for boReport */ + amDl->bo -= sduMap.sduSz; + + sduMap.sdu = sdu; + + /* Update pduInfo */ + kwDatReq->pduInfo.mBuf[kwDatReq->pduInfo.numPdu] = pdu; + kwDatReq->pduInfo.numPdu++; + numNewPdu++; + /* kw005.201 ccpu00117318, updating the statistics */ + gCb->genSts.pdusSent++; + gRlcStats.amRlcStats.numRlcAmCellSduBytesTx = gRlcStats.amRlcStats.numRlcAmCellSduBytesTx + sduMap.sduSz; + /* Update the RLC Tx buffer with the new PDU info */ + KW_MEM_CPY(&pduInfo->sduMap, &sduMap, sizeof(KwSduMap)); + pdu = NULLP; + + macGrntSz -= sduMap.sduSz; + /* Get next sdu for assembly */ + KW_LLIST_NEXT_SDU(amDl->sduQ, sdu); + + } /*End of pduSz loop */ + + kwDatReq->pduSz = macGrntSz; + /* Updating nxtTx to sdu in the Q */ + if (!nxtTxUpd) + amDl->nxtTx = sdu; + +#ifdef LTE_L2_MEAS + if(KW_MEAS_IS_DL_ANY_MEAS_ON_FOR_RB(gCb,rbCb) && + (rbCb->rlcId.rbType == CM_LTE_DRB)) + { + numSdus = 0; + currSduIdx = 0; + l2MeasTb = kwUtlGetCurMeasTb(gCb, rbCb); + kwUtlUpdateBurstSdus(gCb, rbCb, &contSduLst, dataVol, *totMacGrant); + if ((lchInfo.numSdus != 0) && (l2MeasTb != NULLP)) + { + for (lchIdx = 0; ((lchIdx < l2MeasTb->numLchInfo) + && (lchIdx < KW_MAX_ACTV_DRB )); lchIdx++) + { + if (l2MeasTb->lchInfo[lchIdx].lcId == rbCb->lch.lChId) + { + /* Lch Info already added in Retx procedure */ + break; + } + } + if (lchIdx < KW_MAX_ACTV_DRB) + { + if (lchIdx == l2MeasTb->numLchInfo) + { + l2MeasTb->lchInfo[lchIdx].lcId = rbCb->lch.lChId; + l2MeasTb->lchInfo[lchIdx].numSdus = 0; + l2MeasTb->numLchInfo++; + } + dstLchInfo = &l2MeasTb->lchInfo[lchIdx]; + currSduIdx = l2MeasTb->lchInfo[lchIdx].numSdus; + while ((numSdus < lchInfo.numSdus) && (currSduIdx < KW_L2MEAS_SDUIDX)) + { + dstLchInfo->sduInfo[currSduIdx].arvlTime = lchInfo.sduInfo[numSdus].arvlTime; + dstLchInfo->sduInfo[currSduIdx].isRetxPdu = FALSE; + numSdus++; + currSduIdx++; + } + l2MeasTb->lchInfo[lchIdx].numSdus += numSdus; + } + } + /* Fix Klock warning */ + if(l2MeasTb != NULLP) + { + l2MeasTb->txSegSduCnt += segSduCnt; + } + } + *totMacGrant -= (oldBo - amDl->bo); +#endif + + if(discSduInfo->numSduIds != 0) + { + /* Sap control block */ + KwUiKwuDiscSduCfm(&kwuSap->pst, kwuSap->suId, discSduInfo); + } + else + { + KW_SHRABL_STATIC_BUF_FREE(kwuSap->pst.region, kwuSap->pst.pool, discSduInfo, sizeof(KwuDiscSduInfo)); + } + + +#ifndef ALIGN_64BIT + RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId, + "kwAssembleSdus: BO after assembly = %ld UEID:%d CELLID:%d", + amDl->bo, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); +#else + RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId, + "kwAssembleSdus: BO after assembly = %d UEID:%d CELLID:%d", + amDl->bo, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); +#endif + + RETVOID; +} + +/** + * @brief Private handler to check if the poll bit needs to be set for data PDU + * + * @details + * Its a private function called by kwProcessSdus, to checks if the + * polling bit needs to be set for any RLC data PDU and updates the + * same. + * - For the new PDUs, if the counters exceed the configured + * pduWoPoll/byteWoPoll values, return poll bit. + * - For the PDUs/portion of PDUs, if the SDU list / retxBuf is + * empty, return poll bit. + * - Update the pollPdu, pollByte counters and Poll_SN; start staProhTmr + * + * @param[in] rCb RLC instance control block + * @param[in] rbCb RB control block + * @param[in] newPdu Flag to indicate if its a new AMD PDU. + * @param[in] bufSz Length of the PDU + * + * @return Bool + * -# 1 - To set the poll bit + * -# 0 - Poll bit is not set + * + */ +#ifdef ANSI +PRIVATE Bool kwAmmDlCheckAndSetPoll +( +KwCb *gCb, +KwDlRbCb *rbCb, +Bool newPdu, +MsgLen bufSz +) +#else +PRIVATE Bool kwAmmDlCheckAndSetPoll(gCb, rbCb, newPdu, bufSz) +KwCb *gCb; +KwDlRbCb *rbCb; +Bool newPdu; +MsgLen bufSz; +#endif +{ + Bool pollBit = FALSE; + KwAmDl *amDl = &(rbCb->m.amDl); + + TRC2(kwAmmDlCheckAndSetPoll) + + + /* If it's a new PDU increment PDU without poll and bytes without poll + and check if they cross the configured number of poll pdu and poll bytes*/ + if (newPdu) + { + amDl->pduWoPoll++; + /* Patch kw004.201 */ + amDl->byteWoPoll += bufSz; + + if (((amDl->pollPdu != -1) && (amDl->pduWoPoll >= amDl->pollPdu)) || + ((amDl->pollByte != -1) && (amDl->byteWoPoll >= amDl->pollByte))) + { + pollBit = TRUE; + } + } + + /* Check if both tx/retx buffer are empty or if tx window is stalled */ + if (((amDl->nxtTx == NULLP) && (amDl->nxtRetx == NULLP)) || + KW_AM_IS_TRANS_WIN_STALLED(amDl)) + { + pollBit = TRUE; + } + + if (pollBit) + { + amDl->pduWoPoll = 0; + amDl->byteWoPoll = 0; + + amDl->pollSn = (amDl->txNext - 1) & amDl->snModMask; + + RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId, + "kwAmmDlCheckAndSetPoll: Poll SN = %d UEID:%d CELLID:%d", + amDl->pollSn, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + + /* kw005.201: Fix for poll retransmission timer. + * Timer is stopped if it is already running and + * then starting the timer. Fixes crs + * ccpu00117216 and ccpu00118284 . + * */ + if( TRUE == kwChkTmr(gCb,(PTR)rbCb,KW_EVT_AMDL_POLL_RETX_TMR) ) + { + kwStopTmr(gCb,(PTR)rbCb, KW_EVT_AMDL_POLL_RETX_TMR); + } + + kwStartTmr(gCb,(PTR)rbCb, KW_EVT_AMDL_POLL_RETX_TMR); + } + + RETVALUE(pollBit); +} + +/** + * @brief Private handler to create AMD PDU + * + * @details + * This function constructs header and concatenate it with the data for + * the PDU. It also updates the txBuf with the created PDU. + * + * @param[in] gCB RLC instance control block + * @param[in] rbCb Downlink RB control block + * @param[in] amHdr AM header + * @param[in] KwDlPduInfo Pointer to PduInfo + * @param[in] pdu PDU buffer + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAmmCreatePdu +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwAmHdr *amHdr, +KwDlPduInfo *pduInfo, +Buffer *pdu +) +#else +PRIVATE Void kwAmmCreatePdu(gCb, rbCb, pduInfo, amHdr, pdu) +KwCb *gCb; +KwDlRbCb *rbCb; +KwAmHdr *amHdr; +KwDlPduInfo *pduInfo; +Buffer *pdu; +#endif +{ + U8 hdr[KW_MAX_HDRSZ]; + U16 idx; + KwTx *txBuf; + MsgLen pduSz; + KwAmDl *amDl = &(rbCb->m.amDl); + + TRC2(kwAmmCreatePdu) + + + /* Update sn */ + amHdr->sn = amDl->txNext; + + /*5GNR RLC: Increment txNext only if no segmentation of it is a last segment */ + if((!amHdr->si) || (amHdr->si == KW_SI_LAST_SEG)) + { + //printf("\n 5GNRLOG: no segment/last seg SDU with lcId %d Sn %u txNext %u So %u\n", + // rbCb->lch.lChId, amHdr->sn, amDl->txNext, amHdr->so); + amDl->txNext = (amDl->txNext + 1) & amDl->snModMask; + } + + /* Update hdr Info */ + SFndLenMsg(pdu, &pduSz); + + /* passing newPDU = TRUE*/ + amHdr->p = kwAmmDlCheckAndSetPoll(gCb,rbCb, TRUE, pduSz); + + /* Construct header with the available hdr Info, set isSegment to FALSE */ + kwConstructAmHdr(amHdr, hdr, amDl->snLen, &idx); + + /* Concatenate hdr and data */ + SAddPreMsgMultInOrder(hdr, idx+1, pdu); + + txBuf = kwUtlGetTxBuf(amDl->txBufLst, amHdr->sn); + kwCpyMsg(gCb,pdu,&(pduInfo->pdu)); + pduInfo->pduSz = pduSz; + pduInfo->hdrSz = idx+1; + + /*Update estHdrSz. deduct current hdrSz */ + amDl->estHdrSz -= pduInfo->hdrSz; + /* Reestimate estHdrSz for mid and last seg */ + if(amHdr->si & 0x1) + { + amDl->estHdrSz += ((amHdr->si == KW_SI_MID_SEG)? pduInfo->hdrSz : (pduInfo->hdrSz + 2)); + } + + cmLListAdd2Tail(&txBuf->pduLst, &pduInfo->lstEnt); + pduInfo->lstEnt.node = (PTR)pduInfo; + + gCb->genSts.bytesSent += pduSz; + + RETVOID; +} + +/** + * @brief Private handler to remove the retx PDU from the rbCb + * + * @details + * This function releases a retx PDU stored on DL portion of rbCb. + * It also updates the BO if wtForAck flag is not set which implies + * that it is not sent out yet. + * + * @param[in] gCb RLC instance control block + * @param[in] retx retransmit PDU to be removed + * @param[in] rbCb Radio Bearer Control Block + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwRemRetxPdu +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwRetx *retx +) +#else +PRIVATE Void kwRemRetxPdu(gCb, rbCb, retx) +KwCb *gCb; +KwDlRbCb *rbCb; +KwRetx *retx; +#endif +{ + TRC2(kwRemRetxPdu) + + cmLListDelFrm(&AMDL.retxLst, &retx->lstEnt); + + if( AMDL.retxLst.count == 0) + { + AMDL.nxtRetx = NULLP; + } + + if(retx->pendingReTrans == TRUE) + { + AMDL.retxBo -= retx->segSz; + AMDL.estHdrSz -= retx->hdrSz; + } + + kwUtlAddReTxPduToBeFreedQueue(gCb, retx); + kwUtlRaiseDlCleanupEvent(gCb); + + RETVOID; +} + +/** + * @brief Private handler to mark a retx PDU for further retransmission + * + * @details + * This function sets a retx PDU that has not been ACKed in the + * received Status PDU for futher retransmission. If the retransmission + * limit is reached, it releases the retx PDU and informs the higher + * layers about the same. + * + * @param[in] gCb RLC instance control block + * @param[in] retx retransmit PDU to be removed + * @param[in] rbCb Radio Bearer Control Block + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAmmDlMarkPduForReTx +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwRetx *retx +) +#else +PRIVATE Void kwAmmDlMarkPduForReTx(*gCb, rbCb, retx) +KwCb *gCb; +KwDlRbCb *rbCb; +KwRetx *retx; +#endif +{ + TRC2(kwAmmDlMarkPduForReTx) + if (AMDL.maxReTxReached == TRUE) + { + RETVOID; + } + + if(retx->pendingReTrans == FALSE) + { + retx->pendingReTrans = TRUE; + ++retx->retxCnt; + + AMDL.retxBo += retx->segSz; + AMDL.estHdrSz += retx->hdrSz; + + if (retx->retxCnt > AMDL.maxRetx) + { + /* RLC_DL_MAX_RETX fix */ + /* Marking the RB stalled for DL scheduling. This is to avoid unnecessary */ + /* preparation of RLC PDUs and adding the same to Tx Buffer */ + /* This condition is to avoid sending StaIndication more than once */ + if (TRUE != rbCb->m.amDl.maxReTxReached) + { + rbCb->m.amDl.maxReTxReached = TRUE; + rbCb->m.amDl.bo = 0; + rbCb->m.amDl.cntrlBo = 0; + rbCb->m.amDl.retxBo = 0; + /* Sending BO update to SCH */ + kwUtlSndDStaRsp(gCb, rbCb, 0,0,0,0); + kwAmmSndStaInd(gCb, rbCb, retx); + gRlcStats.amRlcStats.numDLMaxRetx++; + } + + kwRemRetxPdu(gCb,rbCb, retx); + + RETVOID; + } + + + if (AMDL.nxtRetx == NULLP) + { + AMDL.nxtRetx = retx; + } + + gRlcStats.amRlcStats.numDLRetransPdus++; + } + + + RETVOID; +} + +/** + * @brief Private handler to check if SDU is completely deliverd and + * send higher layers data confirmation + * + * @details + * This function sends higher layers data confirmation for SDUs which + * have been successfully delivered to the peer RLC entity. + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb Radio Bearer Control Block + * @param[in] sduLst List of SDUs that were part of the PDU + * @param[in] numSdu Number of SDUs in the list + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAmmDlCheckIsSDUDelivered +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwSduMap *sduMap, +KwuDatCfmInfo **datCfm +) +#else +PRIVATE Void kwAmmDlCheckIsSDUDelivered(gCb, rbCb, sduMap, datCfm) +KwCb *gCb; +KwDlRbCb *rbCb; +KwSduMap *sduMap; +KwuDatCfmInfo **datCfm; +#endif +{ + KwSdu *sdu; + + TRC2(kwAmmDlCheckIsSDUDelivered) + + sdu = sduMap->sdu; + + sdu->mode.am.rcvdSz += sduMap->sduSz; + + /* send a dat cfm if all the bytes of the sdu have been received */ + if (sdu->mode.am.rcvdSz == sdu->actSz) + { + /* Send DatCfm for this sdu */ + if((*datCfm)->numSduIds < KWU_MAX_DAT_CFM) + { + (*datCfm)->sduIds[(*datCfm)->numSduIds++] = sdu->mode.am.sduId; + } + else + { + /* This is an error that should never happen, we should resize + * the #define to a larger value or check why we need to + * send so many confirms in one go + * Confrims to PDCP are being dropped in this case + */ + KwKwuSapCb *kwuSap; + kwuSap = gCb->u.dlCb->kwuDlSap + KW_UI_PDCP; + KwUiKwuDatCfm(&kwuSap->pst, kwuSap->suId, *datCfm); + + KW_SHRABL_STATIC_BUF_ALLOC(kwuSap->pst.region, kwuSap->pst.pool, *datCfm, sizeof(KwuDatCfmInfo)); + +#if (ERRCLASS & ERRCLS_ADD_RES) + if (*datCfm == NULLP) + { + RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId, + "Memory allocation failed UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + RETVOID; + } +#endif /* ERRCLASS & ERRCLS_RES */ + + (*datCfm)->numSduIds = 0; + (*datCfm)->rlcId = rbCb->rlcId; + /* ccpu00135618: say total 1026 sduIds to copy the 1025 sduId after + * new allocation of datCfm */ + (*datCfm)->sduIds[(*datCfm)->numSduIds++] = sdu->mode.am.sduId; + } + + /* Remove SDU from the sduQ */ + cmLListDelFrm(&AMDL.sduQ, &sdu->lstEnt); + kwUtlAddSduToBeFreedQueue(gCb, sdu); + kwUtlRaiseDlCleanupEvent(gCb); + } + + RETVOID; +} + +/** + * @brief Private handler to mark a PDU successful. + * + * @details + * This function is called when we receive a STATUS pdu that marks + * a PDU as successful. It releases the PDU from RLC entity and + * informs PDCP of successful SDUs delivered as a result of this PDU. + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb Radio Bearer Control Block + * @param[in] sn SN that is successfully delivered to the peer + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAmmDlProcessSuccessfulTxPdu +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwSn sn, +KwuDatCfmInfo **datCfm +) +#else +PRIVATE Void kwAmmDlProcessSuccessfulTxPdu(gCb, rbCb, sn, datCfm) +KwCb *gCb; +KwDlRbCb *rbCb; +KwSn sn; +KwuDatCfmInfo **datCfm; +#endif +{ + TRC2(kwAmmDlProcessSuccessfulTxPdu) + CmLList *pduNode; + + KwTx *txBuf = kwUtlGetTxBuf(AMDL.txBufLst, sn); + + if (txBuf == NULLP) + { + RETVOID; + } + pduNode = txBuf->pduLst.first; + while(pduNode) + { + KwDlPduInfo *pduInfo = (KwDlPduInfo *)(pduNode->node); + kwAmmDlCheckIsSDUDelivered(gCb, + rbCb, + &(pduInfo->sduMap), + datCfm); + pduNode = pduNode->next; + } + + kwUtlAddTxPduToBeFreedQueue(gCb, txBuf); + kwUtlRaiseDlCleanupEvent(gCb); + /* so that it is not processed again */ + kwUtlRemovTxBuf(AMDL.txBufLst, txBuf, gCb); + + RETVOID; +} + +/** + * @brief Handler to send Status Indication to PDCP + * + * @details + * This function is used to send status indication to PDCP when the + * maximum retransmission threshold value is reached for a PDU. + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb RB control block + * @param[in] retx The PDU/segment that failed max re-transmissions + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAmmSndStaInd +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwRetx *retx +) +#else +PRIVATE Void kwAmmSndStaInd(gCb, rbCb, retx) +KwCb *gCb; +KwDlRbCb *rbCb; +KwRetx *retx; +#endif +{ + KwuStaIndInfo *staInd; + KwKwuSapCb *kwuSap; + + TRC2(kwAmmSndStaInd); + + + /* Sap control block */ + kwuSap = gCb->u.dlCb->kwuDlSap + KW_UI_PDCP; + + /* Allocate memory for staInd Info */ + KW_SHRABL_STATIC_BUF_ALLOC(kwuSap->pst.region, kwuSap->pst.pool, staInd, sizeof(KwuStaIndInfo)); + +#if (ERRCLASS & ERRCLS_ADD_RES) + if (staInd == NULLP) + { + RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId, + "Memory allocation failed UEID:%d CELLID:%d", + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + RETVOID; + } +#endif /* ERRCLASS & ERRCLS_RES */ + + /* Fill staInd Info */ + KW_MEM_CPY(&staInd->rlcId, &rbCb->rlcId, sizeof(CmLteRlcId)); + + staInd->numSdu = 1; + staInd->sduId[0] = retx->sduMap.sdu->mode.am.sduId; + +#ifdef KW_PDCP +#else + KwUiKwuStaInd(&kwuSap->pst, kwuSap->suId, staInd); +#endif /* KW_PDCP */ + + RETVOID; +} + +/** + * @brief Handler to get the next node to be retransmitted from retxLst + * + * @details + * This function is used to get the next node to be retransmitted + * from the retxLst + * + * @param[in] gCb RLC instance control block + * @param[in] retx The PDU/segment after which to find a node to be + * retransmitted + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwGetNxtRetx +( +KwCb *gCb, +KwRetx **retx +) +#else +PRIVATE Void kwGetNxtRetx(gCb, retx) +KwCb *gCb; +KwRetx **retx; +#endif +{ + CmLList *tNode; + + TRC2(kwGetNxtRetx); + + do + { + tNode = &((*retx)->lstEnt); + tNode = tNode->next; + + if (tNode) + { + *retx = (KwRetx *)tNode->node; + } + else + { + *retx = NULLP; + RETVOID; + } + }while((*retx)->pendingReTrans == FALSE); + + RETVOID; +} + +/** + * @brief Handler to process the re-establishment request received from UIM + * + * @param[in] gCb RLC instance control block + * @param[in] rlcId Identity of the RB in the UE/Cell for which + * re-establishment is to be done + * @param[in] rbCb Downlink RB control block (rbCb is freed in this + * function) + * + * @return Void + * + */ +#ifdef ANSI +PUBLIC Void kwAmmDlReEstablish +( +KwCb *gCb, +CmLteRlcId rlcId, +KwDlRbCb *rbCb +) +#else +PUBLIC Void kwAmmDlReEstablish(gCb, rlcId, rbCb) +KwCb *gCb; +CmLteRlcId rlcId; +KwDlRbCb *rbCb; +#endif +{ + /* create a new AM DL RB, reset it and replace in the UeCb*/ + KwDlUeCb *ueCb; + KwDlRbCb *resetRb; + KwAmDl* newAmDl; + KwAmDl* oldAmDl; + KW_ALLOC(gCb, resetRb, sizeof(KwDlRbCb)); + + /* ccpu00135170 Removing KLOCK warning */ + if(resetRb == NULLP) + { + RETVOID; + } + + KW_MEM_CPY(resetRb, rbCb, sizeof(KwDlRbCb)); + KW_MEM_SET(&resetRb->m.amDl, 0 , sizeof(KwAmDl)); + +/* AGHOSH changes start */ + /* restore the old AM values */ + newAmDl = &resetRb->m.amDl; + oldAmDl = &rbCb->m.amDl; + + newAmDl->pollPdu = oldAmDl->pollPdu; + newAmDl->pollByte = oldAmDl->pollByte; + newAmDl->maxRetx = oldAmDl->maxRetx; + newAmDl->snLen = oldAmDl->snLen; + newAmDl->snModMask = oldAmDl->snModMask; + newAmDl->pollRetxTmrInt = oldAmDl->pollRetxTmrInt; + rbCb->boUnRprtdCnt = (U32)0; + rbCb->lastRprtdBoToMac = (U32)0; + cmInitTimers(&(resetRb->m.amDl.pollRetxTmr), 1); +/* AGHOSH changes end */ + + if (ROK != kwDbmFetchDlUeCb(gCb,rlcId.ueId, rlcId.cellId, &ueCb)) + { + RLOG_ARG2(L_ERROR,DBG_CELLID, rlcId.cellId, + "UeId [%d]: UeCb not found RBID;%d", + rlcId.ueId, + rlcId.rbId); + RETVOID; + } + + if(rlcId.rbType == CM_LTE_SRB) + { + ueCb->srbCb[rlcId.rbId] = resetRb; + } + else + { + ueCb->drbCb[rlcId.rbId] = resetRb; + } + /* update into the logical channel array also */ + ueCb->lCh[rbCb->lch.lChId - 1].dlRbCb = resetRb; + + if((resetRb->rlcId.rbType == CM_LTE_SRB) + &&(resetRb->rlcId.rbId == 1)) + { + /* To stop the traffic on SRB2 and other DRBs*/ + kwDlUtlSetReestInProgressForAllRBs(gCb, ueCb); + } + else + { + kwDlUtlSetReestInProgressForRB(gCb, resetRb); + } + + /* allocate the TX array again */ +#ifndef LTE_TDD + U32 hashIndex; + KW_ALLOC(gCb, + resetRb->m.amDl.txBufLst, + (KW_TX_BUF_BIN_SIZE * sizeof(CmLListCp))); + for(hashIndex = 0; hashIndex < KW_TX_BUF_BIN_SIZE; hashIndex++) + { + cmLListInit(&(resetRb->m.amDl.txBufLst[hashIndex])); + } +#endif + /* send the old rb of deletion */ + kwAmmFreeDlRbCb(gCb,rbCb); + + + /* TODO: for now we are re-settting the re-establishment flag here + this needs to be fixed + There should be a proper intreface to resume the RBs */ + if(rlcId.rbType == CM_LTE_SRB) + { + kwDlUtlResetReestInProgress(ueCb->srbCb[rlcId.rbId]); + } + else + { + kwDlUtlResetReestInProgress(ueCb->drbCb[rlcId.rbId]); + } + + RETVOID; +} + +/** + * @brief Handler to discard a SDU. + * + * @details + * This function is used to discard a SDU after receiving + * the Discard Request from UIM. The SDU is discarded if its + * available and is not mapped to any PDU yet. + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb RB control block + * @param[in] sduId Sdu ID of the SDU to be discarded + * + * @return S16 + * -# ROK In case of successful discard + * -# RFAILED In case the SDU is not found or already mapped + */ +#ifdef ANSI +PUBLIC S16 kwAmmDiscSdu +( +KwCb *gCb, +KwDlRbCb *rbCb, +U32 sduId +) +#else +PUBLIC S16 kwAmmDiscSdu(gCb, rbCb, sduId) +KwCb *gCb; +KwDlRbCb *rbCb; +U32 sduId; +#endif +{ + TRC2(kwAmmDiscSdu); + RETVALUE(RFAILED); +} + +/** + * @brief Handler for Poll retransmit timer expiry + * + * @details + * This function is used to handle events upon expiry of Poll + * retransmit timer. + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb Downlink RB control block + * + * @return Void + */ +#ifdef ANSI +PUBLIC Void kwAmmPollRetxTmrExp +( +KwCb *gCb, +KwDlRbCb *rbCb +) +#else +PUBLIC Void kwAmmPollRetxTmrExp(gCb, rbCb) +KwCb *gCb; +KwDlRbCb *rbCb; +#endif +{ + KwRetx *retx; + KwAmDl *amDl = &(rbCb->m.amDl); + KwSn sn; + KwTx *txBuf; + TRC2(kwAmmPollRetxTmrExp); + + + /* kw003.201 - Correcting the logic for determmining whether to do */ + /* any transmission of PDU. As per the spec section */ + /* 5.2.2.3, if there is any to transmit or retransmit, */ + /* do nothing. Else, pick up the VT(S) -1 for retx */ + /* We have nothing to transmit if window is stalled or */ + /* there are no SDUs to be transmitted or if there are */ + /* PDUs to be retransmitted. */ + if(CM_LTE_SRB == rbCb->rlcId.rbType) + { + gRlcStats.amRlcStats.numDLPollTimerExpiresSrb++; + } + else + { + gRlcStats.amRlcStats.numDLPollTimerExpiresDrb++; + } + + if (((amDl->nxtTx == NULLP) && (amDl->nxtRetx == NULLP)) || + KW_AM_IS_TRANS_WIN_STALLED(amDl)) + { + sn = (amDl->txNext - 1) & amDl->snModMask; + txBuf = kwUtlGetTxBuf(amDl->txBufLst, sn); + + if (txBuf != NULLP) + { + kwAmmDlMoveFrmTxtoRetxBuffer(gCb,amDl, &retx, sn); + + if (AMDL.nxtRetx == NULLP) + { + AMDL.nxtRetx = retx; + } + + kwAmmSendDStaRsp(gCb, rbCb, &AMDL); + RETVOID; + } + /* Get the last node in retxLst */ + KW_LLIST_LAST_RETX(amDl->retxLst, retx); + + /* Unset wtForAck flag for the NACK PDUs */ + if (retx != NULLP) + { + kwAmmDlMarkPduForReTx(gCb, rbCb, retx); + kwAmmSendDStaRsp(gCb, rbCb, &AMDL); + } + } + + RETVOID; +} + +/** + * @brief Handler to update Acks for the remaining PDUs after the last accessed + * NACK PDU. + * + * @details + * This function is used to handle ACKs for the PDUs remaining after the + * last accessed NACK PDU, It updates the txBuf/retxBuf for the ACKs and + * sends DatCfm to PDCP for the same. + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb Downlink Radio Bearer control block + * @param[in] mAckSn The ACK SN after doing the base modulus + * @param[in] rextNode Next node in the re-transmission buffer + * + * @return Void + * + */ + +#ifdef ANSI +PRIVATE Void kwAmmDlUpdateTxAndReTxBufForAckSn +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwSn mAckSn, +CmLList *retxNode, +KwuDatCfmInfo **datCfm +) +#else +PRIVATE Void kwAmmDlUpdateTxAndReTxBufForAckSn(gCb, rbCb, mAckSn, retxNode, datCfm) +KwCb *gCb; +KwDlRbCb *rbCb; +KwSn mAckSn; +CmLList *retxNode; +KwuDatCfmInfo **datCfm; +#endif +{ + KwSn mSn; + KwSn sn; + KwRetx *retx; + KwTx *txBuf; + + TRC2(kwAmmDlUpdateTxAndReTxBufForAckSn); + + /* Remove pdus/segs from retxLst */ + while (retxNode) + { + retx = (KwRetx *)(retxNode->node); + retxNode = retxNode->next; + MODAMT(retx->amHdr.sn, mSn, AMDL.txNextAck,AMDL.snModMask); + if (mSn < mAckSn) + { + kwAmmDlProcessSuccessfulReTx(gCb,rbCb, retx, datCfm); + } + } + + /* For the remaining; pdus not acknowldeged by the NACK_SN but being + acknowledged by the ACK_SN*/ + /* start from the starting of the transmission window and remove till just + before ACK_SN*/ + mSn = 0; /* same as MODAMT(AMDL.txNextAck, mSn, AMDL.txNextAck);*/ + sn = AMDL.txNextAck; + while(mSn < mAckSn) + { + txBuf = kwUtlGetTxBuf(AMDL.txBufLst, sn); + if (txBuf != NULLP) + { + RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId, + "kwAmmDlUpdateTxAndReTxBufForAckSn: ACK for PDU " + "with sn = %ld UEID:%ld CELLID:%ld", + sn, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + + kwAmmDlProcessSuccessfulTxPdu(gCb,rbCb, sn, datCfm); + } + + sn = (sn + 1) & AMDL.snModMask; + MODAMT(sn, mSn, AMDL.txNextAck,AMDL.snModMask); + } + + RETVOID; +} + +/** + * @brief Handler to update Acks for the remaining PDUs after the last accessed + * NACK PDU. + * + * @details + * This function is used to handle ACKs for the PDUs remaining after the + * last accessed NACK PDU, It updates the txBuf/retxBuf for the ACKs and + * sends DatCfm to PDCP for the same. + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb Downlink Radio Bearer control block + * @param[in] mAckSn The ACK SN after doing the base modulus + * @param[in] rextNode Next node in the re-transmission buffer + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAmmDlUpdTxAndReTxBufForLessThanNackSn +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwSn sn, +KwSn mNackSn, +CmLList **retxNode, +KwuDatCfmInfo **datCfm +) +#else +PRIVATE Void kwAmmDlUpdTxAndReTxBufForLessThanNackSn(gCb, rbCb, sn, mNackSn, retxNode, datCfm) +KwCb *gCb; +KwDlRbCb *rbCb; +KwSn sn; +KwSn mNackSn; +CmLList **retxNode; +KwuDatCfmInfo **datCfm; +#endif +{ + KwSn mSn; + KwRetx *retx; + KwTx *txBuf=NULLP; + + TRC2(kwAmmDlUpdTxAndReTxBufForLessThanNackSn); + + while (*retxNode) + { + retx = (KwRetx *)((*retxNode)->node); + MODAMT(retx->amHdr.sn, mSn, AMDL.txNextAck,AMDL.snModMask); + if (mSn < mNackSn) + { + (*retxNode) = (*retxNode)->next; + kwAmmDlProcessSuccessfulReTx(gCb,rbCb, retx, datCfm); + } + else + { + break; + } + } + + /* Remove all pdus with SN < NACK_SN from the transmission buffer */ + MODAMT(sn, mSn, AMDL.txNextAck,AMDL.snModMask); + while (mSn < mNackSn) + { + /* this if check seems redundant,why should mSn ever be mTxSn + (which actually is VT(A) */ + txBuf = kwUtlGetTxBuf(AMDL.txBufLst, sn); + if ((txBuf != NULLP)) + { + RLOG_ARG3(L_DEBUG,DBG_RBID, rbCb->rlcId.rbId, + "kwHndlStaRsp: Handle ACK (sn = %d) UEID:%d CELLID:%d", + sn, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + + /* Remove pdus from txBuf */ + kwAmmDlProcessSuccessfulTxPdu(gCb,rbCb, sn, datCfm); + } + + sn = (sn + 1) & AMDL.snModMask; + MODAMT(sn, mSn, AMDL.txNextAck,AMDL.snModMask); + } + + RETVOID; +} + + +/** + * @brief Handler to form construct AM header + * + * @details + * This function is used to construct am header with the available header + * elements. + * + * @param[in] gCb RLC instance control block + * @param[in] amHdr AM Header + * @param[in] isSeg Check for Segmentation of PDU + * @param[in] hdr Header field + * @param[in] idx Index + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwConstructAmHdr +( +KwAmHdr *amHdr, +U8 *hdr, +U8 snLen, +U16 *idx +) +#else +PRIVATE Void kwConstructAmHdr(amHdr, hdr, snLen, idx) +KwAmHdr *amHdr; +U8 *hdr; +U8 snLen; +U16 *idx; +#endif +{ + TRC2(kwConstructAmHdr); + + *idx = 0; + hdr[0] = KW_DATA_BITMASK; + + hdr[0] = hdr[0] | (amHdr->p << 6); + hdr[0] = hdr[0] | ((amHdr->si & 0x3) << 4); + if(snLen == KW_AM_CFG_12BIT_SN_LEN) + { + hdr[0] = hdr[0] | (U8)((amHdr->sn & 0xF00) >> 8); + hdr[1] = (U8)(amHdr->sn & 0x0FF); + (*idx)++; + } + else + { + hdr[0] = hdr[0] | (U8)((amHdr->sn & 0x30000) >> 16); + hdr[1] = (U8)((amHdr->sn & 0xFF00) >> 8); + (*idx)++; + hdr[2] = (U8)(amHdr->sn & 0xFF); + (*idx)++; + } + + if ((amHdr->si == KW_SI_MID_SEG) || (amHdr->si == KW_SI_LAST_SEG)) + { + (*idx)++; + hdr[(*idx)] = (U8)((amHdr->so & 0xFF00)>> 8); + (*idx)++; + hdr[(*idx)] = (U8)(amHdr->so & 0xFF); + } + + RETVOID; +} + +/** + * @brief This function adds a retx PDU to list of retx PDUs + * + * @details + * kw003.201 - Poll expiry may cause an SN to be added to retx + * out of sequence and hence all additions to retx + * must validate that they are added in sequence + * + * @param[in] amDl AM Downlink Control Block + * @param[in] retx Retransmit PDU + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAmmAddPduToRetxLst +( +KwAmDl *amDl, +KwRetx *retx +) +#else +PRIVATE Void kwAmmAddPduToRetxLst(amDl, retx) +KwAmDl *amDl; +KwRetx *retx; +#endif +{ + CmLList *node; + KwRetx *tRetx; + KwSn tMSn; + KwSn retxMSn; + + TRC2(kwAmmAddPduToRetxLst); + + node = amDl->retxLst.last; + MODAMT(retx->amHdr.sn, retxMSn, amDl->txNextAck,amDl->snModMask); + while(node != NULLP) + { + tRetx = (KwRetx *)(node->node); + MODAMT(tRetx->amHdr.sn, tMSn, amDl->txNextAck,amDl->snModMask); + if (tMSn > retxMSn) + { + node = node->prev; + } + else + { + break; + } + } + if (node) + { + amDl->retxLst.crnt = node; + cmLListInsAfterCrnt(&amDl->retxLst, &retx->lstEnt); + retx->lstEnt.node = (PTR)retx; + } + else + { + amDl->retxLst.crnt = amDl->retxLst.first; + cmLListInsCrnt(&amDl->retxLst, &retx->lstEnt); + retx->lstEnt.node = (PTR)retx; + } + + if (amDl->nxtRetx == NULLP) + { + amDl->nxtRetx = retx; + } + + RETVOID; +} + +/** + * @brief Handler to Move the PDU from txBuf to re-transmission buffer + * + * @details + * This function is used to move the PDU from the txBuf to re-transmit buffer + * + * @param[in] gCb RLC instance control block + * @param[in] amDl AM Downlink Control Block + * @param[in] retx node in the reTx buffer to be moved to, allocated by + * this function + * @param[in] sn SN in the tx buffer which needs to be moved + * + * @return Void + * + */ + +#ifdef ANSI +PRIVATE Void kwAmmDlMoveFrmTxtoRetxBuffer +( +KwCb *gCb, +KwAmDl *amDl, +KwRetx **retx, +KwSn sn +) +#else +PRIVATE Void kwAmmDlMoveFrmTxtoRetxBuffer(gCb, amDl, retx, sn) +KwCb *gCb; +KwAmDl *amDl; +KwRetx **retx; +KwSn sn; +#endif +{ + KwTx* txBuf = kwUtlGetTxBuf(amDl->txBufLst, sn); + TRC2(kwAmmDlMoveFrmTxtoRetxBuffer); + + if (txBuf == NULLP) + { + RETVOID; + } + while(txBuf->pduLst.first) + { + KwDlPduInfo *pduInfo = (KwDlPduInfo *)(txBuf->pduLst.first->node); + KW_ALLOC_WC(gCb,*retx, sizeof(KwRetx)); + +#if (ERRCLASS & ERRCLS_ADD_RES) + if (*retx == NULLP) + { + RLOG0(L_FATAL, "Memory allocation failed"); + RETVOID; + } +#endif /* ERRCLASS & ERRCLS_RES */ + + /* Move Sdu byte segment from TX buf to retx buf*/ + kwAmmDlMoveSduByteSegFrmTxtoRetxBuffer(gCb, + amDl, + retx, + pduInfo); + + /* Delete node from the txBuf Pdu lst */ + cmLListDelFrm(&txBuf->pduLst, txBuf->pduLst.first); + KW_FREE_WC(gCb, pduInfo, sizeof(KwDlPduInfo)); + } + /* Remove PDU from txBuf */ + kwUtlDelTxBuf(amDl->txBufLst, txBuf,gCb); + + RETVOID; +} + + + +/* + * @brief + * function to free/release the Acknowledged mode RBCB buffers + * + * @details + * This primitive Frees the Acknowledged Mode RbCb transmission Buffer, + * retransmission Buffer and reciption Buffers + * + * @param [in] gCb - RLC instance control block + * @param [in] rbCb - Downlink RB Control Block + * + * @return Void + */ +#ifdef ANSI +PUBLIC Void kwAmmFreeDlRbCb +( +KwCb *gCb, +KwDlRbCb *rbCb +) +#else +PUBLIC Void kwAmmFreeDlRbCb(gCb,rbCb) +KwCb *gCb; +KwDlRbCb *rbCb; +#endif +{ + /* stop the re-transmission timer */ + if(TRUE == kwChkTmr(gCb,(PTR)rbCb,KW_EVT_AMDL_POLL_RETX_TMR)) + { + kwStopTmr(gCb,(PTR)rbCb, KW_EVT_AMDL_POLL_RETX_TMR); + } + + /* store the entire Rb pointer */ + rbCb->rlsLnk.node = (PTR)rbCb; + cmLListAdd2Tail(&gCb->u.dlCb->toBeFreed.rbLst, &rbCb->rlsLnk); + + /* the sdu queue */ + cmLListCatLList(&(gCb->u.dlCb->toBeFreed.sduLst),&(rbCb->m.amDl.sduQ)); + + kwUtlRaiseDlCleanupEvent(gCb); + + RETVOID; +} + +/** + * @brief Handler to create STATUS Pdu + * + * @details + * This function is used to create status pdu + * + * @param[in] gCb RLC instance control block + * @param[in] rbCb Downlink RB control block + * @param[in] kwDatReq The data to be passed to MAC + * + * @return Void + * + */ +#ifdef ANSI +PRIVATE Void kwAmmCreateStatusPdu +( +KwCb *gCb, +KwDlRbCb *rbCb, +KwDatReq *kwDatReq +) +#else +PRIVATE Void kwAmmCreateStatusPdu(gCb, rbCb, kwDatReq) +KwCb *gCb; +KwDlRbCb *rbCb; +KwDatReq *kwDatReq; +#endif +{ + KwSn sn; /* sequence number */ + KwSn ack_sn; /* Ack sequence number */ + Buffer *mBuf; /* control pdu buffer */ + MsgLen cntrlPduSz; /* control pdu size */ + U8 cntrlPdu[KW_MAX_CNTRL_FIELDS]; /* control pdu to be added to mBuf */ + KwUdxDlStaPdu *pStaPdu; + U16 bytesToEncode = 0; /* bytes required to encode the STATUS PDU */ + U16 encIdx = 0; + U16 prevEncIdx = 0; + KwNackInfo *kwNackInfo; + U16 nkCnt = 0; + + TRC2(kwAmmCreateStatusPdu) + + + pStaPdu = AMDL.pStaPdu; + + + /* D/C| CPT| */ + /* 0 - Control + 1 - Data */ + cntrlPdu[0] = 0x00; + cntrlPdu[2] = 0x00; + + /* ACK SN Field will be set in the end based on available Grant */ + + encIdx = bytesToEncode = 3; /* Num Octets before NACK SN info encoding*/ + + ack_sn = pStaPdu->ackSn; + + if (rbCb->m.amDl.snLen == KW_AM_CFG_12BIT_SN_LEN) + { + + /* If alteast one NACK SN Info then set the E1 field */ + if (pStaPdu->nackCount) + { + /* 12 BIT SN CASE: + In Third Octet: + 7 6 5 4 3 2 1 0 + E1 R R R R R R R + */ + cntrlPdu[2] = 0x80; + } + + for(nkCnt = 0;nkCnt < pStaPdu->nackCount; nkCnt++) + { + sn = pStaPdu->nackInfo[nkCnt].sn; + + kwNackInfo = &(pStaPdu->nackInfo[nkCnt]); + + bytesToEncode += 2; /* 2 Octets for NACK SN */ + + /* Check if E2 : isSegment is set */ + if (kwNackInfo->isSegment) + { + bytesToEncode += 4; /* 4 Octets: SOstart, SOend */ + } + + /* Check if E3 : nackRange is set */ + if (kwNackInfo->nackRange) + { + bytesToEncode += 1; /* 1 Octet: NACK range */ + } + + /* Check if this NACK info can be accomodated in the Grant */ + if( kwDatReq->pduSz >= bytesToEncode) + { + /* If there is a NACK SN before this then set its + E1 bit */ + if(prevEncIdx) + { + /* NACKSN E1 E2 E3 R */ + cntrlPdu[prevEncIdx + 1] |= 0x8; + } + + /* 12 BIT Nack SN encode */ + cntrlPdu[encIdx] = (sn & 0xFF0) >> 4; + + /* Next Octet */ + cntrlPdu[encIdx + 1] = (sn & 0xF) << 4; + + if (kwNackInfo->isSegment) + { + /*Set E2 Bit */ + cntrlPdu[encIdx + 1] |= 0x4; + + + /* Add soStart and soEnd */ + /* SOstart */ + cntrlPdu[encIdx + 2] = (kwNackInfo->soStart) >> 8; + cntrlPdu[encIdx + 3] = kwNackInfo->soStart & 0xFF; + + /* SOend */ + cntrlPdu[encIdx + 4] = (kwNackInfo->soEnd) >> 8; + cntrlPdu[encIdx + 5] = kwNackInfo->soEnd & 0xFF; + } + + if (kwNackInfo->nackRange) + { + /*Set E3 Bit */ + cntrlPdu[encIdx + 1] |= 0x2; + if(kwNackInfo->isSegment) + { + cntrlPdu[encIdx + 6] = kwNackInfo->nackRange; + } + else + { + cntrlPdu[encIdx + 2] = kwNackInfo->nackRange; + } + } + + gRlcStats.amRlcStats.numDLNacksInStaPdu++; + } + /* Set ACK SN now */ + else + { + ack_sn = kwNackInfo->sn; + + /* Not even one nack can be accomodated */ + if (nkCnt == 0) + { + cntrlPdu[2] = 0x0; + } + + break; + } + + prevEncIdx = encIdx; + encIdx = bytesToEncode; + + }/* Loop is done for the NackCount */ + + /* set ACK SN */ + { + + RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId, + "kwAssembleCntrlInfo: ACK PDU's SN = %d" + "UEID:%d CELLID:%d", + ack_sn, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + + cntrlPdu[0] |= (ack_sn & 0xF00)>> 8; + cntrlPdu[1] = (U8)ack_sn; + } + + } + else if (rbCb->m.amDl.snLen == KW_AM_CFG_18BIT_SN_LEN) + { + /* If alteast one NACK SN Info then set the E1 field */ + if (pStaPdu->nackCount) + { + /* 12 BIT SN CASE: + In Third Octet: + 7 6 5 4 3 2 1 0 + ACKSN E1 R + */ + cntrlPdu[2] = 0x2; + } + + for(nkCnt = 0;nkCnt < pStaPdu->nackCount; nkCnt++) + { + sn = pStaPdu->nackInfo[nkCnt].sn; + + kwNackInfo = &(pStaPdu->nackInfo[nkCnt]); + + bytesToEncode += 3; /* 3 Octets for NACK SN */ + + /* Check if E2 : isSegment is set */ + if (kwNackInfo->isSegment) + { + bytesToEncode += 4; /* 4 Octets: SOstart, SOend */ + } + + /* Check if E3 : nackRange is set */ + if (kwNackInfo->nackRange) + { + bytesToEncode += 1; /* 1 Octet: NACK range */ + } + + /* Check if this NACK info can be accomodated in the Grant */ + if( kwDatReq->pduSz >= bytesToEncode) + { + /* If there is a NACK SN before this then set its + E1 bit */ + if(prevEncIdx) + { + /* NACKSN E1 E2 E3 R R R */ + cntrlPdu[prevEncIdx + 2] |= 0x20; + } + + /* 18 BIT Nack SN encode */ + cntrlPdu[encIdx] = (U8)((sn & 0x3FC00) >> 10); + + /* Next Octet */ + cntrlPdu[encIdx + 1] = (U8)((sn & 0x3FC) >> 2); + + /* Next Octet */ + cntrlPdu[encIdx + 2] = (U8)((sn & 0x3)<< 6); + + if (kwNackInfo->isSegment) + { + /* NACKSN E1 E2 E3 R R R */ + /*Set E2 Bit */ + cntrlPdu[encIdx + 2] |= 0x10; + + + /* Add soStart and soEnd */ + /* SOstart */ + cntrlPdu[encIdx + 3] = (kwNackInfo->soStart) >> 8; + cntrlPdu[encIdx + 4] = (U8)kwNackInfo->soStart; + + /* SOend */ + cntrlPdu[encIdx + 5] = (kwNackInfo->soEnd) >> 8; + cntrlPdu[encIdx + 6] = (U8)(kwNackInfo->soEnd); + } + + if (kwNackInfo->nackRange) + { + /* NACKSN E1 E2 E3 R R R */ + /*Set E3 Bit */ + cntrlPdu[encIdx + 2] |= 0x08; + + if (kwNackInfo->isSegment) + { + cntrlPdu[encIdx + 7] = kwNackInfo->nackRange; + } + else + { + cntrlPdu[encIdx + 3] = kwNackInfo->nackRange; + } + } + + gRlcStats.amRlcStats.numDLNacksInStaPdu++; + } + /* Set ACK SN now */ + else + { + ack_sn = kwNackInfo->sn; + + /* Not even one nack can be accomodated */ + if (nkCnt == 0) + { + cntrlPdu[2] &= 0xFD; + } + + break; + } + + prevEncIdx = encIdx; + encIdx = bytesToEncode; + + }/* Loop is done for the NackCount */ + + /* set ACK SN */ + { + + RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId, + "kwAssembleCntrlInfo: ACK PDU's SN = %d" + "UEID:%d CELLID:%d", + ack_sn, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + + + cntrlPdu[0] |= (ack_sn & 0x3C000) >> 14; + cntrlPdu[1] = (ack_sn & 0x3FC0) >> 6; + cntrlPdu[2] |= (ack_sn & 0x3F)<< 2; + } + + } + else + { + /* ERROR Log */ + RLOG_ARG3(L_ERROR,DBG_RBID,rbCb->rlcId.rbId, + "kwAssembleCntrlInfo:Conf SN LEN %d is INVALID !!!! UEID:%d CELLID:%d", + rbCb->m.amDl.snLen, + rbCb->rlcId.ueId, + rbCb->rlcId.cellId); + } + + +#ifndef L2_OPTMZ + SGetMsg(KW_GET_MEM_REGION(gCb), KW_GET_MEM_POOL(gCb),&mBuf); +#else + mBuf = (Buffer *)kwAmmStaPduList[kwAmmStaPduListCnt++]; + SResetMBuf(mBuf); + if(kwAmmStaPduListCnt > 511) + kwAmmStaPduListCnt = 0; +#endif + + cntrlPduSz = encIdx; + SAddPstMsgMult (cntrlPdu, cntrlPduSz, mBuf); + + kwDatReq->pduSz -= cntrlPduSz; + /* Add mBuf to AMDL.mBuf */ + AMDL.mBuf = mBuf; + + RETVOID; +} + +#ifdef RLC_STA_PROC_IN_MAC/* RLC Status PDU Processing */ + +S16 kwProcDlStatusPdu(Pst *udxPst,SuId suId, + CmLteCellId cellId,CmLteRnti rnti,CmLteLcId lcId,Buffer *rlcSdu); + +#ifdef ANSI +PRIVATE Void rgAmmExtractElmnt +( +KwCb *gCb, +Buffer *pdu, +KwExtHdr *hdrInfo +) +#else +PRIVATE Void rgAmmExtractElmnt(gCb, pdu, hdrInfo) +KwCb *gCb; +Buffer *pdu; +KwExtHdr *hdrInfo; +#endif +{ + U8 hdr; + U8 pLen = hdrInfo->pLen; + U8 len = (U8)hdrInfo->len; + U16 val; + U8 tHdr; + U8 fLen; + U8 rLen; + /* U8 rLen1 = 0; */ + U16 tVal; + + TRC2(kwAmmExtractElmnt); + + hdr = hdrInfo->hdr; + + if (pLen == 0) + { + SRemPreMsg(&hdr, pdu); + pLen = 8; + } + tHdr = hdr; + if (len <= 8) + { + val = tHdr >> (KW_BYTE_LEN - (len)); + hdr = hdr << len; + pLen -= len; + } + else /*if (len > 8) */ + { + fLen = pLen; + val = tHdr; + val = val >> (KW_BYTE_LEN - fLen); + val = val << (len - fLen); + rLen = len - fLen; + SRemPreMsg(&hdr, pdu); + tHdr = hdr; + if (rLen <= 8) + { + hdr = hdr >> (KW_BYTE_LEN - rLen); + val = val | hdr; + hdr = tHdr << rLen; + pLen = (KW_BYTE_LEN - rLen); + } + else + { + rLen = rLen - KW_BYTE_LEN; + tVal = hdr; + tVal = tVal << rLen; + val = val | tVal; + + SRemPreMsg(&hdr, pdu); + tHdr = hdr; + hdr = hdr >> (KW_BYTE_LEN - rLen); + val = val | hdr; + hdr = tHdr << rLen; + pLen = (KW_BYTE_LEN - rLen); + } + } + + hdrInfo->pLen = pLen; + hdrInfo->hdr = hdr; + hdrInfo->val = val; + + RETVOID; +} + + + + +#ifdef ANSI +PRIVATE Void rgAmmUlHndlStatusPdu +( +Pst *udxPst, +SuId suId, +KwCb *gCb, +KwDlRbCb *rbCb, +Buffer *cntrlPdu, +U8 *fByte +) +#else +PRIVATE Void rgAmmUlHndlStatusPdu(udxPst,suId,gCb, rbCb, cntrlPdu, fByte) +Pst *udxPst; +SuId suId; +KwCb *gCb; +KwDlRbCb *rbCb; +Buffer *cntrlPdu; +U8 *fByte; +#endif +{ + U8 e1; + KwExtHdr hdrInfo; + KwUdxStaPdu *pStaPdu; + U8 e3; /* NACK RANGE : 5GNR */ + U32 snLen; + U32 snRange; + U32 resrvdBitsAckSn; + U32 resrvdBitsNackSn; + + + TRC2(rgAmmUlHndlStatusPdu) + + KWDBGP_BRIEF(gCb, "rgAmmUlHndlStatusPdu(rbCb, cntrlPdu, fByte) \n"); + + KW_MEM_ZERO(&hdrInfo, sizeof(KwExtHdr)); + + /* Extract the Control PDU */ + hdrInfo.hdr = (*fByte << 1); + hdrInfo.pLen = 4; + + /* D/C has been shifted in the calling function */ + if (hdrInfo.hdr & 0xE0) + { + KWDBGP_ERROR(gCb, "rgAmmUlHndlStatusPdu: Reserved value for CPT received \n"); + RETVOID; + } + + KW_ALLOC_SHRABL_BUF(udxPst->region, + udxPst->pool, + pStaPdu, + sizeof(KwUdxStaPdu)); + +#if (ERRCLASS & ERRCLS_ADD_RES) + /* Memory allocation failure can not be expected */ + if(!pStaPdu) + { + RETVOID; + } +#endif + + if (rbCb->m.amDl.snLen == KW_AM_CFG_12BIT_SN_LEN) + { + snLen = 12; + resrvdBitsAckSn = KW_STA_PDU_R_BITS_ACKSN_12BITS; + resrvdBitsNackSn = KW_STA_PDU_R_BITS_NACKSN_12BITS; + } + else if (rbCb->m.amDl.snLen == KW_AM_CFG_18BIT_SN_LEN) + { + snLen = 18; + resrvdBitsAckSn = KW_STA_PDU_R_BITS_ACKSN_18BITS; + resrvdBitsNackSn = KW_STA_PDU_R_BITS_NACKSN_18BITS; + } + else + { + snLen = KW_SN_LEN; + resrvdBitsAckSn = 0; + resrvdBitsAckSn = 0; + } + + pStaPdu->nackCnt = 0; + /* For CPT */ + hdrInfo.hdr = hdrInfo.hdr << KW_CPT_LEN; + + /* ACK Sn */ + hdrInfo.len = KW_SN_LEN; + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + pStaPdu->ackSn = hdrInfo.val; + + /* Check if NACK Exists */ + hdrInfo.len = KW_E1_LEN; + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + e1 = (U8)hdrInfo.val; + KWDBGP_DETAIL(gCb, "rgAmmUlHndlStatusPdu: ACK SN = %d \n", pStaPdu->ackSn); + + /* Extract the Reserved Bits after ACK SN field */ + hdrInfo.len = resrvdBitsAckSn; + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + + + /* If NACK exists in control PDU */ + /* For ACKs and NACKs */ + while (e1 && (pStaPdu->nackCnt < KW_MAX_NACK_CNT)) + { + hdrInfo.len = snLen; + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + pStaPdu->nackInfo[pStaPdu->nackCnt].sn = hdrInfo.val; + + hdrInfo.len = KW_E1_LEN; + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + e1 = (U8)hdrInfo.val; + + /* Extract e2 */ + /* hdrInfo.len = KW_E1_LEN; --> previusly stored value (for e1) is + already present*/ + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + /* e2 = (U8) hdrInfo.val;*/ + + /* Store e2 value */ + pStaPdu->nackInfo[pStaPdu->nackCnt].isSegment = (U8) hdrInfo.val; + + /* Extract e3 : 5GNR */ + /* hdrInfo.len = KW_E1_LEN; --> previusly stored value (for e1) is + already present*/ + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + e3 = (U8) hdrInfo.val; + + /* Extract Reserved Bits after NACK SN */ + hdrInfo.len = resrvdBitsNackSn; + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + + /* Test for resegmentation */ + if (pStaPdu->nackInfo[pStaPdu->nackCnt].isSegment) + { + hdrInfo.len = KW_SO_LEN_5GNR; /* 5GNR : SO Len 16 Bits */ + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + pStaPdu->nackInfo[pStaPdu->nackCnt].soStart = hdrInfo.val; + + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + pStaPdu->nackInfo[pStaPdu->nackCnt].soEnd = hdrInfo.val; + + KWDBGP_DETAIL(gCb, + "rgAmmUlHndlStatusPdu: soStart and soEnd = %d %d \n", + pStaPdu->nackInfo[pStaPdu->nackCnt].soStart, + pStaPdu->nackInfo[pStaPdu->nackCnt].soEnd); + } + else + { + hdrInfo.len = 0; + pStaPdu->nackInfo[pStaPdu->nackCnt].soStart = 0; + pStaPdu->nackInfo[pStaPdu->nackCnt].soEnd = 0; + + } + /* NACK RANGE Field is SET */ + if (e3) + { + /* Extract NACK range field */ + hdrInfo.len = KW_NACK_RANGE_LEN; + rgAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo); + snRange = (U8)hdrInfo.val; + + pStaPdu->nackInfo[pStaPdu->nackCnt].nackRange = snRange; + + } + pStaPdu->nackCnt++; + } + + gRlcStats.amRlcStats.numULStaPduRcvd++; + gRlcStats.amRlcStats.numULNackInStaPduRcvd += pStaPdu->nackCnt; + + /* In case we have reached the MAX NACK CNT, then we should modify the ACK_SN + to the last NACK SN + 1 and discard the original ACK_SN*/ + if(pStaPdu->nackCnt == KW_MAX_NACK_CNT) + { + pStaPdu->ackSn = (pStaPdu->nackInfo[KW_MAX_NACK_CNT-1].sn + 1) & amDl->snModMask; + } + + + /* Parse & send Status PDU to RLC-DL */ + //KwUlUdxStaUpdReq(&(sapCb->pst), sapCb->spId, &rbCb->rlcId, pStaPdu); + KwUlUdxStaUpdReq(udxPst, suId, &rbCb->rlcId, pStaPdu); + + RETVOID; +} + +PUBLIC S16 kwProcDlStatusPdu(Pst *udxPst,SuId suId, + CmLteCellId cellId,CmLteRnti rnti,CmLteLcId lcId,Buffer *rlcSdu) +{ + KwDlRbCb *rbCb = NULLP; + KwDlUeCb *ueCb = NULLP; + U8 fByte; + U8 temp; + S16 retVal = RFAILED; + KwCb *gCb; + Pst dlRlcPst = *udxPst; + + gCb = KW_GET_KWCB(1); /* DL RLC instance */ + + if( ROK != kwDbmFetchDlUeCb(gCb,rnti,cellId,&(ueCb))) + { + printf("\n RLC UECb Not found...\n"); + RETVALUE(RFAILED); + } + + + rbCb = ueCb->lCh[lcId - 1].dlRbCb; + + /* Skip if mode is not AM */ + if((rbCb == NULLP) || (rbCb->mode != CM_LTE_MODE_AM)) + { + RETVALUE(RFAILED); + } + + if(ROK != SExamMsg((Data *)(&fByte), + rlcSdu, 0)) + { + printf("\n Failure in Rlc Hdr SExamMsg\n"); + RETVALUE(RFAILED); + } + + if(KW_CNTRL_PDU == ((fByte & KW_DC_POS) >> KW_DC_SHT)) + { + SRemPreMsg(&temp, rlcSdu); + dlRlcPst.selector = 1;/* LWLC*/ + rgAmmUlHndlStatusPdu(&dlRlcPst,suId,gCb, rbCb, rlcSdu, &fByte); + retVal = ROK; + } + + RETVALUE(retVal); +} + + +#endif + + +/*@}*/ + + +/********************************************************************30** + + End of file +**********************************************************************/