text: /*******************************************************************************
  ################################################################################
  #   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:     NR RLC Layer 
    
       Type:     C file
    
       Desc:     Source code for RLC Unacknowledged mode assembly and
                 reassembly.This file contains following functions
  
                    --rlcUmmQSdu
                    --rlcUmmProcessSdus
                    --rlcUmmProcessPdus
                    --rlcUmUlReAssembleSdus
                    --kwUmmReEstablish 
  
       File:     rlc_umm_ul.c
  
  **********************************************************************/
  /** 
   * @file rlc_umm_ul.c
   * @brief RLC Unacknowledged Mode uplink module
  */
  
  /* header (.h) include files */
  #include "common_def.h"
  #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 "rlc_env.h"             /* RLC environment options */
  
  /* header/extern include files (.x) */
  
  #include "ckw.x"                /* RRC layer */
  #include "kwu.x"                /* RLC service user */
  #include "lkw.x"                /* LM Interface */
  #include "rgu.x"                /* MAC later */
  
  #include "rlc_utils.h"                 /* RLC layer */
  #include "rlc_err.h"
  #include "rlc_ul.h"
  
  #define RLC_MODULE (RLC_DBGMASK_UM | RLC_DBGMASK_UL)
  
  static uint8_t rlcUmmExtractHdr ARGS ((RlcCb *gCb, 
                                     RlcUlRbCb *rbCb,
                                     Buffer *pdu,
                                     RlcUmHdr *umHdr));
  
  uint8_t rlcUmmReAssembleSdus ARGS ((RlcCb *gCb,
                                      RlcUlRbCb *rbCb,
                                      RlcUmRecBuf *umRecBuf));
  
  bool rlcUmmAddRcvdSeg ARGS ((RlcCb *gCb,
                               RlcUlRbCb   *rbCb,
                               RlcUmHdr    *umHdr,
                               Buffer      *pdu,
                               uint16_t    pduSz));
  
  void rlcUmmRelAllSegs(RlcCb *gCb, RlcUmRecBuf *recBuf);
  
  #ifndef TENB_ACC
  #ifndef LTE_PAL_ENB
  uint32_t 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 reassembled
   *
   * @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
  */ 
  static void rlcUmmFindRxNextReassembly (RlcCb *gCb, RlcUmUl* umUl, RlcSn nextSn)
  {
     RlcSn ur = RLC_UM_GET_VALUE(umUl->vrUr, *umUl);
     RlcSn prevRxNextReassembly = umUl->vrUr;
  
     RlcSn nextSnToCompare = RLC_UM_GET_VALUE(nextSn,*umUl);
     RlcUmRecBuf *recBuf = rlcUtlGetUmRecBuf(umUl->recBufLst,nextSn);
     RlcUmRecBuf *prevRecBuf = rlcUtlGetUmRecBuf(umUl->recBufLst,prevRxNextReassembly);
     while (ur < nextSnToCompare)
     {
        if((ur + 1) == nextSnToCompare)
        {
           if ((recBuf == NULLP) || (!(recBuf->allSegRcvd))) /* if the buffer is empty, SN not received */
           {
              umUl->vrUr = nextSn;
              if(prevRecBuf)
              {
                 rlcUmmRelAllSegs(gCb, prevRecBuf);
                 rlcUtlDelUmRecBuf(gCb, umUl->recBufLst, prevRecBuf);
              }
              break;
           }
           else
           {
              if(prevRecBuf)
              {
                 rlcUmmRelAllSegs(gCb, prevRecBuf);
                 rlcUtlDelUmRecBuf(gCb, umUl->recBufLst, prevRecBuf);
              }
           }
           prevRecBuf = rlcUtlGetUmRecBuf(umUl->recBufLst,nextSn);
           nextSn = (nextSn + 1) & umUl->modBitMask;
           nextSnToCompare = RLC_UM_GET_VALUE(nextSn,*umUl);
           recBuf = rlcUtlGetUmRecBuf(umUl->recBufLst,nextSn);
        }
        else
        {
           umUl->vrUr++;
           ur = RLC_UM_GET_VALUE(umUl->vrUr, *umUl);
           if(prevRecBuf)
           {
              rlcUmmRelAllSegs(gCb, prevRecBuf);
              rlcUtlDelUmRecBuf(gCb, umUl->recBufLst, prevRecBuf);
           }
           prevRecBuf = rlcUtlGetUmRecBuf(umUl->recBufLst,umUl->vrUr);
        }
     }
  }
  
  /**
   * @brief  Checks whether a sequence number is within the 
   *         re-assembly 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
  */
  static int16_t rlcUmmCheckSnInReassemblyWindow (RlcSn sn, const RlcUmUl* const umUl)  
  {
     return (RLC_UM_GET_VALUE(sn, *umUl) < RLC_UM_GET_VALUE(umUl->vrUh, *umUl)); 
  }
  
  /**
   * @brief  Handler to updated expected byte seg
   *
   * @details
   *    This function is used to update expected byte segment. The next segment
   *    expected is indicated by the SO of the segment which is expected. Intially
   *    the segment with SO 0 is expected and then in order. When all the segments
   *    are received (which would happen when an expected SO is encountered
   *    with LSF set) the allRcvd flag is set to TRUE
   *
   * @param[in]  gCb   RLC instance control block
   * @param[in]  umUl  AM Uplink Control Block
   * @param[in]  seg   Newly received segment
   *
   * @return void
   *
   */
  
  void rlcUmmUpdExpByteSeg(RlcCb *gCb, RlcUmUl *umUl, RlcUmSeg *seg)
  {
     uint16_t  newExpSo; /* The new expected SO */
     RlcSn     sn = seg->umHdr.sn;
     bool      lstRcvd=FALSE;
     RlcUmRecBuf *recBuf = NULLP;
     
     recBuf = rlcUtlGetUmRecBuf(umUl->recBufLst, sn);
     if ((recBuf == NULLP) || (recBuf && (seg->umHdr.so != recBuf->expSo)))
     {
        return;
     }
     recBuf->noMissingSeg = FALSE;
     newExpSo   = seg->soEnd + 1;
     recBuf->expSo = newExpSo;
     if(seg->umHdr.si == RLC_SI_LAST_SEG)
     {  
        lstRcvd = TRUE;
     } 
     RLC_UMM_LLIST_NEXT_SEG(recBuf->segLst, seg);
     while(seg)
     {
        /* keep going ahead as long as the expectedSo match with the header so
           else store the expSo for later checking again */
        if(seg->umHdr.si == RLC_SI_LAST_SEG)
        {  
           lstRcvd = TRUE;
        } 
        if (seg->umHdr.so == newExpSo)
        {
           newExpSo = seg->soEnd + 1;
           recBuf->expSo = newExpSo;
           RLC_UMM_LLIST_NEXT_SEG(recBuf->segLst, seg);
        }
        else
        {
           recBuf->expSo = newExpSo;
           return;
        }
     }
     if (lstRcvd == TRUE)
     {
        recBuf->allSegRcvd = TRUE;
     }
     recBuf->noMissingSeg = TRUE;
  
     return;
  }
  /**
   * @brief Private handler to store the received segment
   *
   * @details
   *    Private handler invokded by rlcUmmUlPlacePduInRecBuf to add a received
   *    segment in reception buffer of a RBCB.
   *    - It is responsible for detecting duplicate segments
   *    - Adding it at appropriate position in the received buffer
   *    - Calling ExpByteSeg to set expSo field in the receiver buffer
   *
   * @param[in]  gCb      RLC instance control block
   * @param[in]  rbCb     Radio Bearer Contro Block
   * @param[in]  umHdr    UM Header received
   * @param[in]  pdu      Buffer received other than the headers
   * @param[in]  pduSz    size of the PDU buffer received
   *
   * @return  bool
   *   -#TRUE  Successful insertion into the receiver buffer
   *   -#FALSE Possibly a duplicate segment
   */
  
  bool rlcUmmAddRcvdSeg(RlcCb *gCb, RlcUlRbCb *rbCb, RlcUmHdr *umHdr, Buffer *pdu, uint16_t pduSz)
  {
     RlcUmRecBuf   *recBuf = NULLP;
     RlcUmSeg      *seg;
     RlcUmSeg      *tseg;
     uint16_t      soEnd;       /* Holds the SoEnd of received segment */
     uint16_t      expSo = 0;   /* Expected SO */
  
     soEnd = umHdr->so + pduSz - 1;
     recBuf =  rlcUtlGetUmRecBuf(RLC_UMUL.recBufLst, umHdr->sn);
  
     if (NULLP == recBuf)
     {
        RLC_ALLOC(gCb,recBuf, sizeof(RlcUmRecBuf));
        if (recBuf == NULLP)
        {
           DU_LOG("\nERROR  -->  RLC_UL : Failed to allocate memory to recv buf for UEID:%d CELLID:%d in rlcUmmAddRcvdSeg()",
              rbCb->rlcId.ueId, rbCb->rlcId.cellId);
  
           ODU_PUT_MSG_BUF(pdu);
           return FALSE;
        }
        rlcUtlStoreUmRecBuf(RLC_UMUL.recBufLst, recBuf, umHdr->sn);
     }
     else
     {
        if (recBuf->allSegRcvd == TRUE)
        {
           ODU_PUT_MSG_BUF(pdu);
           return FALSE;
        }
     }
   			
     RLC_UMM_LLIST_FIRST_SEG(recBuf->segLst, seg);
     while ((seg != NULLP) && (seg->umHdr.so < umHdr->so))
     {
        expSo = seg->umHdr.so + seg->segSz;
        RLC_UMM_LLIST_NEXT_SEG(recBuf->segLst, seg);
     }
  
     if (expSo > umHdr->so)
     {
        DU_LOG("\nDEBUG  -->  RLC_UL : Received duplicate segment in rlcUmmAddRcvdSeg()");
        /* This is a duplicate segment */
        ODU_PUT_MSG_BUF(pdu);
        return FALSE;
     }
  
     if ((seg) && (seg->umHdr.so <= soEnd))
     {
        DU_LOG("\nDEBUG  -->  RLC_UL : Received duplicate segment in rlcUmmAddRcvdSeg()");
        /* This is a duplicate segment */
        ODU_PUT_MSG_BUF(pdu);
        return FALSE;
     }
  
     /* If we have come this far, we have to add this segment to the   */
     /* reception buffer as we either have eliminated duplicates or    */
     /* have found none.                                               */
     RLC_ALLOC_WC(gCb, tseg, sizeof(RlcUmSeg));
     if (tseg == NULLP)
     {
        DU_LOG("\nERROR -->  RLC_UL : Failed to allocate memory to segment for UEID:%d CELLID:%d in rlcUmmAddRcvdSeg()",
           rbCb->rlcId.ueId, rbCb->rlcId.cellId);
        ODU_PUT_MSG_BUF(pdu);
        return FALSE;
     }
  
     tseg->seg = pdu;
     tseg->segSz = pduSz;
     RLC_MEM_CPY(&tseg->umHdr, umHdr, sizeof(RlcUmHdr));
     RLC_MEM_CPY(&recBuf->umHdr, umHdr, sizeof(RlcUmHdr));
     recBuf->sn = umHdr->sn;
     tseg->soEnd = soEnd;
     if (seg == NULLP)
     {
        cmLListAdd2Tail(&recBuf->segLst, &tseg->lstEnt);
     }
     else
     {
        recBuf->segLst.crnt = &seg->lstEnt;
        cmLListInsCrnt(&recBuf->segLst, &tseg->lstEnt);
     }
     tseg->lstEnt.node = (PTR)tseg;
     rlcUmmUpdExpByteSeg(gCb, &RLC_UMUL, tseg);
     return TRUE;
  }
  
  /**
   * @brief Private handler to release all stored segments
   *
   * @details
   *    Private handler invokded by rlcUmmRelAllSegs to release the
   *    stored segements in case a complete PDU is received later.
   *
   * @param[in]  gCb      RLC instance control block
   * @param[in]  recBuf   Buffer that stores a received PDU or segments
   *
   * @return  void
   *
   */
  void rlcUmmRelAllSegs(RlcCb *gCb, RlcUmRecBuf *recBuf)
  {
     RlcUmSeg *seg;
  
     RLC_UMM_LLIST_FIRST_SEG(recBuf->segLst, seg);
     while (seg != NULLP)
     {
        ODU_PUT_MSG_BUF(seg->seg);
        cmLListDelFrm(&(recBuf->segLst), &(seg->lstEnt));
        RLC_FREE(gCb, seg, sizeof(RlcUmSeg));
        RLC_UMM_LLIST_FIRST_SEG(recBuf->segLst, seg);
     }
  
     return;
  }
  
  /**
   * @brief Private handler to reassemble from a segment or a PDU
   *
   * @details
   *    Private handler invokded by rlcUmmReAssembleSdus with either a
   *    PDU or a segment of a PDU. This is also called in the case of
   *    reestablishment and hence out of sequence joining is also to
   *    be supported
   *
   *
   * @param[in]  gCb     RLC instance control block
   * @param[in]  rbCb    Uplink RB control block
   * @param[in]  umHdr   UM header received for this segment/PDU
   * @param[in]  pdu     PDU to be reassembled
   *
   * @return  ROK/RFILED
   *
   */
  
  uint8_t rlcUmmProcSeg(RlcCb *gCb, RlcUlRbCb *rbCb, RlcUmHdr *umHdr, Buffer *pdu)
  {
  
     if ((RLC_UMUL.expSn != umHdr->sn) || (RLC_UMUL.expSo != umHdr->so))
     {
        /* Release the existing SDU as we have PDUs or */
        /* segments that are out of sequence           */
        DU_LOG("\nDEBUG  -->  RLC_UL : Received Segments are out of sequence in rlcUmmProcSeg()");
        ODU_PUT_MSG_BUF(RLC_UMUL.assembleSdu);
        return RFAILED;
     }
  
     if (umHdr->si == RLC_SI_FIRST_SEG)
     {/* first Segment of the SDU */
        if (RLC_UMUL.assembleSdu != NULLP)
        { /* Some old SDU may be present */
           ODU_PUT_MSG_BUF(RLC_UMUL.assembleSdu);
        }
        RLC_UMUL.assembleSdu = pdu;
        pdu = NULLP;
     }
     else if(umHdr->si == RLC_SI_MID_SEG)
     {/* Middle Segment of the SDU */
        ODU_CAT_MSG(RLC_UMUL.assembleSdu,pdu, M1M2);
        ODU_PUT_MSG_BUF(pdu);
        pdu = NULLP;
     }
     else if(umHdr->si ==  RLC_SI_LAST_SEG)
     {
        ODU_CAT_MSG(pdu, RLC_UMUL.assembleSdu, M2M1);
        ODU_PUT_MSG_BUF(RLC_UMUL.assembleSdu);
     }
  
     if (pdu != NULLP)
     {
        RLC_UMUL.assembleSdu = NULLP;
        rlcUtlSendUlDataToDu(gCb,rbCb, pdu);
     }
  
     return ROK;
  }
  /**
   *
   * @brief Private handler to reassemble SDUs
   *
   * @details
   *    Private handler invokded by rlcUmmProcessPdus with the PDU
   *    from the reception buffer in sequence to reassemble SDUs and
   *    send it to PDCP.
   *
   *        - With the stored header info, FI and LSF segment / concatenate
   *          PDUs or byte segments of PDUs to get the associated SDU.
   *
   * @param[in]  rbCb     RB control block
   * @param[in]  pdu      PDU to be reassembled
   *
   *  @return  uint8_t
   *      -# ROK
   *      -# RFAILED
   *
   */
  uint8_t rlcUmmReAssembleSdus(RlcCb *gCb, RlcUlRbCb *rbCb, RlcUmRecBuf *recBuf)
  {
     RlcUmSeg        *seg;
  
     /* This is a set of segments */
     RLC_UMM_LLIST_FIRST_SEG(recBuf->segLst, seg);
     RLC_UMUL.expSn = recBuf->sn;
     RLC_UMUL.expSo = 0;
     while(seg)
     {
        if(rlcUmmProcSeg(gCb, rbCb, &seg->umHdr, seg->seg) == RFAILED)
        {
           rlcUmmRelAllSegs(gCb, recBuf);
  	 break;
        }
        RLC_UMUL.expSo = seg->soEnd + 1;
        cmLListDelFrm(&(recBuf->segLst),&(seg->lstEnt));
        RLC_FREE(gCb, seg, sizeof(RlcSeg));
        RLC_UMM_LLIST_FIRST_SEG(recBuf->segLst, seg);
     }
     RLC_UMUL.expSn = (recBuf->umHdr.sn + 1) & (RLC_UMUL.modBitMask);
     RLC_UMUL.expSo = 0;
     return ROK;
  }
  
  /**
   * @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
  void rlcUmmProcessPdus(RlcCb *gCb, RlcUlRbCb  *rbCb, KwPduInfo *pduInfo, uint32_t ttiCnt)
  #else
  void rlcUmmProcessPdus(RlcCb *gCb, RlcUlRbCb *rbCb, KwPduInfo *pduInfo)
  #endif
  {
     RlcSn         *vrUh;      /* vr(uh) */
     RlcSn         *vrUr;      /* vr(ur) */
     RlcSn         *vrUx;      /* vr(ux) */
     uint16_t      curSn;      /* Current Sequence Number */
     uint32_t      pduCount;   /* PDU count */
     uint32_t      count;      /* Loop counter */
     RlcUmRecBuf   *recBuf;    /* UM Reception Buffer */
     RlcUmHdr      umHdr;      /* Um header*/
     bool          tmrRunning; /* Boolean for checking Tmr */
     MsgLen        pduSz;      /* Pdu Size */
     RlcSn         tRxNextReassembly;
     RlcSn         tRxNextReassemblyNxt;
     RlcSn         tRxNextHighest;
  
     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);
  
     while (count < pduCount)
     {
        RlcSn   ur; 
        RlcSn   seqNum;
        Buffer  *pdu = pduInfo->mBuf[count];
  
        gCb->genSts.pdusRecv++;
  #ifndef RGL_SPECIFIC_CHANGES
  #ifndef TENB_ACC
  #ifndef LTE_PAL_ENB
        uint32_t ulrate_rgu;
        MsgLen len;
        ODU_GET_MSG_LEN(pdu, &len);
        ulrate_rgu += len;
  #endif
  #endif      
  #endif      
        /* 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(rlcCb[0]->init.region) == TRUE)
       {
          uint32_t rlculdrop;
          rlculdrop++;
          ODU_PUT_MSG_BUF(pdu);
          count++;
          continue;
       }
  #endif     
  #endif
  #endif
  #endif
        /* get the pdu header */
        if (rlcUmmExtractHdr(gCb, rbCb, pdu, &umHdr))  
        {
           DU_LOG("\nERROR  --> RLC_UL: rlcUmmProcessPdus: Header Extraction Failed UEID:%d CELLID:%d",\
               rbCb->rlcId.ueId, rbCb->rlcId.cellId);
           ODU_PUT_MSG_BUF(pdu);
           count++;
           gCb->genSts.errorPdusRecv++;
           continue;
        }
  
        /* Check if the PDU should be delivered to upper layer */
        if(umHdr.si == 0)
        {
           rlcUtlSendUlDataToDu(gCb, rbCb, pdu);
  	 ODU_PUT_MSG_BUF(pdu);
  	 count++;
  	 continue;
        }
  
        curSn = umHdr.sn;
  
        /* Check if the PDU should be discarded or not */
        ur = RLC_UM_GET_VALUE(RLC_UMUL.vrUr, RLC_UMUL); 
        seqNum = RLC_UM_GET_VALUE(curSn, RLC_UMUL); 
  
        if (seqNum < ur)
        {
           /* PDU needs to be discarded */
           DU_LOG("\nINFO  -->  RLC_UL: rlcUmmProcessPdus: Received an unexpected pdu with sn %d \
  	    UEID:%d CELLID:%d", curSn, rbCb->rlcId.ueId, rbCb->rlcId.cellId);
  
           ODU_PUT_MSG_BUF(pdu);
           count++;
           gCb->genSts.unexpPdusRecv++;
           continue;
        } 
  
  #ifdef LTE_L2_MEAS
        rlcUtlCalUlIpThrPut(gCb,rbCb, pdu, ttiCnt);
  #endif 
  
        ODU_GET_MSG_LEN(pdu, &pduSz);
        /* Place sdu segment into recption buffer */
        if(rlcUmmAddRcvdSeg(gCb, rbCb, &umHdr, pdu, pduSz) == TRUE)      
        {
           recBuf = rlcUtlGetUmRecBuf(RLC_UMUL.recBufLst, umHdr.sn);
           if (recBuf == NULLP)
           {
              DU_LOG("\nERROR  -->  RLC_UL: rlcUmmProcessPdus: recBuf is NULLP UEID:%d CELLID:%d", \
                 rbCb->rlcId.ueId, rbCb->rlcId.cellId);
  	    ODU_PUT_MSG_BUF(pdu);
  	    count++;
              continue;
           }
  
           /* If all bytes segments of curSn are received, deliver assembled SDU to upper layer */
           if(recBuf != NULLP && recBuf->allSegRcvd)
           {
              rlcUmmReAssembleSdus(gCb, rbCb, recBuf);
              DU_LOG("\nDEBUG  -->  RLC_UL: rlcUmmProcessPdus: Assembled the Sdus for  sn = %d UEID:%d CELLID:%d",\
                 umHdr.sn, rbCb->rlcId.ueId, rbCb->rlcId.cellId);
  
              /* if curSn is same as the RX_NEXT_Reassembly */
              if (seqNum == ur)
              {
                 /* set RX_NEXT_Reassembly to next SN > current RX_NEXT_Reassembly which is not reassembled yet */
                 RlcSn nextVrUr = (*vrUr + 1) & RLC_UMUL.modBitMask;
                 rlcUmmFindRxNextReassembly(gCb, &RLC_UMUL, nextVrUr);
              }
           }
           /* If curSn is outside re-assembly window */
           else if (!rlcUmmCheckSnInReassemblyWindow(curSn,&RLC_UMUL))
           {  
              DU_LOG("\nDEBUG  -->  RLC_UL: rlcUmmProcessPdus: curent sn is outSide the re-Assembly window. \
                 sn = %d UEID:%d CELLID:%d", umHdr.sn, rbCb->rlcId.ueId, rbCb->rlcId.cellId);
  
  	    /* update RX_NEXT_Highest */
              *vrUh  = (curSn + 1) & RLC_UMUL.modBitMask;
  
              /* Discard all pdus outside the modified re-assembly window */
              if (!rlcUmmCheckSnInReassemblyWindow(*vrUr,&RLC_UMUL))
              {
                 RlcSn sn = *vrUr; /* Stores SNs which need to be discarded. First SN is VR(UR) */
                 RlcSn lowerEdge;  /* to hold the lower-edge of the re-assembly window */
                 RlcSn packetCount;
  
                 /* Set RX_NEXT_Reassembly to next SN >= (RX_NEXT_Highest - windowSize) that has not been reassembled yet */
  	       *vrUr = (*vrUh - RLC_UMUL.umWinSz) &  RLC_UMUL.modBitMask;
  	       lowerEdge = *vrUr;
                 packetCount = (lowerEdge - sn) & RLC_UMUL.modBitMask;
              
                 while (packetCount)
                 {
                    recBuf = rlcUtlGetUmRecBuf(RLC_UMUL.recBufLst, sn);
                    if (recBuf)
                    {
                       rlcUmmRelAllSegs(gCb, recBuf);
                       rlcUtlDelUmRecBuf(gCb, RLC_UMUL.recBufLst, recBuf);
                    }
                    sn = (sn + 1) & RLC_UMUL.modBitMask;
                    packetCount--;
                 }
  	       recBuf = rlcUtlGetUmRecBuf(RLC_UMUL.recBufLst, *vrUr);
  	       if (recBuf != NULLP && recBuf->allSegRcvd)
  	       {
  	          /* set rxNextReassembly to next SN > current rxNextReassembly which is not received */
  	          RlcSn nextRxNextReassembly = (*vrUr + 1) & RLC_UMUL.modBitMask;
  	          rlcUmmFindRxNextReassembly(gCb ,&RLC_UMUL, nextRxNextReassembly);
  	       }
              }
           }
  
  	 tmrRunning = rlcChkTmr(gCb,(PTR)rbCb, EVENT_RLC_UMUL_REASSEMBLE_TMR);
           tRxNextReassembly = RLC_UM_GET_VALUE(*vrUr ,RLC_UMUL);
           tRxNextReassemblyNxt = (*vrUr + 1) & rbCb->m.umUl.modBitMask;
           tRxNextHighest = RLC_UM_GET_VALUE(*vrUh, RLC_UMUL);
           tRxNextReassemblyNxt = RLC_UM_GET_VALUE(tRxNextReassemblyNxt ,RLC_UMUL);
  
  	 /* If reassemby timer is running */
  	 if (tmrRunning) 
  	 {
  	    RlcSn  tRxTimerTigger = RLC_UM_GET_VALUE(*vrUx, RLC_UMUL);
  	    uint8_t ret = rlcUmmCheckSnInReassemblyWindow(*vrUx, &RLC_UMUL);
  	    recBuf = rlcUtlGetUmRecBuf(RLC_UMUL.recBufLst,*vrUr);
  
              if ((tRxTimerTigger <= tRxNextReassembly) || ((!ret) && (tRxTimerTigger != tRxNextHighest)) ||
                  (tRxNextHighest ==  tRxNextReassemblyNxt && recBuf && recBuf->noMissingSeg))
              {
                 rlcStopTmr(gCb,(PTR)rbCb, EVENT_RLC_UMUL_REASSEMBLE_TMR);             
                 tmrRunning = FALSE;
                 DU_LOG("\nINFO  --> RLC_UL: rlcUmmProcessPdus: Stopped ReAssembly Timer rxTimerTigger = %d \
                    rxNextReassembly = %d rxNextHighest = %d ", tRxTimerTigger, tRxNextReassembly, tRxNextHighest);
              }
  	 }
  
  	 /* If Reassembly timer is not running */
  	 if (!tmrRunning)
  	 {
              recBuf = rlcUtlGetUmRecBuf(RLC_UMUL.recBufLst, curSn);
              if ((tRxNextHighest > tRxNextReassemblyNxt) || ((tRxNextHighest == tRxNextReassemblyNxt)
                   && (recBuf && (!recBuf->noMissingSeg))))
              {
                  DU_LOG("\nDEBUG  -->  RLC_UL: rlcUmmProcessPdus: Start ReAssembly Timer tRxNextReassemblyNxt = %d \
                     tRxNextHighest %d", tRxNextReassemblyNxt, tRxNextHighest);
                  rlcStartTmr(gCb, (PTR)rbCb, EVENT_RLC_UMUL_REASSEMBLE_TMR); 
                  *vrUx = *vrUh;
              }
  	 }
        }
        else
        {
           DU_LOG("\nERROR  -->  RLC_UL: rlcUmmProcessPdus:Failed to assemble the PDU for SN  = %d UEID:%d CELLID:%d",\
              umHdr.sn, rbCb->rlcId.ueId, rbCb->rlcId.cellId);
  
        }
        count++;
     }/* end while count < pduCount */
  #ifdef LTE_L2_MEAS
     rlcUtlCalUlIpThrPutIncTTI(gCb, rbCb,ttiCnt);
  #endif /* LTE_L2_MEAS */
     return;   
  }
  
  /**
   * @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 rlcUmUlReAssembleSdus() 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
  */ 
  Void rlcUmmUlReEstablish
  (
  RlcCb         *gCb,
  CmLteRlcId   *rlcId,
  RlcUlRbCb     *rbCb
  )
  {
     RlcSn         curSn;
     RlcSn         vrUh;
     RlcUmRecBuf   *recBuf;   /* UM Reception Buffer */
     RlcKwuSapCb   *rlcKwSap;   /* KWU SAP Information */
  
     curSn = rbCb->m.umUl.vrUr;
     vrUh  = RLC_UM_GET_VALUE(rbCb->m.umUl.vrUh,rbCb->m.umUl);
  
     if(TRUE == rlcChkTmr(gCb,(PTR)rbCb,EVENT_RLC_UMUL_REASSEMBLE_TMR))
     {
         rlcStopTmr(gCb,(PTR)rbCb,EVENT_RLC_UMUL_REASSEMBLE_TMR);
     }
     
     while (RLC_UM_GET_VALUE(curSn, rbCb->m.umUl) < vrUh)
     {
        recBuf = rlcUtlGetUmRecBuf(RLC_UMUL.recBufLst, curSn);
        if ( recBuf != NULLP )
        {
           if(recBuf->allRcvd == TRUE)
  	 {
             rlcUmmReAssembleSdus(gCb,rbCb,recBuf);
  	 }
  	 else
  	 {
  	    /* Remove PDU and segments */
  	    if(recBuf->pdu)
  	    {
  	       ODU_PUT_MSG_BUF(recBuf->pdu);
  	    }
              /* Release all the segments*/
  	    rlcUmmRelAllSegs(gCb,recBuf);
           }
  	 rlcUtlDelUmRecBuf(gCb, RLC_UMUL.recBufLst, recBuf);
        } 
        curSn = (curSn + 1) & rbCb->m.umUl.modBitMask;
     }
     rbCb->m.umUl.vrUr = 0;
     rbCb->m.umUl.vrUh = 0;
     rbCb->m.umUl.vrUx = 0;
  
     rlcKwSap = gCb->u.ulCb->rlcKwuUlSap + RLC_UI_PDCP;
  
     /* In the UM Mode always send reestablish-indication to Upper Latyer*/
     RlcUiKwuReEstCmpInd(&rlcKwSap->pst, rlcKwSap->suId, *rlcId);
  
     return;
  }
  
  /**
   * @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, segmentation info 
   *    and segmentation offset 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
  */
  static uint8_t rlcUmmExtractHdr(RlcCb *gCb, RlcUlRbCb *rbCb, Buffer *pdu, RlcUmHdr *umHdr)
  {
     Data      dst[2];    /* Destination Buffer */
     uint8_t   ret;       /* Return Value */
  
     memset(umHdr, 0, sizeof(RlcUmHdr));
  
     ret = ODU_REM_PRE_MSG(dst,pdu);
     if (ret != ROK)
     {
        DU_LOG("\nERROR  -->  RLC_UL: rlcUmmExtractHdr : ODU_REM_PRE_MSG Failed for SI\
          UEID:%d CELLID:%d", rbCb->rlcId.ueId, rbCb->rlcId.cellId);
        return RFAILED;
     }
     umHdr->si = (dst[0]) >> 6;
  
     /* If SI = 0, the RLC PDU contains complete RLC SDU. Header extraction complete. 
      * No other fields present in header */
     if(umHdr->si == 0)
        return ROK;
  
     /* If SI != 0, one SDU segment is present in RLC PDU. Hence extracting SN */
     if (rbCb->m.umUl.snLen == RLC_UM_CFG_6BIT_SN_LEN)
     {
        /* Extractin 6-bit SN */
        umHdr->sn = (dst[0]) & 0x3F; 
     }
     else
     {
        /* Extracting 12 bits SN */ 
        umHdr->sn = (dst[0]) & 0x0F;
        ret = ODU_REM_PRE_MSG(dst,pdu);
        if (ret != ROK)
        {
  	 DU_LOG("\nERROR  -->  RLC_UL: rlcUmmExtractHdr : ODU_REM_PRE_MSG Failed for SN\
  	       UEID:%d CELLID:%d", rbCb->rlcId.ueId, rbCb->rlcId.cellId);
  	 return RFAILED;
        }
        umHdr->sn = (umHdr->sn << 8) | dst[0];
     }
  
     /* SO field is present for middle and last segments of SDU, not present for first segment */
     if((umHdr->si == RLC_SI_LAST_SEG) || (umHdr->si == RLC_SI_MID_SEG))
     {
        ret = ODU_REM_PRE_MSG_MULT(dst,2,pdu);
        if (ret != ROK)
        {
           DU_LOG("\nERROR  -->  RLC_UL: rlcUmmExtractHdr : ODU_REM_PRE_MSG_MULT Failed for 16 bits SO \
              UEID:%d CELLID:%d", rbCb->rlcId.ueId, rbCb->rlcId.cellId);
           return RFAILED;
        }
        umHdr->so = dst[0];
        umHdr->so = (umHdr->so << 8) | dst[1];
     }
     return ROK; 
  }
     
  /**
   * @brief Handles expiry of re-assembly timer
   *
   * @param[in] gCb     RLC Instance control block
   * @param[in] rbCb    Rb Control block for which re-assembly timer expired
   *
   * @return  Void
  */
  void rlcUmmReAsmblTmrExp
  (
  RlcCb       *gCb,
  RlcUlRbCb   *rbCb     
  )
  {
     RlcSn tRxNextHighest;
     RlcSn tRxNextReassembly;
     RlcUmRecBuf *recBuf;
  
     DU_LOG("\nINFO  -->  RLC_UL: rlcUmmReAsmblTmrExp: UM Re-assembly timer expired");
  
     /* set VR(UR) to SN >= VR(UX) that has not been reassembled */
     rlcUmmFindRxNextReassembly(gCb, &RLC_UMUL, RLC_UMUL.vrUx);
  
     tRxNextHighest = RLC_UM_GET_VALUE(RLC_UMUL.vrUh, RLC_UMUL);
     tRxNextReassembly = (RLC_UMUL.vrUr + 1) & rbCb->m.umUl.modBitMask;
     tRxNextReassembly = RLC_UM_GET_VALUE(tRxNextReassembly, RLC_UMUL);
     recBuf = rlcUtlGetUmRecBuf(RLC_UMUL.recBufLst,RLC_UMUL.vrUr);
     if ((tRxNextHighest > tRxNextReassembly) || ((tRxNextHighest == tRxNextReassembly) &&
              ((recBuf) && !(recBuf->noMissingSeg))))
     {
        rlcStartTmr(gCb, (PTR)rbCb, EVENT_RLC_UMUL_REASSEMBLE_TMR);
        RLC_UMUL.vrUx = RLC_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
   */
  
  Void rlcUmmFreeUlRbCb
  (
  RlcCb       *gCb,
  RlcUlRbCb   *rbCb
  )
  {
     RlcSn         curSn = 0;           /* Sequence number of PDU */
     RlcUmRecBuf   *recBuf = NULLP;
  
     if(TRUE == rlcChkTmr(gCb,(PTR)rbCb,EVENT_RLC_UMUL_REASSEMBLE_TMR))
     {
        rlcStopTmr(gCb,(PTR)rbCb, EVENT_RLC_UMUL_REASSEMBLE_TMR);
     }
  
     do
     {
        recBuf = rlcUtlGetUmRecBuf(rbCb->m.umUl.recBufLst, curSn);
        if ( recBuf != NULLP )
        {
           if (recBuf->pdu != NULLP)
           {
              ODU_PUT_MSG_BUF(recBuf->pdu);
           }
           /* Release all the segments */
           rlcUmmRelAllSegs(gCb,recBuf);
           rlcUtlDelUmRecBuf(gCb, rbCb->m.umUl.recBufLst, recBuf);
        }
        curSn++;
     }while ( curSn < RLC_RCV_BUF_BIN_SIZE);
  
     RLC_FREE(gCb,rbCb->snssai, sizeof (Snssai));
     RLC_FREE(gCb,rbCb->m.umUl.recBufLst, (RLC_RCV_BUF_BIN_SIZE * sizeof(CmLListCp)));
     rbCb->m.umUl.recBufLst = NULLP;
  
     if(rbCb->m.umUl.assembleSdu != NULLP)
     {
        ODU_PUT_MSG_BUF(rbCb->m.umUl.assembleSdu);
     }
     return;
  } /* rlcAmmFreeUlRbCb */
  
  /********************************************************************30**
           End of file
  **********************************************************************/
  