--- /dev/null
+/*******************************************************************************
+################################################################################
+# 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 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_ul.c
+
+*********************************************************************21*/
+static const char* RLOG_MODULE_NAME="AMM";
+static int RLOG_MODULE_ID=2048;
+static int RLOG_FILE_ID=190;
+
+/* 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_udx.h"
+#include "kw_err.h" /* Err defines */
+#include "kw_env.h" /* RLC environment options */
+
+#include "kw.h" /* RLC defines */
+#include "kw_ul.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_ul.x"
+#include "kw_udx.x"
+
+/* Variable for logging, declared in cl */
+#ifndef RGL_SPECIFIC_CHANGES
+#ifndef TENB_ACC
+#ifndef LTE_PAL_ENB
+extern U32 ulrate_rgu;
+#endif
+#endif
+#endif
+#ifndef RGL_SPECIFIC_CHANGES
+#ifndef TENB_ACC
+#ifndef TENB_T2K3K_SPECIFIC_CHANGES
+#ifndef LTE_PAL_ENB
+extern U32 isMemThreshReached(Region region);
+#endif
+#else
+#ifndef LTE_PAL_ENB
+extern U32 isMemThreshReached(Region region);
+#endif
+#endif
+#endif
+#endif
+/** @file gp_amm_ul.c
+@brief RLC Acknowledged Mode Uplink Module
+**/
+#define KW_MODULE (KW_DBGMASK_AM | KW_DBGMASK_UL) /* for debugging purpose */
+
+/* private function declarations */
+
+PRIVATE Void kwAmmUlAssembleCntrlInfo ARGS ((KwCb *gCb, KwUlRbCb *rbCb));
+
+PRIVATE S16 kwAmmExtractHdr ARGS ((KwCb *gCb,
+ KwUlRbCb *rbCb,
+ Buffer *pdu,
+ KwAmHdr *amHdr,
+ U8 *fByte));
+
+PRIVATE Bool kwAmmUlPlacePduInRecBuf ARGS ((KwCb *gCb,
+ Buffer *pdu,
+ KwUlRbCb *rbCb,
+ KwAmHdr *amHdr));
+
+PRIVATE Void kwAmmTriggerStatus ARGS ((KwCb *gCb,
+ KwUlRbCb *rbCb,
+ KwSn sn,
+ Bool discFlg));
+
+PRIVATE S16 kwAmmUlReassembleSdus ARGS ((KwCb *gCb,
+ KwUlRbCb *rbCb,
+ KwAmRecBuf *recBuf));
+
+PRIVATE Void kwAmmProcPduOrSeg ARGS ((KwCb *gCb,
+ KwUlRbCb *rbCb,
+ KwAmHdr *amHdr,
+ Buffer *pdu));
+
+PRIVATE Void kwAmmUpdExpByteSeg ARGS ((KwCb *gCb,KwAmUl *amUl, KwSeg* newSeg));
+
+PRIVATE Void kwAmmExtractElmnt ARGS ((KwCb *gCb, Buffer *pdu, KwExtHdr *hdrInfo));
+
+PRIVATE Void kwAmmUlHndlStatusPdu ARGS ((KwCb *gCb,
+ KwUlRbCb *rbCb,
+ Buffer *cntrlPdu,
+ U8 *fByte));
+
+/******************************************************************************
+
+ AM Module contains the following funcitons:
+
+ - kwAmmProcessSdus
+ - kwAmmUlAssembleCntrlInfo
+ - kwResegRetxPdus
+ - kwAssembleSdus
+ - kwChkandSetPoll
+ - kwAmmProcessPdus
+ - kwAmmUlHndlStatusPdu
+ - kwAmmTriggerStatus
+ - kwAmmUlReassembleSdus
+
+*******************************************************************************/
+/** @addtogroup ammode */
+/*@{*/
+
+/**
+ * @brief Private function to fill NACK information in status Pdu as per 5GNR
+ *
+ * @param[in] rbCb Ul RbCb
+ * @param[in] sn Sequence number of the PDU for which the NACK
+ * @param[in] isSegment TRUE means NACK for segment; FALSE for PDU
+ * @param[in] soStart SOStart
+ * @param[in] soEnd SOEnd
+ * @param[out] statusPdu status Pdu holder to be filled
+ * @param[in] prevNackSn It holds previous nack Sn
+ *
+ * @return S16
+ * The number of bytes required to encode this NACK information
+ *
+ */
+#ifdef ANSI
+PRIVATE S16 kwAmmUlSetNackInfo
+(
+KwUlRbCb *rbCb,
+KwSn sn,
+Bool isSegment,
+U16 soStart,
+U16 soEnd,
+KwUdxDlStaPdu *statusPdu,
+KwSn *prevNackSn
+)
+#else
+PRIVATE S16 kwAmmUlSetNackInfo(rbCb, sn, isSegment, soStart, statusPdu, prevNackSn)
+KwUlRbCb *rbCb;
+KwSn sn;
+Bool isSegment;
+U16 soStart;
+U16 soEnd;
+KwUdxDlStaPdu *statusPdu,
+KwSn *prevNackSn;
+#endif
+{
+ KwNackInfo *nackInfo = (statusPdu->nackInfo + statusPdu->nackCount);
+ S16 sizeToBeEncd = 0; /* Status PDu size to be encoded */
+
+ TRC2(kwAmmUlSetNackInfo)
+
+ /* In following cases we should increment the nackCnt & fill new NACK_SN info:
+ * 1) First NACK_SN of the statusdPdu
+ * 2) NACK_SN is not continuous with previous
+ * 3) NACK_SN is same as previuos but segments are not continuous
+ * 4) NACK_SN is continuous with previous but previous NACK_SN segments
+ * are not missing in sequence till end
+ */
+ if((*prevNackSn == 0xffffffff) || ((((*prevNackSn) + 1) & AMUL.snModMask) != sn) ||
+ (((*prevNackSn) == sn) && (((nackInfo->soEnd + 1) != soStart))) ||
+ ((nackInfo->isSegment) && (((*prevNackSn) + 1) == sn) && (nackInfo->soEnd != KW_ALL_BYTES_MISSING)))
+ {
+ if(nackInfo->nackRange)
+ {
+ if((nackInfo->soEnd) && (!nackInfo->soStart))
+ {
+ /*First nack_sn of this nackRange not segmented but last is segmented */
+ sizeToBeEncd = 5; /*32 for soStart and soEnd and 8 for nackRange */
+ }
+ else
+ {
+ /*First nack_sn of this nackRange was segmented */
+ sizeToBeEncd = 1; /*8 for nackRange */
+ }
+ }
+
+ if(*prevNackSn != 0xffffffff)
+ {
+ /* Increment nackCount as this sn is continous */
+ statusPdu->nackCount++;
+ nackInfo = statusPdu->nackInfo + statusPdu->nackCount;
+ }
+
+ nackInfo->sn = sn;
+ nackInfo->isSegment = isSegment;
+ nackInfo->soStart = soStart;
+ nackInfo->soEnd = soEnd;
+ nackInfo->nackRange = 0;
+
+ if(isSegment)
+ {
+ sizeToBeEncd += ((AMUL.snLen == KW_AM_CFG_12BIT_SN_LEN)?6:7); /* NACK,E1,E2,Sostart,SoEnd */
+ }
+ else
+ {
+ sizeToBeEncd += ((AMUL.snLen == KW_AM_CFG_12BIT_SN_LEN)?2:3); /* NACK,E1,E2 */
+ }
+ }
+ else
+ {
+ if(!(nackInfo->nackRange))
+ {
+ nackInfo->nackRange++;
+ }
+ /* This case means there are continuous SNs/Segments. If it is the next
+ * Sn then increment nackRnage. if same SN but different segment then
+ * dont increment nackRange */
+ if((((*prevNackSn) + 1) & AMUL.snModMask) == sn)
+ {
+ nackInfo->nackRange++;
+ }
+
+ /* If NackRange is reached to max value then increment statusPdu->nackCount*/
+ if(nackInfo->nackRange == 255)
+ {
+ statusPdu->nackCount++;
+ if(nackInfo->isSegment)
+ {
+ sizeToBeEncd = 1; /* return only nackRangeSize*/
+ }
+ else if (isSegment)
+ {
+ /* First SN was not segmented of this nackRange but last SN is segmented */
+ sizeToBeEncd = 5; /* return size of soSatrt + soEnd + nackRnage */
+ }
+ }
+
+ if(isSegment)
+ {
+ nackInfo->isSegment = isSegment;
+ nackInfo->soEnd = soEnd;
+ }
+ else if(nackInfo->isSegment)
+ {
+ nackInfo->soEnd = KW_ALL_BYTES_MISSING;
+ }
+ else
+ {
+ nackInfo->soStart = 0;
+ nackInfo->soEnd = 0;
+ }
+
+ }
+ *prevNackSn = sn;
+
+ RETVALUE(sizeToBeEncd);
+}
+
+/**
+ * @brief Private handler to gather information required to create the STATUS
+ * PDU
+ *
+ * @details
+ * Scans the reception buffer and copies information to the UdxDlStaPdu
+ * structure about SN's and segments not yet received. This data is
+ * sent to the DL instance so that it can create an appropriate (depending
+ * on the grants from MAC) STATUS PDU and send it to MAC.
+ *
+ * @param[in] gCb RLC instance control block
+ * @param[in] rbCb Uplink RB control block
+ *
+ * @return Void
+ *
+ */
+#ifdef ANSI
+PRIVATE Void kwAmmUlAssembleCntrlInfo
+(
+KwCb *gCb,
+KwUlRbCb *rbCb
+)
+#else
+PRIVATE Void kwAmmUlAssembleCntrlInfo(gCb, rbCb)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+#endif
+{
+ KwUdxDlStaPdu *pStatusPdu;
+ KwNackInfo *nackInfo;
+ KwSn sn; /* sequence number */
+ KwSn mSn; /* Mod val of sequence number */
+ KwSn rxHighestStatus; /* Mod val of rxHighestStatus */
+ KwSeg *seg; /* pdu segment */
+ U16 nackCnt = 0; /* Index for staPdu */
+ U16 seqSo; /* segmment offset */
+ KwUdxUlSapCb *sapCb;
+ U16 staPduEncSize = 3; /* size that would be of the encoded
+ STATUS PDU, it is in bits; 15 for
+ first fixed part of STATUS PDU */
+ KwAmRecBuf *recBuf = NULLP;
+ KwSn prevNackSn = 0xffffffff;
+
+ TRC2(kwAmmUlAssembleCntrlInfo)
+
+
+ sapCb = KW_GET_UDX_SAP(gCb);
+
+ KW_ALLOC_SHRABL_BUF(sapCb->pst.region,
+ sapCb->pst.pool,
+ pStatusPdu,
+ sizeof(KwUdxDlStaPdu));
+
+#if (ERRCLASS & ERRCLS_ADD_RES)
+ /* Memory allocation failure can not be expected */
+ if(!pStatusPdu)
+ {
+ RETVOID;
+ }
+#endif
+
+ sn = AMUL.rxNext;
+ MODAMR(sn, mSn, AMUL.rxNext, AMUL.snModMask);
+ MODAMR(AMUL.rxHighestStatus, rxHighestStatus, AMUL.rxNext, AMUL.snModMask);
+
+ recBuf = kwUtlGetRecBuf(AMUL.recBufLst, sn);
+
+ while (mSn < rxHighestStatus )
+ {
+ /* For missing PDUs */
+ if ((NULLP == recBuf) && nackCnt < KW_MAX_NACK_CNT )
+ {
+ RLOG_ARG3(L_DEBUG,DBG_RBID,rbCb->rlcId.rbId,
+ "Missing PDU's SN = %d UEID:%d CELLID:%d",
+ sn,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ staPduEncSize += kwAmmUlSetNackInfo(rbCb,
+ sn,
+ FALSE, /* isSegment */
+ 0, /* SOStart */
+ 0, /* SOEnd */
+ pStatusPdu,
+ &prevNackSn);
+ }
+ else if (recBuf && (recBuf->pdu == NULLP) &&
+ (recBuf->segLst.count > 0))
+ {
+ /* Scan through the byte segments of PDU and add this sn
+ with soStart and soEnd info to staPdu */
+
+ seqSo = 0;
+ KW_LLIST_FIRST_SEG(recBuf->segLst, seg);
+ while (seg != NULLP && nackCnt < KW_MAX_NACK_CNT)
+ {
+ /* For missing byte segments */
+ if (seg->amHdr.so != seqSo)
+ {
+ staPduEncSize += kwAmmUlSetNackInfo(rbCb,
+ sn,
+ TRUE,
+ seqSo,
+ seg->amHdr.so - 1,
+ pStatusPdu,
+ &prevNackSn);
+
+ RLOG_ARG3(L_DEBUG,DBG_RBID,rbCb->rlcId.rbId,
+ "Missing byte segment's"
+ " SN:%d UEID:%d CELLID:%d",
+ 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",
+ seqSo,
+ seg->amHdr.so - 1,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ }
+
+ seqSo = seg->soEnd + 1;
+ KW_LLIST_NEXT_SEG(recBuf->segLst, seg);
+ }
+
+ /* Check if the last segment is missing */
+ KW_LLIST_LAST_SEG(recBuf->segLst, seg);
+ if ((seg != NULLP) &&
+ (seg->amHdr.si != KW_SI_LAST_SEG && nackCnt < KW_MAX_NACK_CNT))
+ {
+ staPduEncSize += kwAmmUlSetNackInfo(rbCb,
+ sn,
+ TRUE,
+ seqSo,
+ KW_ALL_BYTES_MISSING,
+ pStatusPdu,
+ &prevNackSn);
+
+ RLOG_ARG3(L_DEBUG,DBG_RBID,rbCb->rlcId.rbId,
+ "kwAmmUlAssembleCntrlInfo: Missing (last) byte "
+ "segment's SN:%d UEID:%d CELLID:%d",
+ 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",
+ seqSo,
+ KW_ALL_BYTES_MISSING,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ }
+ }
+
+
+ sn = (sn + 1) & (AMUL.snModMask); /* MOD 1024 */
+ MODAMR(sn, mSn, AMUL.rxNext, AMUL.snModMask);
+
+ /* Get the received Buffer the updated/next SN */
+ recBuf = kwUtlGetRecBuf(AMUL.recBufLst, sn);
+
+ /* Find the next missing sequence number if nackCnt reaches maximum and
+ still Reordering window has some missing AMDPDUs / AMDPDU segments. The
+ next missing sequence number will be considered as the ack sequnece
+ number in the status pdu.*/
+ if((nackCnt == KW_MAX_NACK_CNT) &&
+ ((recBuf == NULLP) ||
+ ((recBuf->pdu == NULLP) &&
+ (recBuf->segLst.count > 0))))
+ {
+ break;
+ }
+ }
+
+ /*Unfortunately i have write below peice of code here because kwAmmsetNackInfo()
+ * don't know that this is the last nackSn with nackRange*/
+ nackInfo = &(pStatusPdu->nackInfo[pStatusPdu->nackCount]);
+ if(nackInfo->nackRange)
+ {
+ if((nackInfo->soEnd) && (!nackInfo->soStart))
+ {
+ /*First nack_sn of this nackRange not segmented but last is segmented */
+ staPduEncSize += 5; /*32 for soStart and soEnd and 8 for nackRange */
+ }
+ else
+ {
+ /*First nack_sn of this nackRange was segmented */
+ staPduEncSize += 1; /*8 for nackRange */
+ }
+ }
+ /* nackCount is used as an index to nackInfo array but in status Pdu it
+ * should be equal to number nackInfo that are filled. hence incrementing by 1*/
+ if(prevNackSn != 0xffffffff)
+ {
+ pStatusPdu->nackCount++;
+ }
+ /* Update ACK SN with the last sn for which feedback is not assembled */
+ if ( mSn == rxHighestStatus)
+ {
+ pStatusPdu->ackSn = AMUL.rxHighestStatus;
+ }
+ else
+ {
+ pStatusPdu->ackSn = sn;
+ }
+
+ RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId,
+ "kwAmmUlAssembleCntrlInfo: ACK PDU's SN = %d"
+ "UEID:%d CELLID:%d",
+ pStatusPdu->ackSn,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+
+ pStatusPdu->controlBo = staPduEncSize; /*Its already in bytes */
+
+ AMUL.staTrg = FALSE;
+ AMUL.gatherStaPduInfo = FALSE;
+
+
+ if (KwUlUdxStaPduReq(&sapCb->pst,
+ sapCb->spId,
+ &rbCb->rlcId,
+ pStatusPdu) != ROK)
+ {
+ RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+ "Failed to Send Sta Pdu UEID:%d CELLID:%d",
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ KW_FREE_SHRABL_BUF_WC(sapCb->pst.region,
+ sapCb->pst.pool,
+ pStatusPdu,
+ sizeof(KwUdxDlStaPdu));
+ }
+
+ RETVOID;
+}
+
+#ifdef XEON_SPECIFIC_CHANGES
+extern U32 gRlcDatIndUL;
+#endif
+
+#ifdef T2K_TRIGGER_RLC_REEST
+PUBLIC U32 drpRlcDrbPack;
+#endif
+/**
+ * @brief Handler to process the PDUs received from MAC and send it to PDCP
+ *
+ * @details
+ * This function is invoked by UTL with the PDU(s) received from MAC.
+ * It reorders the received data PDUs and trigger status report as
+ * needed. Reassembles the SDUs in sequence and send it to PDCP.
+ * It also processes the control PDU
+ *
+ * @param[in] gCb RLC instance control block
+ * @param[in] rbCb RB control block
+ * @param[out] pduInfo PDU Info received from MAC
+ *
+ * @return Void
+ *
+ */
+#ifdef LTE_L2_MEAS
+#ifdef ANSI
+PUBLIC Void kwAmmProcessPdus
+(
+KwCb *gCb,
+KwUlRbCb *rbCb,
+KwPduInfo *pduInfo,
+U32 ttiCnt
+)
+#else
+PUBLIC Void kwAmmProcessPdus(gCb, rbCb, pduInfo, ulTimeInfo)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+KwPduInfo *pduInfo;
+U32 ttiCnt;
+#endif
+#else
+#ifdef ANSI
+PUBLIC Void kwAmmProcessPdus
+(
+KwCb *gCb,
+KwUlRbCb *rbCb,
+KwPduInfo *pduInfo
+)
+#else
+PUBLIC Void kwAmmProcessPdus(gCb, rbCb, pduInfo)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+KwPduInfo *pduInfo;
+#endif
+#endif
+{
+ Buffer *pdu;
+ KwAmUl *amUl;
+ KwAmHdr amHdr;
+ U8 numPdu = 0;
+ U8 numPduToProcess;
+ KwSn sn;
+ KwSn tSn;
+ KwSn mSn;
+ U8 fByte;
+ Bool discFlg;
+#ifdef LTE_L2_MEAS_RLC
+ MsgLen rlcSduSz; /*Holds length of Rlc Sdu*/
+#endif /* LTE_L2_MEAS */
+
+ TRC2(kwAmmProcessPdus)
+
+
+ amUl = &AMUL;
+
+ numPduToProcess = KW_MIN(pduInfo->numPdu, RGU_MAX_PDU);
+ RLOG_ARG4(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId,
+ "numPdu[%ld],numPduToProcess[%ld] UEID:%ld CELLID:%ld",
+ numPdu,
+ numPduToProcess,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+
+ //printf ("++++++++++++ 5GNRLOG numPduToProcess %d \n", numPduToProcess);
+ while (numPdu < numPduToProcess)
+ {
+ //printf ("++++++++++++ 5GNRLOG processing pdu %d \n", numPdu);
+ discFlg = FALSE;
+ pdu = pduInfo->mBuf[numPdu++];
+
+ if (! pdu)
+ {
+
+ RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+ "Null Pdu UEID:%d CELLID:%d",
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ gCb->genSts.errorPdusRecv++;
+ break;
+ }
+#ifndef RGL_SPECIFIC_CHANGES
+#ifndef TENB_ACC
+#ifndef LTE_PAL_ENB
+ MsgLen len;
+ SFndLenMsg(pdu, &len);
+ ulrate_rgu += len;
+#endif
+#endif
+#endif
+ /* Extract AM PDU/SEG header Info */
+ KW_MEM_ZERO(&amHdr, sizeof(KwAmHdr));
+ /* Avoided the allocation of amHdr and sending
+ a single pointer */
+ if (kwAmmExtractHdr(gCb, rbCb, pdu, &amHdr, &fByte) != ROK)
+ {
+ RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+ "Header Extraction Failed UEID:%d CELLID:%d",
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ KW_FREE_BUF(pdu);
+ gCb->genSts.errorPdusRecv++;
+ continue;
+ }
+ /* Check if its a control PDU */
+ if (amHdr.dc == 0)
+ {
+ kwAmmUlHndlStatusPdu(gCb, rbCb, pdu, &fByte);
+ KW_FREE_BUF(pdu);
+ continue;
+ }
+ if((amHdr.si == KW_SI_LAST_SEG) && (!amHdr.so))
+ {
+ RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId,
+ "kwAmmProcessPdus: Dropping PDU because SO can't be zero for last segment sn:%u "
+ "UEID:%d CELLID:%d",
+ amHdr.sn,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ KW_FREE_BUF(pdu);
+ continue;
+ }
+#ifndef RGL_SPECIFIC_CHANGES
+#ifdef LTE_TDD
+#ifndef TENB_ACC
+#ifndef TENB_T2K3K_SPECIFIC_CHANGES
+#ifndef LTE_PAL_ENB
+ /* Changed the condition to TRUE from ROK */
+ if(isMemThreshReached(kwCb[0]->init.region) == TRUE)
+ {
+ extern U32 rlculdrop;
+ rlculdrop++;
+ KW_FREE_BUF(pdu);
+ continue;
+ }
+#endif
+#else
+#ifndef LTE_PAL_ENB
+ /*ccpu00142274 - UL memory based flow control*/
+ if(isMemThreshReached(kwCb[0]->init.region) != ROK)
+ {
+ extern U32 rlculdrop;
+ rlculdrop++;
+ KW_FREE_BUF(pdu);
+ continue;
+ }
+#endif
+#endif
+#endif
+#endif
+#endif
+
+#ifdef T2K_TRIGGER_RLC_REEST
+ if(drpRlcDrbPack > 1000)
+ {
+ if(rbCb->rlcId.rbType == CM_LTE_DRB)
+ {
+ KW_FREE_BUF(pdu);
+ continue;
+ }
+ }
+ drpRlcDrbPack++;
+#endif
+ /* Reordering data PDU */
+ sn = amHdr.sn;
+ if (kwAmmUlPlacePduInRecBuf(gCb,pdu, rbCb, &amHdr) == TRUE)
+ {
+ KwAmRecBuf *recBuf;
+ Bool tmrRunning;
+ KwSn tVrMr;
+ KwSn mrxNextHighestRcvd;
+
+#ifdef LTE_L2_MEAS
+ kwUtlCalUlIpThrPut(gCb, rbCb, pdu, ttiCnt);
+#endif /* LTE_L2_MEAS */
+
+ /* Update rxNextHighestRcvd */
+ MODAMR(sn, mSn, amUl->rxNext, amUl->snModMask);
+ MODAMR(amUl->rxNextHighestRcvd, mrxNextHighestRcvd, amUl->rxNext, amUl->snModMask);
+ if (mSn >= mrxNextHighestRcvd)
+ {
+ amUl->rxNextHighestRcvd = ((sn + 1) & (amUl->snModMask));
+
+ RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId,
+ "kwAmmProcessPdus: Updated rxNextHighestRcvd = %d UEID:%d CELLID:%d",
+ amUl->rxNextHighestRcvd,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ }
+
+ recBuf = kwUtlGetRecBuf(amUl->recBufLst, sn);
+ if ((NULLP != recBuf) && ( recBuf->allRcvd))
+ {
+ /* deliver the reassembled RLC SDU to upper layer,
+ But not removed from the table */
+ kwAmmUlReassembleSdus(gCb, rbCb, recBuf);
+ recBuf->isDelvUpperLayer = TRUE;
+
+ MODAMR(amUl->vrMr, tVrMr, amUl->rxNext, amUl->snModMask);
+
+ /* Update rxHighestStatus */
+ if (sn == amUl->rxHighestStatus)
+ {
+ tSn = (sn + 1) & (amUl->snModMask) ; /* MOD (2 Pwr SN LEN- 1) */
+
+ recBuf = kwUtlGetRecBuf(amUl->recBufLst, tSn);
+ /* Scan through till the upper edge of the window */
+ MODAMR(tSn, mSn, amUl->rxNext, amUl->snModMask);
+ while (mSn <= tVrMr)
+ {
+ if ((NULLP == recBuf) || (!recBuf->allRcvd))
+ {
+ RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId,
+ "kwAmmProcessPdus: Updated rxHighestStatus:%d "
+ "UEID:%d CELLID:%d",
+ tSn,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+
+ amUl->rxHighestStatus = tSn;
+ break;
+ }
+ tSn = (tSn + 1) & (amUl->snModMask); /* MOD (2 Pwr SN LEN- 1) */
+ recBuf = kwUtlGetRecBuf(amUl->recBufLst, tSn);
+ mSn++;
+ }
+ }
+
+
+ /* Update rxNext */
+ if (sn == amUl->rxNext)
+ {
+ tSn = sn;
+ recBuf = kwUtlGetRecBuf(amUl->recBufLst, tSn);
+ MODAMR(tSn, mSn, amUl->rxNext, amUl->snModMask);
+ /* Scan through till the upper edge of the window */
+ while (mSn <= tVrMr)
+ {
+ if ((NULLP != recBuf) && (recBuf->allRcvd) &&
+ (TRUE == recBuf->isDelvUpperLayer))
+ {
+ /* RecBuf should remove from table
+ since PDU is already sent to upper layer */
+ recBuf->isDelvUpperLayer = FALSE;
+ kwUtlDelRecBuf(amUl->recBufLst, recBuf, gCb);
+ }
+ else
+ {
+ amUl->rxNext = tSn;
+ amUl->vrMr = (amUl->rxNext + (KW_AM_GET_WIN_SZ(amUl->snLen))) & (amUl->snModMask);
+ break;
+ }
+ tSn = (tSn + 1) & (amUl->snModMask);
+ recBuf = kwUtlGetRecBuf(amUl->recBufLst, tSn);
+ mSn++;
+ }
+ }
+ }
+
+ /* Check if reOrdTmr is running and update rxNextStatusTrig accordingly */
+ tmrRunning = kwChkTmr(gCb,(PTR)rbCb, KW_EVT_AMUL_REORD_TMR);
+ if (tmrRunning)
+ {
+ Bool snInWin = KW_AM_CHK_SN_WITHIN_RECV_WINDOW(amUl->rxNextStatusTrig, amUl);
+
+ if ( (amUl->rxNextStatusTrig == amUl->rxNext) || ( (!snInWin) &&
+ (amUl->rxNextStatusTrig != amUl->vrMr) ) )
+ {
+ kwStopTmr(gCb,(PTR)rbCb, KW_EVT_AMUL_REORD_TMR);
+ tmrRunning = FALSE;
+ }
+ }
+
+ if (!tmrRunning)
+ {
+ if (amUl->rxNextHighestRcvd > amUl->rxNext)
+ {
+ kwStartTmr(gCb,(PTR)rbCb, KW_EVT_AMUL_REORD_TMR);
+ amUl->rxNextStatusTrig = amUl->rxNextHighestRcvd;
+
+ RLOG_ARG3(L_DEBUG,DBG_RBID,rbCb->rlcId.rbId,
+ "kwAmmProcessPdus: Updated rxNextStatusTrig = %d UEID:%d CELLID:%d",
+ amUl->rxNextStatusTrig,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ }
+ }
+ }
+ else
+ {
+ discFlg = TRUE;
+ gRlcStats.amRlcStats.numULPdusDiscarded++;
+ }
+
+ if (amHdr.p)
+ {
+ kwAmmTriggerStatus(gCb,rbCb, sn, discFlg);
+ }
+ }
+
+#ifdef LTE_L2_MEAS
+ kwUtlCalUlIpThrPutIncTTI(gCb, rbCb,ttiCnt);
+#endif /* LTE_L2_MEAS */
+ gCb->genSts.pdusRecv += pduInfo->numPdu;
+ if (amUl->gatherStaPduInfo)
+ {
+ kwAmmUlAssembleCntrlInfo(gCb,rbCb);
+ }
+
+ RETVOID;
+}
+
+
+/**
+ * @brief Private handler to extract header Information of the PDU
+ *
+ * @details
+ * This function extracts the header elements of the PDU and store them
+ * in db for future reference.
+ *
+ * fByte - is the first byte removed from the PDU as part of calling
+ * functions
+ *
+ * @param[in] gCb RLC instance control block
+ * @param[in] rbCb Uplink RB control block
+ * @param[in] pdu Received PDU
+ * @param[out] amHdr Pointer to the extracted AM header
+ * @param[out] fByte First byte removed from the PDU
+ *
+ * @return S16
+ * -# ROK
+ * -# RFAILED
+ *
+ */
+#ifdef ANSI
+PRIVATE S16 kwAmmExtractHdr
+(
+KwCb *gCb,
+KwUlRbCb *rbCb,
+Buffer *pdu,
+KwAmHdr *amHdr,
+U8 *fByte
+)
+#else
+PRIVATE S16 kwAmmExtractHdr(gCb, rbCb, pdu, amHdr, fByte)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+Buffer *pdu;
+KwAmHdr *amHdr;
+U8 *fByte;
+#endif
+{
+ U8 snByte;
+ KwSn sn = 0;
+ MsgLen pduSz;
+ KwExtHdr hdrInfo;
+
+ TRC2(kwAmmExtractHdr)
+
+
+ KW_MEM_ZERO(&hdrInfo, sizeof(KwExtHdr));
+
+ /* Extract fixed part of the header */
+ SFndLenMsg(pdu,&pduSz);
+ SRemPreMsg(fByte, pdu);
+ amHdr->dc = (*fByte & KW_DC_POS) >> KW_DC_SHT;
+ if (KW_CNTRL_PDU == amHdr->dc)
+ {
+ //printf ("++++++++++++ 5GNRLOG HDR extracted CTRL : \n");
+ RETVALUE(ROK);
+ }
+
+ amHdr->p = (*fByte & KW_POLL_POS) >> KW_POLL_SHT;
+
+ amHdr->si = (*fByte & KW_SI_POS) >> KW_SI_SHT;
+
+ /* 12 BIT SN */
+ if (rbCb->m.amUl.snLen == KW_AM_CFG_12BIT_SN_LEN)
+ {
+ SRemPreMsg(&snByte, pdu);
+ sn = (KwSn)(((*fByte & KW_SN_POS_12BIT) << KW_BYTE_LEN ) | snByte);
+ amHdr->sn = sn;
+ }
+ else if (rbCb->m.amUl.snLen == KW_AM_CFG_18BIT_SN_LEN)
+ {
+ SRemPreMsg(&snByte, pdu);
+ sn = (KwSn)(((*fByte & KW_SN_POS_18BIT) << KW_BYTE_LEN ) | snByte);
+
+ SRemPreMsg(&snByte, pdu);
+ sn = ((sn << KW_BYTE_LEN) | snByte);
+
+ amHdr->sn = sn;
+ }
+ if ((amHdr->si != 0) && (amHdr->si != 0x01))
+ {
+ hdrInfo.len = KW_SO_LEN_5GNR;
+ kwAmmExtractElmnt(gCb, pdu, &hdrInfo);
+ amHdr->so = hdrInfo.val;
+ pduSz -= 2;
+ }
+
+ //printf ("++++++++++++ 5GNRLOG HDR extracted DATA : sn %d \n", sn);
+ RETVALUE(ROK);
+}
+
+#ifdef OLD
+/**
+ * @brief Private handler to extract header Information of the PDU
+ *
+ * @details
+ * This function extracts the header elements of the PDU and store them
+ * in db for future reference.
+ *
+ * fByte - is the first byte removed from the PDU as part of calling
+ * functions
+ *
+ * @param[in] gCb RLC instance control block
+ * @param[in] rbCb Uplink RB control block
+ * @param[in] pdu Received PDU
+ * @param[out] amHdr Pointer to the extracted AM header
+ * @param[out] fByte First byte removed from the PDU
+ *
+ * @return S16
+ * -# ROK
+ * -# RFAILED
+ *
+ */
+#ifdef ANSI
+PRIVATE S16 kwAmmExtractHdrOld
+(
+KwCb *gCb,
+Buffer *pdu,
+KwAmHdr *amHdr,
+U8 *fByte
+)
+#else
+PRIVATE S16 kwAmmExtractHdrOld(gCb, pdu, amHdr, fByte)
+KwCb *gCb;
+Buffer *pdu;
+KwAmHdr *amHdr;
+U8 *fByte;
+#endif
+{
+ U8 e;
+ U8 snByte;
+ U16 sn;
+ MsgLen pduSz;
+ MsgLen totalSz = 0;
+ KwExtHdr hdrInfo;
+
+ TRC2(kwAmmExtractHdrOld)
+
+
+ KW_MEM_ZERO(&hdrInfo, sizeof(KwExtHdr));
+
+ /* Extract fixed part of the header */
+ SFndLenMsg(pdu,&pduSz);
+ SRemPreMsg(fByte, pdu);
+ amHdr->dc = (*fByte & KW_DC_POS) >> KW_DC_SHT;
+ if (KW_CNTRL_PDU == amHdr->dc)
+ {
+ RETVALUE(ROK);
+ }
+ /* kw002.201 : Changed the extraction of hdr elements to avoid */
+ /* function calls */
+ amHdr->rf = (*fByte & KW_RF_POS) >> KW_RF_SHT;
+ amHdr->p = (*fByte & KW_POLL_POS) >> KW_POLL_SHT;
+ amHdr->fi = (*fByte & KW_FI_POS) >> KW_FI_SHT;
+ e = amHdr->e = (*fByte & KW_E_POS)>> KW_E_SHT;
+
+ SRemPreMsg(&snByte, pdu);
+ sn = (U16)(((*fByte & KW_SN_POS) << KW_BYTE_LEN ) | snByte);
+ amHdr->sn = sn;
+ if (amHdr->rf == 1)
+ {
+ /* Extract extn part of the header */
+ hdrInfo.len = KW_LSF_LEN;
+ kwAmmExtractElmnt(gCb, pdu, &hdrInfo);
+ amHdr->lsf = (U8)hdrInfo.val;
+
+ hdrInfo.len = KW_SO_LEN;
+ kwAmmExtractElmnt(gCb, pdu, &hdrInfo);
+ amHdr->so = hdrInfo.val;
+ pduSz -= 2;
+ }
+
+ amHdr->numLi = 0;
+ /* Extract LIs */
+ while (e && (amHdr->numLi < KW_MAX_UL_LI))
+ {
+ hdrInfo.len = KW_E_LEN;
+ kwAmmExtractElmnt(gCb, pdu, &hdrInfo);
+ e = amHdr->e = (U8)hdrInfo.val;
+
+ /* Extract LI value*/
+ hdrInfo.len = KW_LI_LEN;
+ kwAmmExtractElmnt(gCb, pdu, &hdrInfo);
+ /* li = hdrInfo.val;*/
+
+ /* check if LI is zero */
+ if (! hdrInfo.val)
+ {
+ RLOG0(L_ERROR, "Received LI as 0");
+ RETVALUE(RFAILED);
+ }
+
+ /* store the extracted LI value */
+ amHdr->li[amHdr->numLi++] = hdrInfo.val;
+ totalSz += hdrInfo.val; /* incrment the size by LI value */
+ }
+
+ /*ccpu00122597:PDU is dropped if liCnt exceeds KW_MAX_LI*/
+ if(e && (amHdr->numLi >= KW_MAX_UL_LI))
+ {
+ RLOG2(L_ERROR,"LI Count [%u] exceeds Max LI Count[%u]",
+ amHdr->numLi, KW_MAX_UL_LI);
+ RETVALUE(RFAILED);
+ }
+
+ /* first 2 bytes + Add one for Odd LI*/
+ pduSz -= ( amHdr->numLi + (amHdr->numLi >> 1) + 2 + (amHdr->numLi & 1) );
+
+ if ( totalSz >= pduSz )
+ {
+ RLOG3(L_ERROR,"SN [%d]:Corrupted PDU as TotSz[%lu] PduSz[%lu] ",
+ amHdr->sn, totalSz, pduSz);
+ RETVALUE(RFAILED);
+ }
+
+ RETVALUE(ROK);
+}
+#endif
+
+/**
+ * @brief Private handler to process the status PDU
+ *
+ * @details
+ * Private handler invokded by kwAmmProcessPdus to process the
+ * control PDU (status report) received from its peer RLC entity.
+ *
+ * - Decode the values from the received control pdu
+ * - Create a KwUdxStaPdu structure, copy the values onto it and
+ * send it to the DL instance for further processing
+ *
+ * @param[in] gCb RLC instance control block
+ * @param[in] rbCb Uplink RB control block
+ * @param[in] cntrlPdu Control PDU received from MAC
+ * @param[in] fByte First byte already removed from the STATUS PDU
+ *
+ * @return Void
+ *
+ */
+#ifdef ANSI
+PRIVATE Void kwAmmUlHndlStatusPdu
+(
+KwCb *gCb,
+KwUlRbCb *rbCb,
+Buffer *cntrlPdu,
+U8 *fByte
+)
+#else
+PRIVATE Void kwAmmUlHndlStatusPdu(gCb, rbCb, cntrlPdu, fByte)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+Buffer *cntrlPdu;
+U8 *fByte;
+#endif
+{
+ U8 e1;
+ KwExtHdr hdrInfo;
+ KwUdxStaPdu *pStaPdu;
+ KwUdxUlSapCb *sapCb;
+ U8 e3; /* NACK RANGE : 5GNR */
+ U32 snLen;
+ U32 snRange;
+ U32 resrvdBitsAckSn=0;
+ U32 resrvdBitsNackSn=0;
+
+ TRC2(kwAmmUlHndlStatusPdu)
+
+
+ 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)
+ {
+ RLOG_ARG2(L_ERROR,DBG_RBID,rbCb->rlcId.rbId,
+ "Reserved value for CPT received UEID:%d CELLID:%d",
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+
+ RETVOID;
+ }
+
+ sapCb = KW_GET_UDX_SAP(gCb);
+
+ KW_ALLOC_SHRABL_BUF(sapCb->pst.region,
+ sapCb->pst.pool,
+ pStaPdu,
+ sizeof(KwUdxStaPdu));
+
+#if (ERRCLASS & ERRCLS_ADD_RES)
+ /* Memory allocation failure can not be expected */
+ if(!pStaPdu)
+ {
+ RETVOID;
+ }
+#endif
+
+ if (rbCb->m.amUl.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.amUl.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 = snLen;
+ kwAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo);
+ pStaPdu->ackSn = hdrInfo.val;
+
+ //printf ("++++++++++++ 5GNRLOG HNDL STATUS acksn %d : \n", pStaPdu->ackSn);
+ /* Check if NACK Exists */
+ hdrInfo.len = KW_E1_LEN;
+ kwAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo);
+ e1 = (U8)hdrInfo.val;
+ RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId,
+ "kwAmmUlHndlStatusPdu: ACK SN = %d UEID:%d CELLID:%d",
+ pStaPdu->ackSn,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+
+ /* Extract the Reserved Bits after ACK SN field */
+ hdrInfo.len = resrvdBitsAckSn;
+ kwAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo);
+
+ /* If NACK exists in control PDU */
+ /* For ACKs and NACKs */
+ while (e1 && (pStaPdu->nackCnt < KW_MAX_NACK_CNT))
+ {
+ hdrInfo.len = snLen;
+ kwAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo);
+ pStaPdu->nackInfo[pStaPdu->nackCnt].sn = hdrInfo.val;
+
+ hdrInfo.len = KW_E1_LEN;
+ kwAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo);
+ e1 = (U8)hdrInfo.val;
+
+ /* Extract e2 */
+ /* hdrInfo.len = KW_E1_LEN; --> previusly stored value (for e1) is
+ already present*/
+ kwAmmExtractElmnt(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*/
+ kwAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo);
+ e3 = (U8) hdrInfo.val;
+
+ /* Extract Reserved Bits after NACK SN */
+ hdrInfo.len = resrvdBitsNackSn;
+ kwAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo);
+
+ /* Test for resegmentation */
+ if (pStaPdu->nackInfo[pStaPdu->nackCnt].isSegment)
+ {
+ hdrInfo.len = KW_SO_LEN_5GNR; /* 5GNR : SO Len 16 Bits */
+ kwAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo);
+ pStaPdu->nackInfo[pStaPdu->nackCnt].soStart = hdrInfo.val;
+
+ kwAmmExtractElmnt(gCb, cntrlPdu, &hdrInfo);
+ pStaPdu->nackInfo[pStaPdu->nackCnt].soEnd = hdrInfo.val;
+
+ RLOG_ARG4(L_DEBUG,DBG_RBID,rbCb->rlcId.rbId,
+ "kwAmmUlHndlStatusPdu: soStart and soEnd = %d %d"
+ "UEID:%d CELLID:%d",
+ pStaPdu->nackInfo[pStaPdu->nackCnt].soStart,
+ pStaPdu->nackInfo[pStaPdu->nackCnt].soEnd,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ }
+ 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;
+ kwAmmExtractElmnt(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) & (rbCb->m.amUl.snModMask);
+ }
+
+
+ /* Parse & send Status PDU to RLC-DL */
+ KwUlUdxStaUpdReq(&(sapCb->pst), sapCb->spId, &rbCb->rlcId, pStaPdu);
+
+ RETVOID;
+}
+
+/**
+ * @brief Private handler to release all stored segments
+ *
+ * @details
+ * Private handler invokded by kwAmmUlPlacePduInRecBuf 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
+ *
+ */
+#ifdef ANSI
+PRIVATE Void kwAmmUlRlsAllSegs
+(
+KwCb *gCb,
+KwAmRecBuf *recBuf
+)
+#else
+PRIVATE Void kwAmmUlRlsAllSegs(gCb,recBuf)
+KwCb *gCb;
+KwAmRecBuf *recBuf;
+#endif
+{
+ KwSeg *seg;
+
+ TRC2(kwAmmUlRlsAllSegs)
+
+ KW_LLIST_FIRST_SEG(recBuf->segLst, seg);
+ while (seg != NULLP)
+ {
+ KW_FREE_BUF_WC(seg->seg);
+ cmLListDelFrm(&(recBuf->segLst),&(seg->lstEnt));
+ KW_FREE_WC(gCb,seg, sizeof(KwSeg));
+ KW_LLIST_FIRST_SEG(recBuf->segLst, seg);
+ }
+
+ RETVOID;
+}
+
+/**
+ * @brief Private handler to store the received segment
+ *
+ * @details
+ * Private handler invokded by kwAmmUlPlacePduInRecBuf 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] amHdr AM 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
+ */
+#ifdef ANSI
+PRIVATE Bool kwAmmAddRcvdSeg
+(
+KwCb *gCb,
+KwUlRbCb *rbCb,
+KwAmHdr *amHdr,
+Buffer *pdu,
+U16 pduSz
+)
+#else
+PRIVATE Bool kwAmmAddRcvdSeg(gCb, rbCb, amHdr, pdu, pduSz)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+KwAmHdr *amHdr;
+Buffer *pdu;
+U16 pduSz;
+#endif
+{
+ KwAmRecBuf *recBuf = NULLP;
+ KwSeg *seg;
+ KwSeg *tseg;
+ U16 soEnd; /* Holds the SoEnd of received segment */
+ U16 expSo = 0; /* Expected SO */
+
+ TRC2(kwAmmAddRcvdSeg)
+
+ soEnd = amHdr->so + pduSz - 1;
+ recBuf = kwUtlGetRecBuf(AMUL.recBufLst, amHdr->sn);
+
+ if (NULLP == recBuf)
+ {
+ KW_ALLOC(gCb,recBuf, sizeof(KwAmRecBuf));
+#if (ERRCLASS & ERRCLS_ADD_RES)
+ if (recBuf == NULLP)
+ {
+ RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId,
+ "Memory allocation failed UEID:%d CELLID:%d",
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+
+ KW_FREE_BUF(pdu);
+ RETVALUE(FALSE);
+ }
+#endif /* ERRCLASS & ERRCLS_RES */
+ kwUtlStoreRecBuf(AMUL.recBufLst, recBuf, amHdr->sn);
+ }
+ else
+ {
+ if (recBuf->allRcvd == TRUE)
+ {
+ KW_FREE_BUF(pdu);
+ RETVALUE(FALSE);
+ }
+ }
+
+ recBuf->isDelvUpperLayer = FALSE;
+ /* kw003.201 - Move past the segments that are different than the */
+ /* one received. */
+ KW_LLIST_FIRST_SEG(recBuf->segLst, seg);
+ while ((seg != NULLP) && (seg->amHdr.so < amHdr->so))
+ {
+ expSo = seg->amHdr.so + seg->segSz;
+ KW_LLIST_NEXT_SEG(recBuf->segLst, seg);
+ }
+
+ /* The received segment should start after the end of previous seg */
+ if (expSo > amHdr->so)
+ {
+ /* This is a duplicate segment */
+ gRlcStats.amRlcStats.numRlcAmCellDupPduRx++;
+ KW_FREE_BUF(pdu);
+ RETVALUE(FALSE);
+ }
+
+ if ((seg) && (seg->amHdr.so <= soEnd))
+ {
+ /* This is a duplicate segment */
+ gRlcStats.amRlcStats.numRlcAmCellDupPduRx++;
+ KW_FREE_BUF(pdu);
+ RETVALUE(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. */
+ KW_ALLOC_WC(gCb,tseg, sizeof(KwSeg));
+#if (ERRCLASS & ERRCLS_ADD_RES)
+ if (tseg == NULLP)
+ {
+ RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId,
+ "Memory allocation failed UEID:%d CELLID:%d",
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ KW_FREE_BUF(pdu);
+ RETVALUE(FALSE);
+ }
+#endif /* ERRCLASS & ERRCLS_RES */
+
+ tseg->seg = pdu;
+ tseg->segSz = pduSz;
+ KW_MEM_CPY(&tseg->amHdr, amHdr, sizeof(KwAmHdr));
+ recBuf->amHdr.si = amHdr->si;
+ recBuf->amHdr.sn = amHdr->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;
+ kwAmmUpdExpByteSeg(gCb,&AMUL,tseg);
+
+ RETVALUE(TRUE);
+}
+
+/**
+ * @brief Private handler to place the PDU in the reception buffer
+ *
+ * @details
+ * This function checks if the received PDU's SN falls within the
+ * receiving window, after which it places the same in the reception
+ * buffer if its not a duplicate.
+ *
+ * @param[in] gCb RLC instance control block
+ * @param[in] pdu Received PDU
+ * @param[in] rbCb Uplink AM Radio Bearer
+ * @param[out] amUl AM UL Info
+ *
+ * @return Bool
+ * -# TRUE
+ * -# FALSE
+ *
+ */
+#ifdef ANSI
+PRIVATE Bool kwAmmUlPlacePduInRecBuf
+(
+KwCb *gCb,
+Buffer *pdu,
+KwUlRbCb *rbCb,
+KwAmHdr *amHdr
+)
+#else
+PRIVATE Bool kwAmmUlPlacePduInRecBuf(gCb, pdu, rbCb, amHdr)
+KwCb *gCb;
+Buffer *pdu;
+KwUlRbCb *rbCb;
+KwAmHdr *amHdr;
+#endif
+{
+ KwSn sn;
+ MsgLen pduSz;
+ KwAmUl *amUl = &(rbCb->m.amUl);
+
+ TRC2(kwAmmUlPlacePduInRecBuf)
+
+
+ sn = amHdr->sn;
+ SFndLenMsg(pdu, &pduSz);
+
+ gCb->genSts.bytesRecv += pduSz;
+ gRlcStats.amRlcStats.numRlcAmCellSduBytesRx += pduSz;
+ if (!KW_AM_CHK_SN_WITHIN_RECV_WINDOW(sn, amUl))
+ {
+ gRlcStats.amRlcStats.numRlcAmCellDropOutWinRx++;
+ RLOG_ARG3(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId,
+ "kwAmmUlPlacePduInRecBuf: SN %d outside the window"
+ "UEID:%d CELLID:%d",
+ sn,
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+
+ gCb->genSts.unexpPdusRecv++;
+ KW_FREE_BUF(pdu);
+ RETVALUE(FALSE);
+ }
+
+ if (amHdr->si == 0)
+ {
+ KwAmRecBuf *recBuf = kwUtlGetRecBuf(amUl->recBufLst, sn);
+
+ /* We received a complete PDU. Either we already have it, in which */
+ /* case we just ignore the new PDU and discard it. Otherwise, */
+ /* store the received PDU in the reception buffer */
+ if (NULLP == recBuf)
+ {
+ KW_ALLOC(gCb, recBuf, sizeof(KwAmRecBuf));
+#if (ERRCLASS & ERRCLS_ADD_RES)
+ if (recBuf == NULLP)
+ {
+ RLOG_ARG2(L_FATAL,DBG_RBID,rbCb->rlcId.rbId,
+ "Memory allocation failed UEID:%d CELLID:%d",
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+ KW_FREE_BUF(pdu);
+ RETVALUE(FALSE);
+ }
+#endif /* ERRCLASS & ERRCLS_RES */
+ kwUtlStoreRecBuf(AMUL.recBufLst, recBuf, sn);
+ }
+ else if (recBuf->allRcvd != TRUE)
+ {
+ kwAmmUlRlsAllSegs(gCb,recBuf);
+ }
+ else
+ {
+ gRlcStats.amRlcStats.numRlcAmCellDupPduRx++;
+ gCb->genSts.unexpPdusRecv++;
+ KW_FREE_BUF(pdu);
+ RETVALUE(FALSE);
+ }
+ recBuf->isDelvUpperLayer = FALSE;
+ recBuf->pdu = pdu;
+ recBuf->pduSz = pduSz;
+ recBuf->allRcvd = TRUE;
+ gRlcStats.amRlcStats.numRlcAmCellSduRx++;
+ KW_MEM_CPY(&recBuf->amHdr, amHdr, sizeof(KwAmHdr));
+ RETVALUE(TRUE);
+ }
+ else
+ {
+ /* We received a segment. We need to add that to the existing */
+ /* segments, if any. */
+ RETVALUE(kwAmmAddRcvdSeg(gCb,rbCb, amHdr, pdu, pduSz));
+ }
+}
+
+/**
+ * @brief Private handler to trigger status report
+ *
+ * @details
+ * Private handler invokded by kwAmmProcessPdus to check if the
+ * status report need to be sent, and update the status trigger
+ * flag accordingly based on status prohibit timer.
+ *
+ * - Check if the received pdu's sn is less than rxHighestStatus, set the
+ * staTrg flag.
+ * - If staProhTmr is not running, calculate cntrlBo, else it'll be
+ * updated at the expiry of staProhTmr.
+ * - Expiry of reOrdTmr also will set staTrg flag.
+ *
+ * @param[in] gCb RLC instance control block
+ * @param[in] rbCb Uplink RB control block
+ * @param[in] sn Sequence number of the pdu based on which to check if
+ * status needs to be triggered
+ * @param[in] discFlg Whether this pdu was discarded or not
+ *
+ * @return Void
+ *
+ */
+#ifdef ANSI
+PRIVATE Void kwAmmTriggerStatus
+(
+KwCb *gCb,
+KwUlRbCb *rbCb,
+KwSn sn,
+Bool discFlg
+)
+#else
+PRIVATE Void kwAmmTriggerStatus(gCb,rbCb, sn, discFlg)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+KwSn sn;
+Bool discFlg;
+#endif
+{
+ Bool tmrRunning;
+ KwSn tSn;
+ KwSn tVrMr;
+ KwSn trxHighestStatus;
+ KwAmUl *amUl = &(rbCb->m.amUl);
+
+ TRC2(kwAmmTriggerStatus)
+
+
+ MODAMR(amUl->vrMr, tVrMr, amUl->rxNext, amUl->snModMask);
+ MODAMR(amUl->rxHighestStatus, trxHighestStatus, amUl->rxNext, amUl->snModMask);
+ MODAMR(sn , tSn, amUl->rxNext, amUl->snModMask);
+
+ /* kw005.201 Product CR ccpu00117114 *
+ * The "=" in the 2nd condition is removed */
+ if ((discFlg) || (tSn < trxHighestStatus) || (tSn >= tVrMr))
+ {
+ RLOG_ARG2(L_UNUSED,DBG_RBID,rbCb->rlcId.rbId,
+ "kwAmmTriggerStatus: Set Status Trigger UEID:%d CELLID:%d",
+ rbCb->rlcId.ueId,
+ rbCb->rlcId.cellId);
+
+ amUl->staTrg = TRUE;
+ amUl->gatherStaPduInfo = FALSE;
+
+ /* Check if staProhTmr is running */
+ tmrRunning = kwChkTmr(gCb,(PTR) rbCb, KW_EVT_AMUL_STA_PROH_TMR);
+
+ if (!tmrRunning)
+ {
+ amUl->gatherStaPduInfo = TRUE;
+ }
+ }
+
+ RETVOID;
+}
+
+/**
+ * @brief Private handler to reassemble from a segment or a PDU
+ *
+ * @details
+ * Private handler invokded by kwAmmReassembleSdus 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] amHdr AM header received for this segment/PDU
+ * @param[in] pdu PDU to be reassembled
+ *
+ * @return Void
+ *
+ */
+#ifdef ANSI
+PRIVATE Void kwAmmProcPduOrSeg
+(
+KwCb *gCb,
+KwUlRbCb *rbCb,
+KwAmHdr *amHdr,
+Buffer *pdu
+)
+#else
+PRIVATE Void kwAmmProcPduOrSeg(gCb, rbCb, amHdr, pdu)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+KwAmHdr *amHdr;
+Buffer *pdu;
+#endif
+{
+
+ TRC2(kwAmmProcPduOrSeg)
+
+ if ((AMUL.expSn != amHdr->sn) || (AMUL.expSo != amHdr->so))
+ {
+ /* Release the existing partial SDU as we have PDUs or */
+ /* segments that are out of sequence */
+ rbCb->m.amUl.isOutOfSeq = TRUE;
+ KW_FREE_BUF(AMUL.partialSdu);
+ }
+
+ //if (amHdr->fi & KW_FI_FIRST_SEG)
+ if (amHdr->si == 0x01)
+ {/* first Segment of the SDU */
+ if (AMUL.partialSdu != NULLP)
+ { /* Some old SDU may be present */
+ KW_FREE_BUF_WC(AMUL.partialSdu);
+ }
+ AMUL.partialSdu = pdu;
+ pdu = NULLP;
+ }
+ else if(amHdr->si == 0x03)
+ {/* Middle or last segment of the SUD */
+ SCatMsg(AMUL.partialSdu,pdu, M1M2);
+ KW_FREE_BUF_WC(pdu);
+ pdu = NULLP;
+ }
+ else if (amHdr->si == 0x02)
+ {
+ SCatMsg(pdu,AMUL.partialSdu,M2M1);
+ KW_FREE_BUF_WC(AMUL.partialSdu);
+ }
+
+ if (pdu != NULLP)
+ {
+ AMUL.partialSdu = NULLP;
+ kwUtlSndDatInd(gCb,rbCb, pdu);
+ }
+
+ RETVOID;
+}
+
+/**
+ *
+ * @brief Private handler to reassemble SDUs
+ *
+ * @details
+ * Private handler invokded by kwAmmProcessPdus 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 S16
+ * -# ROK
+ * -# RFAILED
+ *
+ */
+#ifdef ANSI
+PRIVATE S16 kwAmmUlReassembleSdus
+(
+KwCb *gCb,
+KwUlRbCb *rbCb,
+KwAmRecBuf *recBuf
+)
+#else
+PRIVATE S16 kwAmmUlReassembleSdus(gCb, rbCb, recBuf)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+KwAmRecBuf *recBuf;
+#endif
+{
+ KwSeg *seg;
+
+ TRC2(kwAmmUlReassembleSdus)
+ //if (recBuf->amHdr.rf == 0)
+ if (recBuf->amHdr.si == 0)
+ {
+ /* This is a PDU */
+ kwAmmProcPduOrSeg(gCb,rbCb, &recBuf->amHdr, recBuf->pdu);
+ /* Assign NULLP to recBuf->pdu as this PDU is sent to PDCP */
+ recBuf->pdu = NULLP;
+ AMUL.expSn = (recBuf->amHdr.sn + 1) & (AMUL.snModMask); /* MOD 1024 */
+ AMUL.expSo = 0;
+ }
+ else
+ {
+ /* This is a set of segments */
+ KW_LLIST_FIRST_SEG(recBuf->segLst, seg);
+ AMUL.expSn = recBuf->amHdr.sn;
+ AMUL.expSo = 0;
+ while(seg)
+ {
+ kwAmmProcPduOrSeg(gCb,rbCb, &seg->amHdr, seg->seg);
+ AMUL.expSo = seg->soEnd + 1;
+
+ cmLListDelFrm(&(recBuf->segLst),&(seg->lstEnt));
+ KW_FREE_WC(gCb, seg, sizeof(KwSeg));
+
+ KW_LLIST_FIRST_SEG(recBuf->segLst, seg);
+ }
+ AMUL.expSn = (recBuf->amHdr.sn + 1) & (AMUL.snModMask); /* MOD 1024 */
+ AMUL.expSo = 0;
+ }
+
+ RETVALUE(ROK);
+}
+
+/**
+ * @brief Handler to process the re-establishment request received from UIM
+ *
+ * @param[in] gCb RLC instance control block
+ * @param[in] rlcId RLC identifier
+ * @param[in] sendReEst Whether to send back restablishment complete or not
+ * @param[in] rbCb Uplink RB control block
+ *
+ * @return Void
+ *
+ */
+#ifdef ANSI
+PUBLIC Void kwAmmUlReEstablish
+(
+KwCb *gCb,
+CmLteRlcId rlcId,
+Bool sendReEst,
+KwUlRbCb *rbCb
+)
+#else
+PUBLIC Void kwAmmUlReEstablish(gCb, rlcId, sendReEst, rbCb)
+KwCb *gCb;
+CmLteRlcId rlcId;
+Bool sendReEst;
+KwUlRbCb *rbCb;
+#endif
+{
+ KwSn sn;
+ KwSn mSn;
+ KwSn mVrMr;
+
+#ifndef KW_PDCP
+ KwKwuSapCb *kwKwSap;
+#endif
+ KwAmRecBuf *recBuf = NULLP;
+
+ TRC2(kwAmmUlReEstablish);
+
+
+ sn = AMUL.rxNext;
+
+ MODAMR(AMUL.vrMr, mVrMr, AMUL.rxNext, AMUL.snModMask);
+ MODAMR(sn, mSn, AMUL.rxNext, AMUL.snModMask);
+
+ /* Reassemble SDUs from PDUs with SN less than upper edge of the window */
+ while (mSn < mVrMr)
+ {
+ recBuf = kwUtlGetRecBuf(AMUL.recBufLst, sn);
+ if (NULLP != recBuf)
+ {
+ if (recBuf->allRcvd == TRUE)
+ {
+ kwAmmUlReassembleSdus(gCb,rbCb, recBuf);
+ }
+ else
+ {
+ /* Remove PDU and segments */
+ if(recBuf->pdu)
+ {
+ KW_FREE_BUF_WC(recBuf->pdu);
+ }
+ /* Release all the segments*/
+ kwAmmUlRlsAllSegs(gCb,recBuf);
+ }
+ kwUtlDelRecBuf(AMUL.recBufLst, recBuf, gCb);
+ }
+ sn = (sn + 1) & (AMUL.snModMask); /* MOD 1024 */
+ MODAMR(sn, mSn, AMUL.rxNext, AMUL.snModMask);
+ }
+ /* Discard remaining PDUs and bytesegments in recBuf */
+
+ /* Stop all timers and reset variables */
+ if(TRUE == kwChkTmr(gCb,(PTR)rbCb,KW_EVT_AMUL_REORD_TMR))
+ {
+ kwStopTmr(gCb,(PTR)rbCb, KW_EVT_AMUL_REORD_TMR);
+ }
+ if(TRUE == kwChkTmr(gCb,(PTR)rbCb,KW_EVT_AMUL_STA_PROH_TMR))
+ {
+ kwStopTmr(gCb,(PTR)rbCb, KW_EVT_AMUL_STA_PROH_TMR);
+ }
+
+ AMUL.rxNext = 0;
+ AMUL.rxNextHighestRcvd = 0;
+ AMUL.rxNextStatusTrig = 0;
+ rbCb->m.amUl.vrMr = (rbCb->m.amUl.rxNext + KW_AM_GET_WIN_SZ(rbCb->m.amUl.snLen)) & (rbCb->m.amUl.snModMask);
+ AMUL.rxHighestStatus = 0;
+ AMUL.staTrg = FALSE;
+ AMUL.gatherStaPduInfo = FALSE;
+ AMUL.expSn = 0;
+ AMUL.expSo = 0;
+ if (AMUL.partialSdu != NULLP)
+ {
+ KW_FREE_BUF(AMUL.partialSdu);
+ }
+ kwKwSap = gCb->u.ulCb->kwuUlSap + KW_UI_PDCP;
+
+ if(sendReEst)
+ {
+ KwUiKwuReEstCmpInd(&kwKwSap->pst, kwKwSap->suId, rlcId);
+ rbCb->m.amUl.isOutOfSeq = FALSE;
+ }
+
+ RETVOID;
+}
+
+/**
+ * @brief Handler for reorder timer expiry
+ *
+ * @details
+ * This function is used to handle events upon expiry of reorder timer
+ *
+ * @param[in] gCb RLC instance control block
+ * @param[in] rbCb RB control block
+ *
+ * @return Void
+ *
+ */
+
+#ifdef ANSI
+PUBLIC Void kwAmmReOrdTmrExp
+(
+KwCb *gCb,
+KwUlRbCb *rbCb
+)
+#else
+PUBLIC Void kwAmmReOrdTmrExp(rbCb)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+#endif
+{
+ KwAmUl *amUl = &(rbCb->m.amUl);
+ KwSn sn;
+ KwSn mSn;
+ KwSn mVrMr;
+ KwSn mrxHighestStatus;
+ KwSn mrxNextHighestRcvd;
+ Bool tmrRunning = FALSE;
+ KwAmRecBuf *recBuf = NULLP;
+
+ TRC2(kwAmmReOrdTmrExp);
+
+
+ /* Update rxHighestStatus */
+ sn = amUl->rxNextStatusTrig;
+
+ MODAMR(sn, mSn, amUl->rxNext, amUl->snModMask);
+ MODAMR(amUl->vrMr, mVrMr, amUl->rxNext, amUl->snModMask);
+ recBuf = kwUtlGetRecBuf(AMUL.recBufLst, sn);
+
+ while (mSn < mVrMr)
+ {
+ if ((recBuf == NULLP) ||
+ ((recBuf != NULLP) && (!recBuf->allRcvd)) )
+ {
+ amUl->rxHighestStatus = sn;
+ amUl->staTrg = TRUE;
+ amUl->gatherStaPduInfo = FALSE;
+
+ /* Check if staProhTmr is running */
+ tmrRunning = kwChkTmr(gCb,(PTR) rbCb, KW_EVT_AMUL_STA_PROH_TMR);
+
+ if (!tmrRunning)
+ {
+ gRlcStats.amRlcStats.numULReOrdTimerExpires++;
+ amUl->gatherStaPduInfo = TRUE;
+ kwAmmUlAssembleCntrlInfo(gCb, rbCb);
+ }
+
+ break;
+ }
+ sn = (sn + 1) & (amUl->snModMask);
+ MODAMR(sn, mSn, amUl->rxNext, amUl->snModMask);
+ }
+
+ /* Update rxNextStatusTrig */
+ MODAMR(amUl->rxNextHighestRcvd, mrxNextHighestRcvd, amUl->rxNext, amUl->snModMask);
+ MODAMR(amUl->rxHighestStatus, mrxHighestStatus, amUl->rxNext, amUl->snModMask);
+ if (mrxNextHighestRcvd > mrxHighestStatus)
+ {
+ kwStartTmr(gCb,(PTR)rbCb, KW_EVT_AMUL_REORD_TMR);
+ amUl->rxNextStatusTrig = amUl->rxNextHighestRcvd;
+ }
+
+ RETVOID;
+} /* kwAmmReOrdTmrExp */
+
+/**
+ * @brief Handler for status prohibit timer expiry
+ *
+ * @details
+ * This function is used to handle events upon expiry of status prohibit
+ * timer
+ *
+ * @param[in] gCb RLC instance control block
+ * @param[in] rbCb RB control block
+ *
+ * @return Void
+ *
+ */
+
+#ifdef ANSI
+PUBLIC Void kwAmmStaProTmrExp
+(
+KwCb *gCb,
+KwUlRbCb *rbCb
+)
+#else
+PUBLIC Void kwAmmStaProTmrExp(gCb, rbCb)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+#endif
+{
+ KwAmUl *amUl = &(rbCb->m.amUl);
+
+ TRC2(kwAmmStaProTmrExp);
+
+
+ amUl->gatherStaPduInfo = FALSE;
+
+ if (amUl->staTrg == TRUE)
+ {
+ amUl->gatherStaPduInfo = TRUE;
+ /* kw002.201 : Sending StaRsp after StaProhibit tmr expiry */
+ kwAmmUlAssembleCntrlInfo(gCb,rbCb);
+ }
+
+ RETVOID;
+} /* kwAmmStaProTmrExp */
+
+/**
+ * @brief Handler to extract an element of AM Header
+ *
+ * @details
+ * This function is used to extract an element of AM header.
+ *
+ * @param[in] pdu The pdu to be decoded
+ * @param[in,out] hdrInfo Container to hold the decoded info
+ *
+ * @return Void
+ *
+ */
+
+#ifdef ANSI
+PRIVATE Void kwAmmExtractElmnt
+(
+KwCb *gCb,
+Buffer *pdu,
+KwExtHdr *hdrInfo
+)
+#else
+PRIVATE Void kwAmmExtractElmnt(gCb, pdu, hdrInfo)
+KwCb *gCb;
+Buffer *pdu;
+KwExtHdr *hdrInfo;
+#endif
+{
+ U8 hdr;
+ U8 pLen = hdrInfo->pLen;
+ U8 len = (U8)hdrInfo->len;
+ U32 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;
+}
+
+/**
+ * @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] amUl AM Uplink Control Block
+ * @param[in] seg Newly received segment
+ *
+ * @return Void
+ *
+ */
+
+#ifdef ANSI
+PRIVATE Void kwAmmUpdExpByteSeg
+(
+KwCb *gCb,
+KwAmUl *amUl,
+KwSeg *seg
+)
+#else
+PRIVATE Void kwAmmUpdExpByteSeg(gCb, amUl, seg)
+KwCb *gCb;
+KwAmUl *amUl;
+KwSeg *seg;
+#endif
+{
+ U16 newExpSo; /* The new expected SO */
+ KwSn sn = seg->amHdr.sn;
+ Bool lstRcvd=FALSE;
+ KwAmRecBuf *recBuf = NULLP;
+
+ TRC2(kwAmmUpdExpByteSeg);
+
+
+ recBuf = kwUtlGetRecBuf(amUl->recBufLst, sn);
+ if ((recBuf == NULLP) || (recBuf && (seg->amHdr.so != recBuf->expSo)))
+ {
+ RETVOID;
+ }
+
+ newExpSo = seg->soEnd + 1;
+ recBuf->expSo = newExpSo;
+ //lstRcvd = seg->amHdr.lsf;
+ if(seg->amHdr.si == 0x2)
+ {
+ lstRcvd = TRUE;
+ }
+ /* kw003.201 - This should update seg with the one after newSeg */
+ KW_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->amHdr.si == 0x2)
+ {
+ lstRcvd = TRUE;
+ }
+ if (seg->amHdr.so == newExpSo)
+ {
+ newExpSo = seg->soEnd + 1;
+ recBuf->expSo = newExpSo;
+ //lstRcvd = seg->amHdr.lsf;
+ KW_LLIST_NEXT_SEG(recBuf->segLst, seg);
+ }
+ else
+ {
+ recBuf->expSo = newExpSo;
+ RETVOID;
+ }
+ }
+ if (lstRcvd == TRUE)
+ {
+ recBuf->allRcvd = TRUE;
+ gRlcStats.amRlcStats.numRlcAmCellSduRx++;
+ }
+
+ RETVOID;
+}
+
+/**
+ * @brief
+ * Function to release/free the Acknowledged Mode Module RbCb buffers
+ *
+ * @details
+ * This primitive Frees the AM 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 kwAmmFreeUlRbCb
+(
+KwCb *gCb,
+KwUlRbCb *rbCb
+)
+#else
+PUBLIC Void kwAmmFreeUlRbCb(gCb,rbCb)
+KwCb *gCb;
+KwUlRbCb *rbCb;
+#endif
+{
+ KwSn curSn = 0; /* Sequence number of PDU */
+ KwSn windSz; /* PDU window size */
+ KwAmRecBuf *recBuf = NULLP;
+
+ TRC2(kwAmmFreeUlRbCb)
+
+
+ windSz = (KW_AM_GET_WIN_SZ(rbCb->m.amUl.snLen)) << 1;
+
+ if(TRUE == kwChkTmr(gCb,(PTR)rbCb,KW_EVT_AMUL_REORD_TMR))
+ {
+ kwStopTmr(gCb,(PTR)rbCb, KW_EVT_AMUL_REORD_TMR);
+ }
+ if(TRUE == kwChkTmr(gCb,(PTR)rbCb,KW_EVT_AMUL_STA_PROH_TMR))
+ {
+ kwStopTmr(gCb,(PTR)rbCb, KW_EVT_AMUL_STA_PROH_TMR);
+ }
+
+
+ /* on the first loop winSz is always greater than zero
+ while( ( curSn < windSz ) hence changing to do while */
+ do
+ {
+ recBuf = kwUtlGetRecBuf(rbCb->m.amUl.recBufLst, curSn);
+ if ( recBuf != NULLP )
+ {
+ if (recBuf->pdu != NULLP)
+ {
+ KW_FREE_BUF_WC(recBuf->pdu);
+ }
+ /* Release all the segments */
+ kwAmmUlRlsAllSegs(gCb,recBuf);
+ kwUtlDelRecBuf(rbCb->m.amUl.recBufLst, recBuf, gCb);
+ }
+ curSn++;
+ }while ( curSn < windSz );
+
+#ifndef LTE_TDD
+ KW_FREE_WC(gCb,rbCb->m.amUl.recBufLst, (KW_RCV_BUF_BIN_SIZE * sizeof(CmLListCp)));
+ rbCb->m.amUl.recBufLst = NULLP;
+#endif
+
+ if(rbCb->m.amUl.partialSdu != NULLP)
+ {
+ KW_FREE_BUF_WC(rbCb->m.amUl.partialSdu);
+ }
+ RETVOID;
+} /* kwAmmFreeUlRbCb */
+
+
+/*@}*/
+
+\f
+/********************************************************************30**
+
+ End of file
+**********************************************************************/