+/*******************************************************************************
+################################################################################
+# 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
+**********************************************************************/