Initial commit
[o-du/l2.git] / src / 5gnrrlc / kw_amm_dl.c
diff --git a/src/5gnrrlc/kw_amm_dl.c b/src/5gnrrlc/kw_amm_dl.c
new file mode 100755 (executable)
index 0000000..7b309f8
--- /dev/null
@@ -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
+
+
+/*@}*/
+
+\f  
+/********************************************************************30**
+  
+         End of file
+**********************************************************************/