1 /*******************************************************************************
2 ################################################################################
3 # Copyright (c) [2017-2019] [Radisys] #
5 # Licensed under the Apache License, Version 2.0 (the "License"); #
6 # you may not use this file except in compliance with the License. #
7 # You may obtain a copy of the License at #
9 # http://www.apache.org/licenses/LICENSE-2.0 #
11 # Unless required by applicable law or agreed to in writing, software #
12 # distributed under the License is distributed on an "AS IS" BASIS, #
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
14 # See the License for the specific language governing permissions and #
15 # limitations under the License. #
16 ################################################################################
17 *******************************************************************************/
19 /**********************************************************************
25 Desc: Source code for RLC Unacknowledged mode assembly and
26 reassembly.This file contains following functions
31 --rlcUmmReAssembleSdus
36 **********************************************************************/
39 * @brief RLC Unacknowledged Mode downlink module
42 /* header (.h) include files */
43 #include "common_def.h"
44 #include "ckw.h" /* RRC layer */
45 #include "lkw.h" /* RRC layer */
46 #include "kwu.h" /* RLC service user */
47 #include "lkw.h" /* LM Interface */
48 #include "rgu.h" /* MAC layer */
49 #include "kw_env.h" /* RLC environment options */
51 #include "kw.h" /* RLC layer */
57 /* header/extern include files (.x) */
59 #include "ckw.x" /* RRC layer */
60 #include "kwu.x" /* RLC service user */
61 #include "lkw.x" /* LM Interface */
62 #include "rgu.x" /* MAC later */
64 #include "kw.x" /* RLC layer */
68 #define RLC_MODULE (RLC_DBGMASK_UM | RLC_DBGMASK_DL)
70 /* variables for logging :declared in BRDCM cl */
72 uint32_t dldrops_kwu_um;
78 static void rlcUmmEstHdrSz ARGS ((RlcUmDl *umUl));
80 static Void rlcUmmCreatePdu ARGS ((RlcCb *gCb,
84 KwPduInfo *datReqPduInfo));
86 /** @addtogroup ummode */
90 * @brief Handler to queue a SDU in the SDU queue, update BO and report
91 * it to the lower layer.
94 * This function is used to queue the received SDU in the
95 * SDU queue maintained in the radio bearer control block.
96 * After queuing the SDU, BO is updated and is reported
99 * @param[in] gCb RLC Instance control block
100 * @param[in] rbCb RB control block
101 * @param[in] datReq Ptr to the datReq sent from PDCP
102 * @param[in] mBuf Sdu data
106 void rlcUmmQSdu(RlcCb *gCb, RlcDlRbCb *rbCb, KwuDatReqInfo *datReq, Buffer *mBuf)
108 MsgLen len; /* SDU buffer length */
109 RlcSdu *sdu; /* SDU */
111 RLC_UPD_L2_DL_TOT_SDU_STS(gCb,rbCb);
113 RLC_ALLOC_WC(gCb, sdu, (Size)sizeof(RlcSdu));
114 #if (ERRCLASS & ERRCLS_ADD_RES)
117 DU_LOG("\nRLC : Memory allocation failed in rlcUmmQSdu for UEID:%d CELLID:%d",\
120 ODU_PUT_MSG_BUF(mBuf);
123 #endif /* ERRCLASS & ERRCLS_ADD_RES */
125 /* Discard new changes starts */
126 rlcUtlGetCurrTime(&sdu->arrTime);
127 /* Discard new changes ends */
128 ODU_GET_MSG_LEN(mBuf,&len);
133 sdu->mode.um.sduId = datReq->sduId;
134 sdu->mode.um.isSegmented = FALSE;
135 #ifndef RGL_SPECIFIC_CHANGES
144 rbCb->m.umDl.bo += len;
146 cmLListAdd2Tail(&(rbCb->m.umDl.sduQ), &sdu->lstEnt);
147 sdu->lstEnt.node = (PTR)sdu;
149 rlcUmmEstHdrSz(&rbCb->m.umDl);
151 if(!rlcDlUtlIsReestInProgress(rbCb))
153 rlcUtlSendDedLcBoStatus(gCb,rbCb,rbCb->m.umDl.bo,rbCb->m.umDl.estHdrSz,FALSE,0);
156 /* kw005.201 added support for L2 Measurement */
157 #ifdef LTE_L2_MEAS_RLC
158 /* Update numActUe if it is not active */
159 if((rbCb->rbL2Cb.measOn & LKW_L2MEAS_ACT_UE) &&
160 (rbCb->ueCb->numActRb[rbCb->qci]++ == 0))
162 rlcCb.rlcL2Cb.numActUe[rbCb->qci]++;
171 * @brief Handler to form PDU(s) and update the BO.
174 * -# This function forms pdu(s) from the SDU(s) in the
175 * SDU queue and returns them.
176 * -# This function also updates the BO along with the
177 * along with the estimated Header size.
179 * @param[in] rbCb RB control block
180 * @param[out] pduInfo Pdu Info to be filled and the PDU size to be
181 * formed and the updated BO
182 * @param[in] pduSz The size for which PDUs have to constructed
185 * -# ROK In case of success
186 * -# RFAILED If allocation of Sdu fails
188 void rlcUmmProcessSdus(RlcCb *gCb, RlcDlRbCb *rbCb, RlcDatReq *datReq)
190 CmLList *firstNode; /* First Node in SDU queue */
191 uint8_t fi=0; /* Framing Info */
192 Buffer *pdu; /* Buffer for holding the formed PDU */
193 KwPduInfo *pduInfo; /* PDU Info pointer */
194 int16_t pduSz; /* PDU Size to be constructed */
196 /* kw005.201 added support for L2 Measurement */
198 RlcContSduLst contSduLst; /*Contained sduLst */
199 int32_t dataVol = rbCb->m.umDl.bo;
200 uint32_t* totMacGrant= &(datReq->totMacGrant);
201 RlcL2MeasDlIpTh *dlIpThPut = &rbCb->l2MeasIpThruput.dlIpTh;
202 uint8_t *sduIdx = &dlIpThPut->lastSduIdx;
205 RlclchInfo lchInfo = {0};
206 uint32_t segSduCnt = 0;
209 int16_t timeDiff = 0;
214 pduInfo = &(datReq->pduInfo);
215 pduSz = datReq->pduSz;
218 contSduLst.numSdus = 0;
219 contSduLst.lcId = rbCb->lch.lChId;
220 oldBo = rbCb->m.umDl.bo;
221 lchInfo.lcId = rbCb->lch.lChId;
225 /* Discard new changes starts */
226 rlcUtlGetCurrTime(&curTime);
229 while ((pduSz > 0) && (rbCb->m.umDl.sduQ.count > 0) &&
230 (rbCb->m.umDl.numLi < RLC_MAX_DL_LI) && (pduInfo->numPdu < RLC_MAX_PDU))
232 CM_LLIST_FIRST_NODE(&rbCb->m.umDl.sduQ,firstNode);
233 sdu = (RlcSdu *)(firstNode->node);
235 if ((sdu->mode.um.isSegmented == FALSE) && (rbCb->discTmrInt > 0) &&
236 (rbCb->rlcId.rbType == CM_LTE_DRB))
238 timeDiff = RLC_TIME_DIFF(curTime,sdu->arrTime);
240 if (timeDiff >= rbCb->discTmrInt)
243 RLC_UPD_L2_DL_DISC_SDU_STS(gCb, rbCb);
245 rbCb->m.umDl.bo -= sdu->sduSz;
246 RLC_RMV_SDU(gCb,&rbCb->m.umDl.sduQ,sdu);
250 /* When forming a new PDU, pdu == NULLP
251 -# Eliminate MAC header size for each pdu
252 -# Initialize the li array to 0
253 -# Substract the fixed header length based on SN length
260 RLC_RMV_MAC_HDR_SZ(pduSz);
262 /* account for the RLC header size */
263 pduSz -= rbCb->m.umDl.snLen;
265 /* kw005.201 fixing pduSz <= 0 problem, ccpu00119417 */
271 rbCb->m.umDl.numLi = 0;
272 if (sdu->mode.um.isSegmented == TRUE)
282 rlcUtlCalcLiForSdu(gCb,rbCb->m.umDl.numLi,sdu->sduSz,&pduSz);
284 /* Exact fit scenario :
285 If the SDU size matches with the PDU size
286 -# Allocate memory equal to PDU size;
288 -# Remove SDu from queue
289 -# Append to already existing PDU portion if present .
290 -# Add Header and create complete PDU and place it in
293 if (sdu->sduSz == pduSz)
302 SCatMsg(pdu, sdu->mBuf, M1M2);
305 rbCb->m.umDl.bo -= pduSz;
309 if(RLC_MEAS_IS_DL_ANY_MEAS_ON_FOR_RB(gCb,rbCb))
311 if(sdu->mode.um.isSegmented)
313 *sduIdx = dlIpThPut->lastSduIdx;
317 RLC_GETSDUIDX(*sduIdx);
320 rlcUtlUpdateContainedSduLst(*sduIdx, &contSduLst);
321 rlcUtlUpdateOutStandingSduLst(dlIpThPut, *sduIdx, sdu->actSz,
322 sdu->mode.um.sduId, newIdx);
324 if ( lchInfo.numSdus < RLC_L2MEAS_SDUIDX)
326 lchInfo.sduInfo[lchInfo.numSdus].arvlTime = sdu->arrTime;
327 lchInfo.sduInfo[lchInfo.numSdus].isRetxPdu = FALSE;
332 /* kw005.201 added support for L2 Measurement */
333 #ifdef LTE_L2_MEAS_RLC
334 rlcUtlUpdSduSnMap(rbCb, sdu, datReq, TRUE);
335 if((rbCb->rbL2Cb.measOn & LKW_L2MEAS_DL_DELAY) ||
336 (rbCb->rbL2Cb.measOn & LKW_L2MEAS_UU_LOSS))
339 if ( lchInfo.numSdus < RLC_L2MEAS_SDUIDX)
341 lchInfo.arvlTime[lchInfo.numSdus] = sdu->arrTime;
345 #endif /* LTE_L2_MEAS */
346 RLC_RMV_SDU(gCb,&(rbCb->m.umDl.sduQ),sdu); /* kw003.201 */
347 rlcUtlIncrementKwuStsSduTx(gCb->u.dlCb->rlcKwuDlSap + rbCb->k1wuSapId);
349 rlcUmmCreatePdu(gCb,rbCb,pdu,fi,pduInfo);
353 /* Concatenation scenario :
354 If SDU size is less than the requested PDU size
355 -# Allocate memory and copy SDU into it.
357 -# Remove SDU from the Queue.
358 -# Append to already existing PDU portion if present .
359 -# If the SDU size is greater than 2047 or the number of i
360 LIs reaches max, place it as a separate PDU in pduInfo and
363 place the msglen in li array and continue with the next SDU.
364 -# If the number of PDUs is more than RLC_MAX_PDU, return from
365 the function even if pduSize > 0.
367 else if (sdu->sduSz < pduSz)
376 ODU_CAT_MSG(pdu, sdu->mBuf ,M1M2);
378 rbCb->m.umDl.bo -= sdu->sduSz;
381 /* kw005.201 added support for L2 Measurement */
382 #ifdef LTE_L2_MEAS_RLC
383 rlcUtlUpdSduSnMap(rbCb, sdu, datReq, TRUE);
384 #endif /* LTE_L2_MEAS */
385 if (sdu->sduSz < 2048 && rbCb->m.umDl.numLi < RLC_MAX_DL_LI)
387 rbCb->m.umDl.li[(rbCb->m.umDl.numLi)++] = sdu->sduSz;
391 rlcUmmCreatePdu(gCb, rbCb, pdu, fi, pduInfo);
394 if ( pduInfo->numPdu == RLC_MAX_PDU)
396 /* Could not transmit what MAC asked for because the number
397 * of PDUs to be transmitted has reached maximum. */
398 DU_LOG("\nRLC: rlcUmmProcessSdus: Maximum Pdu limit has been reached\
399 UEID:%d CELLID:%d", rbCb->rlcId.ueId, rbCb->rlcId.cellId);
404 if(RLC_MEAS_IS_DL_ANY_MEAS_ON_FOR_RB(gCb,rbCb) )
406 if(sdu->mode.um.isSegmented)
408 *sduIdx = dlIpThPut->lastSduIdx;
412 RLC_GETSDUIDX(*sduIdx);
415 rlcUtlUpdateContainedSduLst(*sduIdx, &contSduLst);
416 rlcUtlUpdateOutStandingSduLst(dlIpThPut, *sduIdx, sdu->actSz,
417 sdu->mode.um.sduId, newIdx);
419 if ( lchInfo.numSdus < RLC_L2MEAS_SDUIDX)
421 lchInfo.sduInfo[lchInfo.numSdus].arvlTime = sdu->arrTime;
422 lchInfo.sduInfo[lchInfo.numSdus].isRetxPdu = FALSE;
427 RLC_RMV_SDU(gCb,&(rbCb->m.umDl.sduQ),sdu);
428 /* kw005.201 ccpu00117318, updating the statistics */
429 rlcUtlIncrementKwuStsSduTx(gCb->u.dlCb->rlcKwuDlSap + rbCb->k1wuSapId);
431 /* Segmentation scenario :
432 If size of SDU is greater than PDU size
433 -# Allocate memory and Segment the Sdu.
435 -# Append to already existing PDU if any.
436 -# Set the second bit of the framing info.
437 -# Create the complete PDU and place in pduInfo.
443 ODU_SEGMENT_MSG(sdu->mBuf,pduSz,&remSdu);
446 if(RLC_MEAS_IS_DL_IP_MEAS_ON_FOR_RB(gCb, rbCb))
448 if(sdu->mode.um.isSegmented)
450 *sduIdx = dlIpThPut->lastSduIdx;
454 RLC_GETSDUIDX(*sduIdx);
457 rlcUtlUpdateContainedSduLst(*sduIdx, &contSduLst);
458 rlcUtlUpdateOutStandingSduLst(dlIpThPut, *sduIdx, sdu->actSz,
459 sdu->mode.um.sduId, newIdx);
461 if(RLC_MEAS_IS_DL_UU_LOSS_MEAS_ON_FOR_RB(gCb,rbCb))
463 if(sdu->actSz == sdu->sduSz)
475 ODU_CAT_MSG(pdu, sdu->mBuf, M1M2);
476 ODU_PUT_MSG_BUF(sdu->mBuf);
480 rbCb->m.umDl.bo -= pduSz;
481 sdu->mode.um.isSegmented = TRUE;
486 /* kw005.201 added support for L2 Measurement */
487 #ifdef LTE_L2_MEAS_RLC
488 rlcUtlUpdSduSnMap(rbCb, sdu, datReq, FALSE);
489 #endif /* LTE_L2_MEAS */
491 rlcUmmCreatePdu(gCb,rbCb,pdu,fi,pduInfo);
494 /* kw005.201 added support for L2 Measurement */
496 #ifdef LTE_L2_MEAS_RLC
497 if((rbCb->rbL2Cb.measOn) &&
498 (rbCb->m.umDl.sduQ.count == 0) &&
501 if(--(rbCb->ueCb->numActRb[rbCb->qci]) == 0)
503 rlcCb.rlcL2Cb.numActUe[rbCb->qci]--;
506 #endif /* LTE_L2_MEAS */
508 rlcUtlUpdateBurstSdus(gCb, rbCb, &contSduLst, dataVol, *totMacGrant);
509 /* Need to check into optimizing this code : TODO */
510 if(RLC_MEAS_IS_DL_ANY_MEAS_ON_FOR_RB(gCb,rbCb) && (lchInfo.numSdus != 0))
512 RlcL2MeasTb *l2MeasTb = rlcUtlGetCurMeasTb(gCb, rbCb);
514 /* Fix Klock warning */
515 if ((lchInfo.numSdus != 0) && (l2MeasTb != NULLP) &&
516 (l2MeasTb->numLchInfo < RLC_MAX_ACTV_DRB))
518 memcpy( &l2MeasTb->lchInfo[l2MeasTb->numLchInfo], &lchInfo, sizeof(RlclchInfo));
519 l2MeasTb->numLchInfo++;
521 l2MeasTb->txSegSduCnt += segSduCnt;
523 *totMacGrant -= (oldBo - rbCb->m.umDl.bo);
526 /* If we have a situation wherein the size requested is greater than the total size of SDUs
527 and a pdu buffer which is not null, this if loop helps to send
528 a non null PDU to the lower layer.
530 if (pduSz > 0 && pdu)
532 if (pduInfo->numPdu != RLC_MAX_PDU)
534 rbCb->m.umDl.numLi--;
535 rlcUmmCreatePdu(gCb,rbCb,pdu,fi,pduInfo);
540 ODU_PUT_MSG_BUF(pdu);
544 rlcUmmEstHdrSz(&rbCb->m.umDl);
545 datReq->boRep.bo = rbCb->m.umDl.bo;
546 datReq->boRep.estHdrSz = rbCb->m.umDl.estHdrSz;
547 datReq->boRep.staPduPrsnt = FALSE;
548 if (rbCb->m.umDl.sduQ.count > 0)
550 datReq->boRep.oldestSduArrTime =
551 ((RlcSdu *)(rbCb->m.umDl.sduQ.first->node))->arrTime;
557 * @brief Handler to process the re-establishment request received from i
561 * This function does the following functions :
562 * Remove all the SDUs in the SDU queue.
564 * @param[in] gCb RLC Instance control block
565 * @param[in] rlcID Identity of the RLC entity for which
566 * re-establishment is to be done
567 * @param[in] sendReEst Whether to send re-establishment complete
568 * indication to upper layer or not
569 * @param[in] rbCb RB control block for which re-establishment
574 Void rlcDlUmmReEstablish(RlcCb *gCb,CmLteRlcId rlcId,Bool sendReEst,RlcDlRbCb *rbCb)
576 /* The re-establishment indication is sent from the UL only */
578 rlcUmmFreeDlRbCb(gCb, rbCb);
580 rbCb->m.umDl.vtUs = 0;
582 /* this would have been set when re-establishment was triggered
584 rlcDlUtlResetReestInProgress(rbCb);
589 * @brief Handler to create the header and complete a PDU.
592 * This function is used to create the header of a PDU and concatenate it
593 * with the data part of the PDU.
594 * Also updates the statistics
595 * Sets the passed pdu to NULLP
597 * @param[in] gCb RLC instance control block
598 * @param[in,out] rbCb RB control block
600 * @param[in] fi Framing Info field
601 * @param[out] datReqPduInfo Holder in which to copy the created PDU pointer
605 static void rlcUmmCreatePdu(RlcCb *gCb, RlcDlRbCb *rbCb, Buffer *pdu, uint8_t fi, KwPduInfo *datReqPduInfo)
607 RlcSn sn; /* Sequence Number */
608 uint32_t liCount; /* LI count */
609 uint8_t e = 0; /* Extension Bit */
610 uint32_t count; /* Loop Counter */
613 /* create a big array to store the header, assuming 3 bytes per 2 L1s
614 * (2 bytes if only a single LI) and 2 bytes for the
616 * size of header = ( NumLi /2 ) * 3 + (NumLi % 2) * 2 + 2;
617 * where NumLi = Number of Length Indicators to be sent
619 uint8_t hdr[((RLC_MAX_DL_LI >> 1) * 3) + ((RLC_MAX_DL_LI & 0x01) << 1) + 2];
620 uint32_t idx = 0; /* To index to the hdr array */
622 /* Note: idx is not checked against crossing the hdr array bound as
623 * liCount will be < RLC_MAX_DL_LI and as per the size calculated above;
624 * idx cannot cross the array
627 /* stats updated before for bytes sent before adding RLC headers */
628 rlcUtlIncrementGenStsBytesAndPdusSent(&gCb->genSts, pdu);
630 sn = rbCb->m.umDl.vtUs;
631 liCount = rbCb->m.umDl.numLi;
633 if(liCount > RLC_MAX_DL_LI)
634 liCount = RLC_MAX_DL_LI;
636 /* if there are any LI's then set the first E bit */
642 if (rbCb->m.umDl.snLen == 1)
644 hdr[idx++] = (fi << 6) | (e << 5) | sn;
646 else /* SN length is 2 */
648 /* SN length is 10 bits */
649 hdr[idx] = (fi << 3) | (e << 2) | (sn >> 8);
650 hdr[++idx] = sn & 0xff;
655 for (count = 0;count < liCount;count++)
657 /* In each iteration we try and encode 2 LIs */
658 /* if this is the last LI then e should be 0 */
659 if(count == liCount - 1)
664 /* ccpu00135170 Fixing KLOCK warning */
665 if((idx + 1)>= hdrSz)
669 /* odd LI, 1st , 3rd etc */
670 hdr[idx] = (e << 7) | (rbCb->m.umDl.li[count] >> 4);
671 hdr[++idx] = (rbCb->m.umDl.li[count] & 0xf) << 4;
674 if(count == liCount - 1)
678 else if(count >= liCount)
682 /* ccpu00135170 Fixing KLOCK warning */
683 if((idx + 1)>= hdrSz)
687 /* even one, 2nd , 4th etc LI's, count starts at 0 */
688 hdr[idx] |= ((e << 3) | (rbCb->m.umDl.li[count] >> 8));
689 hdr[++idx] = rbCb->m.umDl.li[count] & 0xff;
693 /* if odd number of L1s increment idx */
699 /* increment VT(US) */
700 rbCb->m.umDl.vtUs = (rbCb->m.umDl.vtUs + 1) & rbCb->m.umDl.modBitMask;
702 /* add the header to the beginning of the pdu */
703 ODU_ADD_PRE_MSG_MULT_IN_ORDER(hdr, idx, pdu);
705 datReqPduInfo->mBuf[datReqPduInfo->numPdu++] = pdu;
710 * @brief Handler to estimate the header size of the RLC SDUs
711 * present in the SDU queue.
714 * This function is used to update the estimated header size variable in RB.
715 * This function is called when a SDU is queued and when a PDU is formed and
716 * sent to the lower layer.
718 * @param[in] umDl UM mode downlink control block
722 static void rlcUmmEstHdrSz(RlcUmDl *umDl)
724 /* The header size is estimated as :
725 If sdu count = 0 then 0
726 else sdu count * 2 + 1; the 1 is added for the FI and SN byte;
729 umDl->estHdrSz = (umDl->sduQ.count)?((umDl->sduQ.count << 1) + 1) : 0;
735 * @brief Handler to discard a SDU.
738 * This function is used to discard a SDU after receiving
739 * the Discard Request from the upper layer.The SDU is discarded if
740 * it is present and is not mapped to any PDU yet.
741 * The discards coming from the upper layer would be coming in
742 * sequence according to the sduId, so we should find the sduId at the
743 * head of the sduQ. Discards if there is a match else does nothing.
745 * @param[in] rbCb RB control block
746 * @param[in] sduId SDU ID of the SDU to be discarded
750 Void rlcUmmDiscSdu(RlcCb *gCb,RlcDlRbCb *rbCb,uint32_t sduId)
752 CmLList *tmpNode; /* Temporary Node in SDU queue */
753 CM_LLIST_FIRST_NODE(&rbCb->m.umDl.sduQ,tmpNode);
757 RlcSdu *sdu = (RlcSdu *)tmpNode->node;
759 if (sdu->mode.um.sduId == sduId && sdu->mode.um.isSegmented == FALSE)
761 /* kw005.201 added support for L2 Measurement */
762 RLC_RMV_SDU(gCb,&rbCb->m.umDl.sduQ,sdu);
763 gCb->genSts.numSduDisc++;
773 * function to free/release the UnAcknowledged mode RBCB buffers
776 * This primitive Frees the Unacknowldged Mode RbCb sdu queue
778 * @param [in] gCb - RLC instance control block
779 * @param [in] rbCb - RB Control Block
783 Void rlcUmmFreeDlRbCb(RlcCb *gCb,RlcDlRbCb *rbCb)
786 /* cat the SDU queue to the to be freed list */
787 cmLListCatLList(&(gCb->u.dlCb->toBeFreed.sduLst),&(rbCb->m.umDl.sduQ));
788 rlcUtlRaiseDlCleanupEvent(gCb);
791 } /* rlcUmmFreeDlRbCb */
793 /********************************************************************30**
795 **********************************************************************/