Initial commit
[o-du/l2.git] / src / 5gnrrlc / kw_umm_ul.c
diff --git a/src/5gnrrlc/kw_umm_ul.c b/src/5gnrrlc/kw_umm_ul.c
new file mode 100755 (executable)
index 0000000..449e186
--- /dev/null
@@ -0,0 +1,966 @@
+/*******************************************************************************
+################################################################################
+#   Copyright (c) [2017-2019] [Radisys]                                        #
+#                                                                              #
+#   Licensed under the Apache License, Version 2.0 (the "License");            #
+#   you may not use this file except in compliance with the License.           #
+#   You may obtain a copy of the License at                                    #
+#                                                                              #
+#       http://www.apache.org/licenses/LICENSE-2.0                             #
+#                                                                              #
+#   Unless required by applicable law or agreed to in writing, software        #
+#   distributed under the License is distributed on an "AS IS" BASIS,          #
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
+#   See the License for the specific language governing permissions and        #
+#   limitations under the License.                                             #
+################################################################################
+*******************************************************************************/
+
+/**********************************************************************
+
+     Name:     LTE-RLC Layer 
+  
+     Type:     C file
+  
+     Desc:     Source code for RLC Unacknowledged mode assembly and
+               reassembly.This file contains following functions
+
+                  --kwUmmQSdu
+                  --kwUmmProcessSdus
+                  --kwUmmProcessPdus
+                  --kwUmmReAssembleSdus
+                  --kwUmmReEstablish 
+
+     File:     kw_umm_ul.c
+
+**********************************************************************/
+static const char* RLOG_MODULE_NAME="RLC";
+static int RLOG_MODULE_ID=2048;
+static int RLOG_FILE_ID=240;
+
+/** 
+ * @file kw_umm_ul.c
+ * @brief RLC Unacknowledged Mode uplink module
+*/
+
+/* header (.h) include files */
+#include "envopt.h"             /* environment options */
+#include "envdep.h"             /* environment dependent */
+#include "envind.h"             /* environment independent */
+
+#include "gen.h"                /* general */
+#include "ssi.h"                /* system services interface */
+#include "cm5.h"                /* Timer Functions */
+#include "cm_lte.h"             /* common umts header file */
+#include "cm_hash.h"            /* common hash module  file */
+#include "cm_llist.h"           /* common list header file */
+#include "ckw.h"                /* RRC layer */
+#include "lkw.h"                /* RRC layer */
+#include "kwu.h"                /* RLC service user */
+#include "lkw.h"                /* LM Interface */
+#include "rgu.h"                /* MAC layer */
+#include "kw_env.h"             /* RLC environment options */
+
+#include "kw.h"                 /* RLC layer */
+#include "kw_err.h"
+#include "kw_ul.h"
+
+
+/* header/extern include files (.x) */
+
+#include "gen.x"                /* general */
+#include "ssi.x"                /* system services interface */
+#include "cm_lib.x"             /* common library */
+#include "cm5.x"                /* Timer Functions */
+#include "cm_hash.x"            /* common hash module */
+#include "cm_lte.x"             /* common umts file */
+#include "cm_llist.x"           /* common list header file */
+#include "ckw.x"                /* RRC layer */
+#include "kwu.x"                /* RLC service user */
+#include "lkw.x"                /* LM Interface */
+#include "rgu.x"                /* MAC later */
+
+#include "kw.x"                 /* RLC layer */
+#include "kw_ul.x"
+
+#define KW_MODULE (KW_DBGMASK_UM | KW_DBGMASK_UL)
+
+PRIVATE S16 kwUmmExtractHdr ARGS ((KwCb *gCb, 
+                                   KwUlRbCb *rbCb,
+                                   Buffer *pdu,
+                                   KwUmHdr *umHdr));
+
+PRIVATE Void kwUmmReAssembleSdus ARGS ((KwCb *gCb,
+                                        KwUlRbCb *rbCb,
+                                        KwUmRecBuf *umRecBuf));
+
+#ifndef TENB_ACC
+#ifndef LTE_PAL_ENB
+extern U32 isMemThreshReached(Region region);
+#endif
+#endif
+/**
+ * @brief  Finds and sets the next VR(UR) depending on the 
+ *         passed sequence number
+ *       
+ * @details
+ *    Finds the next VR(UR) depending on the passed SN. Updates VR(UR) to 
+ *    the SN of the first UMD PDU with SN >= _nextSn that has not been received
+ *
+ * @param[in] umUl      pointer to Um mode uplink control block
+ * @param[in] nextSn    Sequence number after which the VR(UR) is to set to
+ *
+ * @return  Void
+*/ 
+PRIVATE Void kwUmmFindNextVRUR (KwUmUl* umUl, KwSn nextSn)
+{
+   KwSn ur = KW_UM_GET_VALUE(umUl->vrUr, *umUl);
+   
+   KwSn nextSnToCompare = KW_UM_GET_VALUE(nextSn,*umUl);
+   
+   while (ur < nextSnToCompare)
+   {
+      if (!(umUl->recBuf[nextSn])) /* if the buffer is empty, SN not received */
+      {
+         umUl->vrUr = nextSn;
+         break;
+      }
+      nextSn = (nextSn + 1) & umUl->modBitMask; 
+      nextSnToCompare = KW_UM_GET_VALUE(nextSn,*umUl);
+   }
+}
+
+/**
+ * @brief  Checks whether a sequence number is within the 
+ *         re-ordering window or not
+ *       
+ * @param[in] sn        Sequence Number to be checked
+ * @param[in] umUl      pointer to Um mode uplink control block
+ *
+ * @return  S16
+ *      -# TRUE 
+ *      -# FALSE
+ *
+ * @return  Void
+*/
+PRIVATE S16 kwUmmCheckSnInReordWindow (KwSn sn, 
+                                       CONSTANT KwUmUl* CONSTANT umUl)  
+{
+   return (KW_UM_GET_VALUE(sn, *umUl) < KW_UM_GET_VALUE(umUl->vrUh, *umUl)); 
+}
+
+/**
+ * @brief  Handler to process the Data Indication from the lower layer 
+ *         and send the PDUs to re-assembly unit. 
+ *       
+ * @details
+ *    This function processes the PDUs received from the lower layer
+ *    re-orders them and sends them one after the other in sequence 
+ *    to the re-assembly unit.
+ *
+ * @param[in] gCb      RLC Instance control block
+ * @param[in] rbCb     RB control block 
+ * @param[in] pduInfo  Pdu information 
+ *
+ *  @return Void
+*/
+/* kw005.201 added support for L2 Measurement */
+#ifdef LTE_L2_MEAS
+
+#ifdef ANSI
+PUBLIC Void kwUmmProcessPdus
+(
+KwCb      *gCb,
+KwUlRbCb  *rbCb,                   /* Rb Control Block */
+KwPduInfo *pduInfo,                 /* Pdu  data and related information */
+U32       ttiCnt                  /* ttiCnt received from MAC */
+)
+#else
+PUBLIC Void kwUmmProcessPdus(rbCb,pduInfo,ttiCnt)
+KwCb      *gCb;
+KwUlRbCb  *rbCb;                   /* Rb Control Block */
+KwPduInfo *pduInfo;                /* Pdu  data and related information */
+U32       ttiCnt;                  /* ttiCnt received from MAC */
+#endif
+#else
+#ifdef ANSI
+PUBLIC Void kwUmmProcessPdus
+(
+KwCb      *gCb,
+KwUlRbCb  *rbCb,                /* Rb Control Block */
+KwPduInfo *pduInfo              /* Pdu  data and related information */
+)
+#else
+PUBLIC Void kwUmmProcessPdus(rbCb,pduInfo)
+KwCb      *gCb;
+KwUlRbCb  *rbCb;                /* Rb Control Block */
+KwPduInfo *pduInfo;             /* Pdu  data and related information */
+#endif
+#endif
+{
+   KwSn          *vrUh;      /* vr(uh) */
+   KwSn          *vrUr;      /* vr(ur) */
+   KwSn         *vrUx;      /* vr(ux) */
+   U16          curSn;      /* Current Sequence Number */
+   U32          pduCount;   /* PDU count */
+   U32          count;      /* Loop counter */
+   KwUmRecBuf   **recBuf;   /* UM Reception Buffer */
+
+   Bool         tmrRunning;   /* Boolean for checking Tmr */
+/* kw005.201 added support for L2 Measurement */
+
+   TRC2(kwUmmProcessPdus)
+
+
+   count = 0;
+
+   /* pduCount should be the min of RGU_MAX_PDU and pduInfo->numPdu */
+   pduCount = (pduInfo->numPdu < RGU_MAX_PDU)? pduInfo->numPdu : RGU_MAX_PDU;
+   
+   vrUh   = &(rbCb->m.umUl.vrUh);
+   vrUr   = &(rbCb->m.umUl.vrUr);
+   vrUx   = &(rbCb->m.umUl.vrUx);
+   recBuf = (rbCb->m.umUl.recBuf);
+
+   while (count < pduCount)
+   {
+      KwSn   ur; 
+      KwSn   uh; 
+      KwSn   seqNum;
+      Buffer *pdu = pduInfo->mBuf[count];
+      KwUmRecBuf *tmpRecBuf;
+      gCb->genSts.pdusRecv++;
+#ifndef RGL_SPECIFIC_CHANGES
+#ifndef TENB_ACC
+#ifndef LTE_PAL_ENB
+      extern U32 ulrate_rgu;
+      MsgLen len;
+      SFndLenMsg(pdu, &len);
+      ulrate_rgu += len;
+#endif
+#endif      
+#endif      
+      /* create a buffer to be later inserted into the reception buffer */
+      KW_ALLOC_WC(gCb, tmpRecBuf, sizeof(KwUmRecBuf));
+#if (ERRCLASS & ERRCLS_ADD_RES)
+      if (tmpRecBuf == NULLP)
+      {   
+         RLOG_ARG2(L_FATAL, DBG_RBID,rbCb->rlcId.rbId,
+                  "Memory allocation failed UEID:%d CELLID:%d",
+                  rbCb->rlcId.ueId,
+                  rbCb->rlcId.cellId);
+         SPutMsg(pdu);
+
+         RETVOID;
+      }
+#endif /* ERRCLASS & ERRCLS_ADD_RES */      
+      /* ccpu00142274 - UL memory based flow control*/ 
+#ifndef RGL_SPECIFIC_CHANGES
+#ifndef TENB_ACC
+#ifndef LTE_PAL_ENB
+    /* Changed the condition to TRUE from ROK  */
+#ifndef XEON_SPECIFIC_CHANGES    
+     if(isMemThreshReached(kwCb[0]->init.region) == TRUE)
+     {
+        extern U32 rlculdrop;
+        rlculdrop++;
+        KW_FREE_BUF(pdu);
+        KW_FREE_WC(gCb, tmpRecBuf, sizeof(KwUmRecBuf));
+        /*Fix for CR ccpu00144030: If threshhold is hit then also count
+         *should be incrmented */
+        count++;
+        continue;
+     }
+#endif     
+#endif
+#endif
+#endif
+      /* get the pdu header */
+      if (kwUmmExtractHdr(gCb, rbCb, pdu, &(tmpRecBuf->umHdr)))  
+      {
+         RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+                  "Header Extraction Failed UEID:%d CELLID:%d",
+                  rbCb->rlcId.ueId,
+                  rbCb->rlcId.cellId);
+
+         /* Header extraction is a problem. 
+          * log an error and free the allocated memory */
+         /* ccpu00136940 */
+         KW_FREE_WC(gCb, tmpRecBuf, sizeof(KwUmRecBuf));
+         SPutMsg(pdu);
+         count++;
+         /* kw005.201 ccpu00117318, updating the statistics */
+         gCb->genSts.errorPdusRecv++;
+         continue;
+      }
+      curSn = tmpRecBuf->umHdr.sn;
+
+      /* Check if the PDU should be discarded or not */
+      ur = KW_UM_GET_VALUE(KW_UMUL.vrUr, KW_UMUL); 
+      uh = KW_UM_GET_VALUE(KW_UMUL.vrUh, KW_UMUL); 
+      seqNum = KW_UM_GET_VALUE(curSn, KW_UMUL); 
+
+      if (((ur < seqNum) && (seqNum < uh) && (KW_UMUL.recBuf[curSn])) || 
+          (seqNum < ur)) 
+      {
+         /* PDU needs to be discarded */
+         RLOG_ARG3(L_DEBUG,DBG_RBID,rbCb->rlcId.rbId,
+                  "Received a duplicate pdu with sn %d UEID:%d CELLID:%d",
+                  curSn,
+                  rbCb->rlcId.ueId,
+                  rbCb->rlcId.cellId);
+
+         KW_FREE_BUF(pdu);
+         KW_FREE_WC(gCb, tmpRecBuf, sizeof(KwUmRecBuf));
+         count++;
+         /* kw005.201 ccpu00117318, updating the statistics */
+         gCb->genSts.unexpPdusRecv++;
+         continue;
+      } 
+
+      /* kw005.201 added support for L2 Measurement */
+#ifdef LTE_L2_MEAS
+
+      /* kw006.201 ccpu00120058, reduced code complexity by adding new function */
+      kwUtlCalUlIpThrPut(gCb,rbCb, pdu, ttiCnt);
+
+#endif 
+
+      recBuf[curSn] = tmpRecBuf;
+
+      recBuf[curSn]->pdu = pdu;
+      SFndLenMsg(pdu,&(recBuf[curSn]->pduSz));
+      /* kw005.201 ccpu00117318, updating the statistics */
+      gCb->genSts.bytesRecv += recBuf[curSn]->pduSz;
+      
+      if (!kwUmmCheckSnInReordWindow(curSn,&KW_UMUL))
+      {  /* currSn is outside re-ordering window */
+         *vrUh  = (curSn + 1) & KW_UMUL.modBitMask;
+
+         /* re-assemble all pdus outside the modified re-ordering window */
+         /* the first SN is VR(UR) */
+         if (!kwUmmCheckSnInReordWindow(*vrUr,&KW_UMUL))
+         {
+            /* TODO : should it be VR(UR) + 1 ?... check, earlier it was so */
+            KwSn sn = *vrUr; /* SN's which need to be re-assembled */
+            KwSn lowerEdge;  /* to hold the lower-edge of the 
+                                re-ordering window */
+
+            /* The new value ov VR(UR) is the lower end of the window i
+             * and SN's still this value need to be re-assembled */
+            
+            *vrUr = (*vrUh - KW_UMUL.umWinSz) &  KW_UMUL.modBitMask;         
+            lowerEdge = KW_UM_GET_VALUE(*vrUr ,KW_UMUL);
+            
+            while (KW_UM_GET_VALUE(sn, KW_UMUL) < lowerEdge)
+            {
+               if (recBuf[sn])
+               {
+                  kwUmmReAssembleSdus(gCb,rbCb,recBuf[sn]);
+                  KW_FREE_WC(gCb,recBuf[sn],sizeof(KwUmRecBuf));
+                  recBuf[sn] = NULLP;
+               }
+               sn = (sn + 1) & KW_UMUL.modBitMask;
+            }
+         }
+      }
+      if (recBuf[*vrUr])
+      {
+         KwSn sn       = *vrUr;
+         KwSn tSn      = KW_UM_GET_VALUE(sn,KW_UMUL); 
+         KwSn tVrUr;       
+
+         /* set VR(UR) to next SN > current VR(UR) which is not received */
+         KwSn nextVrUr = (*vrUr + 1) & KW_UMUL.modBitMask;
+         kwUmmFindNextVRUR(&KW_UMUL, nextVrUr);
+
+         /* re-assemble SDUs with SN < Vr(UR) */
+         tVrUr = KW_UM_GET_VALUE(*vrUr,KW_UMUL);
+         while (recBuf[sn] && tSn < tVrUr)
+         {
+            kwUmmReAssembleSdus(gCb,rbCb,recBuf[sn]);
+            KW_FREE_WC(gCb,recBuf[sn],sizeof(KwUmRecBuf));
+            recBuf[sn] = NULLP;
+            sn = (sn + 1) & KW_UMUL.modBitMask;
+            tSn = KW_UM_GET_VALUE(sn, KW_UMUL);
+         }
+      }
+
+      tmrRunning = kwChkTmr(gCb,(PTR)rbCb, KW_EVT_UMUL_REORD_TMR);
+
+      if (tmrRunning) 
+      {
+         KwSn tVrUx = KW_UM_GET_VALUE(*vrUx, KW_UMUL);
+         KwSn tVrUr = KW_UM_GET_VALUE(*vrUr ,KW_UMUL);
+
+         KwSn tVrUh = KW_UM_GET_VALUE(*vrUh, KW_UMUL);
+
+         S16 ret = kwUmmCheckSnInReordWindow(*vrUx, &KW_UMUL);
+
+         if ( (tVrUx <= tVrUr) || ((!ret) && (tVrUx != tVrUh))) 
+         {
+            kwStopTmr(gCb,(PTR)rbCb,KW_EVT_UMUL_REORD_TMR);
+            tmrRunning = FALSE;
+         }
+      }
+
+      if (!tmrRunning)
+      {
+         if (KW_UM_GET_VALUE(*vrUh, KW_UMUL) > KW_UM_GET_VALUE(*vrUr, KW_UMUL))
+         {
+            kwStartTmr(gCb,(PTR)rbCb,KW_EVT_UMUL_REORD_TMR); 
+            *vrUx = *vrUh;
+         }
+      }
+      count++;
+   }/* end while count < pduCount */
+#ifdef LTE_L2_MEAS
+   kwUtlCalUlIpThrPutIncTTI(gCb, rbCb,ttiCnt);
+#endif /* LTE_L2_MEAS */
+   RETVOID;   
+}
+
+/**
+ * @brief  Handler to reassemble the SDUs and send them to the upper layer.
+ *       
+ * @details
+ *    This function processes the received in-sequence PDU and
+ *    re-assembles the SDUs and sends them to the upper layer.
+ *
+ * @param[in] gCb        RLC Instance control block
+ * @param[in] rbCb       RB control block
+ * @param[in] umRecBuf   Reception Buffer to be Re-Assembled 
+ *
+ * @return  Void
+*/
+#ifdef ANSI
+PRIVATE Void kwUmmReAssembleSdus
+(
+KwCb         *gCb,
+KwUlRbCb     *rbCb,   
+KwUmRecBuf   *umRecBuf 
+)
+#else
+PRIVATE Void kwUmmReAssembleSdus(gCb,rbCb,umRecBuf)
+KwCb         *gCb;
+KwUlRbCb     *rbCb;     
+KwUmRecBuf   *umRecBuf; 
+#endif
+{
+   U32      liCount;        /* LI count */
+   U32      count;          /* Loop counter */
+   U8       fi;             /* Framing Info */
+   U16      sn;             /* Sequence Number of current PDU */
+   MsgLen   len;            /* PDU Length */
+   Buffer   *sdu;           /* SDU to be sent to upper layer */
+   Buffer   *remPdu;        /* Remaining PDU */
+   Buffer   **partialSdu;   /* Partial SDU */
+
+   TRC2(kwUmmReAssembleSdus)  
+
+
+   liCount = umRecBuf->umHdr.numLi;
+   fi = umRecBuf->umHdr.fi;
+   sn =  umRecBuf->umHdr.sn;
+   for (count = 0; (count <= liCount);count++)
+   {
+      if (count < liCount )
+         len = umRecBuf->umHdr.li[count];
+      else
+      {
+         if (!(umRecBuf->pdu))
+         {
+            RETVOID;
+         }
+         SFndLenMsg(umRecBuf->pdu,&len);
+      }
+         
+      /* get the sdu out of the pdu */
+      SSegMsg(umRecBuf->pdu,len,&remPdu);
+      sdu = umRecBuf->pdu;
+      umRecBuf->pdu = remPdu;
+      
+      partialSdu = &(rbCb->m.umUl.partialSdu);
+      /* While re-assembling the SDUs, consider the first LI and perform 
+       * the following steps.
+          -# If the first bit of FI(Framing Info of 2  bits) is set => 
+                -# The current Data field in the PDU is a segment. 
+                   So form a SDU only if the 
+                       rbCb->m.um.umUl.partialSdu exists and the SNs are 
+                       in-sequence.
+                -# If there are no LIs and the second bit of LI is 1 
+                   then a partial SDU is formed which would not be sent 
+                   to the upper layer.
+          -# else
+                -# If rbCb->partialSdu is not NULL then flush it off.
+                -# If LI count > 0 or LI count is 0 and second bit 
+                   of FI is not 1 
+                         The SDU is complete.So send it to upper layer.
+                -# else
+                         The SDU is partial and is placed 
+                         in rbCb->m.um.umUl.partialSdu;
+      */
+             
+      if (0 == count )
+      {
+         if (fi & 2)
+         {
+            if ((*partialSdu) && 
+                (sn == ((rbCb->m.umUl.sn + 1) & rbCb->m.umUl.modBitMask)))
+            {
+               SCatMsg(*partialSdu,sdu,M1M2);
+               KW_FREE_BUF(sdu);
+               if (liCount > 0 || !(fi & 1))
+               {
+                  kwUtlSndDatInd(gCb,rbCb,*partialSdu);
+                  *partialSdu = NULLP;
+               }
+            }
+            else
+            {
+                /* Partial Sdu stored is not valid now.So free it */
+               if (*partialSdu)
+               {
+                  KW_FREE_BUF(*partialSdu);
+                  *partialSdu = NULLP;
+               }
+                  
+               KW_FREE_BUF(sdu);
+               sdu = NULLP;
+            }
+         }
+         else
+         {
+            if (*partialSdu)
+            {
+               KW_FREE_BUF(*partialSdu);  /* RLC mem leak fix */
+               *partialSdu = NULLP;
+            }
+            
+            if (liCount > 0 || !( fi & 1))
+            {
+               kwUtlSndDatInd(gCb,rbCb,sdu);
+            }
+            else
+            {
+                  *partialSdu = sdu; 
+            }
+         }
+      }
+      /*
+          If the SDU pointer has the last Data field of the PDU
+            -# If FI is 1,place the SDU in rbCb->m.um.umDl.partialSdu
+            -# else send the SDU to upper layer.
+      */
+       else if (count == liCount)
+      {
+         if (fi & 1)
+         {
+            *partialSdu = sdu;
+         }
+         else
+         {
+            kwUtlSndDatInd(gCb, rbCb, sdu);
+         }
+      }
+      /*  
+         If the LI is something other than the first one, 
+         just send the SDU to the upper layer */
+      else
+      {
+         kwUtlSndDatInd(gCb, rbCb, sdu);
+      }
+   }
+   rbCb->m.umUl.sn = sn;
+
+   RETVOID;
+}
+
+/**
+ * @brief  Handler to process the re-establishment request received 
+ *         from the upper layer.
+ *       
+ * @details
+ *    This function does the following functions : 
+ *       - If direction of the RB is downlink : 
+ *         Remove all the SDUs in the SDU queue.
+ *       - If direction of the RB is uplink   : 
+ *         Call kwUmmReAssembleSdus() for each PDU with SN < VR(UH)
+ *
+ * @param[in] gCb        RLC Instance control block
+ * @param[in] rlcID      Identity of the RLC entity for which 
+ *                       re-establishment is to be done
+ * @param[in] rbCb       RB control block for which re-establishment 
+ *                       is to be done
+ *
+ * @return  Void
+*/ 
+#ifdef ANSI
+PUBLIC Void kwUmmUlReEstablish
+(
+KwCb         *gCb,
+CmLteRlcId   *rlcId,
+KwUlRbCb     *rbCb
+)
+#else
+PUBLIC Void kwUmmUlReEstablish(gCb, rlcId, rbCb)
+KwCb         *gCb;
+CmLteRlcId   *rlcId;
+KwUlRbCb     *rbCb;
+#endif
+{
+   KwSn         curSn;
+   KwSn         vrUh;
+   KwUmRecBuf   **recBuf;   /* UM Reception Buffer */
+   KwKwuSapCb   *kwKwSap;   /* KWU SAP Information */
+
+   TRC2(kwUmmUlReEstablish)
+
+
+   curSn = rbCb->m.umUl.vrUr;
+   vrUh  = KW_UM_GET_VALUE(rbCb->m.umUl.vrUh,rbCb->m.umUl);
+   recBuf =  rbCb->m.umUl.recBuf;
+
+   if(TRUE == kwChkTmr(gCb,(PTR)rbCb,KW_EVT_UMUL_REORD_TMR))
+   {
+       kwStopTmr(gCb,(PTR)rbCb,KW_EVT_UMUL_REORD_TMR);
+   }
+   
+   while (KW_UM_GET_VALUE(curSn,rbCb->m.umUl) < vrUh)
+   {
+      if ( recBuf[curSn] != NULLP )
+      {
+         kwUmmReAssembleSdus(gCb,rbCb,recBuf[curSn]);
+         KW_FREE_WC(gCb,recBuf[curSn],sizeof(KwUmRecBuf));
+         recBuf[curSn] = NULLP;
+      } 
+      curSn = (curSn + 1) & rbCb->m.umUl.modBitMask;
+   }
+   rbCb->m.umUl.vrUr = 0;
+   rbCb->m.umUl.vrUh = 0;
+   rbCb->m.umUl.vrUx = 0;
+
+   kwKwSap = gCb->u.ulCb->kwuUlSap + KW_UI_PDCP;
+
+   /* In the UM Mode always send reestablish-indication to Upper Latyer*/
+   KwUiKwuReEstCmpInd(&kwKwSap->pst, kwKwSap->suId, *rlcId);
+
+   RETVOID;
+}
+
+/**
+ * @brief  Handler to extract the header from a PDU
+ *       
+ * @details
+ *    This function is used to extract the header of a PDU and store it 
+ *    along with the PDU buffer.The sequence number,framing info 
+ *    and LIs are extracted by this function.
+ *
+ * @param[in] gCb      RLC Instance control block
+ * @param[in] rbCb     Rb Control block for which the pdu is received
+ * @param[in] pdu      PDU buffer
+ * @param[out] umHdr   UM header to be filled after extraction
+ *
+ * @return  S16
+ *      -# TRUE 
+ *      -# FALSE
+*/
+#ifdef ANSI
+PRIVATE S16 kwUmmExtractHdr
+(
+KwCb       *gCb,
+KwUlRbCb   *rbCb, 
+Buffer     *pdu, 
+KwUmHdr    *umHdr 
+)
+#else
+PRIVATE S16 kwUmmExtractHdr(gCb, rbCb, pdu, umHdr)
+KwCb       *gCb;
+KwUlRbCb   *rbCb; 
+Buffer     *pdu; 
+KwUmHdr    *umHdr; 
+#endif
+{
+   U8        e;         /* Extension Bit */
+   Data      dst[2];    /* Destination Buffer */
+   S32       totalSz;   /* Sum of LIs */
+   MsgLen    pduSz;     /* PDU size */
+#if (ERRCLASS & ERRCLS_DEBUG)
+   S16       ret;       /* Return Value */
+#endif
+
+   TRC3(kwUmmExtractHdr)
+
+
+   SFndLenMsg(pdu,&pduSz);
+   if ( rbCb->m.umUl.snLen == 1)
+   {
+#if (ERRCLASS & ERRCLS_DEBUG)
+      ret = SRemPreMsg(dst,pdu);
+      if (ret != ROK)
+      {
+         RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+                  "SRemPreMsg Failed for 5 bit SN UEID:%d CELLID:%d",
+                  rbCb->rlcId.ueId,
+                  rbCb->rlcId.cellId);
+
+         RETVALUE(RFAILED);
+      }
+#else
+      SRemPreMsg(dst,pdu);
+#endif
+      pduSz--;
+      umHdr->sn = (dst[0]) & 0x1F; 
+      umHdr->fi = (dst[0]) >> 6;
+      e       = (dst[0]>>5) & 0x01;
+   }
+   else
+   {
+      /* snLen - sequnce length will be 10 bits requiring 2 bytes */ 
+#if (ERRCLASS & ERRCLS_DEBUG)
+      ret = SRemPreMsgMult(dst,2,pdu);
+      if (ret != ROK)
+      {
+         RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+                  "SRemPreMsgMult Failed for 10 bits SN UEID:%d CELLID:%d",
+                  rbCb->rlcId.ueId,
+                  rbCb->rlcId.cellId);
+         RETVALUE(RFAILED);
+      }
+#else
+      SRemPreMsgMult(dst,2,pdu);
+#endif
+      pduSz -= 2;
+   
+      /* kw005.201 R9 Upgrade 3gpp spec 36.322 ver9.3.0 CR0082      *
+       * Removed the "if" condition for checking the reserved field *
+       * Added mask 0x03 for extracting the FI field.          */
+
+      umHdr->fi = ( (dst[0] ) >> 3) & 0x03;
+      e       = ( (dst[0] ) >> 2) & 0x01;
+      umHdr->sn = (  dst[0] & 0x03) << 8;
+      umHdr->sn  |= dst[1];
+   }
+
+   umHdr->numLi = 0;
+   
+   totalSz = 0;
+   while(e && umHdr->numLi < KW_MAX_UL_LI )
+   {
+#if (ERRCLASS & ERRCLS_DEBUG)
+      ret = SRemPreMsgMult(dst,2,pdu);
+      if (ret != ROK)
+      {
+         RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+                  "SRemPreMsgMult Failed UEID:%d CELLID:%d",
+                  rbCb->rlcId.ueId,
+                  rbCb->rlcId.cellId);
+         RETVALUE(RFAILED);
+      }
+#else
+      SRemPreMsgMult(dst,2,pdu);
+#endif
+      umHdr->li[umHdr->numLi] = ((dst[0]) & 0x7F) << 4;
+      umHdr->li[umHdr->numLi] |= dst[1] >> 4;
+      if ( 0 == umHdr->li[umHdr->numLi] )
+      {
+         RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+                  "Received LI as 0 UEID:%d CELLID:%d",
+                  rbCb->rlcId.ueId,
+                  rbCb->rlcId.cellId);
+         RETVALUE(RFAILED); 
+      }
+      totalSz += umHdr->li[umHdr->numLi];
+      if ( pduSz <=  totalSz )
+      {
+         RLOG_ARG3(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+                  "SN [%d]: UEID:%d CELLID:%d",
+                  umHdr->sn, 
+                  rbCb->rlcId.ueId,
+                  rbCb->rlcId.cellId);
+         RLOG_ARG4(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+                  "Corrupted PDU as TotSz[%lu] PduSz[%lu] UEID:%d CELLID:%d ",
+                  totalSz, 
+                  pduSz,
+                  rbCb->rlcId.ueId,
+                  rbCb->rlcId.cellId);
+         RETVALUE(RFAILED); /* the situation where in the PDU size 
+                            is something that does not match with 
+                            the size in LIs*/
+      }
+      umHdr->numLi++;
+      pduSz -= 2;
+
+      e = ((dst[0]) & 0x80) >> 7;
+   
+      if ( e && umHdr->numLi < KW_MAX_UL_LI)
+      {
+         U8 tmp = ((dst[1]) & 0x08) >> 3;
+         umHdr->li[umHdr->numLi] = ( dst[1] & 0x07) << 8;
+
+
+#if (ERRCLASS & ERRCLS_DEBUG)
+         ret = SRemPreMsg(dst,pdu);
+         if (ret != ROK)
+         {
+            RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+                     "SRemPreMsg Failed UEID:%d CELLID:%d",
+                     rbCb->rlcId.ueId,
+                     rbCb->rlcId.cellId);
+            RETVALUE(RFAILED);
+         }
+#else
+         SRemPreMsg(dst,pdu);
+#endif
+         umHdr->li[umHdr->numLi] |= ( dst[0] );    /* The first byte lies in 
+                                                   the first 8 bits.We want 
+                                                   them in the last 8 bits */
+         if ( 0 == umHdr->li[umHdr->numLi] )
+         {
+            RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+                     "Received LI as 0 UEID:%d CELLID:%d",
+                     rbCb->rlcId.ueId,
+                     rbCb->rlcId.cellId);
+            RETVALUE(RFAILED); 
+         }
+         totalSz += umHdr->li[umHdr->numLi];
+         pduSz--;
+         umHdr->numLi++;
+
+         if (pduSz < totalSz)
+         {
+            RETVALUE(RFAILED); /* the situation where in the PDU size is 
+                               something that does not match with the 
+                               size in LIs*/
+         }
+
+         e = tmp;
+      }
+   } /* while(e && umHdr->numLi < KW_MAX_LI ) */
+   if (e)
+   {
+      /* PDU was constructed with LIs that exceeded KW_MAX_LI */
+      RETVALUE(RFAILED);
+   }
+   RETVALUE(ROK); 
+}
+   
+/**
+ * @brief Handles expiry of re-ordering timer
+ *
+ * @param[in] gCb     RLC Instance control block
+ * @param[in] rbCb    Rb Control block for which re-order timer expired
+ *
+ * @return  Void
+*/
+#ifdef ANSI
+PUBLIC Void kwUmmReOrdTmrExp
+(
+KwCb       *gCb,
+KwUlRbCb   *rbCb     
+)
+#else
+PUBLIC Void kwUmmReOrdTmrExp(gCb, rbCb)
+KwCb       *gCb;
+KwUlRbCb   *rbCb;   
+#endif
+{
+   KwSn prevVrUr;   /* prevVrUr */
+
+   TRC3(kwUmmReOrdTmrExp)
+
+
+   prevVrUr = KW_UMUL.vrUr;
+
+   /* set VR(UR) to SN >= VR(UX) that has not been received */
+   kwUmmFindNextVRUR(&KW_UMUL, KW_UMUL.vrUx);
+
+   while (KW_UM_GET_VALUE(prevVrUr,KW_UMUL) < 
+          KW_UM_GET_VALUE(KW_UMUL.vrUr,KW_UMUL))
+   {
+      if (KW_UMUL.recBuf[prevVrUr])
+      {
+         kwUmmReAssembleSdus(gCb, rbCb, KW_UMUL.recBuf[prevVrUr]);
+         if(KW_UMUL.recBuf[prevVrUr]->pdu != NULLP) /* RLC mem leak fix */
+         {
+            KW_FREE_BUF(KW_UMUL.recBuf[prevVrUr]->pdu);
+         }
+         KW_FREE_WC(gCb, KW_UMUL.recBuf[prevVrUr], sizeof(KwUmRecBuf));
+         KW_UMUL.recBuf[prevVrUr] = NULLP;
+      }
+
+      prevVrUr = (prevVrUr + 1) & rbCb->m.umUl.modBitMask;
+   }
+
+   if (KW_UM_GET_VALUE(KW_UMUL.vrUh, KW_UMUL) > 
+       KW_UM_GET_VALUE(KW_UMUL.vrUr, KW_UMUL))
+   {
+      kwStartTmr(gCb,(PTR)rbCb,KW_EVT_UMUL_REORD_TMR);
+      KW_UMUL.vrUx = KW_UMUL.vrUh;
+   }
+}
+
+/**
+ * @brief
+ *   Function to release/free the UnAcknowledged Mode Module  RbCb buffers
+ *
+ * @details
+ *   This primitive Frees the UM RbCb transmission Buffer, retransmission
+ *   Buffer and reciption Buffers
+ *
+ * @param [in]   gCb   - RLC instance Control Block
+ * @param [in]   rbCb  - RB Control Block
+ *
+ * @return   void
+ */
+
+#ifdef ANSI
+PUBLIC Void kwUmmFreeUlRbCb
+(
+KwCb       *gCb,
+KwUlRbCb   *rbCb
+)
+#else
+PUBLIC Void kwUmmFreeUlRbCb(gCb,rbCb)
+KwCb       *gCb;
+KwUlRbCb   *rbCb;
+#endif
+{
+   KwSn         curSn = 0;           /* sequence number of PDU */
+   KwSn         windSz;              /* PDU window size */
+   KwUmRecBuf   **umRecBuf;          /* UM module receive buffer */
+
+   TRC2(kwUmmFreeUlRbCb)
+
+
+   windSz  = rbCb->m.umUl.umWinSz << 1;
+
+   umRecBuf =  rbCb->m.umUl.recBuf;
+
+   if(TRUE == kwChkTmr(gCb,(PTR)rbCb,KW_EVT_UMUL_REORD_TMR))
+   {
+      kwStopTmr(gCb,(PTR)rbCb,KW_EVT_UMUL_REORD_TMR);
+   }
+   while (curSn < windSz)
+   {
+      if (umRecBuf[curSn] != NULLP)
+      {
+         KW_FREE_BUF_WC(umRecBuf[curSn]->pdu);
+         umRecBuf[curSn]->pdu = NULLP;
+
+         KW_FREE_WC(gCb, umRecBuf[curSn], sizeof(KwUmRecBuf));
+         umRecBuf[curSn] = NULLP;
+      }
+      curSn++;
+   }
+   KW_FREE_WC(gCb,rbCb->m.umUl.recBuf, (windSz ) * sizeof(KwUmRecBuf*));
+   rbCb->m.umUl.recBuf = NULLP;
+   RETVOID;
+} 
+
+
+/********************************************************************30**
+         End of file
+**********************************************************************/