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: C source code for scheduler fucntions
29 **********************************************************************/
32 @brief This file implements the schedulers main access to MAC layer code.
34 #include "common_def.h"
35 #include "du_app_mac_inf.h"
40 #include "rg_sch_inf.h"
43 #include "tfu.x" /* TFU types */
44 #include "lrg.x" /* layer management typedefs for MAC */
45 #include "rgr.x" /* layer management typedefs for MAC */
46 #include "rg_sch_inf.x" /* typedefs for Scheduler */
47 #include "mac_sch_interface.h"
50 #include "sch_utils.h"
52 #include "sch_slice_based.h"
55 * @brief Scheduler All Apis initialized.
59 * Function : schAllApisInit
61 * This function initializes all Scheduler APIs/functionality for each kind
64 * @param[in] Inst inst, the Scheduler instance
67 void schAllApisInit(Inst inst)
69 schFcfsAllApisInit(&schCb[inst].allApis[SCH_FCFS]);
70 schSliceBasedAllApisInit(&schCb[inst].allApis[SCH_SLICE_BASED]);
74 * @brief Scheduler instance Configuration Handler.
78 * Function : SchInstCfg
80 * This function in called by SchProcGenCfgReq(). It handles the
81 * general configurations of the scheduler instance. Returns
82 * reason for success/failure of this function.
84 * @param[in] RgCfg *cfg, the Configuaration information
86 * -# LCM_REASON_NOT_APPL
87 * -# LCM_REASON_INVALID_MSGTYPE
88 * -# LCM_REASON_MEM_NOAVAIL
90 uint8_t SchInstCfg(RgCfg *cfg, Inst dInst)
92 uint16_t ret = LCM_REASON_NOT_APPL;
93 Inst inst = (dInst - SCH_INST_START);
95 DU_LOG("\nDEBUG --> SCH : Entered SchInstCfg()");
96 /* Check if Instance Configuration is done already */
97 if (schCb[inst].schInit.cfgDone == TRUE)
99 return LCM_REASON_INVALID_MSGTYPE;
101 /* Update the Pst structure for LM interface */
102 memcpy(&schCb[inst].schInit.lmPst, &cfg->s.schInstCfg.genCfg.lmPst, sizeof(Pst));
104 schCb[inst].schInit.inst = inst;
105 schCb[inst].schInit.lmPst.srcProcId = schCb[inst].schInit.procId;
106 schCb[inst].schInit.lmPst.srcEnt = schCb[inst].schInit.ent;
107 schCb[inst].schInit.lmPst.srcInst = schCb[inst].schInit.inst +
109 schCb[inst].schInit.lmPst.event = EVTNONE;
111 schCb[inst].schInit.region = cfg->s.schInstCfg.genCfg.mem.region;
112 schCb[inst].schInit.pool = cfg->s.schInstCfg.genCfg.mem.pool;
114 schCb[inst].genCfg.forceCntrlSrbBoOnPCel = cfg->s.schInstCfg.genCfg.forceCntrlSrbBoOnPCel;
115 schCb[inst].genCfg.isSCellActDeactAlgoEnable = cfg->s.schInstCfg.genCfg.isSCellActDeactAlgoEnable;
117 schCb[inst].genCfg.startCellId = cfg->s.schInstCfg.genCfg.startCellId;
119 schCb[inst].schTimersInfo.tmrRes = cfg->s.schInstCfg.genCfg.tmrRes;
120 /* Initialzie the timer queue */
121 memset(&schCb[inst].schTimersInfo.tmrTq, 0, sizeof(CmTqType) * SCH_TQ_SIZE);
122 /* Initialize the timer control point */
123 memset(&schCb[inst].schTimersInfo.tmrTqCp, 0, sizeof(CmTqCp));
124 schCb[inst].schTimersInfo.tmrTqCp.tmrLen = SCH_TQ_SIZE;
126 /* SS_MT_TMR needs to be enabled as schActvTmr needs instance information */
127 /* Timer Registration request to system services */
128 if (ODU_REG_TMR_MT(schCb[inst].schInit.ent, dInst, (int)schCb[inst].schTimersInfo.tmrRes, schActvTmr) != ROK)
130 DU_LOG("\nERROR --> SCH : SchInstCfg(): Failed to register timer.");
131 return (LCM_REASON_MEM_NOAVAIL);
134 /* Initialize statistics related configurations */
135 memset(&schCb[inst].statistics, 0, sizeof(SchStatistics));
136 cmLListInit(&schCb[inst].statistics.activeKpiList.dlTotPrbUseList);
137 cmLListInit(&schCb[inst].statistics.activeKpiList.ulTotPrbUseList);
139 /* Set Config done in TskInit */
140 schCb[inst].schInit.cfgDone = TRUE;
141 DU_LOG("\nINFO --> SCH : Scheduler gen config done");
143 schAllApisInit(inst);
148 * @brief Layer Manager Configuration request handler.
152 * Function : SchProcGenCfgReq
154 * This function handles the configuration
155 * request received at scheduler instance from the Layer Manager.
156 * -# Based on the cfg->hdr.elmId.elmnt value it invokes one of the
157 * functions rgHdlGenCfg() or rgHdlSapCfg().
158 * -# Invokes RgMiLrgSchCfgCfm() to send back the confirmation to the LM.
160 * @param[in] Pst *pst, the post structure
161 * @param[in] RgMngmt *cfg, the configuration parameter's structure
165 uint8_t SchProcGenCfgReq(Pst *pst, RgMngmt *cfg)
167 uint8_t ret = LCM_PRIM_OK;
168 uint16_t reason = LCM_REASON_NOT_APPL;
172 if(pst->dstInst < SCH_INST_START)
174 DU_LOG("\nERROR --> SCH : Invalid inst ID");
175 DU_LOG("\nERROR --> SCH : SchProcGenCfgReq(): "
176 "pst->dstInst=%d SCH_INST_START=%d", pst->dstInst,SCH_INST_START);
179 DU_LOG("\nINFO --> SCH : Received scheduler gen config");
180 /* Fill the post structure for sending the confirmation */
181 memset(&cfmPst, 0 , sizeof(Pst));
182 SchFillCfmPst(pst, &cfmPst, cfg);
184 memset(&cfm, 0, sizeof(RgMngmt));
191 cfm.hdr.elmId.elmnt = cfg->hdr.elmId.elmnt;
192 switch(cfg->hdr.elmId.elmnt)
195 reason = SchInstCfg(&cfg->t.cfg,pst->dstInst );
199 reason = LCM_REASON_INVALID_ELMNT;
200 DU_LOG("\nERROR --> SCH : Invalid Elmnt=%d", cfg->hdr.elmId.elmnt);
204 if (reason != LCM_REASON_NOT_APPL)
209 cfm.cfm.status = ret;
210 cfm.cfm.reason = reason;
212 SchSendCfgCfm(&cfmPst, &cfm);
213 /* SCH_FREE(pst->region, pst->pool, (Data *)cfg, sizeof(RgMngmt)); */
216 }/*-- SchProcGenCfgReq --*/
220 *@brief Returns TDD periodicity in micro seconds
224 * Function : schGetPeriodicityInMsec
226 * This API retunrs TDD periodicity in micro seconds
228 * @param[in] DlUlTxPeriodicity
229 * @return periodicityInMsec
232 uint16_t schGetPeriodicityInMsec(DlUlTxPeriodicity tddPeriod)
234 uint16_t periodicityInMsec = 0;
237 case TX_PRDCTY_MS_0P5:
239 periodicityInMsec = 500;
242 case TX_PRDCTY_MS_0P625:
244 periodicityInMsec = 625;
249 periodicityInMsec = 1000;
252 case TX_PRDCTY_MS_1P25:
254 periodicityInMsec = 1250;
259 periodicityInMsec = 2000;
262 case TX_PRDCTY_MS_2P5:
264 periodicityInMsec = 2500;
269 periodicityInMsec = 5000;
272 case TX_PRDCTY_MS_10:
274 periodicityInMsec = 10000;
279 DU_LOG("\nERROR --> SCH : Invalid DlUlTxPeriodicity:%d", tddPeriod);
283 return periodicityInMsec;
287 *@brief Fills the slotCfg from CellCfg
291 * Function : schFillSlotConfig
293 * This API Fills the slotCfg from CellCfg
295 * @param[in] SchCellCb *cell, TDDCfg tddCfg
298 void schFillSlotConfig(SchCellCb *cell, TDDCfg tddCfg)
300 uint8_t slotIdx = 0, symbolIdx = 0;
302 for(slotIdx =0 ;slotIdx < MAX_TDD_PERIODICITY_SLOTS; slotIdx++)
304 for(symbolIdx = 0; symbolIdx < MAX_SYMB_PER_SLOT; symbolIdx++)
306 /*Fill Full-DL Slots as well as DL symbols ini 1st Flexi Slo*/
307 if(slotIdx < tddCfg.nrOfDlSlots || \
308 (slotIdx == tddCfg.nrOfDlSlots && symbolIdx < tddCfg.nrOfDlSymbols))
310 cell->slotCfg[slotIdx][symbolIdx] = DL_SYMBOL;
313 /*Fill Full-FLEXI SLOT and as well as Flexi Symbols in 1 slot preceding FULL-UL slot*/
314 else if(slotIdx < (MAX_TDD_PERIODICITY_SLOTS - tddCfg.nrOfUlSlots -1) || \
315 (slotIdx == (MAX_TDD_PERIODICITY_SLOTS - tddCfg.nrOfUlSlots -1) && \
316 symbolIdx < (MAX_SYMB_PER_SLOT - tddCfg.nrOfUlSymbols)))
318 cell->slotCfg[slotIdx][symbolIdx] = FLEXI_SYMBOL;
320 /*Fill Partial UL symbols and Full-UL slot*/
323 cell->slotCfg[slotIdx][symbolIdx] = UL_SYMBOL;
330 * @brief init TDD slot config
334 * Function : schInitTddSlotCfg
336 * This API is invoked after receiving schCellCfg
338 * @param[in] schCellCb *cell
339 * @param[in] SchCellCfg *schCellCfg
342 void schInitTddSlotCfg(SchCellCb *cell, SchCellCfg *schCellCfg)
344 uint16_t periodicityInMicroSec = 0;
345 int8_t slotIdx, symbIdx;
347 periodicityInMicroSec = schGetPeriodicityInMsec(schCellCfg->tddCfg.tddPeriod);
348 cell->numSlotsInPeriodicity = (periodicityInMicroSec * pow(2, cell->numerology))/1000;
349 cell->slotFrmtBitMap = 0;
350 schFillSlotConfig(cell, schCellCfg->tddCfg);
351 for(slotIdx = cell->numSlotsInPeriodicity-1; slotIdx >= 0; slotIdx--)
354 /* If the first and last symbol are the same, the entire slot is the same type */
355 if((cell->slotCfg[slotIdx][symbIdx] == cell->slotCfg[slotIdx][MAX_SYMB_PER_SLOT-1]) &&
356 cell->slotCfg[slotIdx][symbIdx] != FLEXI_SYMBOL)
358 switch(cell->slotCfg[slotIdx][symbIdx])
362 /*BitMap to be set to 00 */
363 cell->slotFrmtBitMap = (cell->slotFrmtBitMap<<2);
368 /*BitMap to be set to 01 */
369 cell->slotFrmtBitMap = ((cell->slotFrmtBitMap<<2) | (UL_SLOT));
373 DU_LOG("\nERROR --> SCH : Invalid slot Config in schInitTddSlotCfg");
377 /* slot config is flexible. First set slotBitMap to 10 */
378 cell->slotFrmtBitMap = ((cell->slotFrmtBitMap<<2) | (FLEXI_SLOT));
384 * @brief Fill SSB start symbol
388 * Function : fillSsbStartSymb
390 * This API stores SSB start index per beam
392 * @param[in] SchCellCb *cellCb
397 void fillSsbStartSymb(SchCellCb *cellCb)
399 uint8_t cnt, scs, symbIdx, ssbStartSymbArr[SCH_MAX_SSB_BEAM];
401 scs = cellCb->cellCfg.ssbScs;
403 memset(ssbStartSymbArr, 0, sizeof(SCH_MAX_SSB_BEAM));
405 /* Determine value of "n" based on Section 4.1 of 3GPP TS 38.213 */
410 if(cellCb->cellCfg.ssbFrequency <= 300000)
411 cnt = 2;/* n = 0, 1 */
413 cnt = 4; /* n = 0, 1, 2, 3 */
414 for(uint8_t idx=0; idx<cnt; idx++)
416 /* start symbol determined using {2, 8} + 14n */
417 ssbStartSymbArr[symbIdx++] = 2 + MAX_SYMB_PER_SLOT*idx;
418 ssbStartSymbArr[symbIdx++] = 8 + MAX_SYMB_PER_SLOT*idx;
424 if(cellCb->cellCfg.ssbFrequency <= 300000)
427 cnt = 2; /* n = 0, 1 */
428 for(uint8_t idx=0; idx<cnt; idx++)
430 /* start symbol determined using {4, 8, 16, 20} + 28n */
431 ssbStartSymbArr[symbIdx++] = 4 + MAX_SYMB_PER_SLOT*idx;
432 ssbStartSymbArr[symbIdx++] = 8 + MAX_SYMB_PER_SLOT*idx;
433 ssbStartSymbArr[symbIdx++] = 16 + MAX_SYMB_PER_SLOT*idx;
434 ssbStartSymbArr[symbIdx++] = 20 + MAX_SYMB_PER_SLOT*idx;
439 DU_LOG("\nERROR --> SCH : SCS %d is currently not supported", scs);
441 memset(cellCb->ssbStartSymbArr, 0, sizeof(SCH_MAX_SSB_BEAM));
442 memcpy(cellCb->ssbStartSymbArr, ssbStartSymbArr, SCH_MAX_SSB_BEAM);
447 * @brief init cellCb based on cellCfg
451 * Function : schInitCellCb
453 * This API is invoked after receiving schCellCfg
455 * @param[in] schCellCb *cell
456 * @param[in] SchCellCfg *schCellCfg
461 uint8_t schInitCellCb(Inst inst, SchCellCfg *schCellCfg)
463 uint16_t scsInKhz = 0;
464 SchCellCb *cell= NULLP;
465 SCH_ALLOC(cell, sizeof(SchCellCb));
468 DU_LOG("\nERROR --> SCH : Memory allocation failed in schInitCellCb");
472 cell->cellId = schCellCfg->cellId;
473 cell->instIdx = inst;
474 scsInKhz = convertScsEnumValToScsVal(schCellCfg->ssbScs);
476 /*Ref : 3GPP 38.211 Table 4.2-1: SCS = (2 ^ numerology * 15kHz)*/
477 cell->numerology = log2(scsInKhz/BASE_SCS);
478 switch(cell->numerology)
480 case SCH_NUMEROLOGY_0:
482 cell->numSlots = SCH_MU0_NUM_SLOTS;
485 case SCH_NUMEROLOGY_1:
487 cell->numSlots = SCH_MU1_NUM_SLOTS;
490 case SCH_NUMEROLOGY_2:
492 cell->numSlots = SCH_MU2_NUM_SLOTS;
495 case SCH_NUMEROLOGY_3:
497 cell->numSlots = SCH_MU3_NUM_SLOTS;
500 case SCH_NUMEROLOGY_4:
502 cell->numSlots = SCH_MU4_NUM_SLOTS;
506 DU_LOG("\nERROR --> SCH : Numerology %d not supported", cell->numerology);
509 schInitTddSlotCfg(cell, schCellCfg);
512 SCH_ALLOC(cell->schDlSlotInfo, cell->numSlots * sizeof(SchDlSlotInfo*));
513 if(!cell->schDlSlotInfo)
515 DU_LOG("\nERROR --> SCH : Memory allocation failed in schInitCellCb for schDlSlotInfo");
519 SCH_ALLOC(cell->schUlSlotInfo, cell->numSlots * sizeof(SchUlSlotInfo*));
520 if(!cell->schUlSlotInfo)
522 DU_LOG("\nERROR --> SCH : Memory allocation failed in schInitCellCb for schUlSlotInfo");
526 for(uint8_t idx=0; idx<cell->numSlots; idx++)
528 SchDlSlotInfo *schDlSlotInfo;
529 SchUlSlotInfo *schUlSlotInfo;
532 SCH_ALLOC(schDlSlotInfo, sizeof(SchDlSlotInfo));
535 DU_LOG("\nERROR --> SCH : Memory allocation failed in schInitCellCb");
540 SCH_ALLOC(schUlSlotInfo, sizeof(SchUlSlotInfo));
543 DU_LOG("\nERROR --> SCH : Memory allocation failed in schInitCellCb");
547 schInitDlSlot(schDlSlotInfo);
548 schInitUlSlot(schUlSlotInfo);
550 cell->schDlSlotInfo[idx] = schDlSlotInfo;
551 cell->schUlSlotInfo[idx] = schUlSlotInfo;
554 cell->firstSsbTransmitted = false;
555 cell->firstSib1Transmitted = false;
556 fillSsbStartSymb(cell);
559 memset(cell->drxCb, 0, MAX_DRX_SIZE*sizeof(SchDrxCb));
561 schCb[inst].cells[inst] = cell;
563 DU_LOG("\nINFO --> SCH : Cell init completed for cellId:%d", cell->cellId);
569 * @brief Fill SIB1 configuration
573 * Function : fillSchSib1Cfg
575 * Fill SIB1 configuration
577 * @param[in] uint8_t bandwidth : total available bandwidth
578 * uint8_t numSlots : total slots per SFN
579 * SchSib1Cfg *sib1SchCfg : cfg to be filled
580 * uint16_t pci : physical cell Id
581 * uint8_t offsetPointA : offset
584 uint8_t fillSchSib1Cfg(uint8_t mu, uint8_t bandwidth, uint8_t numSlots,SchPdcchConfigSib1 *pdcchCfgSib1,\
585 SchSib1Cfg *sib1SchCfg, uint16_t pci, uint8_t offsetPointA, uint16_t sib1PduLen)
587 uint8_t coreset0Idx = 0;
588 uint8_t searchSpace0Idx = 0;
589 //uint8_t ssbMuxPattern = 0;
591 uint8_t numSymbols = 0;
594 //uint8_t numSearchSpacePerSlot = 0;
596 uint8_t firstSymbol = 0; /* need to calculate using formula mentioned in 38.213 */
597 uint8_t slotIndex = 0;
598 uint8_t FreqDomainResource[FREQ_DOM_RSRC_SIZE] = {0};
605 pdcch = &(sib1SchCfg->sib1PdcchCfg);
606 bwp = &(sib1SchCfg->bwp);
608 coreset0Idx = pdcchCfgSib1->coresetZeroIndex;
609 searchSpace0Idx = pdcchCfgSib1->searchSpaceZeroIndex;
611 /* derive the sib1 coreset0 params from table 13-1 spec 38.213 */
612 //ssbMuxPattern = coresetIdxTable[coreset0Idx][0];
613 numRbs = coresetIdxTable[coreset0Idx][1];
614 numSymbols = coresetIdxTable[coreset0Idx][2];
615 offset = coresetIdxTable[coreset0Idx][3];
617 /* derive the search space params from table 13-11 spec 38.213 */
618 oValue = searchSpaceIdxTable[searchSpace0Idx][0];
619 //numSearchSpacePerSlot = searchSpaceIdxTable[searchSpace0Idx][1];
620 mValue = searchSpaceIdxTable[searchSpace0Idx][2];
621 firstSymbol = searchSpaceIdxTable[searchSpace0Idx][3];
623 /* calculate the n0, need to add the formulae, as of now the value is 0
624 * Need to add the even and odd values of i during configuration
625 * [(O . 2^u + i . M ) ] mod numSlotsPerSubframe
626 * assuming u = 0, i = 0, numSlotsPerSubframe = 10
627 * Also, from this configuration, coreset0 is only on even subframe */
628 slotIndex = (int)((oValue*pow(2, mu)) + floor(ssbIdx*mValue))%numSlots;
629 sib1SchCfg->n0 = slotIndex;
634 case BANDWIDTH_20MHZ:
636 bwp->freqAlloc.numPrb = TOTAL_PRB_20MHZ_MU0;
639 case BANDWIDTH_100MHZ:
641 bwp->freqAlloc.numPrb = TOTAL_PRB_100MHZ_MU1;
645 DU_LOG("\nERROR --> SCH : Bandwidth %d not supported", bandwidth);
648 bwp->freqAlloc.startPrb = 0;
649 bwp->subcarrierSpacing = 0; /* 15Khz */
650 bwp->cyclicPrefix = 0; /* normal */
652 /* fill the PDCCH PDU */
653 pdcch->coresetCfg.coreSetSize = numRbs;
654 pdcch->coresetCfg.startSymbolIndex = firstSymbol;
655 pdcch->coresetCfg.durationSymbols = numSymbols;
657 /* Fill Bitmap for PRBs in coreset */
658 fillCoresetFeqDomAllocMap(((offsetPointA-offset)/6), (numRbs/6), FreqDomainResource);
659 covertFreqDomRsrcMapToIAPIFormat(FreqDomainResource, pdcch->coresetCfg.freqDomainResource);
661 pdcch->coresetCfg.cceRegMappingType = 1; /* coreset0 is always interleaved */
662 pdcch->coresetCfg.regBundleSize = 6; /* spec-38.211 sec 7.3.2.2 */
663 pdcch->coresetCfg.interleaverSize = 2; /* spec-38.211 sec 7.3.2.2 */
664 pdcch->coresetCfg.coreSetType = 0;
665 pdcch->coresetCfg.shiftIndex = pci;
666 pdcch->coresetCfg.precoderGranularity = 0; /* sameAsRegBundle */
668 pdcch->dci.rnti = SI_RNTI;
669 pdcch->dci.scramblingId = pci;
670 pdcch->dci.scramblingRnti = 0;
671 pdcch->dci.cceIndex = 0;
672 pdcch->dci.aggregLevel = 4;
673 pdcch->dci.beamPdcchInfo.numPrgs = 1;
674 pdcch->dci.beamPdcchInfo.prgSize = 1;
675 pdcch->dci.beamPdcchInfo.digBfInterfaces = 0;
676 pdcch->dci.beamPdcchInfo.prg[0].pmIdx = 0;
677 pdcch->dci.beamPdcchInfo.prg[0].beamIdx[0] = 0;
678 pdcch->dci.txPdcchPower.beta_pdcch_1_0= 0;
679 pdcch->dci.txPdcchPower.powerControlOffsetSS = 0;
680 /* Storing pdschCfg pointer here. Required to access pdsch config while
681 fillig up pdcch pdu */
682 pdsch = &pdcch->dci.pdschCfg;
684 /* fill the PDSCH PDU */
686 pdsch->pduBitmap = 0; /* PTRS and CBG params are excluded */
687 pdsch->rnti = 0xFFFF; /* SI-RNTI */
689 pdsch->numCodewords = 1;
690 for(cwCount = 0; cwCount < pdsch->numCodewords; cwCount++)
692 pdsch->codeword[cwCount].targetCodeRate = 308;
693 pdsch->codeword[cwCount].qamModOrder = 2;
694 pdsch->codeword[cwCount].mcsIndex = DEFAULT_MCS;
695 pdsch->codeword[cwCount].mcsTable = 0; /* notqam256 */
696 pdsch->codeword[cwCount].rvIndex = 0;
697 tbSize = schCalcTbSize(sib1PduLen + TX_PAYLOAD_HDR_LEN);
698 pdsch->codeword[cwCount].tbSize = tbSize;
700 pdsch->dataScramblingId = pci;
701 pdsch->numLayers = 1;
702 pdsch->transmissionScheme = 0;
704 pdsch->dmrs.dlDmrsSymbPos = DL_DMRS_SYMBOL_POS;
705 pdsch->dmrs.dmrsConfigType = 0; /* type-1 */
706 pdsch->dmrs.dlDmrsScramblingId = pci;
707 pdsch->dmrs.scid = 0;
708 pdsch->dmrs.numDmrsCdmGrpsNoData = 1;
709 pdsch->dmrs.dmrsPorts = 0x0001;
710 pdsch->dmrs.mappingType = DMRS_MAP_TYPE_A; /* Type-A */
711 pdsch->dmrs.nrOfDmrsSymbols = NUM_DMRS_SYMBOLS;
712 pdsch->dmrs.dmrsAddPos = DMRS_ADDITIONAL_POS;
714 pdsch->pdschFreqAlloc.resourceAllocType = 1; /* RAT type-1 RIV format */
715 /* the RB numbering starts from coreset0, and PDSCH is always above SSB */
716 pdsch->pdschFreqAlloc.startPrb = offsetPointA + SCH_SSB_NUM_PRB;
717 pdsch->pdschFreqAlloc.numPrb = schCalcNumPrb(tbSize, DEFAULT_MCS, NUM_PDSCH_SYMBOL);
718 pdsch->pdschFreqAlloc.vrbPrbMapping = 0; /* non-interleaved */
719 pdsch->pdschTimeAlloc.rowIndex = 1;
720 /* This is Intel's requirement. PDSCH should start after PDSCH DRMS symbol */
721 pdsch->pdschTimeAlloc.startSymb = 3; /* spec-38.214, Table 5.1.2.1-1 */
722 pdsch->pdschTimeAlloc.numSymb = NUM_PDSCH_SYMBOL;
723 pdsch->beamPdschInfo.numPrgs = 1;
724 pdsch->beamPdschInfo.prgSize = 1;
725 pdsch->beamPdschInfo.digBfInterfaces = 0;
726 pdsch->beamPdschInfo.prg[0].pmIdx = 0;
727 pdsch->beamPdschInfo.prg[0].beamIdx[0] = 0;
728 pdsch->txPdschPower.powerControlOffset = 0;
729 pdsch->txPdschPower.powerControlOffsetSS = 0;
735 * @brief cell config from MAC to SCH.
739 * Function : macSchCellCfgReq
741 * This API is invoked by MAC to send cell config to SCH
743 * @param[in] Pst *pst
744 * @param[in] SchCellCfg *schCellCfg
749 uint8_t SchProcCellCfgReq(Pst *pst, SchCellCfg *schCellCfg)
753 SchCellCfgCfm schCellCfgCfm;
755 Inst inst = pst->dstInst - SCH_INST_START;
756 uint8_t coreset0Idx = 0;
759 uint8_t freqDomainResource[FREQ_DOM_RSRC_SIZE] = {0};
760 SchPdschConfig pdschCfg;
762 schInitCellCb(inst, schCellCfg);
763 cellCb = schCb[inst].cells[inst]; //cells is of MAX_CELLS, why inst
764 cellCb->macInst = pst->srcInst;
766 /* derive the SIB1 config parameters */
767 ret = fillSchSib1Cfg(cellCb->numerology, schCellCfg->dlBandwidth, cellCb->numSlots,
768 &(schCellCfg->pdcchCfgSib1), &(cellCb->sib1SchCfg), schCellCfg->phyCellId,
769 schCellCfg->dlCfgCommon.schFreqInfoDlSib.offsetToPointA, schCellCfg->sib1PduLen);
773 DU_LOG("\nERROR --> SCH : Failed to fill sib1 configuration");
776 memcpy(&cellCb->cellCfg, schCellCfg, sizeof(SchCellCfg));
777 schProcPagingCfg(cellCb);
779 /* Fill coreset frequencyDomainResource bitmap */
780 coreset0Idx = cellCb->cellCfg.dlCfgCommon.schInitialDlBwp.pdcchCommon.commonSearchSpace.coresetId;
781 numRbs = coresetIdxTable[coreset0Idx][1];
782 offset = coresetIdxTable[coreset0Idx][3];
783 fillCoresetFeqDomAllocMap(((cellCb->cellCfg.dlCfgCommon.schFreqInfoDlSib.offsetToPointA - offset)/6), \
784 (numRbs/6), freqDomainResource);
785 covertFreqDomRsrcMapToIAPIFormat(freqDomainResource, \
786 cellCb->cellCfg.dlCfgCommon.schInitialDlBwp.pdcchCommon.commonSearchSpace.freqDomainRsrc);
788 /* Fill K0 - K1 table for common cfg*/
789 BuildK0K1Table(cellCb, &cellCb->k0K1InfoTbl, true, cellCb->cellCfg.dlCfgCommon.schInitialDlBwp.pdschCommon,
790 pdschCfg, DEFAULT_UL_ACK_LIST_COUNT, defaultUlAckTbl);
792 BuildK2InfoTable(cellCb, cellCb->cellCfg.ulCfgCommon.schInitialUlBwp.puschCommon.timeDomRsrcAllocList,\
793 cellCb->cellCfg.ulCfgCommon.schInitialUlBwp.puschCommon.numTimeDomRsrcAlloc, &cellCb->msg3K2InfoTbl, \
796 /*As per Spec 38.211, Sec 6.3.3.2; RootSeq Len(Lra) where Lra=839 or Lra=139,
797 *depending on the PRACH preamble format as given by Tables 6.3.3.1-1 and 6.3.3.1-2.*/
798 if(prachCfgIdxTable[cellCb->cellCfg.ulCfgCommon.schInitialUlBwp.schRachCfg.prachCfgGeneric.prachCfgIdx][0] <= 3)
800 cellCb->cellCfg.ulCfgCommon.schInitialUlBwp.schRachCfg.rootSeqLen = ROOT_SEQ_LEN_1;
804 cellCb->cellCfg.ulCfgCommon.schInitialUlBwp.schRachCfg.rootSeqLen = ROOT_SEQ_LEN_2;
806 /* Initializing global variables */
807 cellCb->actvUeBitMap = 0;
808 cellCb->boIndBitMap = 0;
810 cellCb->schHqCfg.maxDlDataHqTx = SCH_MAX_NUM_DL_HQ_TX;
811 cellCb->schHqCfg.maxMsg4HqTx = SCH_MAX_NUM_MSG4_TX;
812 cellCb->schHqCfg.maxUlDataHqTx = SCH_MAX_NUM_UL_HQ_TX;
813 cellCb->maxMsg3Tx = SCH_MAX_NUM_MSG3_TX;
815 cellCb->schAlgoType = SCH_FCFS;
816 cellCb->api = &schCb[inst].allApis[cellCb->schAlgoType]; /* For FCFS */
817 cellCb->api->SchCellCfgReq(cellCb);
819 /* Fill and send Cell config confirm */
820 memset(&rspPst, 0, sizeof(Pst));
821 FILL_PST_SCH_TO_MAC(rspPst, pst->dstInst);
822 rspPst.event = EVENT_SCH_CELL_CFG_CFM;
824 schCellCfgCfm.cellId = schCellCfg->cellId;
825 schCellCfgCfm.rsp = RSP_OK;
827 ret = MacMessageRouter(&rspPst, (void *)&schCellCfgCfm);
832 /*******************************************************************
834 * @brief Fill and send Cell delete response to MAC
838 * Function : SchSendCellDeleteRspToMac
840 * Functionality: Fill and send Cell delete response to MAC
842 * @params[in] SchCellDelete *ueDelete, Inst inst, SchMacRsp result
843 * @return ROK - success
846 * ****************************************************************/
847 uint8_t SchSendCellDeleteRspToMac(SchCellDeleteReq *ueDelete, Inst inst, SchMacRsp result)
852 SchCellDeleteRsp delRsp;
854 DU_LOG("\nINFO --> SCH : Filling Cell Delete response");
855 memset(&delRsp, 0, sizeof(SchCellDeleteRsp));
856 delRsp.cellId = ueDelete->cellId;
859 /* Filling response post */
860 memset(&rspPst, 0, sizeof(Pst));
861 FILL_PST_SCH_TO_MAC(rspPst, inst);
862 rspPst.event = EVENT_CELL_DELETE_RSP_TO_MAC;
863 ret = MacMessageRouter(&rspPst, (void *)&delRsp);
866 DU_LOG("\nERROR --> SCH : SchSendCellDeleteRspToMac(): failed to send the Cell Delete response");
872 /*******************************************************************
874 * @brief Function for cellCb Deletion
878 * Function : deleteSchCellCb
880 * Functionality: Function for cellCb Deletion
882 * @params[in] SchCellDelete *cellDelete
883 * @return ROK - success
886 * ****************************************************************/
887 void deleteSchCellCb(SchCellCb *cellCb)
889 uint8_t sliceIdx=0, slotIdx=0, plmnIdx = 0;
890 CmLListCp *list=NULL;
891 CmLList *node=NULL, *next=NULL;
892 SchPageInfo *tempNode = NULLP;
894 if(cellCb->schDlSlotInfo)
896 for(slotIdx=0; slotIdx<cellCb->numSlots; slotIdx++)
898 list = &cellCb->schDlSlotInfo[slotIdx]->prbAlloc.freePrbBlockList;
903 SCH_FREE(node->node, sizeof(FreePrbBlock));
904 deleteNodeFromLList(list, node);
907 SCH_FREE(cellCb->schDlSlotInfo[slotIdx], sizeof(SchDlSlotInfo));
909 SCH_FREE(cellCb->schDlSlotInfo, cellCb->numSlots *sizeof(SchDlSlotInfo*));
912 if(cellCb->schUlSlotInfo)
914 for(slotIdx=0; slotIdx<cellCb->numSlots; slotIdx++)
916 list = &cellCb->schUlSlotInfo[slotIdx]->prbAlloc.freePrbBlockList;
921 SCH_FREE(node->node, sizeof(FreePrbBlock));
922 deleteNodeFromLList(list, node);
925 SCH_FREE(cellCb->schUlSlotInfo[slotIdx], sizeof(SchUlSlotInfo));
927 SCH_FREE(cellCb->schUlSlotInfo, cellCb->numSlots * sizeof(SchUlSlotInfo*));
930 for(plmnIdx = 0; plmnIdx < MAX_PLMN; plmnIdx++)
932 if(cellCb->cellCfg.plmnInfoList[plmnIdx].snssai)
934 for(sliceIdx=0; sliceIdx<cellCb->cellCfg.plmnInfoList[plmnIdx].numSliceSupport; sliceIdx++)
936 SCH_FREE(cellCb->cellCfg.plmnInfoList[plmnIdx].snssai[sliceIdx], sizeof(Snssai));
938 SCH_FREE(cellCb->cellCfg.plmnInfoList[plmnIdx].snssai, cellCb->cellCfg.plmnInfoList[plmnIdx].numSliceSupport*sizeof(Snssai*));
942 for(uint16_t idx =0; idx<MAX_SFN; idx++)
944 list = &cellCb->pageCb.pageIndInfoRecord[idx];
951 tempNode = (SchPageInfo*)(node->node);
952 SCH_FREE(tempNode->pagePdu, tempNode->msgLen);
953 SCH_FREE(node->node, sizeof(SchPageInfo));
955 deleteNodeFromLList(list, node);
960 cellCb->api->SchCellDeleteReq(cellCb);
962 memset(cellCb, 0, sizeof(SchCellCb));
965 /*******************************************************************
967 * @brief Function for cell Delete request from MAC to SCH
971 * Function : SchProcCellDeleteReq
973 * Functionality: Function for cell Delete request from MAC to SCH
975 * @params[in] Pst *pst, SchCellDelete *cellDelete
976 * @return ROK - success
979 * ****************************************************************/
980 uint8_t SchProcCellDeleteReq(Pst *pst, SchCellDeleteReq *cellDelete)
982 uint8_t cellIdx=0, ret = RFAILED;
983 Inst inst = pst->dstInst - SCH_INST_START;
984 SchMacRsp result= RSP_OK;
988 DU_LOG("\nERROR --> SCH : SchProcCellDeleteReq(): Ue Delete request failed");
992 GET_CELL_IDX(cellDelete->cellId, cellIdx);
993 if(schCb[inst].cells[cellIdx] == NULLP)
995 DU_LOG("\nERROR --> SCH : SchProcCellDeleteReq(): cell Id[%d] is not available", cellDelete->cellId);
1000 if(schCb[inst].cells[cellIdx]->cellId == cellDelete->cellId)
1002 deleteSchCellCb(schCb[inst].cells[cellIdx]);
1005 SCH_FREE(schCb[inst].cells[cellIdx], sizeof(SchCellCb));
1006 DU_LOG("\nINFO --> SCH : Sending Cell Delete response to MAC");
1010 DU_LOG("\nERROR --> SCH : SchProcCellDeleteReq(): cell Id[%d] is not available",cellDelete->cellId);
1015 if(SchSendCellDeleteRspToMac(cellDelete, inst, result)!=ROK)
1017 DU_LOG("\nERROR --> SCH : SchProcCellDeleteReq(): failed to send Cell Delete response");
1024 /*******************************************************************
1026 * @brief Processes DL RLC BO info from MAC
1030 * Function : SchProcDlRlcBoInfo
1033 * Processes DL RLC BO info from MAC
1036 * @return ROK - success
1039 * ****************************************************************/
1040 uint8_t SchProcDlRlcBoInfo(Pst *pst, DlRlcBoInfo *dlBoInfo)
1044 bool isLcIdValid = false;
1045 SchUeCb *ueCb = NULLP;
1046 SchCellCb *cell = NULLP;
1047 Inst inst = pst->dstInst-SCH_INST_START;
1049 DU_LOG("\nDEBUG --> SCH : Received RLC BO Status indication LCId [%d] BO [%d]", dlBoInfo->lcId, dlBoInfo->dataVolume);
1050 cell = schCb[inst].cells[inst];
1054 DU_LOG("\nERROR --> SCH : SchProcDlRlcBoInfo(): Cell does not exists");
1058 GET_UE_ID(dlBoInfo->crnti, ueId);
1059 ueCb = &cell->ueCb[ueId-1];
1060 if(ueCb->ueCfg.dataTransmissionAction == STOP_DATA_TRANSMISSION)
1062 DU_LOG("INFO --> SCH : DL Data transmission not allowed for UE %d", ueCb->ueCfg.ueId);
1066 lcId = dlBoInfo->lcId;
1067 CHECK_LCID(lcId, isLcIdValid);
1068 if(isLcIdValid == FALSE)
1070 DU_LOG("ERROR --> SCH: LCID:%d is not valid", lcId);
1074 /*Expected when theres a case of Retransmission Failure or Resetablishment
1075 *By Zero BO, the RLC is informing that previous data can be cleared out
1076 *Thus clearing out the LC from the Lc priority list*/
1077 if(dlBoInfo->dataVolume == 0)
1079 /* TODO : Check the LC is Dedicated or default and accordingly LCList
1084 if(lcId == SRB0_LCID)
1086 cell->raCb[ueId -1].msg4recvd = true;
1087 cell->raCb[ueId -1].dlMsgPduLen = dlBoInfo->dataVolume;
1091 /* TODO : These part of changes will be corrected during DL scheduling as
1092 * per K0 - K1 -K2 */
1093 SET_ONE_BIT(ueId, cell->boIndBitMap);
1094 if(ueCb->dlInfo.dlLcCtxt[lcId].lcId == lcId)
1096 ueCb->dlInfo.dlLcCtxt[lcId].bo = dlBoInfo->dataVolume;
1100 DU_LOG("ERROR --> SCH: LCID:%d is not configured in SCH Cb",lcId);
1104 /* Adding UE Id to list of pending UEs to be scheduled */
1105 cell->api->SchDlRlcBoInfo(cell, ueId);
1109 /*******************************************************************
1111 * @brief Processes BSR indiation from MAC
1115 * Function : SchProcBsr
1118 * Processes DL BSR from MAC
1120 * @params[in] Pst pst
1121 * UlBufferStatusRptInd bsrInd
1122 * @return ROK - success
1125 * ****************************************************************/
1126 uint8_t SchProcBsr(Pst *pst, UlBufferStatusRptInd *bsrInd)
1128 Inst schInst = pst->dstInst-SCH_INST_START;
1129 SchCellCb *cellCb = NULLP;
1130 SchUeCb *ueCb = NULLP;
1133 DU_LOG("\nDEBUG --> SCH : Received BSR");
1136 DU_LOG("\nERROR --> SCH : BSR Ind is empty");
1139 cellCb = schCb[schInst].cells[schInst];
1142 DU_LOG("\nERROR --> SCH : CellCb is empty");
1145 ueCb = schGetUeCb(cellCb, bsrInd->crnti);
1149 DU_LOG("\nERROR --> SCH : UeCB is empty");
1153 if(ueCb->ueCfg.dataTransmissionAction == STOP_DATA_TRANSMISSION)
1155 DU_LOG("\nINFO --> SCH: UL Data transmission not allowed for UE %d", ueCb->ueCfg.ueId);
1159 ueCb->bsrRcvd = true;
1160 /* store dataVolume per lcg in uecb */
1161 for(lcgIdx = 0; lcgIdx < bsrInd->numLcg; lcgIdx++)
1163 ueCb->bsrInfo[bsrInd->dataVolInfo[lcgIdx].lcgId].priority = 1; //TODO: determining LCG priority?
1164 ueCb->bsrInfo[bsrInd->dataVolInfo[lcgIdx].lcgId].dataVol = bsrInd->dataVolInfo[lcgIdx].dataVol;
1167 /* Adding UE Id to list of pending UEs to be scheduled */
1168 cellCb->api->SchBsr(cellCb, ueCb->ueId);
1172 /*******************************************************************
1174 * @brief Processes SR UCI indication from MAC
1178 * Function : SchProcSrUciInd
1181 * Processes SR UCI indication from MAC
1183 * @params[in] Post structure
1185 * @return ROK - success
1188 * ****************************************************************/
1189 uint8_t SchProcSrUciInd(Pst *pst, SrUciIndInfo *uciInd)
1191 Inst inst = pst->dstInst-SCH_INST_START;
1194 SchCellCb *cellCb = schCb[inst].cells[inst];
1196 DU_LOG("\nDEBUG --> SCH : Received SR");
1198 ueCb = schGetUeCb(cellCb, uciInd->crnti);
1200 if(ueCb->state == SCH_UE_STATE_INACTIVE)
1202 DU_LOG("\nERROR --> SCH : Crnti %d is inactive", uciInd->crnti);
1205 if(ueCb->ueCfg.dataTransmissionAction == STOP_DATA_TRANSMISSION)
1207 DU_LOG("\nINFO --> SCH: UL Data transmission not allowed for UE %d", ueCb->ueCfg.ueId);
1210 if(uciInd->numSrBits)
1212 ueCb->srRcvd = true;
1213 /* Adding UE Id to list of pending UEs to be scheduled */
1214 cellCb->api->SchSrUciInd(cellCb, ueCb->ueId);
1219 /*******************************************************************
1221 * @brief Processes DL HARQ indication from MAC
1225 * Function : SchProcDlHarqInd
1228 * Processes DL HARQ indication from MAC
1230 * @params[in] Post structure
1231 * DL HARQ Indication
1232 * @return ROK - success
1235 * ****************************************************************/
1236 uint8_t SchProcDlHarqInd(Pst *pst, DlHarqInd *dlHarqInd)
1238 Inst inst = pst->dstInst-SCH_INST_START;
1240 SchCellCb *cellCb = schCb[inst].cells[inst];
1242 DU_LOG("\nDEBUG --> SCH : Received HARQ");
1244 ueCb = schGetUeCb(cellCb, dlHarqInd->crnti);
1246 if(ueCb->state == SCH_UE_STATE_INACTIVE)
1248 DU_LOG("\nERROR --> SCH : Crnti %d is inactive", dlHarqInd->crnti);
1252 schUpdateHarqFdbk(ueCb, dlHarqInd->numHarq, dlHarqInd->harqPayload, &dlHarqInd->slotInd);
1257 /*******************************************************************
1259 * @brief Allocates requested PRBs for DL
1263 * Function : allocatePrbDl
1266 * Allocates requested PRBs in DL
1267 * Keeps track of allocated PRB (using bitmap) and remaining PRBs
1269 * @params[in] prbAlloc table
1275 * @return ROK - success
1278 * ****************************************************************/
1279 uint8_t allocatePrbDl(SchCellCb *cell, SlotTimingInfo slotTime, \
1280 uint8_t startSymbol, uint8_t symbolLength, uint16_t *startPrb, uint16_t numPrb)
1283 uint16_t broadcastPrbStart=0, broadcastPrbEnd=0;
1284 FreePrbBlock *freePrbBlock = NULLP;
1285 CmLList *freePrbNode = NULLP;
1286 PduTxOccsaion ssbOccasion=0, sib1Occasion=0;
1287 SchDlSlotInfo *schDlSlotInfo = cell->schDlSlotInfo[slotTime.slot];
1288 SchPrbAlloc *prbAlloc = &schDlSlotInfo->prbAlloc;
1290 /* If startPrb is set to MAX_NUM_RB, it means startPrb is not known currently.
1291 * Search for an appropriate location in PRB grid and allocate requested resources */
1292 if(*startPrb == MAX_NUM_RB)
1294 /* Check if SSB/SIB1 is also scheduled in this slot */
1295 ssbOccasion = schCheckSsbOcc(cell, slotTime);
1296 sib1Occasion = schCheckSib1Occ(cell, slotTime);
1298 if(ssbOccasion && sib1Occasion)
1300 broadcastPrbStart = cell->cellCfg.dlCfgCommon.schFreqInfoDlSib.offsetToPointA;
1301 broadcastPrbEnd = broadcastPrbStart + SCH_SSB_NUM_PRB + cell->sib1SchCfg.sib1PdcchCfg.dci.pdschCfg.pdschFreqAlloc.numPrb -1;
1303 else if(ssbOccasion)
1305 broadcastPrbStart = cell->cellCfg.dlCfgCommon.schFreqInfoDlSib.offsetToPointA;
1306 broadcastPrbEnd = broadcastPrbStart + SCH_SSB_NUM_PRB -1;
1308 else if(sib1Occasion)
1310 broadcastPrbStart = cell->sib1SchCfg.sib1PdcchCfg.dci.pdschCfg.pdschFreqAlloc.startPrb;
1311 broadcastPrbEnd = broadcastPrbStart + cell->sib1SchCfg.sib1PdcchCfg.dci.pdschCfg.pdschFreqAlloc.numPrb -1;
1314 /* Iterate through all free PRB blocks */
1315 freePrbNode = prbAlloc->freePrbBlockList.first;
1318 freePrbBlock = (FreePrbBlock *)freePrbNode->node;
1320 /* If broadcast message is scheduled in this slot, then check if its PRBs belong to the current free block.
1321 * Since SSB/SIB1 PRB location is fixed, these PRBs cannot be allocated to other message in same slot */
1322 if((ssbOccasion || sib1Occasion) &&
1323 ((broadcastPrbStart >= freePrbBlock->startPrb) && (broadcastPrbStart <= freePrbBlock->endPrb)) && \
1324 ((broadcastPrbEnd >= freePrbBlock->startPrb) && (broadcastPrbEnd <= freePrbBlock->endPrb)))
1326 /* Implmentation is done such that highest-numbered free-RB is allocated first */
1327 if((freePrbBlock->endPrb > broadcastPrbEnd) && ((freePrbBlock->endPrb - broadcastPrbEnd) >= numPrb))
1329 /* If sufficient free PRBs are available above bradcast message then,
1330 * endPrb = freePrbBlock->endPrb
1331 * startPrb = endPrb - numPrb +1;
1333 *startPrb = freePrbBlock->endPrb - numPrb +1;
1336 else if((broadcastPrbStart > freePrbBlock->startPrb) && ((broadcastPrbStart - freePrbBlock->startPrb) >= numPrb))
1338 /* If free PRBs are available below broadcast message then,
1339 * endPrb = broadcastPrbStart - 1
1340 * startPrb = endPrb - numPrb +1
1342 *startPrb = broadcastPrbStart - numPrb;
1347 freePrbNode = freePrbNode->next;
1353 /* Check if requested number of blocks can be allocated from the current block */
1354 if (freePrbBlock->numFreePrb < numPrb)
1356 freePrbNode = freePrbNode->next;
1359 *startPrb = freePrbBlock->endPrb - numPrb +1;
1364 /* If no free block can be used to allocated request number of RBs */
1365 if(*startPrb == MAX_NUM_RB)
1369 /* If startPrb is known already, check if requested PRBs are available for allocation */
1372 freePrbNode = isPrbAvailable(&prbAlloc->freePrbBlockList, *startPrb, numPrb);
1375 DU_LOG("\nERROR --> SCH: Requested DL PRB unavailable");
1380 /* Update bitmap to allocate PRBs */
1381 for(symbol=startSymbol; symbol < (startSymbol+symbolLength); symbol++)
1383 if(fillPrbBitmap(prbAlloc->prbBitMap[symbol], *startPrb, numPrb) != ROK)
1385 DU_LOG("\nERROR --> SCH: fillPrbBitmap() failed for symbol [%d] in DL", symbol);
1390 /* Update statistics of PRB usage if stats calculation is enabled */
1391 if(schCb[cell->instIdx].statistics.activeKpiList.dlTotPrbUseList.count)
1392 prbAlloc->numPrbAlloc += numPrb;
1394 /* Update the remaining number for free PRBs */
1395 removeAllocatedPrbFromFreePrbList(&prbAlloc->freePrbBlockList, freePrbNode, *startPrb, numPrb);
1400 /*******************************************************************
1402 * @brief Allocates requested PRBs for UL
1406 * Function : allocatePrbUl
1409 * Allocates requested PRBs in UL
1410 * Keeps track of allocated PRB (using bitmap) and remaining PRBs
1412 * @params[in] prbAlloc table
1418 * @return ROK - success
1421 * ****************************************************************/
1422 uint8_t allocatePrbUl(SchCellCb *cell, SlotTimingInfo slotTime, \
1423 uint8_t startSymbol, uint8_t symbolLength, uint16_t *startPrb, uint16_t numPrb)
1426 uint16_t prachStartPrb, prachNumPrb, prachEndPrb;
1427 bool isPrachOccasion;
1428 FreePrbBlock *freePrbBlock = NULLP;
1429 CmLList *freePrbNode = NULLP;
1430 SchPrbAlloc *prbAlloc = NULLP;
1434 DU_LOG("\nERROR --> SCH : allocatePrbUl(): Received cellCb is null");
1438 prbAlloc = &cell->schUlSlotInfo[slotTime.slot]->prbAlloc;
1439 /* If startPrb is set to MAX_NUM_RB, it means startPrb is not known currently.
1440 * Search for an appropriate location in PRB grid and allocate requested resources */
1441 if(*startPrb == MAX_NUM_RB)
1443 /* Check if PRACH is also scheduled in this slot */
1444 isPrachOccasion = schCheckPrachOcc(cell, slotTime);
1447 prachStartPrb = cell->cellCfg.ulCfgCommon.schInitialUlBwp.schRachCfg.prachCfgGeneric.msg1FreqStart;
1448 prachNumPrb = schCalcPrachNumRb(cell);
1449 prachEndPrb = prachStartPrb + prachNumPrb -1;
1452 /* Iterate through all free PRB blocks */
1453 freePrbNode = prbAlloc->freePrbBlockList.first;
1456 freePrbBlock = (FreePrbBlock *)freePrbNode->node;
1458 /* If PRACH is scheduled in this slot, then check if its PRBs belong to the current free block.
1459 * PRBs required for PRACH cannot be allocated to any other message */
1460 if((isPrachOccasion) &&
1461 ((prachStartPrb >= freePrbBlock->startPrb) && (prachStartPrb <= freePrbBlock->endPrb)) &&
1462 ((prachEndPrb >= freePrbBlock->startPrb) && (prachEndPrb <= freePrbBlock->endPrb)))
1464 /* Implmentation is done such that highest-numbered free-RB is allocated first */
1465 if((freePrbBlock->endPrb > prachEndPrb) && ((freePrbBlock->endPrb - prachEndPrb) >= numPrb))
1467 /* If sufficient free PRBs are available above PRACH message then,
1468 * endPrb = freePrbBlock->endPrb
1469 * startPrb = endPrb - numPrb +1;
1471 *startPrb = freePrbBlock->endPrb - numPrb +1;
1474 else if((prachStartPrb > freePrbBlock->startPrb) && ((prachStartPrb - freePrbBlock->startPrb) >= numPrb))
1476 /* If free PRBs are available below PRACH message then,
1477 * endPrb = prachStartPrb - 1
1478 * startPrb = endPrb - numPrb +1
1480 *startPrb = prachStartPrb - numPrb;
1485 freePrbNode = freePrbNode->next;
1491 /* Check if requested number of PRBs can be allocated from currect block */
1492 if(freePrbBlock->numFreePrb < numPrb)
1494 freePrbNode = freePrbNode->next;
1497 *startPrb = freePrbBlock->endPrb - numPrb +1;
1502 /* If no free block can be used to allocated requested number of RBs */
1503 if(*startPrb == MAX_NUM_RB)
1508 /* If startPrb is known already, check if requested PRBs are available for allocation */
1509 freePrbNode = isPrbAvailable(&prbAlloc->freePrbBlockList, *startPrb, numPrb);
1512 DU_LOG("\nERROR --> SCH: Requested UL PRB unavailable");
1517 /* Update bitmap to allocate PRBs */
1518 for(symbol=startSymbol; symbol < (startSymbol+symbolLength); symbol++)
1520 if(fillPrbBitmap(prbAlloc->prbBitMap[symbol], *startPrb, numPrb) != ROK)
1522 DU_LOG("\nERROR --> SCH: fillPrbBitmap() failed for symbol [%d] in UL", symbol);
1527 /* Update statistics of PRB usage if stats calculation is enabled */
1528 if(schCb[cell->instIdx].statistics.activeKpiList.ulTotPrbUseList.count)
1529 prbAlloc->numPrbAlloc += numPrb;
1531 /* Update the remaining number for free PRBs */
1532 removeAllocatedPrbFromFreePrbList(&prbAlloc->freePrbBlockList, freePrbNode, *startPrb, numPrb);
1537 /*******************************************************************************
1539 * @brief Try to find Best Free Block with Max Num PRB
1543 * Function : searchLargestFreeBlock
1546 * Finds the FreeBlock with MaxNum of FREE PRB considering SSB/SIB1 ocassions.
1548 * @params[in] I/P > prbAlloc table (FreeBlock list)
1549 * I/P > Slot timing Info
1551 * I/P > Direction (UL/DL)
1554 * @return Max Number of Free PRB
1555 * If 0, then no Suitable Free Block
1557 * ********************************************************************************/
1559 uint16_t searchLargestFreeBlock(SchCellCb *cell, SlotTimingInfo slotTime,uint16_t *startPrb, Direction dir)
1561 uint16_t reservedPrbStart=0, reservedPrbEnd=0, maxFreePRB = 0;
1562 FreePrbBlock *freePrbBlock = NULLP;
1563 CmLList *freePrbNode = NULLP;
1564 SchPrbAlloc *prbAlloc = NULLP;
1565 bool checkOccasion = FALSE;
1567 *startPrb = 0; /*Initialize the StartPRB to zero*/
1569 /*Based on Direction, Reserved Messsages will differi.e.
1570 * DL >> SSB and SIB1 ocassions wheres for UL, PRACH ocassions to be checked
1571 * and reserved before allocation for dedicated DL/UL msg*/
1574 SchDlSlotInfo *schDlSlotInfo = cell->schDlSlotInfo[slotTime.slot];
1575 PduTxOccsaion ssbOccasion=0, sib1Occasion=0;
1577 prbAlloc = &schDlSlotInfo->prbAlloc;
1579 ssbOccasion = schCheckSsbOcc(cell, slotTime);
1580 sib1Occasion = schCheckSib1Occ(cell, slotTime);
1582 checkOccasion = TRUE;
1583 if(ssbOccasion && sib1Occasion)
1585 reservedPrbStart = cell->cellCfg.dlCfgCommon.schFreqInfoDlSib.offsetToPointA;
1586 reservedPrbEnd = reservedPrbStart + SCH_SSB_NUM_PRB + \
1587 cell->sib1SchCfg.sib1PdcchCfg.dci.pdschCfg.pdschFreqAlloc.numPrb -1;
1589 else if(ssbOccasion)
1591 reservedPrbStart = cell->cellCfg.dlCfgCommon.schFreqInfoDlSib.offsetToPointA;
1592 reservedPrbEnd = reservedPrbStart + SCH_SSB_NUM_PRB -1;
1594 else if(sib1Occasion)
1596 reservedPrbStart = cell->sib1SchCfg.sib1PdcchCfg.dci.pdschCfg.pdschFreqAlloc.startPrb;
1597 reservedPrbEnd = reservedPrbStart + cell->sib1SchCfg.sib1PdcchCfg.dci.pdschCfg.pdschFreqAlloc.numPrb -1;
1601 checkOccasion = FALSE;
1604 else if(dir == DIR_UL)
1606 prbAlloc = &cell->schUlSlotInfo[slotTime.slot]->prbAlloc;
1608 /* Check if PRACH is also scheduled in this slot */
1609 checkOccasion = schCheckPrachOcc(cell, slotTime);
1612 reservedPrbStart = cell->cellCfg.ulCfgCommon.schInitialUlBwp.schRachCfg.prachCfgGeneric.msg1FreqStart;
1613 reservedPrbEnd = reservedPrbStart + (schCalcPrachNumRb(cell)) -1;
1618 DU_LOG("\nERROR --> SCH: Invalid Direction!");
1619 return (maxFreePRB);
1622 freePrbNode = prbAlloc->freePrbBlockList.first;
1625 freePrbBlock = (FreePrbBlock *)freePrbNode->node;
1627 /*For block with same numFreeBlocks, choose the one with HighestPRB range
1628 *Since FreeBLockList are arranged in Descending order of PRB range thus Skipping this block*/
1629 if(maxFreePRB >= freePrbBlock->numFreePrb)
1632 freePrbNode = freePrbNode->next;
1636 /* If Broadcast/Prach message is scheduled in this slot, then check if its PRBs belong to the current free block.
1637 * Since SSB/SIB1 PRB location is fixed, these PRBs cannot be allocated to other message in same slot */
1639 ((reservedPrbStart >= freePrbBlock->startPrb) && (reservedPrbStart <= freePrbBlock->endPrb)) && \
1640 ((reservedPrbEnd >= freePrbBlock->startPrb) && (reservedPrbEnd <= freePrbBlock->endPrb)))
1643 /* Implmentation is done such that highest-numbered free-RB is Checked first
1644 and freePRB in this block is greater than Max till now */
1645 if((freePrbBlock->endPrb > reservedPrbEnd) && ((freePrbBlock->endPrb - reservedPrbEnd) > maxFreePRB))
1647 /* If sufficient free PRBs are available above reserved message*/
1648 *startPrb = reservedPrbEnd + 1;
1649 maxFreePRB = (freePrbBlock->endPrb - reservedPrbEnd);
1651 /*Also check the other freeBlock (i.e. Above the reserved message) for MAX FREE PRB*/
1652 if((reservedPrbStart > freePrbBlock->startPrb) && ((reservedPrbStart - freePrbBlock->startPrb) > maxFreePRB))
1654 /* If free PRBs are available below reserved message*/
1655 *startPrb = freePrbBlock->startPrb;
1656 maxFreePRB = (reservedPrbStart - freePrbBlock->startPrb);
1661 if(maxFreePRB < freePrbBlock->numFreePrb)
1663 *startPrb = freePrbBlock->startPrb;
1664 maxFreePRB = freePrbBlock->numFreePrb;
1668 freePrbNode = freePrbNode->next;
1673 /*******************************************************************************
1675 * @brief This function is used to send Slice Cfg rsp to MAC
1679 * Function : SchSendSliceCfgRspToMac
1682 * function is used to send Slice Cfg rsp to MAC
1684 * @params[in] Pst *pst, SchSliceCfgRsp sliceCfgRsp
1688 * ********************************************************************************/
1689 void SchSendSliceCfgRspToMac(Inst inst, SchSliceCfgRsp sliceCfgRsp)
1693 memset(&rspPst, 0, sizeof(Pst));
1694 FILL_PST_SCH_TO_MAC(rspPst, inst);
1695 rspPst.event = EVENT_SLICE_CFG_RSP_TO_MAC;
1697 MacMessageRouter(&rspPst, (void *)&sliceCfgRsp);
1701 /*******************************************************************************
1703 * @brief This function is used to store or modify the slice configuration Sch DB
1707 * Function : addOrModifySliceCfgInSchDb
1710 * function is used to store or modify the slice configuration Sch DB
1712 * @params[in] SchSliceCfg *storeSliceCfg, SchSliceCfgReq *cfgReq,
1713 * SchSliceCfgRsp cfgRsp, uint8_t count
1719 * ********************************************************************************/
1720 uint8_t addSliceCfgInSchDb(CmLListCp *sliceCfgInDb, SchRrmPolicyOfSlice *cfgReq)
1722 SchRrmPolicyOfSlice *sliceToStore;
1724 SCH_ALLOC(sliceToStore, sizeof(SchRrmPolicyOfSlice));
1727 memcpy(&sliceToStore->snssai, &cfgReq->snssai, sizeof(Snssai));
1728 memcpy(&sliceToStore->rrmPolicyRatioInfo, &cfgReq->rrmPolicyRatioInfo, sizeof(SchRrmPolicyRatio));
1729 addNodeToLList(sliceCfgInDb, sliceToStore, NULL);
1733 DU_LOG("\nERROR --> SCH : Memory allocation failed in addOrModifySliceCfgInSchDb");
1739 /*******************************************************************************
1741 * @brief fill slice configuration response
1745 * Function : fillSliceCfgRsp
1748 * fill slice configuration response
1750 * @params[in] SchCellCb, SchSliceCfgReq, SchSliceCfgRsp,uint8_t count
1756 * ********************************************************************************/
1757 uint8_t fillSliceCfgRsp(Inst inst, CmLListCp *storedSliceCfg, SchCellCb *cellCb, SchSliceCfgReq *schSliceCfgReq)
1759 SchMacRsp sliceFound;
1760 uint8_t cfgIdx = 0, sliceIdx = 0, plmnIdx = 0, ret =ROK;
1761 SchSliceCfgRsp schSliceCfgRsp;
1763 for(cfgIdx = 0; cfgIdx<schSliceCfgReq->numOfConfiguredSlice; cfgIdx++)
1765 sliceFound = RSP_NOK;
1766 /* Here comparing the slice cfg request with the slice stored in cellCfg */
1767 for(plmnIdx = 0; plmnIdx < MAX_PLMN; plmnIdx++)
1769 for(sliceIdx = 0; sliceIdx<cellCb->cellCfg.plmnInfoList[plmnIdx].numSliceSupport; sliceIdx++)
1771 /* If we find the SliceCfgReq's SNSSAI in CellCb's SNSSAI DB, we mark this slice as configured and add it to Sch's DB. */
1772 if(!memcmp(&schSliceCfgReq->listOfSlices[cfgIdx]->snssai, cellCb->cellCfg.plmnInfoList[plmnIdx].snssai[sliceIdx], sizeof(Snssai)))
1774 if(addSliceCfgInSchDb(storedSliceCfg, schSliceCfgReq->listOfSlices[cfgIdx]) == ROK)
1776 sliceFound = RSP_OK;
1777 schSliceCfgRsp.cause = SUCCESSFUL;
1781 DU_LOG("\nERROR --> SCH : Failed to store slice configuration in SchDb");
1782 schSliceCfgRsp.cause = RESOURCE_UNAVAILABLE;
1791 if((sliceFound == RSP_NOK) && (schSliceCfgRsp.cause != RESOURCE_UNAVAILABLE))
1792 schSliceCfgRsp.cause = SLICE_NOT_FOUND;
1794 schSliceCfgRsp.snssai = schSliceCfgReq->listOfSlices[cfgIdx]->snssai;
1795 schSliceCfgRsp.rsp = sliceFound;
1796 SchSendSliceCfgRspToMac(inst, schSliceCfgRsp);
1801 /*******************************************************************************
1803 * @brief This function is used to free the slice cfg and re cfg request pointer
1807 * Function : freeSchSliceCfgReq
1810 * function is used to free the slice cfg and re cfg request pointer
1812 * @params[in] Pst *pst, SchSliceCfgReq *schSliceCfgReq
1817 * ********************************************************************************/
1818 void freeSchSliceCfgReq(SchSliceCfgReq *sliceCfgReq)
1824 if(sliceCfgReq->numOfConfiguredSlice)
1826 for(cfgIdx = 0; cfgIdx<sliceCfgReq->numOfConfiguredSlice; cfgIdx++)
1828 if(sliceCfgReq->listOfSlices[cfgIdx])
1830 SCH_FREE(sliceCfgReq->listOfSlices[cfgIdx], sizeof(SchRrmPolicyOfSlice));
1833 SCH_FREE(sliceCfgReq->listOfSlices, sliceCfgReq->numOfConfiguredSlice * sizeof(SchRrmPolicyOfSlice*));
1835 SCH_FREE(sliceCfgReq, sizeof(SchSliceCfgReq));
1838 /*******************************************************************************
1840 * @brief This function is used to store the slice configuration Sch DB
1844 * Function : SchProcSliceCfgReq
1847 * function is used to store the slice configuration Sch DB
1849 * @params[in] Pst *pst, SchSliceCfgReq *schSliceCfgReq
1855 * ********************************************************************************/
1856 uint8_t SchProcSliceCfgReq(Pst *pst, SchSliceCfgReq *schSliceCfgReq)
1859 Inst inst = pst->dstInst - SCH_INST_START;
1861 DU_LOG("\nINFO --> SCH : Received Slice Cfg request from MAC");
1864 if(schSliceCfgReq->listOfSlices)
1866 /* filling the slice configuration response of each slice */
1867 if(fillSliceCfgRsp(inst, &schCb[inst].sliceCfg, schCb[inst].cells[0], schSliceCfgReq) != ROK)
1869 DU_LOG("\nERROR --> SCH : Failed to fill the slice cfg rsp");
1872 freeSchSliceCfgReq(schSliceCfgReq);
1877 DU_LOG("\nERROR --> SCH : Received SchSliceCfgReq is NULL");
1883 /*******************************************************************************
1885 * @brief This function is used to send Slice re Cfg rsp to MAC
1889 * Function : SchSendSliceRecfgRspToMac
1892 * function is used to send Slice re Cfg rsp to MAC
1894 * @params[in] Pst *pst, SchSliceRecfgRsp schSliceRecfgRsp
1898 * ********************************************************************************/
1899 void SchSendSliceRecfgRspToMac(Inst inst, SchSliceRecfgRsp schSliceRecfgRsp)
1903 memset(&rspPst, 0, sizeof(Pst));
1904 FILL_PST_SCH_TO_MAC(rspPst, inst);
1905 rspPst.event = EVENT_SLICE_RECFG_RSP_TO_MAC;
1907 MacMessageRouter(&rspPst, (void *)&schSliceRecfgRsp);
1910 /*******************************************************************************
1912 * @brief fill slice configuration response
1916 * Function : fillSliceRecfgRsp
1918 * Functionality: fill slice reconfiguration response
1920 * @params[in] SchCellCb, SchSliceCfgReq, SchSliceCfgRsp,uint8_t count
1926 * ********************************************************************************/
1928 uint8_t fillSliceRecfgRsp(Inst inst, CmLListCp *storedSliceCfg, SchSliceRecfgReq *schSliceRecfgReq)
1930 SchMacRsp sliceFound;
1932 SchRrmPolicyOfSlice *rrmPolicyOfSlices;
1933 SchSliceRecfgRsp schSliceRecfgRsp;
1935 for(cfgIdx = 0; cfgIdx<schSliceRecfgReq->numOfConfiguredSlice; cfgIdx++)
1937 sliceFound = RSP_NOK;
1938 /* Here comparing the slice recfg request with the StoredSliceCfg */
1939 CmLList *sliceCfg = storedSliceCfg->first;
1943 rrmPolicyOfSlices = (SchRrmPolicyOfSlice*)sliceCfg->node;
1945 if(rrmPolicyOfSlices && (memcmp(&schSliceRecfgReq->listOfSlices[cfgIdx]->snssai, &(rrmPolicyOfSlices->snssai), sizeof(Snssai)) == 0))
1947 memcpy(&rrmPolicyOfSlices->rrmPolicyRatioInfo, &schSliceRecfgReq->listOfSlices[cfgIdx]->rrmPolicyRatioInfo, sizeof(SchRrmPolicyRatio));
1948 sliceFound = RSP_OK;
1951 sliceCfg = sliceCfg->next;
1954 schSliceRecfgRsp.snssai = schSliceRecfgReq->listOfSlices[cfgIdx]->snssai;
1955 schSliceRecfgRsp.rsp = sliceFound;
1956 if(schSliceRecfgRsp.rsp == RSP_OK)
1957 schSliceRecfgRsp.cause = SUCCESSFUL;
1959 schSliceRecfgRsp.cause = SLICE_NOT_FOUND;
1960 SchSendSliceRecfgRspToMac(inst, schSliceRecfgRsp);
1964 /*******************************************************************************
1966 * @brief This function is used to store the slice reconfiguration Sch DB
1970 * Function : SchProcSliceRecfgReq
1973 * function is used to store the slice re configuration Sch DB
1975 * @params[in] Pst *pst, SchSliceRecfgReq *schSliceRecfgReq
1981 * ********************************************************************************/
1982 uint8_t SchProcSliceRecfgReq(Pst *pst, SchSliceRecfgReq *schSliceRecfgReq)
1985 Inst inst = pst->dstInst - SCH_INST_START;
1987 DU_LOG("\nINFO --> SCH : Received Slice ReCfg request from MAC");
1988 if(schSliceRecfgReq)
1990 if(schSliceRecfgReq->listOfSlices)
1992 /* filling the slice configuration response of each slice */
1993 if(fillSliceRecfgRsp(inst, &schCb[inst].sliceCfg, schSliceRecfgReq) != ROK)
1995 DU_LOG("\nERROR --> SCH : Failed to fill sch slice cfg response");
1998 freeSchSliceCfgReq(schSliceRecfgReq);
2003 DU_LOG("\nERROR --> SCH : Received SchSliceRecfgReq is NULL");
2009 /****************************************************************************
2011 * @brief Stores the Paging Configuration from DU APP in CellCb
2015 * Function : schProcPagingParam
2018 * Process the Paging Configuration when FirstPDCCHMonitoring for
2019 * Paging Ocassion is not present.
2021 * As per 38.304 Sec 7.1,
2022 * "When firstPDCCH-MonitoringOccasionOfPO is present, the
2023 * starting PDCCH monitoring occasion number of (i_s + 1)th PO is the
2024 * (i_s + 1)th value of the firstPDCCHMonitoringOccasionOfPO
2025 * parameter; otherwise, it is equal to i_s * S."
2026 * "S = number of actual transmitted SSBs determined according
2027 * to ssb-PositionsInBurst in SIB1"
2029 * @params[in] SchCellCb *cell
2033 *************************************************************************/
2034 void schProcPagingCfg(SchCellCb *cell)
2036 SchPcchCfg *pageCfgRcvd = NULL;
2039 pageCfgRcvd = &(cell->cellCfg.dlCfgCommon.schPcchCfg);
2041 if(pageCfgRcvd->poPresent == TRUE)
2043 /*Fetching first Pdcch Monitoring Occasion for SFN (i_s + 1)th*/
2044 for(i_sIdx = 0; i_sIdx < pageCfgRcvd->numPO; i_sIdx++)
2046 cell->pageCb.pagMonOcc[i_sIdx].pagingOccSlot = pageCfgRcvd->pagingOcc[i_sIdx] / MAX_SYMB_PER_SLOT ;
2047 if ((pageCfgRcvd->pagingOcc[i_sIdx] % MAX_SYMB_PER_SLOT) != 0 )
2049 cell->pageCb.pagMonOcc[i_sIdx].pagingOccSlot++;
2052 cell->pageCb.pagMonOcc[i_sIdx].frameOffset = 0;
2058 schCfgPdcchMonOccOfPO(cell);
2062 /****************************************************************************
2064 * @brief Calculate PO if not present in Configuration
2068 * Function : schCfgPdcchMonOccOfPO
2070 * Functionality: In this function, PO are calculated i_s * S because
2071 * FirstPDCCHMonitoring_ForPO is not present.
2073 * @params[in] SchCellCb *cellCb
2077 *************************************************************************/
2078 void schCfgPdcchMonOccOfPO(SchCellCb *cell)
2080 uint8_t cnt = 0, incr = 1, i_sIdx = 0, frameOffSet = 0;
2081 uint8_t nsValue = cell->cellCfg.dlCfgCommon.schPcchCfg.numPO;
2082 uint8_t totalNumSsb = countSetBits(cell->cellCfg.ssbPosInBurst[0]);
2083 SlotTimingInfo tmpTimingInfo, pdcchTime;
2085 /*Starting with First Sfn and slot*/
2086 tmpTimingInfo.sfn = 0;
2087 tmpTimingInfo.slot = 0;
2089 pdcchTime = tmpTimingInfo;
2091 while(i_sIdx < nsValue)
2093 /*Increment frame Offset if PO falls on next SFN*/
2094 if(pdcchTime.sfn != tmpTimingInfo.sfn)
2098 pdcchTime = tmpTimingInfo;
2099 schIncrSlot(&(tmpTimingInfo), incr, cell->numSlots);
2103 cell->pageCb.pagMonOcc[i_sIdx].pagingOccSlot = pdcchTime.slot;
2104 cell->pageCb.pagMonOcc[i_sIdx].frameOffset = frameOffSet;
2110 if((cnt == totalNumSsb) && (i_sIdx < MAX_PO_PER_PF))
2112 cell->pageCb.pagMonOcc[i_sIdx].pagingOccSlot = pdcchTime.slot;
2113 cell->pageCb.pagMonOcc[i_sIdx].frameOffset = frameOffSet;
2121 /****************************************************************************
2123 * @brief Storing the paging information in SCH database
2127 * Function : schAddPagingIndtoList
2129 * Functionality: Storing the paging information in SCH database
2131 * @params[in] CmLListCp *storedPageList, CmLList *pageIndInfo
2133 * @return ROK - sucess
2136 *************************************************************************/
2137 uint8_t schAddPagingIndtoList(CmLListCp *storedPageList,void * pageIndInfo)
2139 CmLList *firstNodeOfList = NULLP;
2140 CmLList *currentNodeInfo = NULLP;
2141 SchPageInfo *tempNode = NULLP, *recvdNode = NULLP;
2143 recvdNode = (SchPageInfo*) pageIndInfo;
2144 CM_LLIST_FIRST_NODE(storedPageList,firstNodeOfList);
2146 SCH_ALLOC(currentNodeInfo, sizeof(CmLList));
2147 if(!currentNodeInfo)
2149 DU_LOG("\nERROR --> SCH : schAddPagingIndtoList() : Memory allocation failed");
2153 currentNodeInfo->node = (PTR)pageIndInfo;
2154 while(firstNodeOfList)
2156 tempNode = (SchPageInfo*)(firstNodeOfList->node);
2157 if ((recvdNode->pageTxTime.slot < tempNode->pageTxTime.slot))
2159 cmLListInsCrnt(storedPageList, currentNodeInfo);
2162 else if ((recvdNode->pageTxTime.slot == tempNode->pageTxTime.slot))
2164 DU_LOG("\nERROR --> SCH : schAddPagingIndtoList() : Slot[%d] is already present in the list", recvdNode->pageTxTime.slot);
2169 CM_LLIST_NEXT_NODE(storedPageList, firstNodeOfList);
2173 if(!firstNodeOfList)
2175 cmLListAdd2Tail(storedPageList, currentNodeInfo);
2177 DU_LOG("\nDEBUG --> SCH : Paging information is stored successfully for PF:%d, Slot:%d",\
2178 recvdNode->pageTxTime.sfn, recvdNode->pageTxTime.slot);
2182 /****************************************************************************
2184 * @brief Process paging indication at SCH recevied form MAC
2188 * Function : SchProcPagingInd
2190 * Functionality: Process paging indication at SCH recevied form MAC
2192 * @params[in] Pst *pst, SchPageInd *pageInd
2196 *************************************************************************/
2197 uint8_t SchProcPagingInd(Pst *pst, SchPageInd *pageInd)
2199 uint8_t ret = RFAILED;
2200 uint16_t cellIdx = 0;
2201 Inst inst = pst->dstInst - SCH_INST_START;
2202 SchCellCb *cellCb = NULLP;
2203 SchPageInfo *pageInfo = NULLP;
2207 DU_LOG("\nDEBUG --> SCH : Received paging indication from MAC for cellId[%d]",\
2211 for(cellIdx = 0; cellIdx < MAX_NUM_CELL; cellIdx++)
2213 if((schCb[inst].cells[cellIdx]) && (schCb[inst].cells[cellIdx]->cellId == pageInd->cellId))
2215 cellCb = schCb[inst].cells[cellIdx];
2221 if(pageInd->i_s > cellCb->cellCfg.dlCfgCommon.schPcchCfg.numPO)
2223 DU_LOG("\nERROR --> SCH : SchProcPagingInd(): i_s should not be greater than number of paging occasion");
2227 SCH_ALLOC(pageInfo, sizeof(SchPageInfo));
2230 pageInfo->pf = pageInd->pf;
2231 pageInfo->i_s = pageInd->i_s;
2232 pageInfo->pageTxTime.cellId = pageInd->cellId;
2233 pageInfo->pageTxTime.sfn = (pageInd->pf + cellCb->pageCb.pagMonOcc[pageInd->i_s].frameOffset) % MAX_SFN;
2234 pageInfo->pageTxTime.slot = cellCb->pageCb.pagMonOcc[pageInd->i_s].pagingOccSlot;
2235 pageInfo->mcs = DEFAULT_MCS;
2236 pageInfo->msgLen = pageInd->pduLen;
2237 SCH_ALLOC(pageInfo->pagePdu, pageInfo->msgLen);
2238 if(!pageInfo->pagePdu)
2240 DU_LOG("\nERROR --> SCH : SchProcPagingInd(): Failed to allocate memory");
2244 memcpy(pageInfo->pagePdu, pageInd->pagePdu, pageInfo->msgLen);
2245 ret = schAddPagingIndtoList(&cellCb->pageCb.pageIndInfoRecord[pageInfo->pageTxTime.sfn], pageInfo);
2248 DU_LOG("\nERROR --> SCH : SchProcPagingInd(): Failed to store paging record");
2254 DU_LOG("\nERROR --> SCH : SchProcPagingInd(): Failed to allocate memory");
2260 DU_LOG("\nERROR --> SCH : Cell ID [%d] not found", pageInd->cellId);
2262 SCH_FREE(pageInd->pagePdu, pageInd->pduLen);
2263 SCH_FREE(pageInd, sizeof(SchPageInd));
2267 DU_LOG("\nERROR --> SCH : SchProcPagingInd(): Received null pointer");
2273 /***********************************************************
2275 * Func : SchFillCfmPst
2278 * Desc : Fills the Confirmation Post Structure cfmPst using the reqPst
2279 * and the cfm->hdr.response.
2286 * File : rg_sch_lmm.c
2288 **********************************************************/
2298 inst = (reqPst->dstInst - SCH_INST_START);
2300 cfmPst->srcEnt = ENTMAC;
2301 cfmPst->srcInst = (Inst) 1;
2302 cfmPst->srcProcId = schCb[inst].schInit.procId;
2303 cfmPst->dstEnt = ENTMAC;
2304 cfmPst->dstInst = (Inst) 0;
2305 cfmPst->dstProcId = reqPst->srcProcId;
2307 cfmPst->selector = cfm->hdr.response.selector;
2308 cfmPst->region = cfm->hdr.response.mem.region;
2309 cfmPst->pool = cfm->hdr.response.mem.pool;
2314 /*******************************************************************
2316 * @brief Processes DL CQI ind from MAC
2320 * Function : SchProcDlCqiInd
2323 * Processes DL CQI ind from MAC
2326 * @return ROK - success
2329 * ****************************************************************/
2330 uint8_t SchProcDlCqiInd(Pst *pst, SchDlCqiInd *dlCqiInd)
2333 uint16_t ueId = 0, cellIdx = 0;
2334 SchUeCb *ueCb = NULLP;
2335 SchCellCb *cell = NULLP;
2336 Inst inst = pst->dstInst-SCH_INST_START;
2340 DU_LOG("\nERROR --> SCH : SchProcDlCqiInd(): CQI Ind is empty");
2345 GET_CELL_IDX(dlCqiInd->cellId, cellIdx);
2346 cell = schCb[inst].cells[cellIdx];
2349 DU_LOG("\nERROR --> SCH : SchProcDlCqiInd(): cell Id[%d] not found", dlCqiInd->cellId);
2354 if(cell->cellId == dlCqiInd->cellId)
2356 GET_UE_ID(dlCqiInd->crnti, ueId);
2357 ueCb = &cell->ueCb[ueId-1];
2358 if(ueCb->crnti != dlCqiInd->crnti)
2360 DU_LOG("\nERROR --> SCH : SchProcDlCqiInd(): UeCb for received crnti[%d] not found", dlCqiInd->crnti);
2365 /*TODO: complete the processing of DL CQI Ind*/
2370 DU_LOG("\nERROR --> SCH : SchProcDlCqiInd(): Received cell Id[%d] from MAC is not matching with CellID[%d] in SCH Cb",\
2371 dlCqiInd->cellId, cell->cellId);
2379 /*******************************************************************
2381 * @brief Processes UL CQI ind from MAC
2385 * Function : SchProcUlCqiInd
2388 * Processes UL CQI ind from MAC
2391 * @return ROK - success
2394 * ****************************************************************/
2395 uint8_t SchProcUlCqiInd(Pst *pst, SchUlCqiInd *ulCqiInd)
2398 uint16_t ueId = 0, cellIdx = 0;
2399 SchUeCb *ueCb = NULLP;
2400 SchCellCb *cell = NULLP;
2401 Inst inst = pst->dstInst-SCH_INST_START;
2405 DU_LOG("\nERROR --> SCH : SchProcUlCqiInd(): CQI Ind is empty");
2410 GET_CELL_IDX(ulCqiInd->cellId, cellIdx);
2411 cell = schCb[inst].cells[cellIdx];
2414 DU_LOG("\nERROR --> SCH : SchProcUlCqiInd(): cell Id[%d] not found", ulCqiInd->cellId);
2419 if(cell->cellId == ulCqiInd->cellId)
2421 GET_UE_ID(ulCqiInd->crnti, ueId);
2422 ueCb = &cell->ueCb[ueId-1];
2423 if(ueCb->crnti != ulCqiInd->crnti)
2425 DU_LOG("\nERROR --> SCH : SchProcUlCqiInd(): UeCb for received crnti[%d] not found",ulCqiInd->crnti);
2430 /*TODO: complete the processing of UL CQI Ind*/
2435 DU_LOG("\nERROR --> SCH : SchProcUlCqiInd(): Received cell Id[%d] from MAC is not matching with CellId[%d] in SCH Cb",\
2436 ulCqiInd->cellId, cell->cellId);
2444 /*******************************************************************
2446 * @brief Processes PHR ind from MAC
2450 * Function : SchProcPhrInd
2453 * Processes PHR ind from MAC
2456 * @return ROK - success
2459 * ****************************************************************/
2460 uint8_t SchProcPhrInd(Pst *pst, SchPwrHeadroomInd *schPhrInd)
2463 uint16_t ueId = 0, cellIdx = 0;
2464 SchUeCb *ueCb = NULLP;
2465 SchCellCb *cell = NULLP;
2466 Inst inst = pst->dstInst-SCH_INST_START;
2470 DU_LOG("\nERROR --> SCH : SchProcPhrInd(): PHR is empty");
2475 GET_CELL_IDX(schPhrInd->cellId, cellIdx);
2476 cell = schCb[inst].cells[cellIdx];
2479 DU_LOG("\nERROR --> SCH : schProcPhrInd(): cell Id[%d] is not found", schPhrInd->cellId);
2484 if(cell->cellId == schPhrInd->cellId)
2486 GET_UE_ID(schPhrInd->crnti, ueId);
2487 ueCb = &cell->ueCb[ueId-1];
2488 if(ueCb->crnti != schPhrInd->crnti)
2490 DU_LOG("\nERROR --> SCH : SchProcPhrInd(): UeCb for received crnti[%d] not found",schPhrInd->crnti);
2495 /*TODO: complete the processing of PHR Ind*/
2500 DU_LOG("\nERROR --> SCH : SchProcPhrInd(): Mismatch between Received cell Id[%d] from MAC and CellID[%d] in SCH CB ",\
2501 schPhrInd->cellId, cell->cellId);
2509 /*******************************************************************
2511 * @brief Fill and send statistics response to MAC
2515 * Function : SchSendStatsRspToMac
2517 * Functionality: Fill and send statistics response to MAC
2519 * @params[in] Inst inst, SchMacRsp result
2520 * @return ROK - success
2523 * ****************************************************************/
2524 uint8_t SchSendStatsRspToMac(SchStatsRsp *statsRsp)
2528 SchStatsRsp *schStatsRsp;
2530 DU_LOG("\nINFO --> SCH : Filling statistics response");
2531 SCH_ALLOC(schStatsRsp, sizeof(SchStatsRsp));
2532 if(schStatsRsp == NULLP)
2534 DU_LOG("\nERROR --> SCH : Failed to allocate memory in SchSendStatsRspToMac()");
2538 memcpy(schStatsRsp, statsRsp, sizeof(SchStatsRsp));
2539 memset(statsRsp, 0, sizeof(SchStatsRsp));
2541 /* Filling response post */
2542 memset(&rspPst, 0, sizeof(Pst));
2543 FILL_PST_SCH_TO_MAC(rspPst, inst);
2544 rspPst.event = EVENT_STATISTICS_RSP_TO_MAC;
2546 ret = MacMessageRouter(&rspPst, (void *)schStatsRsp);
2549 DU_LOG("\nERROR --> SCH : SchSendStatsRspToMac(): Failed to send Statistics Response");
2555 /*******************************************************************
2557 * @brief Rejects all statistics group requested by MAC
2561 * Function : SchRejectAllStats
2563 * Functionality: Add all statistics group received in statistics
2564 * request from MAC, to Reject-Stats-Group-List in statistics
2567 * @params[in] Statistics request from MAC
2568 * Cause of rejection
2569 * @return ROK - success
2572 * ****************************************************************/
2573 uint8_t SchRejectAllStats(SchStatsReq *schStatsReq, CauseOfResult cause)
2576 SchStatsRsp schStatsRsp;
2578 memset(&schStatsRsp, 0, sizeof(SchStatsRsp));
2580 /* Copying all stats group from stats request to stats response */
2581 schStatsRsp.subscriptionId = schStatsReq->subscriptionId;
2582 for(grpIdx = 0; grpIdx < schStatsReq->numStatsGroup; grpIdx++)
2584 schStatsRsp.statsGrpRejectedList[grpIdx].groupId = schStatsReq->statsGrpList[grpIdx].groupId;
2585 schStatsRsp.statsGrpRejectedList[grpIdx].cause = cause;
2587 schStatsRsp.numGrpRejected = schStatsReq->numStatsGroup;
2589 return SchSendStatsRspToMac(&schStatsRsp);
2592 /*******************************************************************
2594 * @brief Add active KPI pointers to KPI-Active-List
2598 * Function : schAddToKpiActiveList
2600 * Functionality: For each active statistics group for which timer
2601 * is running, add its KPI pointer to KPI-Active-List.
2603 * When it is needed to update a KPI parameters, we need not
2604 * traverse all statistics group and all KPIs within a group
2605 * to get the specific KPI pointer to be updated.
2606 * Instead, we can traverse through KPI-Active-List and update
2607 * all entries in this list.
2609 * @params[in] Statistics request from MAC
2610 * Cause of rejection
2611 * @return ROK - success
2614 * ****************************************************************/
2615 uint8_t schAddToKpiActiveList(Inst inst, SchStatsGrp *grpInfo)
2617 CmLList *node = NULLP;
2619 /* If DL Total PRB Usage configured for this stats group, add to list */
2620 if(grpInfo->kpiStats.dlTotalPrbUsage)
2622 SCH_ALLOC(node, sizeof(CmLList));
2625 node->node = (PTR)grpInfo->kpiStats.dlTotalPrbUsage;
2626 cmLListAdd2Tail(&schCb[inst].statistics.activeKpiList.dlTotPrbUseList, node);
2630 /* If UL Total PRB Usage configured for this stats group, add to list */
2632 if(grpInfo->kpiStats.ulTotalPrbUsage)
2634 SCH_ALLOC(node, sizeof(CmLList));
2637 node->node = (PTR)grpInfo->kpiStats.ulTotalPrbUsage;
2638 cmLListAdd2Tail(&schCb[inst].statistics.activeKpiList.ulTotPrbUseList, node);
2645 /*******************************************************************
2647 * @brief Processes Statistics Request from MAC
2651 * Function : SchProcStatsReq
2655 * This function process the statistics request from MAC:
2656 * [Step 1] Basic validation. If fails, all stats group in stats request are
2658 * [Step 2] If basic validations passed, traverse all stats group and
2659 * validate each measurement types in each group.
2660 * [Step 3] If any measurement type validation fails in a group, that group
2661 * is not configured and it is added to stats-group-rejected-list in
2662 * sch-stats-response message.
2663 * [Step 4] If a group passes all validation, it is added to SCH database.
2664 * A timer is started for this group. And the group is added to
2665 * stats-group-accepted-list in sch-stats-response message.
2666 * [Step 5] sch-stats-response is sent to du app with stats-group-rejected-list
2667 * and stats-group-accepted-list.
2669 * @params[in] Post structure
2670 * Statistics Request from MAC
2671 * @return ROK - success
2674 * ****************************************************************/
2675 uint8_t SchProcStatsReq(Pst *pst, SchStatsReq *statsReq)
2677 uint8_t grpIdx = 0, reqGrpIdx = 0, reqMeasIdx = 0;
2678 Inst inst = pst->dstInst - SCH_INST_START;
2679 bool measTypeInvalid;
2680 CauseOfResult cause;
2681 SchStatsInfo *statsInfo = NULLP;
2682 SchStatsGrpInfo *grpInfo = NULLP;
2683 SchStatsGrp *grpInfoDb = NULLP;
2684 SchStatsRsp schStatsRsp;
2686 DU_LOG("\nINFO --> SCH : Received Statistics Request from MAC");
2688 if(statsReq == NULLP)
2690 DU_LOG("\nERROR --> SCH : SchProcStatsReq(): Received Null pointer");
2694 /* [Step 1] Basic validation. If fails, all stats group in stats request are rejected */
2696 /* If maximum number of statistics already configured */
2697 if(schCb[inst].statistics.numOfStatsCfgd >= MAX_NUM_STATS_CFG)
2699 DU_LOG("\nERROR --> SCH : SchProcStatsReq: Maximum number of statistics configured. \
2700 Cannot process new request.");
2701 SchRejectAllStats(statsReq, RESOURCE_UNAVAILABLE);
2702 SCH_FREE(statsReq, sizeof(SchStatsReq));
2706 memset(&schStatsRsp, 0, sizeof(SchStatsRsp));
2708 /* [Step 2] Traverse all stats group and validate each measurement types in each group */
2709 statsInfo = &schCb[inst].statistics.statsInfoList[schCb[inst].statistics.numOfStatsCfgd];
2710 statsInfo->numStatsGroup = 0;
2711 for(reqGrpIdx=0; reqGrpIdx<statsReq->numStatsGroup && grpIdx<MAX_NUM_STATS; reqGrpIdx++)
2713 measTypeInvalid = false;
2714 grpInfo = &statsReq->statsGrpList[reqGrpIdx];
2715 grpInfoDb = &statsInfo->statsGrpList[grpIdx];
2716 for(reqMeasIdx = 0; reqMeasIdx < grpInfo->numStats; reqMeasIdx++)
2718 switch(grpInfo->statsList[reqMeasIdx])
2720 case SCH_DL_TOTAL_PRB_USAGE:
2722 /* Allocate memory */
2723 SCH_ALLOC(grpInfoDb->kpiStats.dlTotalPrbUsage, sizeof(TotalPrbUsage));
2724 if(!grpInfoDb->kpiStats.dlTotalPrbUsage)
2726 DU_LOG("\nERROR --> SCH : Memory allocation failed for dlTotalPrbUsage in \
2727 SchProcStatsReq()");
2728 measTypeInvalid = true;
2729 cause = RESOURCE_UNAVAILABLE;
2734 case SCH_UL_TOTAL_PRB_USAGE:
2736 /* Allocate memory */
2737 SCH_ALLOC(grpInfoDb->kpiStats.ulTotalPrbUsage, sizeof(TotalPrbUsage));
2738 if(!grpInfoDb->kpiStats.ulTotalPrbUsage)
2740 DU_LOG("\nERROR --> SCH : Memory allocation failed for dlTotalPrbUsage in \
2741 SchProcStatsReq()");
2742 measTypeInvalid = true;
2743 cause = RESOURCE_UNAVAILABLE;
2750 DU_LOG("\nERROR --> SCH : SchProcStatsReq: Invalid measurement type [%d]", \
2751 grpInfo->statsList[reqMeasIdx]);
2752 measTypeInvalid = true;
2753 cause = PARAM_INVALID;
2758 /* [Step 3 a] If any measurement type validation fails in a group, that group
2759 * is not configured */
2762 SCH_FREE(grpInfoDb->kpiStats.dlTotalPrbUsage, sizeof(TotalPrbUsage));
2763 SCH_FREE(grpInfoDb->kpiStats.ulTotalPrbUsage, sizeof(TotalPrbUsage));
2764 memset(grpInfoDb, 0, sizeof(SchStatsGrp));
2769 /* [Step 4] If a group passes all validation, it is added to SCH database.
2770 * A timer is started for this group. And the group is added to
2771 * stats-group-accepted-list in sch-stats-response message. */
2772 if(!measTypeInvalid)
2774 /* Add this group's configured KPIs to list of Active KPIs */
2775 if(schAddToKpiActiveList(inst, grpInfoDb) == ROK)
2777 grpInfoDb->schInst = inst;
2778 grpInfoDb->subscriptionId = statsReq->subscriptionId;
2779 grpInfoDb->groupId = grpInfo->groupId;
2780 grpInfoDb->periodicity = grpInfo->periodicity;
2784 cmInitTimers(&(grpInfoDb->periodTimer), 1);
2785 schStartTmr(&schCb[inst], (PTR)(grpInfoDb), EVENT_STATISTICS_TMR, grpInfoDb->periodicity);
2787 schStatsRsp.statsGrpAcceptedList[schStatsRsp.numGrpAccepted] = grpInfo->groupId;
2788 schStatsRsp.numGrpAccepted++;
2793 memset(grpInfoDb, 0, sizeof(SchStatsGrp));
2798 /* [Step 3 b] The rejected group is added to stats-group-rejected-list in
2799 * sch-stats-response message */
2800 memset(grpInfoDb, 0, sizeof(SchStatsGrp));
2801 schStatsRsp.statsGrpRejectedList[schStatsRsp.numGrpRejected].groupId = grpInfo->groupId;
2802 schStatsRsp.statsGrpRejectedList[schStatsRsp.numGrpRejected].cause = cause;
2803 schStatsRsp.numGrpRejected++;
2806 statsInfo->numStatsGroup = grpIdx;
2807 if(statsInfo->numStatsGroup)
2809 schCb[inst].statistics.numOfStatsCfgd++;
2813 memset(statsInfo, 0, sizeof(SchStatsInfo));
2815 schStatsRsp.subscriptionId = statsReq->subscriptionId;
2817 SCH_FREE(statsReq, sizeof(SchStatsReq));
2819 /* [Step 5] sch-stats-response is sent to du app with stats-group-rejected-list
2820 * and stats-group-accepted-list. */
2821 SchSendStatsRspToMac(&schStatsRsp);
2824 } /* End of SchProcStatsReq */
2826 /*******************************************************************
2828 * @brief Fill and send statistics indication to MAC
2832 * Function : SchSendStatsIndToMac
2834 * Functionality: Fill and send statistics indication to MAC
2836 * @params[in] SCH Instance
2839 * Size of value parameter
2840 * @return ROK - success
2843 * ****************************************************************/
2844 uint8_t SchSendStatsIndToMac(Inst inst, SchStatsInd *statsInd)
2850 DU_LOG("\nDEBUG --> SCH : Filling statistics indication");
2853 /* Filling post structure */
2854 memset(&pst, 0, sizeof(Pst));
2855 FILL_PST_SCH_TO_MAC(pst, inst);
2856 pst.event = EVENT_STATISTICS_IND_TO_MAC;
2858 ret = MacMessageRouter(&pst, (void *)statsInd);
2861 DU_LOG("\nERROR --> SCH : SchSendStatsIndToMac(): Failed to send Statistics Indication");
2867 * @brief Handler to process Timer expiry of DL Total PRB Usage calculation
2869 * @param[in] cb Control block depending on the type of the timer event.
2870 * @param[in] tmrEvnt Timer event to be started
2872 * @return Bool indicating whether the timer is running or not
2876 double calcDlTotalPrbUsage(TotalPrbUsage *dlTotalPrbUsage)
2878 double percentageOfTotalPrbUsed = 0;
2880 if(dlTotalPrbUsage->totalPrbAvailForTx)
2881 percentageOfTotalPrbUsed = ((100.0 * dlTotalPrbUsage->numPrbUsedForTx) / dlTotalPrbUsage->totalPrbAvailForTx);
2883 memset(dlTotalPrbUsage, 0, sizeof(TotalPrbUsage));
2885 return percentageOfTotalPrbUsed;
2889 * @brief Handler to check if the timer is running
2891 * @param[in] cb Control block depending on the type of the timer event.
2892 * @param[in] tmrEvnt Timer event to be started
2894 * @return Bool indicating whether the timer is running or not
2898 uint8_t calcUlTotalPrbUsage(TotalPrbUsage *ulTotalPrbUsage)
2900 double percentageOfTotalPrbUsed = 0;
2902 if(ulTotalPrbUsage->totalPrbAvailForTx)
2903 percentageOfTotalPrbUsed = ((100.0 * ulTotalPrbUsage->numPrbUsedForTx) / ulTotalPrbUsage->totalPrbAvailForTx);
2905 memset(ulTotalPrbUsage, 0, sizeof(TotalPrbUsage));
2907 return percentageOfTotalPrbUsed;
2911 * @brief Calculates statistics in a group and sends
2912 * statistics indication for this group
2914 * @param[in] Statistics group info
2920 uint8_t schCalcAndSendGrpStats(SchStatsGrp *grpInfo)
2922 uint8_t measStatsIdx = 0;
2923 SchStatsInd statsInd;
2925 memset(&statsInd, 0, sizeof(SchStatsInd));
2926 statsInd.subscriptionId = grpInfo->subscriptionId;
2927 statsInd.groupId = grpInfo->groupId;
2929 if(grpInfo->kpiStats.dlTotalPrbUsage)
2931 statsInd.measuredStatsList[measStatsIdx].type = SCH_DL_TOTAL_PRB_USAGE;
2932 statsInd.measuredStatsList[measStatsIdx].value = calcDlTotalPrbUsage(grpInfo->kpiStats.dlTotalPrbUsage);
2936 if(grpInfo->kpiStats.ulTotalPrbUsage)
2938 statsInd.measuredStatsList[measStatsIdx].type = SCH_UL_TOTAL_PRB_USAGE;
2939 statsInd.measuredStatsList[measStatsIdx].value = calcUlTotalPrbUsage(grpInfo->kpiStats.ulTotalPrbUsage);
2943 statsInd.numStats = measStatsIdx;
2945 return SchSendStatsIndToMac(grpInfo->schInst, &statsInd);
2948 /**********************************************************************
2950 **********************************************************************/