[Epic-ID: ODUHIGH-516][Task-ID: ODUHIGH-523] KPI reporting per statistics group 87/11787/4
authorlal.harshita <Harshita.Lal@radisys.com>
Tue, 12 Sep 2023 09:56:24 +0000 (15:26 +0530)
committerlal.harshita <Harshita.Lal@radisys.com>
Wed, 13 Sep 2023 12:31:11 +0000 (18:01 +0530)
Change-Id: I0e43ebd13a1e1b5a082b0fa342fb547acc7bfc31
Signed-off-by: lal.harshita <Harshita.Lal@radisys.com>
13 files changed:
src/5gnrmac/mac.h
src/5gnrmac/mac_cfg_hdl.c
src/5gnrmac/mac_msg_hdl.c
src/5gnrsch/sch.c
src/5gnrsch/sch.h
src/5gnrsch/sch_common.c
src/5gnrsch/sch_slot_ind.c
src/5gnrsch/sch_tmr.c
src/5gnrsch/sch_tmr.h
src/cm/common_def.h
src/cm/du_app_mac_inf.h
src/cm/mac_sch_interface.h
src/du_app/du_msg_hdl.c

index a245358..a4dc320 100644 (file)
@@ -26,6 +26,7 @@
 #define MAX_ZERO_CORR_CFG_IDX 16 /* max zero correlation config index */
 #define MAC_TQ_SIZE    10        /* Timing Queue Size */
 #define MAX_NUM_TIMER  1         /* MAX number of MAC timers */
+#define MAX_PENDING_STATS_RSP  5      /* Maximum number of statistics request for which response is pending */
 
 #define DEFAULT_CELLS 1
 #define SI_RNTI 0xFFFF
@@ -59,6 +60,7 @@
 #define LC_ID_SIZE 6
 #define TIMING_ADVANCE_SIZE 12
 #define T_CRNTI_SIZE 16
+
 /*  UL Grant is of size = 27 bits. Refer to Spec 38.213, Table 8.2-1 for
  *  contents of UL grant in RAR */
 #define FREQ_HOP_FLAG_SIZE 1
@@ -245,6 +247,15 @@ struct macCellCb
    SlotTimingInfo  currTime;
 };
 
+typedef struct macStatistics
+{
+   uint8_t      numPendingStatsRsp;
+   MacStatsRsp  pendingStatsRsp[MAX_PENDING_STATS_RSP];
+
+   /* This structure can be developed in future to add details of
+    * the statistics to be calculated at MAC */
+}MacStatistics;
+
 typedef struct macCb
 {
    Inst        macInst;
@@ -254,6 +265,7 @@ typedef struct macCb
    CmTqType    tmrTq[MAC_TQ_SIZE];        /*!< Timer Task Queue */
    CmTimer     tmrBlk[MAX_NUM_TIMER];     /*!< Timer Block */
    MacCellCb   *macCell[MAX_NUM_CELL];
+   MacStatistics  statistics;
 }MacCb;
 
 /* global variable */
index 589f730..0572fd7 100644 (file)
@@ -1037,12 +1037,14 @@ uint8_t MacProcDlBroadcastReq(Pst *pst, MacDlBroadcastReq *dlBroadcastReq)
  *  @return  int
  *      -# ROK
  **/
-uint8_t MacSendStatsRspToDuApp(MacRsp rsp, CauseOfResult cause)
+uint8_t MacSendStatsRspToDuApp(MacStatsRsp *statsRsp)
 {
    uint8_t ret = ROK;
    Pst  pst;
    MacStatsRsp *macStatsRsp = NULLP;
 
+    DU_LOG("\nINFO  -->  MAC : MacSendStatsRspToDuApp: Sending Statistics Response to DU APP");
+
    /* Workaround : To skip corrupted memory, allocating a pointer that will
     * remain unused */
    uint8_t *dummyPtr = NULLP;
@@ -1056,8 +1058,8 @@ uint8_t MacSendStatsRspToDuApp(MacRsp rsp, CauseOfResult cause)
    }
    else
    {
-      macStatsRsp->rsp = rsp;
-      macStatsRsp->cause = cause;
+      memcpy(macStatsRsp, statsRsp, sizeof(MacStatsRsp));
+      memset(statsRsp, 0, sizeof(MacStatsRsp));
 
       memset(&pst, 0, sizeof(Pst));
       FILL_PST_MAC_TO_DUAPP(pst, EVENT_MAC_STATISTICS_RSP);
@@ -1074,6 +1076,43 @@ uint8_t MacSendStatsRspToDuApp(MacRsp rsp, CauseOfResult cause)
    return ret;
 }
 
+/*******************************************************************
+ *
+ * @brief Rejects all statistics group requested by DU APP
+ *
+ * @details
+ *
+ *    Function : MacRejectAllStats
+ *
+ *    Functionality: Add all statistics group received in statistics
+ *       request from DU APP, to Reject-Stats-Group-List in statistics
+ *       response to DU APP
+ *
+ * @params[in]  Statistics request from DU APP
+ *              Cause of rejection
+ * @return ROK     - success
+ *         RFAILED - failure
+ *
+ * ****************************************************************/
+uint8_t MacRejectAllStats(MacStatsReq *macStatsReq, CauseOfResult cause)
+{
+   uint8_t grpIdx = 0;
+   MacStatsRsp macStatsRsp;
+
+   memset(&macStatsRsp, 0, sizeof(MacStatsRsp));
+
+   /* Copying all stats group from stats request to stats response */
+   macStatsRsp.subscriptionId = macStatsReq->subscriptionId;
+   for(grpIdx = 0; grpIdx < macStatsReq->numStatsGroup; grpIdx++)
+   {
+      macStatsRsp.statsGrpRejectedList[grpIdx].groupId = macStatsReq->statsGrpList[grpIdx].groupId;
+      macStatsRsp.statsGrpRejectedList[grpIdx].cause = cause;
+   }
+   macStatsRsp.numGrpRejected = macStatsReq->numStatsGroup;
+
+   return MacSendStatsRspToDuApp(&macStatsRsp);
+}
+
 /**
  * @brief Mac process the statistics Req received from DUAPP
  *
@@ -1081,7 +1120,20 @@ uint8_t MacSendStatsRspToDuApp(MacRsp rsp, CauseOfResult cause)
  *
  *     Function : MacProcStatsReq
  *
- *     This function process the statistics request from duapp
+ *     This function process the statistics request from duapp:
+ *     [Step 1] Basic validation. If fails, all stats group in stats request are
+ *     rejected.
+ *     [Step 2] If basic validations passed, traverse all stats group and
+ *     validate each measurement types in each group.
+ *     [Step 3] If any measurement type validation fails in a group, that group 
+ *     is not configured and it is added to stats-group-rejected-list in
+ *     mac-stats-response message.
+ *     [Step 4] Even if one group passes all validation, it is sent to SCH in
+ *     statistics request. The mac-stats-response message is added to
+ *     pending-response list. This will be sent to DU APP after stats response
+ *     is received from SCH.
+ *     [Step 5] If none of the groups passes all validation, mac-stats-response
+ *     is sent to du app with all group as part of stats-group-rejected-list.
  *
  *  @param[in]  Pst      *pst
  *  @param[in]  StatsReq *statsReq
@@ -1090,85 +1142,134 @@ uint8_t MacSendStatsRspToDuApp(MacRsp rsp, CauseOfResult cause)
  **/
 uint8_t MacProcStatsReq(Pst *pst, MacStatsReq *macStatsReq)
 {
-   uint8_t   macStatsIdx = 0, schStatsIdx = 0;
-   uint8_t   ret = RFAILED;
-   bool      measTypeInvalid = false;
-   Pst       schPst;
-   SchStatsReq  *schStatsReq = NULLP;
-   CauseOfResult cause;
+   uint8_t       macStatsGrpIdx = 0, macStatsIdx = 0, schStatsGrpIdx = 0, schStatsIdx = 0;
+   uint8_t       ret = RFAILED;
+   bool          measTypeInvalid = false;
+   Pst           schPst;
+   MacStatsGrpInfo *macStatsGrp = NULLP;
+   SchStatsReq     *schStatsReq = NULLP;
+   MacStatsRsp     *macStatsRsp = NULLP;
 
-   if(macStatsReq)
+   DU_LOG("\nINFO   -->  MAC : Received Statistics Request from DU_APP");
+
+   if(macStatsReq == NULLP)
    {
-      DU_LOG("\nINFO   -->  MAC : Received Statistics Request from DU_APP");
+      DU_LOG("\nERROR  -->  MAC : MacProcStatsReq(): Received Null pointer");
+      return RFAILED;
+   }
+   
+   /* [Step 1] Basic validation. If fails, statistics response is sent to DU APP
+    * that rejectes all stats */
 
-      MAC_ALLOC(schStatsReq, sizeof(SchStatsReq));
-      if(schStatsReq == NULLP)
-      {
-         DU_LOG("\nERROR  -->  MAC : MacProcStatsReq: Failed to allocate memory");
-         cause = RESOURCE_UNAVAILABLE;
-      }
-      else
-      {
-         schStatsReq->numStats = 0;
-         for(macStatsIdx=0; macStatsIdx < macStatsReq->numStats; macStatsIdx++)
-         {
-            /* Checking each measurement type to send only SCH related
-             * measurement config to SCH
-             * This will be useful in future when some measurement type will
-             * be configured for SCH and rest for only MAC */
-            switch(macStatsReq->statsList[macStatsIdx].type)
-            {
-               case MAC_DL_TOTAL_PRB_USAGE:
-                  {
-                     schStatsReq->statsList[schStatsIdx].type = SCH_DL_TOTAL_PRB_USAGE;
-                     break;
-                  }
-               case MAC_UL_TOTAL_PRB_USAGE:
-                  {
-                     schStatsReq->statsList[schStatsIdx].type = SCH_UL_TOTAL_PRB_USAGE;
-                     break;
-                  }
-               default:
-                  {
-                     DU_LOG("\nERROR  -->  MAC : MacProcStatsReq: Invalid measurement type [%d]", \
-                        macStatsReq->statsList[macStatsIdx].type);
-                     measTypeInvalid = true;
-                  }
-            }
+   /* If number of statistics request for which response is still pending
+    * towards DU APP has reached its maximum limit */
+   if(macCb.statistics.numPendingStatsRsp >= MAX_PENDING_STATS_RSP)
+   {
+      DU_LOG("\nERROR  -->  MAC : MacProcStatsReq: Maximum number of statistics response is pending. \
+         Cannot process new request."); 
+      MacRejectAllStats(macStatsReq, RESOURCE_UNAVAILABLE);
+      MAC_FREE_SHRABL_BUF(pst->region, pst->pool, macStatsReq, sizeof(MacStatsReq));
+      return RFAILED;
+   }
 
-            if(!measTypeInvalid)
-            {
-               schStatsReq->statsList[schStatsIdx].periodicity = macStatsReq->statsList[macStatsIdx].periodicity;
-               schStatsIdx++;
-               measTypeInvalid = false;
-            }
-         }
-         schStatsReq->numStats = schStatsIdx;
+   /* If memory resources are unavailable */
+   MAC_ALLOC(schStatsReq, sizeof(SchStatsReq));
+   if(schStatsReq == NULLP)
+   {
+      DU_LOG("\nERROR  -->  MAC : MacProcStatsReq: Failed to allocate memory");
+      MacRejectAllStats(macStatsReq, RESOURCE_UNAVAILABLE);
+      MAC_FREE_SHRABL_BUF(pst->region, pst->pool, macStatsReq, sizeof(MacStatsReq));
+      return RFAILED;
+   }
+
+   /* Add stats response to pending response list */ 
+   macStatsRsp = &macCb.statistics.pendingStatsRsp[macCb.statistics.numPendingStatsRsp];
+   memset(macStatsRsp, 0, sizeof(MacStatsRsp));
+
+   /* [Step 2] Traverse all stats group and validate each measurement types in each group */
+   schStatsReq->subscriptionId = macStatsReq->subscriptionId;
+   schStatsReq->numStatsGroup = 0;
+   for(macStatsGrpIdx = 0; macStatsGrpIdx < macStatsReq->numStatsGroup; macStatsGrpIdx++)
+   {
+      measTypeInvalid = false;
+      schStatsIdx = 0;
+      macStatsGrp = &macStatsReq->statsGrpList[macStatsGrpIdx];
 
-         /* If no measurement types are valid, it is failure scenario.
-          * Even if one measurement type is valid, send to SCH */
-         if(schStatsReq->numStats)
+      for(macStatsIdx=0; macStatsIdx < macStatsGrp->numStats; macStatsIdx++)
+      {
+         /* Validate each measurement type */
+         switch(macStatsGrp->statsList[macStatsIdx])
          {
-            FILL_PST_MAC_TO_SCH(schPst, EVENT_STATISTICS_REQ_TO_SCH);
-            ret = SchMessageRouter(&schPst, (void *)schStatsReq);
+            case MAC_DL_TOTAL_PRB_USAGE:
+               {
+                  schStatsReq->statsGrpList[schStatsGrpIdx].statsList[schStatsIdx] = SCH_DL_TOTAL_PRB_USAGE;
+                  break;
+               }
+            case MAC_UL_TOTAL_PRB_USAGE:
+               {
+                  schStatsReq->statsGrpList[schStatsGrpIdx].statsList[schStatsIdx] = SCH_UL_TOTAL_PRB_USAGE;
+                  break;
+               }
+            default:
+               {
+                  DU_LOG("\nERROR  -->  MAC : MacProcStatsReq: Invalid measurement type [%d]", \
+                     macStatsGrp->statsList[macStatsIdx]);
+                  measTypeInvalid = true;
+               }
          }
-         else
+
+         /* Even if one measurement type is invalid, this group is rejected */
+         if(measTypeInvalid)
          {
-            cause = PARAM_INVALID;
+            memset(&schStatsReq->statsGrpList[schStatsGrpIdx], 0, sizeof(SchStatsGrpInfo));
+            break;
          }
+         
+         schStatsIdx++;
+      }
+
+      /* If all measurement type is valid, add group info to send to SCH */
+      if(!measTypeInvalid)
+      {
+         schStatsReq->statsGrpList[schStatsGrpIdx].groupId = macStatsGrp->groupId;
+         schStatsReq->statsGrpList[schStatsGrpIdx].periodicity = macStatsGrp->periodicity;
+         schStatsReq->statsGrpList[schStatsGrpIdx].numStats = schStatsIdx;
+         schStatsGrpIdx++;
+      }
+      else
+      {
+         /* [Step 3] If any measurement type validation fails in a group, that group 
+          * is not configured and it is added to stats-group-rejected-list in
+          * mac-stats-response message */
+         macStatsRsp->statsGrpRejectedList[macStatsRsp->numGrpRejected].groupId = macStatsGrp->groupId;
+         macStatsRsp->statsGrpRejectedList[macStatsRsp->numGrpRejected].cause = PARAM_INVALID;
+         macStatsRsp->numGrpRejected++;
       }
-      MAC_FREE_SHRABL_BUF(pst->region, pst->pool, macStatsReq, sizeof(MacStatsReq));
    }
-   else
+   schStatsReq->numStatsGroup = schStatsGrpIdx;
+
+   macStatsRsp->subscriptionId = macStatsReq->subscriptionId;
+
+   if(schStatsReq->numStatsGroup)
    {
-      DU_LOG("\nERROR  -->  MAC : MacProcStatsReq(): Received Null pointer");
-      cause = PARAM_INVALID;
-   }
+      /* [Step 4] Even if one group passes all validation, it is sent to SCH in
+       * statistics request. The mac-stats-response message is added to
+       * pending-response list. */     
+      macCb.statistics.numPendingStatsRsp++;
 
-   if(ret == RFAILED)
+      FILL_PST_MAC_TO_SCH(schPst, EVENT_STATISTICS_REQ_TO_SCH);
+      ret = SchMessageRouter(&schPst, (void *)schStatsReq);
+   }
+   else
    {
-      MacSendStatsRspToDuApp(MAC_DU_APP_RSP_NOK, cause);
+      /* [Step 5] If none of the groups passes all validation, mac-stats-response
+       * is sent to du app with all group as part of stats-group-rejected-list. */
+      DU_LOG("\nERROR  -->  MAC : MacProcStatsReq: All statistics group found invalid");
+      MAC_FREE(schStatsReq, sizeof(SchStatsReq));
+      ret = MacSendStatsRspToDuApp(macStatsRsp);
    }
+
+   MAC_FREE_SHRABL_BUF(pst->region, pst->pool, macStatsReq, sizeof(MacStatsReq));
    return ret;
 }
 
@@ -1188,17 +1289,49 @@ uint8_t MacProcStatsReq(Pst *pst, MacStatsReq *macStatsReq)
  **/
 uint8_t MacProcSchStatsRsp(Pst *pst, SchStatsRsp *schStatsRsp)
 {
+   uint8_t idx = 0, accptdIdx = 0, rjctdIdx = 0;
    uint8_t ret = RFAILED;
+   MacStatsRsp *macStatsRsp = NULLP;
 
    if(schStatsRsp)
    {
-      if(schStatsRsp->rsp == RSP_OK)
-         ret = MacSendStatsRspToDuApp(MAC_DU_APP_RSP_OK, schStatsRsp->cause);
-      else
-         ret = MacSendStatsRspToDuApp(MAC_DU_APP_RSP_NOK, schStatsRsp->cause);
+      /* Fetch pointer to statistics response from pending list saved at MAC 
+       * during processing statistics request from DU APP */ 
+      for(idx = 0; idx < macCb.statistics.numPendingStatsRsp; idx++)
+      {
+         if(macCb.statistics.pendingStatsRsp[idx].subscriptionId == schStatsRsp->subscriptionId)
+         {
+            macStatsRsp = &macCb.statistics.pendingStatsRsp[idx];
+            break;
+         }
+      }
 
-      MAC_FREE(schStatsRsp, sizeof(SchStatsRsp));
+      if(macStatsRsp == NULLP)
+      {
+         MAC_FREE(schStatsRsp, sizeof(SchStatsRsp));
+         return RFAILED;
+      }
+      
+      /* Copy Stats-group-accpeted list received from SCH */
+      for(accptdIdx = 0; accptdIdx<schStatsRsp->numGrpAccepted && macStatsRsp->numGrpAccepted<MAX_NUM_STATS_GRP; accptdIdx++)
+      {
+         macStatsRsp->statsGrpAcceptedList[macStatsRsp->numGrpAccepted++] = schStatsRsp->statsGrpAcceptedList[accptdIdx];
+      }
+
+      /* List together all stats group rejected by MAC and by SCH */
+      for(rjctdIdx = 0; rjctdIdx < schStatsRsp->numGrpRejected && macStatsRsp->numGrpRejected<MAX_NUM_STATS_GRP; rjctdIdx++)
+      {
+         macStatsRsp->statsGrpRejectedList[macStatsRsp->numGrpRejected].groupId = \
+            schStatsRsp->statsGrpRejectedList[rjctdIdx].groupId;
+         macStatsRsp->statsGrpRejectedList[macStatsRsp->numGrpRejected].cause = \
+            schStatsRsp->statsGrpRejectedList[rjctdIdx].cause;
+         macStatsRsp->numGrpRejected++;
+      }
+      
+      /* Send statistics response to DU APP */
+      ret = MacSendStatsRspToDuApp(macStatsRsp);
    }
+   MAC_FREE(schStatsRsp, sizeof(SchStatsRsp));
    return ret;
 }
 
index f5e6649..d5544ba 100644 (file)
@@ -1081,6 +1081,7 @@ uint8_t MacProcSliceRecfgReq(Pst *pst, MacSliceRecfgReq *macSliceRecfgReq)
  **/
 uint8_t MacProcSchStatsInd(Pst *pst, SchStatsInd *schStatsInd)
 {
+   uint8_t statsIdx = 0;
    Pst indPst;
    MacStatsInd *macStatsInd;
 
@@ -1101,26 +1102,34 @@ uint8_t MacProcSchStatsInd(Pst *pst, SchStatsInd *schStatsInd)
       return RFAILED;
    }
 
-   switch(schStatsInd->type)
+   macStatsInd->subscriptionId = schStatsInd->subscriptionId;
+   macStatsInd->groupId = schStatsInd->groupId;
+
+   for(statsIdx = 0; statsIdx < schStatsInd->numStats; statsIdx++)
    {
-      case SCH_DL_TOTAL_PRB_USAGE:
-         {
-            macStatsInd->type = MAC_DL_TOTAL_PRB_USAGE;
-            break;
-         }
-      case SCH_UL_TOTAL_PRB_USAGE:
-         {
-            macStatsInd->type = MAC_UL_TOTAL_PRB_USAGE;
-            break;
-         }
-      default:
-         {
-            DU_LOG("\nERROR  -->  MAC : MacProcSchStatsInd: Invalid measurement type [%d]", schStatsInd->type);
-            MAC_FREE_SHRABL_BUF(MAC_MEM_REGION, MAC_POOL, macStatsInd, sizeof(MacStatsInd));
-            return RFAILED;
-         }
+      switch(schStatsInd->measuredStatsList[statsIdx].type)
+      {
+         case SCH_DL_TOTAL_PRB_USAGE:
+            {
+               macStatsInd->measuredStatsList[statsIdx].type = MAC_DL_TOTAL_PRB_USAGE;
+               break;
+            }
+         case SCH_UL_TOTAL_PRB_USAGE:
+            {
+               macStatsInd->measuredStatsList[statsIdx].type = MAC_UL_TOTAL_PRB_USAGE;
+               break;
+            }
+         default:
+            {
+               DU_LOG("\nERROR  -->  MAC : MacProcSchStatsInd: Invalid measurement type [%d]", \
+                     schStatsInd->measuredStatsList[statsIdx].type);
+               MAC_FREE_SHRABL_BUF(MAC_MEM_REGION, MAC_POOL, macStatsInd, sizeof(MacStatsInd));
+               return RFAILED;
+            }
+      }
+      macStatsInd->measuredStatsList[statsIdx].value = schStatsInd->measuredStatsList[statsIdx].value;
    }
-   macStatsInd->value = schStatsInd->value;
+   macStatsInd->numStats = schStatsInd->numStats;
 
    memset(&indPst, 0, sizeof(Pst));
    FILL_PST_MAC_TO_DUAPP(indPst, EVENT_MAC_STATISTICS_IND);
index 397e862..9017f92 100644 (file)
@@ -131,6 +131,11 @@ uint8_t SchInstCfg(RgCfg *cfg, Inst  dInst)
       return (LCM_REASON_MEM_NOAVAIL);
    }   
 
+   /* Initialize statistics related configurations */
+   memset(&schCb[inst].statistics, 0, sizeof(SchStatistics));
+   cmLListInit(&schCb[inst].statistics.activeKpiList.dlTotPrbUseList);
+   cmLListInit(&schCb[inst].statistics.activeKpiList.ulTotPrbUseList);
+
    /* Set Config done in TskInit */
    schCb[inst].schInit.cfgDone = TRUE;
    DU_LOG("\nINFO   -->  SCH : Scheduler gen config done");
@@ -1383,7 +1388,7 @@ uint8_t allocatePrbDl(SchCellCb *cell, SlotTimingInfo slotTime, \
    }
    
    /* Update statistics of PRB usage if stats calculation is enabled */
-   if(schCb[cell->instIdx].statistics.dlTotalPrbUsage)
+   if(schCb[cell->instIdx].statistics.activeKpiList.dlTotPrbUseList.count)
       prbAlloc->numPrbAlloc += numPrb;
 
    /* Update the remaining number for free PRBs */
@@ -1520,7 +1525,7 @@ uint8_t allocatePrbUl(SchCellCb *cell, SlotTimingInfo slotTime, \
    }
 
    /* Update statistics of PRB usage if stats calculation is enabled */
-   if(schCb[cell->instIdx].statistics.ulTotalPrbUsage)
+   if(schCb[cell->instIdx].statistics.activeKpiList.ulTotPrbUseList.count)
       prbAlloc->numPrbAlloc += numPrb;
 
    /* Update the remaining number for free PRBs */
@@ -2516,29 +2521,29 @@ uint8_t SchProcPhrInd(Pst *pst, SchPwrHeadroomInd *schPhrInd)
  *         RFAILED - failure
  *
  * ****************************************************************/
-uint8_t SchSendStatsRspToMac(Inst inst, SchMacRsp result, CauseOfResult cause)
+uint8_t SchSendStatsRspToMac(SchStatsRsp *statsRsp)
 {
    Pst rspPst;
    uint8_t ret = ROK;
-   SchStatsRsp  *statsRsp;
+   SchStatsRsp  *schStatsRsp;
 
    DU_LOG("\nINFO   --> SCH : Filling statistics response");
-   SCH_ALLOC(statsRsp, sizeof(SchStatsRsp));
-   if(statsRsp == NULLP)
+   SCH_ALLOC(schStatsRsp, sizeof(SchStatsRsp));
+   if(schStatsRsp == NULLP)
    {
       DU_LOG("\nERROR  --> SCH : Failed to allocate memory in SchSendStatsRspToMac()");
       return RFAILED;
    }
+   memcpy(schStatsRsp, statsRsp, sizeof(SchStatsRsp));
    memset(statsRsp, 0, sizeof(SchStatsRsp));
-   statsRsp->rsp = result;
-   statsRsp->cause = cause;
 
    /* Filling response post */
    memset(&rspPst, 0, sizeof(Pst));
    FILL_PST_SCH_TO_MAC(rspPst, inst);
    rspPst.event = EVENT_STATISTICS_RSP_TO_MAC;
 
-   ret = MacMessageRouter(&rspPst, (void *)statsRsp);
+   ret = MacMessageRouter(&rspPst, (void *)schStatsRsp);
    if(ret == RFAILED)
    {
       DU_LOG("\nERROR  -->  SCH : SchSendStatsRspToMac(): Failed to send Statistics Response");
@@ -2549,143 +2554,271 @@ uint8_t SchSendStatsRspToMac(Inst inst, SchMacRsp result, CauseOfResult cause)
 
 /*******************************************************************
  *
- * @brief Processes Statistics Request from MAC
+ * @brief Rejects all statistics group requested by MAC
  *
  * @details
  *
- *    Function : SchProcStatsReq
+ *    Function : SchRejectAllStats
  *
- *    Functionality:
- *       Processes Statistics Request from MAC
+ *    Functionality: Add all statistics group received in statistics
+ *       request from MAC, to Reject-Stats-Group-List in statistics
+ *       response to MAC
  *
- * @params[in] 
+ * @params[in]  Statistics request from MAC
+ *              Cause of rejection
  * @return ROK     - success
  *         RFAILED - failure
  *
  * ****************************************************************/
-uint8_t SchProcStatsReq(Pst *pst, SchStatsReq *statsReq)
+uint8_t SchRejectAllStats(SchStatsReq *schStatsReq, CauseOfResult cause)
 {
-   uint8_t idx;
-   Inst    inst = pst->dstInst - SCH_INST_START;
-   SchMacRsp     rsp = RSP_OK;
-   CauseOfResult cause = SUCCESSFUL;
-   bool isDlTotlPrbUseCfgd = false, isUlTotlPrbUseCfgd = false;
+   uint8_t grpIdx = 0;
+   SchStatsRsp schStatsRsp;
 
-   DU_LOG("\nINFO   -->  SCH : Received Statistics Request from MAC");
+   memset(&schStatsRsp, 0, sizeof(SchStatsRsp));
 
-   for(idx=0; idx < statsReq->numStats; idx++)
+   /* Copying all stats group from stats request to stats response */
+   schStatsRsp.subscriptionId = schStatsReq->subscriptionId;
+   for(grpIdx = 0; grpIdx < schStatsReq->numStatsGroup; grpIdx++)
    {
-      switch(statsReq->statsList[idx].type)
-      {
-         case SCH_DL_TOTAL_PRB_USAGE:
-            {
-               /* Check if duplicate configuration */
-               if(schCb[inst].statistics.dlTotalPrbUsage)
-               {
-                  DU_LOG("\nERROR   -->  SCH : SCH_DL_TOTAL_PRB_USAGE stats already configured");
-                  rsp = RSP_NOK;
-                  cause = DUPLICATE_ENTRY;
-               }
-
-               /* Allocate memory */
-               SCH_ALLOC(schCb[inst].statistics.dlTotalPrbUsage, sizeof(TotalPrbUsage));
-               if(!schCb[inst].statistics.dlTotalPrbUsage)
-               {
-                  DU_LOG("\nERROR   -->  SCH : Memory allocation failed for dlTotalPrbUsage in \
-                        SchProcStatsReq()");
-                  rsp = RSP_NOK;
-                  cause = RESOURCE_UNAVAILABLE;
-                  break;
-               }
-
-               /* Initialize */
-               memset(schCb[inst].statistics.dlTotalPrbUsage, 0, sizeof(TotalPrbUsage));
-
-               /* Configure */
-               schCb[inst].statistics.dlTotalPrbUsage->schInst = inst;
-               schCb[inst].statistics.dlTotalPrbUsage->periodicity = statsReq->statsList[idx].periodicity;
-               cmInitTimers(&(schCb[inst].statistics.dlTotalPrbUsage->periodTimer), 1);
+      schStatsRsp.statsGrpRejectedList[grpIdx].groupId = schStatsReq->statsGrpList[grpIdx].groupId;
+      schStatsRsp.statsGrpRejectedList[grpIdx].cause = cause;
+   }
+   schStatsRsp.numGrpRejected = schStatsReq->numStatsGroup;
 
-               /* Start timer */
-               schStartTmr(&schCb[inst], (PTR)(schCb[inst].statistics.dlTotalPrbUsage), \
-                     EVENT_DL_TOTAL_PRB_USAGE_TMR, schCb[inst].statistics.dlTotalPrbUsage->periodicity);
+   return SchSendStatsRspToMac(&schStatsRsp);
+}
 
-               isDlTotlPrbUseCfgd = true;
-               break;
-            }
+/*******************************************************************
+ *
+ * @brief Add active KPI pointers to KPI-Active-List
+ *
+ * @details
+ *
+ *    Function : schAddToKpiActiveList
+ *
+ *    Functionality: For each active statistics group for which timer
+ *       is running, add its KPI pointer to KPI-Active-List.
+ *       Use case :
+ *       When it is needed to update a KPI parameters, we need not
+ *       traverse all statistics group and all KPIs within a group
+ *       to get the specific KPI pointer to be updated.
+ *       Instead, we can traverse through KPI-Active-List and update
+ *       all entries in this list.
+ *
+ * @params[in]  Statistics request from MAC
+ *              Cause of rejection
+ * @return ROK     - success
+ *         RFAILED - failure
+ *
+ * ****************************************************************/
+uint8_t schAddToKpiActiveList(Inst inst, SchStatsGrp *grpInfo)
+{
+   CmLList  *node = NULLP;
 
-         case SCH_UL_TOTAL_PRB_USAGE:
-            {
-               /* Check if duplicate configuration */
-               if(schCb[inst].statistics.ulTotalPrbUsage)
-               {
-                  DU_LOG("\nERROR   -->  SCH : SCH_UL_TOTAL_PRB_USAGE stats already configured");
-                  rsp = RSP_NOK;
-                  cause = DUPLICATE_ENTRY;
-               }
+   /* If DL Total PRB Usage configured for this stats group, add to list */
+   if(grpInfo->kpiStats.dlTotalPrbUsage)
+   {
+      SCH_ALLOC(node, sizeof(CmLList));
+      if(node)
+      {
+         node->node = (PTR)grpInfo->kpiStats.dlTotalPrbUsage; 
+         cmLListAdd2Tail(&schCb[inst].statistics.activeKpiList.dlTotPrbUseList, node);
+      }
+   }
 
-               /* Allocate memory */
-               SCH_ALLOC(schCb[inst].statistics.ulTotalPrbUsage, sizeof(TotalPrbUsage));
-               if(!schCb[inst].statistics.ulTotalPrbUsage)
-               {
-                  DU_LOG("\nERROR   -->  SCH : Memory allocation failed for ulTotalPrbUsage in \
-                        SchProcStatsReq()");
-                  rsp = RSP_NOK;
-                  cause = RESOURCE_UNAVAILABLE;
-                  break;
-               }
+   /* If UL Total PRB Usage configured for this stats group, add to list */
+   node = NULLP;
+   if(grpInfo->kpiStats.ulTotalPrbUsage)
+   {
+      SCH_ALLOC(node, sizeof(CmLList));
+      if(node)
+      {
+         node->node = (PTR)grpInfo->kpiStats.ulTotalPrbUsage; 
+         cmLListAdd2Tail(&schCb[inst].statistics.activeKpiList.ulTotPrbUseList, node);
+      }
+   }
 
-               /* Initialize */
-               memset(schCb[inst].statistics.ulTotalPrbUsage, 0, sizeof(TotalPrbUsage));
+   return ROK;
+}
 
-               /* Configure */
-               schCb[inst].statistics.ulTotalPrbUsage->schInst = inst;
-               schCb[inst].statistics.ulTotalPrbUsage->periodicity = statsReq->statsList[idx].periodicity;
-               cmInitTimers(&(schCb[inst].statistics.ulTotalPrbUsage->periodTimer), 1);
+/*******************************************************************
+ *
+ * @brief Processes Statistics Request from MAC
+ *
+ * @details
+ *
+ *    Function : SchProcStatsReq
+ *
+ *    Functionality:
+ *
+ *     This function process the statistics request from MAC:
+ *     [Step 1] Basic validation. If fails, all stats group in stats request are
+ *     rejected.
+ *     [Step 2] If basic validations passed, traverse all stats group and
+ *     validate each measurement types in each group.
+ *     [Step 3] If any measurement type validation fails in a group, that group
+ *     is not configured and it is added to stats-group-rejected-list in
+ *     sch-stats-response message.
+ *     [Step 4] If a group passes all validation, it is added to SCH database.
+ *     A timer is started for this group. And the group is added to 
+ *     stats-group-accepted-list in sch-stats-response message.
+ *     [Step 5] sch-stats-response is sent to du app with stats-group-rejected-list
+ *     and stats-group-accepted-list.
+ *
+ * @params[in] Post structure
+ *             Statistics Request from MAC 
+ * @return ROK     - success
+ *         RFAILED - failure
+ *
+ * ****************************************************************/
+uint8_t SchProcStatsReq(Pst *pst, SchStatsReq *statsReq)
+{
+   uint8_t grpIdx = 0, reqGrpIdx = 0, reqMeasIdx = 0;
+   Inst    inst = pst->dstInst - SCH_INST_START;
+   bool    measTypeInvalid;
+   CauseOfResult cause;
+   SchStatsInfo  *statsInfo = NULLP;
+   SchStatsGrpInfo *grpInfo = NULLP;
+   SchStatsGrp *grpInfoDb = NULLP;
+   SchStatsRsp schStatsRsp;
 
-               /* Start timer */
-               schStartTmr(&schCb[inst], (PTR)(schCb[inst].statistics.ulTotalPrbUsage), \
-                     EVENT_UL_TOTAL_PRB_USAGE_TMR, schCb[inst].statistics.ulTotalPrbUsage->periodicity);
+   DU_LOG("\nINFO   -->  SCH : Received Statistics Request from MAC");
 
-               isUlTotlPrbUseCfgd = true;
-               break;
-            }
-         default:
-            {
-               DU_LOG("\nERROR   -->  SCH : Invalid statistics type [%d]", statsReq->statsList[idx].type);
-               rsp = RSP_NOK;
-               cause = PARAM_INVALID;
-            }
-      } /* End of switch */
+   if(statsReq == NULLP)
+   {
+      DU_LOG("\nERROR  -->  SCH : SchProcStatsReq(): Received Null pointer");
+      return RFAILED;
+   }
 
-      if(rsp == RSP_NOK)
-      {
-         /* If failed to configure any KPI, then clear configuration of other
-          * KPIs that were configured successfully as part of this statsReq */
-         if(isDlTotlPrbUseCfgd)
-         {
-            if((schChkTmr((PTR)(schCb[inst].statistics.dlTotalPrbUsage), EVENT_DL_TOTAL_PRB_USAGE_TMR)) == FALSE)
-            {
-               schStopTmr(&schCb[inst], (PTR)(schCb[inst].statistics.dlTotalPrbUsage), EVENT_DL_TOTAL_PRB_USAGE_TMR);
-            }
-            SCH_FREE(schCb[inst].statistics.dlTotalPrbUsage, sizeof(TotalPrbUsage));
-         }
+   /* [Step 1] Basic validation. If fails, all stats group in stats request are rejected */
+   
+   /* If maximum number of statistics already configured */
+   if(schCb[inst].statistics.numOfStatsCfgd >= MAX_NUM_STATS_CFG)
+   {
+      DU_LOG("\nERROR  -->  SCH : SchProcStatsReq: Maximum number of statistics configured. \
+            Cannot process new request.");
+      SchRejectAllStats(statsReq, RESOURCE_UNAVAILABLE);
+      SCH_FREE(statsReq, sizeof(SchStatsReq));
+      return RFAILED;
+   }
 
-         if(isUlTotlPrbUseCfgd)
-         {
-            if((schChkTmr((PTR)(schCb[inst].statistics.ulTotalPrbUsage), EVENT_UL_TOTAL_PRB_USAGE_TMR)) == FALSE)
-            {
-               schStopTmr(&schCb[inst], (PTR)(schCb[inst].statistics.ulTotalPrbUsage), EVENT_UL_TOTAL_PRB_USAGE_TMR);
-            }
-            SCH_FREE(schCb[inst].statistics.ulTotalPrbUsage, sizeof(TotalPrbUsage));
-         }
-         break;
-      }
-   } /* End of FOR */
+   memset(&schStatsRsp, 0, sizeof(SchStatsRsp));
+
+   /* [Step 2] Traverse all stats group and validate each measurement types in each group */
+   statsInfo = &schCb[inst].statistics.statsInfoList[schCb[inst].statistics.numOfStatsCfgd];
+   statsInfo->numStatsGroup = 0;
+   for(reqGrpIdx=0; reqGrpIdx<statsReq->numStatsGroup && grpIdx<MAX_NUM_STATS; reqGrpIdx++)
+   {
+       measTypeInvalid = false;
+       grpInfo = &statsReq->statsGrpList[reqGrpIdx];
+       grpInfoDb = &statsInfo->statsGrpList[grpIdx];
+       for(reqMeasIdx = 0; reqMeasIdx < grpInfo->numStats; reqMeasIdx++)
+       {
+          switch(grpInfo->statsList[reqMeasIdx])
+          {
+             case SCH_DL_TOTAL_PRB_USAGE:
+                {
+                   /* Allocate memory */
+                   SCH_ALLOC(grpInfoDb->kpiStats.dlTotalPrbUsage, sizeof(TotalPrbUsage));
+                   if(!grpInfoDb->kpiStats.dlTotalPrbUsage)
+                   {
+                      DU_LOG("\nERROR   -->  SCH : Memory allocation failed for dlTotalPrbUsage in \
+                            SchProcStatsReq()");
+                      measTypeInvalid = true;
+                      cause = RESOURCE_UNAVAILABLE;
+                   }
+                   break;
+                }
+
+             case SCH_UL_TOTAL_PRB_USAGE:
+                {
+                   /* Allocate memory */
+                   SCH_ALLOC(grpInfoDb->kpiStats.ulTotalPrbUsage, sizeof(TotalPrbUsage));
+                   if(!grpInfoDb->kpiStats.ulTotalPrbUsage)
+                   {
+                      DU_LOG("\nERROR   -->  SCH : Memory allocation failed for dlTotalPrbUsage in \
+                            SchProcStatsReq()");
+                      measTypeInvalid = true;
+                      cause = RESOURCE_UNAVAILABLE;
+                   }
+                   break;
+                }
+
+             default:
+                {
+                   DU_LOG("\nERROR  -->  SCH : SchProcStatsReq: Invalid measurement type [%d]", \
+                         grpInfo->statsList[reqMeasIdx]);
+                   measTypeInvalid = true;
+                   cause = PARAM_INVALID;
+                   break;
+                }
+          }
+
+          /* [Step 3 a] If any measurement type validation fails in a group, that group
+           *  is not configured */
+          if(measTypeInvalid)
+          {
+             SCH_FREE(grpInfoDb->kpiStats.dlTotalPrbUsage, sizeof(TotalPrbUsage));
+             SCH_FREE(grpInfoDb->kpiStats.ulTotalPrbUsage, sizeof(TotalPrbUsage));
+             memset(grpInfoDb, 0, sizeof(SchStatsGrp));   
+             break;
+          }
+       }
+   
+       /* [Step 4] If a group passes all validation, it is added to SCH database.
+        * A timer is started for this group. And the group is added to
+        * stats-group-accepted-list in sch-stats-response message. */
+       if(!measTypeInvalid)
+       {
+          /* Add this group's configured KPIs to list of Active KPIs */
+          if(schAddToKpiActiveList(inst, grpInfoDb) == ROK)
+          {
+             grpInfoDb->schInst = inst;
+             grpInfoDb->subscriptionId = statsReq->subscriptionId;
+             grpInfoDb->groupId = grpInfo->groupId;
+             grpInfoDb->periodicity = grpInfo->periodicity;
+
+
+             /* Start timer */
+             cmInitTimers(&(grpInfoDb->periodTimer), 1);
+             schStartTmr(&schCb[inst], (PTR)(grpInfoDb), EVENT_STATISTICS_TMR, grpInfoDb->periodicity);
+
+             schStatsRsp.statsGrpAcceptedList[schStatsRsp.numGrpAccepted] = grpInfo->groupId;
+             schStatsRsp.numGrpAccepted++;
+             grpIdx++;
+          }
+          else
+          {
+            memset(grpInfoDb, 0, sizeof(SchStatsGrp));
+          }
+       }
+       else
+       {
+          /* [Step 3 b] The rejected group is added to stats-group-rejected-list in
+           * sch-stats-response message */
+          memset(grpInfoDb, 0, sizeof(SchStatsGrp));
+          schStatsRsp.statsGrpRejectedList[schStatsRsp.numGrpRejected].groupId = grpInfo->groupId;
+          schStatsRsp.statsGrpRejectedList[schStatsRsp.numGrpRejected].cause = cause;
+          schStatsRsp.numGrpRejected++;
+       }
+   }
+   statsInfo->numStatsGroup = grpIdx;
+   if(statsInfo->numStatsGroup)
+   {
+      schCb[inst].statistics.numOfStatsCfgd++;
+   }
+   else
+   {
+      memset(statsInfo, 0, sizeof(SchStatsInfo));
+   }
+   schStatsRsp.subscriptionId = statsReq->subscriptionId;
 
    SCH_FREE(statsReq, sizeof(SchStatsReq));
 
-   SchSendStatsRspToMac(inst, rsp, cause);
+   /* [Step 5] sch-stats-response is sent to du app with stats-group-rejected-list
+    * and stats-group-accepted-list. */
+   SchSendStatsRspToMac(&schStatsRsp);
 
    return ROK;
 } /* End of SchProcStatsReq */
@@ -2708,26 +2841,21 @@ uint8_t SchProcStatsReq(Pst *pst, SchStatsReq *statsReq)
  *         RFAILED - failure
  *
  * ****************************************************************/
-uint8_t SchSendStatsIndToMac(Inst inst, SchMeasurementType measType, double value)
+uint8_t SchSendStatsIndToMac(Inst inst, SchStatsInd  *statsInd)
 {
    Pst pst;
    uint8_t ret = ROK;
-   SchStatsInd  statsInd;
 
 #ifdef DEBUG_PRINT
    DU_LOG("\nDEBUG  --> SCH : Filling statistics indication");
 #endif
 
-   memset(&statsInd, 0, sizeof(SchStatsInd));
-   statsInd.type = measType;
-   statsInd.value = value;
-
    /* Filling post structure */
    memset(&pst, 0, sizeof(Pst));
    FILL_PST_SCH_TO_MAC(pst, inst);
    pst.event = EVENT_STATISTICS_IND_TO_MAC;
 
-   ret = MacMessageRouter(&pst, (void *)&statsInd);
+   ret = MacMessageRouter(&pst, (void *)statsInd);
    if(ret == RFAILED)
    {
       DU_LOG("\nERROR  -->  SCH : SchSendStatsIndToMac(): Failed to send Statistics Indication");
@@ -2735,6 +2863,87 @@ uint8_t SchSendStatsIndToMac(Inst inst, SchMeasurementType measType, double valu
    return ret;
 }
 
+/**
+ * @brief Handler to process Timer expiry of DL Total PRB Usage calculation 
+ *
+ * @param[in] cb        Control block depending on the type of the timer event.
+ * @param[in] tmrEvnt   Timer event to be started
+ *
+ * @return  Bool indicating whether the timer is running or not
+ *      -# ROK
+ *      -# RFAILED
+*/
+double calcDlTotalPrbUsage(TotalPrbUsage *dlTotalPrbUsage)
+{
+   double percentageOfTotalPrbUsed = 0;
+
+   if(dlTotalPrbUsage->totalPrbAvailForTx)
+      percentageOfTotalPrbUsed = ((100.0 * dlTotalPrbUsage->numPrbUsedForTx) / dlTotalPrbUsage->totalPrbAvailForTx);
+   
+   memset(dlTotalPrbUsage, 0, sizeof(TotalPrbUsage));
+
+   return percentageOfTotalPrbUsed;
+}
+
+/**
+ * @brief Handler to check if the timer is running
+ *
+ * @param[in] cb        Control block depending on the type of the timer event.
+ * @param[in] tmrEvnt   Timer event to be started
+ *
+ * @return  Bool indicating whether the timer is running or not
+ *      -# ROK
+ *      -# RFAILED
+*/
+uint8_t calcUlTotalPrbUsage(TotalPrbUsage *ulTotalPrbUsage)
+{
+   double percentageOfTotalPrbUsed = 0;
+
+   if(ulTotalPrbUsage->totalPrbAvailForTx)
+      percentageOfTotalPrbUsed = ((100.0 * ulTotalPrbUsage->numPrbUsedForTx) / ulTotalPrbUsage->totalPrbAvailForTx);
+
+   memset(ulTotalPrbUsage, 0, sizeof(TotalPrbUsage));
+
+   return percentageOfTotalPrbUsed;
+}
+
+/**
+ * @brief Calculates statistics in a group and sends
+ *          statistics indication for this group
+ *
+ * @param[in] Statistics group info
+ *
+ * @return
+ *      -# ROK
+ *      -# RFAILED
+*/
+uint8_t schCalcAndSendGrpStats(SchStatsGrp *grpInfo)
+{
+   uint8_t measStatsIdx = 0;
+   SchStatsInd  statsInd;
+
+   memset(&statsInd, 0, sizeof(SchStatsInd));
+   statsInd.subscriptionId = grpInfo->subscriptionId;
+   statsInd.groupId = grpInfo->groupId;
+
+   if(grpInfo->kpiStats.dlTotalPrbUsage)
+   {
+      statsInd.measuredStatsList[measStatsIdx].type = SCH_DL_TOTAL_PRB_USAGE;  
+      statsInd.measuredStatsList[measStatsIdx].value = calcDlTotalPrbUsage(grpInfo->kpiStats.dlTotalPrbUsage);   
+      measStatsIdx++;
+   }
+   
+   if(grpInfo->kpiStats.ulTotalPrbUsage)
+   {
+      statsInd.measuredStatsList[measStatsIdx].type = SCH_UL_TOTAL_PRB_USAGE;  
+      statsInd.measuredStatsList[measStatsIdx].value = calcUlTotalPrbUsage(grpInfo->kpiStats.ulTotalPrbUsage);   
+      measStatsIdx++;
+   }
+   
+   statsInd.numStats = measStatsIdx;
+
+   return SchSendStatsIndToMac(grpInfo->schInst, &statsInd);
+}
 
 /**********************************************************************
   End of file
index 38d0063..4a82d17 100644 (file)
@@ -602,17 +602,43 @@ typedef struct
 
 typedef struct dlTotalPrbUsage
 {
-   Inst     schInst;
    uint16_t numPrbUsedForTx;
    uint16_t totalPrbAvailForTx;
-   uint16_t periodicity;
-   CmTimer  periodTimer;
 }TotalPrbUsage;
 
-typedef struct schStatistics
+typedef struct 
 {
    TotalPrbUsage *dlTotalPrbUsage;
    TotalPrbUsage *ulTotalPrbUsage;
+}SchKpiSupported;
+
+typedef struct
+{
+   CmLListCp  dlTotPrbUseList;
+   CmLListCp  ulTotPrbUseList;
+}SchKpiActive;
+
+typedef struct schStatsGrp
+{
+   Inst       schInst;
+   uint64_t   subscriptionId;
+   uint8_t    groupId;
+   uint16_t   periodicity;  /* In milliseconds */
+   CmTimer    periodTimer;
+   SchKpiSupported kpiStats;
+}SchStatsGrp;
+
+typedef struct schStatsInfo
+{
+   uint8_t     numStatsGroup;
+   SchStatsGrp statsGrpList[MAX_NUM_STATS_GRP];
+}SchStatsInfo;
+
+typedef struct schStatistics
+{
+   uint16_t      numOfStatsCfgd;
+   SchStatsInfo  statsInfoList[MAX_NUM_STATS_CFG];
+   SchKpiActive  activeKpiList;
 }SchStatistics;
 
 /**
@@ -796,7 +822,8 @@ void schMsg4Complete(SchUeCb *ueCb);
 
 /* Statistics Function */
 uint8_t SchProcStatsReq(Pst *pst, SchStatsReq *statsReq);
-uint8_t SchSendStatsIndToMac(Inst inst, SchMeasurementType measType, double value);
+uint8_t SchSendStatsIndToMac(Inst inst, SchStatsInd  *statsInd);
+uint8_t schCalcAndSendGrpStats(SchStatsGrp *grpInfo);
 
 /**********************************************************************
   End of file
index d7b4016..c1dee26 100644 (file)
@@ -485,6 +485,9 @@ uint8_t schUlResAlloc(SchCellCb *cell, Inst schInst)
    UlSchedInfo ulSchedInfo;
    SchUlSlotInfo  *schUlSlotInfo = NULLP;
    SlotTimingInfo ulTimingInfo;
+   CmLList        *node = NULLP;
+   TotalPrbUsage  *ulTotalPrbUsage = NULLP;
+
    memset(&ulSchedInfo, 0, sizeof(UlSchedInfo));
 
    /* add PHY delta */
@@ -543,11 +546,14 @@ uint8_t schUlResAlloc(SchCellCb *cell, Inst schInst)
       DU_LOG("\nERROR  -->  SCH : Sending UL Sch info from SCH to MAC failed");
    }
 
-   /* Update UL statistics */
-   if(schCb[schInst].statistics.ulTotalPrbUsage)
+   /* Update DL PRB Usage for all stats group which requested for DL Total PRB Usage */
+   node = cmLListFirst(&schCb[schInst].statistics.activeKpiList.ulTotPrbUseList);
+   while(node)
    {
-      schCb[schInst].statistics.ulTotalPrbUsage->numPrbUsedForTx += schUlSlotInfo->prbAlloc.numPrbAlloc; 
-      schCb[schInst].statistics.ulTotalPrbUsage->totalPrbAvailForTx += MAX_NUM_RB;
+      ulTotalPrbUsage = (TotalPrbUsage *)node->node;
+      ulTotalPrbUsage->numPrbUsedForTx += schUlSlotInfo->prbAlloc.numPrbAlloc;
+      ulTotalPrbUsage->totalPrbAvailForTx += MAX_NUM_RB;
+      node = node->next;
    }
 
    /* Re-initialize UL Slot */
index 643ee65..2a21774 100644 (file)
@@ -651,6 +651,8 @@ uint8_t SchProcSlotInd(Pst *pst, SlotTimingInfo *slotInd)
    DlBrdcstAlloc  *dlBrdcstAlloc = NULLP;
    SchCellCb      *cell = NULLP;
    Inst           schInst = pst->dstInst-SCH_INST_START;
+   CmLList        *node = NULLP;
+   TotalPrbUsage  *dlTotalPrbUsage = NULLP;
 
    cell = schCb[schInst].cells[schInst];
    if(cell == NULLP)
@@ -750,11 +752,14 @@ uint8_t SchProcSlotInd(Pst *pst, SlotTimingInfo *slotInd)
       return (ret);
    }
 
-   /* Update DL statistics */
-   if(schCb[schInst].statistics.dlTotalPrbUsage)
+   /* Update DL PRB Usage for all stats group which requested for DL Total PRB Usage */
+   node = cmLListFirst(&schCb[schInst].statistics.activeKpiList.dlTotPrbUseList);
+   while(node)
    {
-      schCb[schInst].statistics.dlTotalPrbUsage->numPrbUsedForTx += cell->schDlSlotInfo[slot]->prbAlloc.numPrbAlloc; 
-      schCb[schInst].statistics.dlTotalPrbUsage->totalPrbAvailForTx += MAX_NUM_RB;
+      dlTotalPrbUsage = (TotalPrbUsage *)node->node;
+      dlTotalPrbUsage->numPrbUsedForTx += cell->schDlSlotInfo[slot]->prbAlloc.numPrbAlloc;
+      dlTotalPrbUsage->totalPrbAvailForTx += MAX_NUM_RB;
+      node = node->next;
    }
    
    /* Re-initialize DL slot */
index fa71674..1a7fb3f 100644 (file)
@@ -37,32 +37,20 @@ bool schChkTmr(PTR cb, int16_t tmrEvnt)
 {
    switch (tmrEvnt)
    {
-      case EVENT_DL_TOTAL_PRB_USAGE_TMR:
-      {
-         if(((TotalPrbUsage *)cb)->periodTimer.tmrEvnt == EVENT_DL_TOTAL_PRB_USAGE_TMR)
+      case EVENT_STATISTICS_TMR:
          {
-             DU_LOG("\nDEBUG  -->  SCH : schChkTmr: Timer Evnt [%d] already running", tmrEvnt);
-             return TRUE;
+            if(((SchStatsGrp *)cb)->periodTimer.tmrEvnt == EVENT_STATISTICS_TMR)
+            {
+               DU_LOG("\nDEBUG  -->  SCH : schChkTmr: Timer Evnt [%d] already running", tmrEvnt);
+               return TRUE;
+            }
+            break;
          }
-         break;
-      }
-
-      case EVENT_UL_TOTAL_PRB_USAGE_TMR:
-      {
-         if(((TotalPrbUsage *)cb)->periodTimer.tmrEvnt == EVENT_UL_TOTAL_PRB_USAGE_TMR)
+      default:
          {
-             DU_LOG("\nDEBUG  -->  SCH : schChkTmr: Timer Evnt [%d] already running", tmrEvnt);
-             return TRUE;
+            DU_LOG("\nERROR  -->  SCH : schChkTmr: Invalid tmr Evnt [%d]", tmrEvnt);
          }
-         break;
-      }
-
-      default:
-      {
-         DU_LOG("\nERROR  -->  SCH : schChkTmr: Invalid tmr Evnt [%d]", tmrEvnt);
-      }
    }
-
    return FALSE;
 }
 
@@ -74,10 +62,9 @@ bool schChkTmr(PTR cb, int16_t tmrEvnt)
  *
  * @return  Void
 */
-void schStartTmr(SchCb *gCb, PTR cb, int16_t tmrEvnt, uint8_t timerValue)
+void schStartTmr(SchCb *gCb, PTR cb, int16_t tmrEvnt, uint16_t timerValue)
 {
-   TotalPrbUsage *dlTotalPrbUsage;
-   TotalPrbUsage *ulTotalPrbUsage;
+   SchStatsGrp *statsGrp = NULLP;
    CmTmrArg arg;
 
    arg.wait = 0;
@@ -89,25 +76,16 @@ void schStartTmr(SchCb *gCb, PTR cb, int16_t tmrEvnt, uint8_t timerValue)
    
    switch (tmrEvnt)
    {
-      case EVENT_DL_TOTAL_PRB_USAGE_TMR:
+      case EVENT_STATISTICS_TMR:
       {
-         dlTotalPrbUsage = ((TotalPrbUsage *)cb);
+         statsGrp = ((SchStatsGrp *)cb);
          TMR_CALCUATE_WAIT(arg.wait, timerValue, gCb->schTimersInfo.tmrRes);
 
-         arg.timers = &dlTotalPrbUsage->periodTimer;
-         arg.max = MAX_TOTAL_PRB_USAGE_TMR;
+         arg.timers = &statsGrp->periodTimer;
+         arg.max = MAX_NUM_TMR_PER_STATS_GRP;
          break;
       }
 
-      case EVENT_UL_TOTAL_PRB_USAGE_TMR:
-      {
-         ulTotalPrbUsage = ((TotalPrbUsage *)cb);
-         TMR_CALCUATE_WAIT(arg.wait, timerValue, gCb->schTimersInfo.tmrRes);
-
-         arg.timers = &ulTotalPrbUsage->periodTimer;
-         arg.max = MAX_TOTAL_PRB_USAGE_TMR;
-         break;
-      }
       default:
       {
          DU_LOG("\nERROR  -->  SCH : schStartTmr: Invalid tmr Evnt [%d]", tmrEvnt);
@@ -148,16 +126,10 @@ void schStopTmr(SchCb *gCb, PTR cb, uint8_t tmrType)
 
    switch (tmrType)
    {
-      case EVENT_DL_TOTAL_PRB_USAGE_TMR:
-         {
-            arg.timers  = &((TotalPrbUsage *)cb)->periodTimer;
-            arg.max = MAX_TOTAL_PRB_USAGE_TMR;
-            break;
-         }
-         case EVENT_UL_TOTAL_PRB_USAGE_TMR:
+      case EVENT_STATISTICS_TMR:
          {
-            arg.timers  = &((TotalPrbUsage *)cb)->periodTimer;
-            arg.max = MAX_TOTAL_PRB_USAGE_TMR;
+            arg.timers  = &((SchStatsGrp *)cb)->periodTimer;
+            arg.max = MAX_NUM_TMR_PER_STATS_GRP;
             break;
          }
 
@@ -183,56 +155,28 @@ void schStopTmr(SchCb *gCb, PTR cb, uint8_t tmrType)
 }
 
 /**
- * @brief Handler to process Timer expiry of DL Total PRB Usage calculation 
+ * @brief Handler for Statistics group timer expiry
  *
- * @param[in] cb        Control block depending on the type of the timer event.
- * @param[in] tmrEvnt   Timer event to be started
+ * @details
  *
- * @return  Bool indicating whether the timer is running or not
- *      -# ROK
- *      -# RFAILED
-*/
-uint8_t SchProcDlTotalPrbUsageTmrExp(TotalPrbUsage *dlTotalPrbUsage)
-{
-   double percentageOfTotalPrbUsed = 0;
-
-   if(dlTotalPrbUsage->totalPrbAvailForTx)
-      percentageOfTotalPrbUsed = ((100.0 * dlTotalPrbUsage->numPrbUsedForTx) / dlTotalPrbUsage->totalPrbAvailForTx);
-   SchSendStatsIndToMac(dlTotalPrbUsage->schInst, SCH_DL_TOTAL_PRB_USAGE, percentageOfTotalPrbUsed);
-   
-   /* Restart Timer */
-   dlTotalPrbUsage->numPrbUsedForTx = 0;
-   dlTotalPrbUsage->totalPrbAvailForTx = 0;
-   schStartTmr(&schCb[dlTotalPrbUsage->schInst], (PTR)(dlTotalPrbUsage), EVENT_DL_TOTAL_PRB_USAGE_TMR, \
-      dlTotalPrbUsage->periodicity);
-
-   return ROK;
-}
-
-/**
- * @brief Handler to check if the timer is running
+ *     Function : SchProcStatisticsGrpTmrExp
  *
- * @param[in] cb        Control block depending on the type of the timer event.
- * @param[in] tmrEvnt   Timer event to be started
+ *     This function calculates and sends statistics of
+ *     the stats-group for which timer expired.
+ *     Once Statistics Indication is sent, timer for this
+ *     group is restarted.
  *
- * @return  Bool indicating whether the timer is running or not
+ *  @param[in]  Statistics group control block
+ *  @return  uint8_t
  *      -# ROK
- *      -# RFAILED
-*/
-uint8_t SchProcUlTotalPrbUsageTmrExp(TotalPrbUsage *ulTotalPrbUsage)
+ **/
+uint8_t SchProcStatisticsGrpTmrExp(SchStatsGrp *cb)
 {
-   double percentageOfTotalPrbUsed = 0;
-
-   if(ulTotalPrbUsage->totalPrbAvailForTx)
-      percentageOfTotalPrbUsed = ((100.0 * ulTotalPrbUsage->numPrbUsedForTx) / ulTotalPrbUsage->totalPrbAvailForTx);
-   SchSendStatsIndToMac(ulTotalPrbUsage->schInst, SCH_UL_TOTAL_PRB_USAGE, percentageOfTotalPrbUsed);
-
-   /* Restart Timer */
-   ulTotalPrbUsage->numPrbUsedForTx = 0;
-   ulTotalPrbUsage->totalPrbAvailForTx = 0;
-   schStartTmr(&schCb[ulTotalPrbUsage->schInst], (PTR)(ulTotalPrbUsage), EVENT_UL_TOTAL_PRB_USAGE_TMR, \
-      ulTotalPrbUsage->periodicity);
-
+   if(schCalcAndSendGrpStats(cb) != ROK)
+   {
+      DU_LOG("\nERROR  -->  SCH : SchProcStatisticsGrpTmrExp: Fails to send group statistics");
+   }
+   schStartTmr(&schCb[cb->schInst], (PTR)(cb), EVENT_STATISTICS_TMR, cb->periodicity);
    return ROK;
 }
 
@@ -260,16 +204,16 @@ uint8_t schTmrExpiry(PTR cb, uint8_t tmrEvnt)
 
    switch (tmrEvnt)
    {
-      case EVENT_DL_TOTAL_PRB_USAGE_TMR:
-         {
-            SchProcDlTotalPrbUsageTmrExp((TotalPrbUsage*)cb);
-            break;
-         }
-      case EVENT_UL_TOTAL_PRB_USAGE_TMR:
+      case EVENT_STATISTICS_TMR:
          {
-            SchProcUlTotalPrbUsageTmrExp((TotalPrbUsage*)cb);
+#ifdef DEBUG_PRINT
+            DU_LOG("\nDEBUG   -->  SCH : Statistics Timer Expired for Subscription Id [%ld] GroupId [%d]", \
+                  ((SchStatsGrp*)cb)->subscriptionId, ((SchStatsGrp*)cb)->groupId);
+#endif
+            SchProcStatisticsGrpTmrExp((SchStatsGrp*)cb);
             break;
          }
+
       default:
          {
             DU_LOG("\nERROR  -->  DU : duStartTmr: Invalid tmr Evnt [%d]", tmrEvnt);
index 105346d..b3914e7 100644 (file)
 ################################################################################
 *******************************************************************************/
 
-#define MAX_TOTAL_PRB_USAGE_TMR 1
+#define MAX_NUM_TMR_PER_STATS_GRP 1
 
-#define EVENT_DL_TOTAL_PRB_USAGE_TMR 1
-#define EVENT_UL_TOTAL_PRB_USAGE_TMR 2
+#define EVENT_STATISTICS_TMR 1
 
 bool schChkTmr(PTR cb, int16_t tmrEvnt);
-void schStartTmr(SchCb *gCb, PTR cb, int16_t tmrEvnt, uint8_t timerValue);
+void schStartTmr(SchCb *gCb, PTR cb, int16_t tmrEvnt, uint16_t timerValue);
 void schStopTmr(SchCb *gCb, PTR cb, uint8_t tmrType);
 
 /**********************************************************************
index 371a1d5..cbe952c 100644 (file)
 /*First SCS in kHz as per 3gpp spec 38.211 Table 4.2-1 */
 #define BASE_SCS 15
 
-#define MAX_NUM_STATS 10
+#define MAX_NUM_STATS_CFG 2 /* Max number of statistics configuration/Subscription supported */
+#define MAX_NUM_STATS_GRP 5 /* Max number of statistics group per configuration request */
+#define MAX_NUM_STATS 10    /* Max number of statistics per group */
 
 /* Defining macros for common utility functions */
 #define ODU_GET_MSG_BUF SGetMsg
index 4b2f98e..9e5ee54 100644 (file)
@@ -1855,28 +1855,48 @@ typedef struct macDlBroadcastReq
     SiSchedulingInfo **siSchedulingInfo;
 }MacDlBroadcastReq;
 
-typedef struct macStatsInfo
+typedef struct macStatsGrpInfo
 {
-   MacMeasurementType type;
-   uint16_t           periodicity;  /* In milliseconds */
-}MacStatsInfo;
+   uint8_t   groupId;
+   uint16_t  periodicity;  /* In milliseconds */
+   uint8_t   numStats;
+   MacMeasurementType statsList[MAX_NUM_STATS];
+}MacStatsGrpInfo;
 
 typedef struct macStatsReq
 {
-   uint8_t   numStats;
-   MacStatsInfo statsList[MAX_NUM_STATS];
+   uint64_t          subscriptionId;
+   uint8_t           numStatsGroup;
+   MacStatsGrpInfo   statsGrpList[MAX_NUM_STATS_GRP];
 }MacStatsReq;
 
-typedef struct macStatsRsp
+typedef struct macStatsGrpRejected
 {
-   MacRsp  rsp;
+   uint8_t  groupId;
    CauseOfResult cause;
+}MacStatsGrpRejected;
+
+typedef struct macStatsRsp
+{
+   uint64_t             subscriptionId;
+   uint8_t              numGrpAccepted;
+   uint8_t              statsGrpAcceptedList[MAX_NUM_STATS_GRP];
+   uint8_t              numGrpRejected;
+   MacStatsGrpRejected  statsGrpRejectedList[MAX_NUM_STATS_GRP];
 }MacStatsRsp;
 
-typedef struct macStatsInd
+typedef struct macStats
 {
    MacMeasurementType type;
    double value;
+}MacStats;
+
+typedef struct macStatsInd
+{
+   uint64_t    subscriptionId;
+   uint8_t     groupId;
+   uint8_t     numStats;
+   MacStats    measuredStatsList[MAX_NUM_STATS];
 }MacStatsInd;
 
 /****************** FUNCTION POINTERS ********************************/
index 58f894a..ae772ad 100644 (file)
@@ -2248,30 +2248,54 @@ typedef struct schRlsHqInfo
    SchUeHqInfo  *ueHqInfo;
 }SchRlsHqInfo;
 
-typedef struct schStatsInfo
+/* Statistics Request from MAC to SCH */
+typedef struct schStatsGrpInfo
 {
-   SchMeasurementType type;
-   uint16_t           periodicity;  /* In milliseconds */
-}SchStatsInfo;
+   uint8_t   groupId;
+   uint16_t  periodicity;  /* In milliseconds */
+   uint8_t   numStats;
+   SchMeasurementType statsList[MAX_NUM_STATS];
+}SchStatsGrpInfo;
 
 typedef struct schStatsReq
 {
-   uint8_t   numStats;
-   SchStatsInfo statsList[MAX_NUM_STATS];
+   uint64_t  subscriptionId;
+   uint8_t   numStatsGroup;
+   SchStatsGrpInfo   statsGrpList[MAX_NUM_STATS_GRP];
 }SchStatsReq;
 
-typedef struct schStatsRsp
+/* Statistics Response from SCH to MAC */
+typedef struct schStatsGrpRejected
 {
-   SchMacRsp rsp; 
+   uint8_t   groupId;
    CauseOfResult cause;
+}SchStatsGrpRejected;
+
+typedef struct schStatsRsp
+{
+   uint64_t             subscriptionId;
+   uint8_t              numGrpAccepted;
+   uint8_t              statsGrpAcceptedList[MAX_NUM_STATS_GRP];
+   uint8_t              numGrpRejected;
+   SchStatsGrpRejected  statsGrpRejectedList[MAX_NUM_STATS_GRP];
 }SchStatsRsp;
 
-typedef struct schStatsInd
+/* Statistics Indication from SCH to MAC */
+typedef struct schStats
 {
    SchMeasurementType type;
    double value;
+}SchStats;
+
+typedef struct schStatsInd
+{
+   uint64_t    subscriptionId;
+   uint8_t     groupId;
+   uint8_t     numStats;
+   SchStats    measuredStatsList[MAX_NUM_STATS];
 }SchStatsInd;
 
+
 /* function declarations */
 uint8_t MacMessageRouter(Pst *pst, void *msg);
 uint8_t SchMessageRouter(Pst *pst, void *msg);
index ca5c9e5..6444943 100644 (file)
@@ -2150,11 +2150,13 @@ Statistics FetchStatsFromActionDefFormat1(ActionDefFormat1 format1)
 
    /* Hardcoding values for now for testing purpose 
     * Will be removed in next gerrit */
-   stats.macStatsReq.numStats = 2;
-   stats.macStatsReq.statsList[0].type = MAC_DL_TOTAL_PRB_USAGE;
-   stats.macStatsReq.statsList[0].periodicity = 100;
-   stats.macStatsReq.statsList[1].type = MAC_UL_TOTAL_PRB_USAGE;
-   stats.macStatsReq.statsList[1].periodicity = 100;
+   stats.macStatsReq.subscriptionId = 1;
+   stats.macStatsReq.numStatsGroup = 1;
+   stats.macStatsReq.statsGrpList[0].groupId = 1;
+   stats.macStatsReq.statsGrpList[0].periodicity = 100;
+   stats.macStatsReq.statsGrpList[0].numStats = 2;
+   stats.macStatsReq.statsGrpList[0].statsList[0] = MAC_DL_TOTAL_PRB_USAGE;
+   stats.macStatsReq.statsGrpList[0].statsList[1] = MAC_UL_TOTAL_PRB_USAGE;
 
    return stats;
 }
@@ -2238,23 +2240,33 @@ uint8_t BuildAndSendStatsReq(ActionDefinition subscribedAction)
  * ****************************************************************/
 uint8_t DuProcMacStatsRsp(Pst *pst, MacStatsRsp *statsRsp)
 {
+   uint8_t idx = 0;
+
+   DU_LOG("\nINFO  -->  DU_APP : DuProcMacStatsRsp: Received Statistics Response from MAC");
+
    if(statsRsp)
    {
-      if(statsRsp->rsp == MAC_DU_APP_RSP_OK)
+#ifdef DEBUG_PRINT   
+      DU_LOG("\n  Subscription Id [%ld]", statsRsp->subscriptionId);
+
+      DU_LOG("\n  Number of Accepted Groups [%d]", statsRsp->numGrpAccepted);
+      for(idx=0; idx<statsRsp->numGrpAccepted; idx++)
       {
-         DU_LOG("\nINFO  -->  DU_APP : Statistics configured successfully");
-         /* TODO : Start Reporting period timer for this subscription request
-          * To be handled in next gerrit */
+         DU_LOG("\n    Group Id [%d]", statsRsp->statsGrpAcceptedList[idx]);
       }
-      else
+
+      DU_LOG("\n  Number of Rejected Groups [%d]", statsRsp->numGrpRejected);
+      for(idx=0; idx<statsRsp->numGrpRejected; idx++)
       {
-         DU_LOG("\nERROR  -->  DU_APP : Statistics configuration failed with cause [%d]", statsRsp->cause);
+         DU_LOG("\n    Group Id [%d]", statsRsp->statsGrpRejectedList[idx]);
       }
+#endif      
+
       DU_FREE_SHRABL_BUF(pst->region, pst->pool, statsRsp, sizeof(MacStatsRsp));
       return ROK;
    }
 
-   DU_LOG("\nINFO  -->  DU_APP : DuProcMacStatsRsp: Received NULL Pointer");
+   DU_LOG("\nERROR  -->  DU_APP : DuProcMacStatsRsp: Received NULL Pointer");
    return RFAILED;
 }
 
@@ -2280,7 +2292,13 @@ uint8_t DuProcMacStatsInd(Pst *pst, MacStatsInd *statsInd)
    {
 #ifdef DEBUG_PRINT   
       DU_LOG("\nDEBUG  -->  DU_APP : DuProcMacStatsInd: Received Statistics Indication");
-      DU_LOG("\nMeasurement type [%d]  Measurement Value [%lf]", statsInd->type, statsInd->value);
+      DU_LOG("\n  Subscription Id [%ld]", statsInd->subscriptionId);
+      DU_LOG("\n  Group Id [%d]", statsInd->groupId);
+      for(int idx = 0; idx < statsInd->numStats; idx++)
+      {
+         DU_LOG("\n  Meas type [%d] Meas Value [%lf]", statsInd->measuredStatsList[idx].type,\
+            statsInd->measuredStatsList[idx].value);
+      }
 #endif      
 
       /* TODO : When stats indication is received