From 3982cc2e2393446f654b8ece9dc9fceeb2b8ba1d Mon Sep 17 00:00:00 2001
From: Robert Schmidt <robert.schmidt@openairinterface.org>
Date: Fri, 18 Aug 2023 09:33:05 +0200
Subject: [PATCH] Forward UE capabilities to DU and use for CellGroupConfig

- Forward UE capabilities in F1 handlers
- Store at MAC
- Update CellGroupConfig with UE Capabilities of the UE
---
 .../NR_MAC_gNB/gNB_scheduler_primitives.c     |  1 +
 .../LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c    | 68 ++++++++++++++++-
 openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h       |  1 +
 openair2/RRC/NR/MESSAGES/asn1_msg.c           | 14 +---
 openair2/RRC/NR/mac_rrc_dl_f1ap.c             | 16 +++-
 openair2/RRC/NR/nr_rrc_config.c               | 17 +----
 openair2/RRC/NR/nr_rrc_defs.h                 |  3 +-
 openair2/RRC/NR/rrc_gNB.c                     | 73 ++++++++++++++-----
 8 files changed, 139 insertions(+), 54 deletions(-)

diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
index 07c8da30928..ec6d24a0bd3 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
@@ -1948,6 +1948,7 @@ void delete_nr_ue_data(NR_UE_info_t *UE, NR_COMMON_channels_t *ccPtr, uid_alloca
 {
   ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup);
   ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->reconfigCellGroup);
+  ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->capability);
   NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
   destroy_nr_list(&sched_ctrl->available_dl_harq);
   destroy_nr_list(&sched_ctrl->feedback_dl_harq);
diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c
index 71d2ecc36ab..0e383e4eaf6 100644
--- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c
+++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c
@@ -138,6 +138,42 @@ static int handle_ue_context_drbs_setup(int rnti,
   return drbs_len;
 }
 
+static NR_UE_NR_Capability_t *get_ue_nr_cap(int rnti, uint8_t *buf, uint32_t len)
+{
+  if (buf == NULL || len == 0)
+    return NULL;
+
+  NR_UE_CapabilityRAT_ContainerList_t *clist = NULL;
+  asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList, (void **)&clist, buf, len, 0, 0);
+  if (dec_rval.code != RC_OK) {
+    LOG_W(NR_MAC, "cannot decode UE capability container list of UE RNTI %04x, ignoring capabilities\n", rnti);
+    return NULL;
+  }
+
+  NR_UE_NR_Capability_t *cap = NULL;
+  for (int i = 0; i < clist->list.count; i++) {
+    const NR_UE_CapabilityRAT_Container_t *c = clist->list.array[i];
+    if (cap != NULL || c->rat_Type != NR_RAT_Type_nr) {
+      LOG_W(NR_MAC, "UE RNTI %04x: ignoring capability of type %ld\n", rnti, c->rat_Type);
+      continue;
+    }
+
+    asn_dec_rval_t dec_rval = uper_decode(NULL,
+                                          &asn_DEF_NR_UE_NR_Capability,
+                                          (void **)&cap,
+                                          c->ue_CapabilityRAT_Container.buf,
+                                          c->ue_CapabilityRAT_Container.size,
+                                          0,
+                                          0);
+    if (dec_rval.code != RC_OK) {
+      LOG_W(NR_MAC, "cannot decode NR UE capabilities of UE RNTI %04x, ignoring NR capability\n", rnti);
+      cap = NULL;
+      continue;
+    }
+  }
+  return cap;
+}
+
 static NR_CellGroupConfig_t *clone_CellGroupConfig(const NR_CellGroupConfig_t *orig)
 {
   uint8_t buf[16636];
@@ -159,9 +195,12 @@ void ue_context_setup_request(const f1ap_ue_context_setup_t *req)
     .gNB_DU_ue_id = req->gNB_DU_ue_id,
   };
 
+  NR_UE_NR_Capability_t *ue_cap = NULL;
   if (req->cu_to_du_rrc_information != NULL) {
     AssertFatal(req->cu_to_du_rrc_information->cG_ConfigInfo == NULL, "CG-ConfigInfo not handled\n");
-    AssertFatal(req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList == NULL, "UE capabilities not handled yet\n");
+    ue_cap = get_ue_nr_cap(req->gNB_DU_ue_id,
+                           req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList,
+                           req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList_length);
     AssertFatal(req->cu_to_du_rrc_information->measConfig == NULL, "MeasConfig not handled\n");
   }
 
@@ -191,7 +230,16 @@ void ue_context_setup_request(const f1ap_ue_context_setup_t *req)
   if (req->rrc_container != NULL)
     nr_rlc_srb_recv_sdu(req->gNB_DU_ue_id, DCCH, req->rrc_container, req->rrc_container_length);
 
-  //nr_mac_update_cellgroup()
+  UE->capability = ue_cap;
+  if (ue_cap != NULL) {
+    // store the new UE capabilities, and update the cellGroupConfig
+    ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->capability);
+    UE->capability = ue_cap;
+    LOG_I(NR_MAC, "UE %04x: received capabilities, updating CellGroupConfig\n", UE->rnti);
+    NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
+    update_cellGroupConfig(new_CellGroup, UE->uid, UE->capability, &mac->radio_config, scc);
+  }
+
   resp.du_to_cu_rrc_information = calloc(1, sizeof(du_to_cu_rrc_information_t));
   AssertFatal(resp.du_to_cu_rrc_information != NULL, "out of memory\n");
   resp.du_to_cu_rrc_information->cellGroupConfig = calloc(1,1024);
@@ -228,9 +276,12 @@ void ue_context_modification_request(const f1ap_ue_context_modif_req_t *req)
     .gNB_DU_ue_id = req->gNB_DU_ue_id,
   };
 
+  NR_UE_NR_Capability_t *ue_cap = NULL;
   if (req->cu_to_du_rrc_information != NULL) {
     AssertFatal(req->cu_to_du_rrc_information->cG_ConfigInfo == NULL, "CG-ConfigInfo not handled\n");
-    AssertFatal(req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList == NULL, "UE capabilities not handled yet\n");
+    ue_cap = get_ue_nr_cap(req->gNB_DU_ue_id,
+                           req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList,
+                           req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList_length);
     AssertFatal(req->cu_to_du_rrc_information->measConfig == NULL, "MeasConfig not handled\n");
   }
 
@@ -263,7 +314,16 @@ void ue_context_modification_request(const f1ap_ue_context_modif_req_t *req)
           "RRC reconfiguration outcome unsuccessful, but no rollback mechanism implemented to come back to old configuration\n");
   }
 
-  if (req->srbs_to_be_setup_length > 0 || req->drbs_to_be_setup_length > 0) {
+  if (ue_cap != NULL) {
+    // store the new UE capabilities, and update the cellGroupConfig
+    ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->capability);
+    UE->capability = ue_cap;
+    LOG_I(NR_MAC, "UE %04x: received capabilities, updating CellGroupConfig\n", UE->rnti);
+    NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
+    update_cellGroupConfig(new_CellGroup, UE->uid, UE->capability, &mac->radio_config, scc);
+  }
+
+  if (req->srbs_to_be_setup_length > 0 || req->drbs_to_be_setup_length > 0 || ue_cap != NULL) {
     resp.du_to_cu_rrc_information = calloc(1, sizeof(du_to_cu_rrc_information_t));
     AssertFatal(resp.du_to_cu_rrc_information != NULL, "out of memory\n");
     resp.du_to_cu_rrc_information->cellGroupConfig = calloc(1, 1024);
diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
index d7c38f31792..eacb8c84dd7 100644
--- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
+++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
@@ -703,6 +703,7 @@ typedef struct {
   bool expect_reconfiguration;
   NR_CellGroupConfig_t *reconfigCellGroup;
   bool apply_cellgroup;
+  NR_UE_NR_Capability_t *capability;
   // UE selected beam index
   uint8_t UE_beam_index;
   bool Msg4_ACKed;
diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c
index ee3ddd08eea..6cd4fc3500c 100644
--- a/openair2/RRC/NR/MESSAGES/asn1_msg.c
+++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c
@@ -739,19 +739,7 @@ int16_t do_RRCReconfiguration(
         ie->nonCriticalExtension->dedicatedNAS_MessageList = dedicatedNAS_MessageList;
     }
 
-    if(cellGroupConfig!=NULL){
-      /* TODO UECAP: update_cellGroupConfig should not even be here, it is
-       * updated by DU */
-      if (scc != NULL) {
-        update_cellGroupConfig(cellGroupConfig,
-                               ue_context_pP->ue_context.rrc_ue_id,
-                               ue_context_pP ? ue_context_pP->ue_context.UE_Capability_nr : NULL,
-                               NULL,
-                               scc);
-      } else {
-        LOG_W(RRC, "no scc, cannot update cellGroup\n");
-      }
-
+    if (cellGroupConfig != NULL) {
       enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig,
                                        NULL,
                                        (void *)cellGroupConfig,
diff --git a/openair2/RRC/NR/mac_rrc_dl_f1ap.c b/openair2/RRC/NR/mac_rrc_dl_f1ap.c
index 270541a4b9c..88c5ad125e1 100644
--- a/openair2/RRC/NR/mac_rrc_dl_f1ap.c
+++ b/openair2/RRC/NR/mac_rrc_dl_f1ap.c
@@ -64,7 +64,21 @@ static void ue_context_modification_request_f1ap(const f1ap_ue_context_modif_req
   MessageDef *msg = itti_alloc_new_message(TASK_RRC_GNB, 0, F1AP_UE_CONTEXT_MODIFICATION_REQ);
   f1ap_ue_context_modif_req_t *f1ap_msg = &F1AP_UE_CONTEXT_MODIFICATION_REQ(msg);
   *f1ap_msg = *req;
-  AssertFatal(req->cu_to_du_rrc_information == NULL, "cu_to_du_rrc_information not supported yet\n");
+  if (req->cu_to_du_rrc_information != NULL) {
+    f1ap_msg->cu_to_du_rrc_information = calloc(1, sizeof(*f1ap_msg->cu_to_du_rrc_information));
+    AssertFatal(f1ap_msg->cu_to_du_rrc_information != NULL, "out of memory\n");
+    AssertFatal(req->cu_to_du_rrc_information->cG_ConfigInfo == NULL && req->cu_to_du_rrc_information->cG_ConfigInfo_length == 0, "cg_ConfigInfo not implemented\n");
+    AssertFatal(req->cu_to_du_rrc_information->measConfig == NULL && req->cu_to_du_rrc_information->measConfig_length == 0, "cg_ConfigInfo not implemented\n");
+    if (req->cu_to_du_rrc_information->uE_CapabilityRAT_ContainerList != NULL) {
+      const cu_to_du_rrc_information_t *du2cu_req = req->cu_to_du_rrc_information;
+      cu_to_du_rrc_information_t* du2cu_new = f1ap_msg->cu_to_du_rrc_information;
+      DevAssert(du2cu_req->uE_CapabilityRAT_ContainerList_length > 0);
+      du2cu_new->uE_CapabilityRAT_ContainerList_length = du2cu_req->uE_CapabilityRAT_ContainerList_length;
+      du2cu_new->uE_CapabilityRAT_ContainerList = malloc(du2cu_new->uE_CapabilityRAT_ContainerList_length);
+      AssertFatal(du2cu_new->uE_CapabilityRAT_ContainerList != NULL, "out of memory\n");
+      memcpy(du2cu_new->uE_CapabilityRAT_ContainerList, du2cu_req->uE_CapabilityRAT_ContainerList, du2cu_new->uE_CapabilityRAT_ContainerList_length);
+    }
+  }
   AssertFatal(req->drbs_to_be_modified_length == 0, "drbs_to_be_modified not supported yet\n");
   if (req->drbs_to_be_setup_length > 0) {
     int n = req->drbs_to_be_setup_length;
diff --git a/openair2/RRC/NR/nr_rrc_config.c b/openair2/RRC/NR/nr_rrc_config.c
index d1c55d17cfb..5eaaf7eeac8 100644
--- a/openair2/RRC/NR/nr_rrc_config.c
+++ b/openair2/RRC/NR/nr_rrc_config.c
@@ -2348,23 +2348,10 @@ void update_cellGroupConfig(NR_CellGroupConfig_t *cellGroupConfig,
                             const NR_ServingCellConfigCommon_t *scc)
 {
   DevAssert(cellGroupConfig != NULL);
-  /* this is wrong: we should not call this function is spCellConfig is not
-   * allocated */
-  if (cellGroupConfig->spCellConfig == NULL)
-    return;
-  /* same as for spCellConfig */
-  if (configuration == NULL)
-    return;
-  /* if scc is not allocated, there is a serious problem */
+  DevAssert(cellGroupConfig->spCellConfig != NULL);
+  DevAssert(configuration != NULL);
   DevAssert(scc != NULL);
 
-  /* This is a hack and will be removed once the CellGroupConfig is fully
-   * handled at the DU */
-  if (NODE_IS_CU(RC.nrrrc[0]->node_type)) {
-    LOG_W(RRC, "update of CellGroupConfig not yet supported in F1\n");
-    return;
-  }
-
   NR_SpCellConfig_t *SpCellConfig = cellGroupConfig->spCellConfig;
 
   int curr_bwp = NRRIV2BW(scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h
index 29ad2ccd356..465f968a4d1 100644
--- a/openair2/RRC/NR/nr_rrc_defs.h
+++ b/openair2/RRC/NR/nr_rrc_defs.h
@@ -38,6 +38,7 @@
 #include "collection/tree.h"
 #include "collection/linear_alloc.h"
 #include "nr_rrc_common.h"
+#include "ds/byte_array.h"
 
 #include "common/ngran_types.h"
 #include "common/platform_constants.h"
@@ -243,7 +244,7 @@ typedef struct gNB_RRC_UE_s {
   NR_HANDOVER_INFO                  *handover_info;
   NR_MeasResults_t                  *measResults;
 
-
+  byte_array_t ue_cap_buffer;
   NR_UE_NR_Capability_t*             UE_Capability_nr;
   int                                UE_Capability_size;
   NR_UE_MRDC_Capability_t*           UE_Capability_MRDC;
diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c
index 1b00e380a28..86d50a4bb02 100644
--- a/openair2/RRC/NR/rrc_gNB.c
+++ b/openair2/RRC/NR/rrc_gNB.c
@@ -1460,6 +1460,18 @@ static int handle_ueCapabilityInformation(const protocol_ctxt_t *const ctxt_pP,
   if (ue_cap_info->criticalExtensions.present == NR_UECapabilityInformation__criticalExtensions_PR_ueCapabilityInformation) {
     const NR_UE_CapabilityRAT_ContainerList_t *ue_CapabilityRAT_ContainerList =
         ue_cap_info->criticalExtensions.choice.ueCapabilityInformation->ue_CapabilityRAT_ContainerList;
+
+    /* Encode UE-CapabilityRAT-ContainerList for sending to the DU */
+    free(UE->ue_cap_buffer.buf);
+    UE->ue_cap_buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_UE_CapabilityRAT_ContainerList,
+                                                      NULL,
+                                                      ue_CapabilityRAT_ContainerList,
+                                                      (void **)&UE->ue_cap_buffer.buf);
+    if (UE->ue_cap_buffer.len <= 0) {
+      LOG_E(RRC, "could not encode UE-CapabilityRAT-ContainerList, abort handling capabilities\n");
+      return -1;
+    }
+
     for (int i = 0; i < ue_CapabilityRAT_ContainerList->list.count; i++) {
       const NR_UE_CapabilityRAT_Container_t *ue_cap_container = ue_CapabilityRAT_ContainerList->list.array[i];
       if (ue_cap_container->rat_Type == NR_RAT_Type_nr) {
@@ -2370,19 +2382,28 @@ void prepare_and_send_ue_context_modification_f1(rrc_gNB_ue_context_t *ue_contex
     srbs[0].lcid = 2;
   }
 
+  cu_to_du_rrc_information_t cu2du = {0};
+  cu_to_du_rrc_information_t *cu2du_p = NULL;
+  if (UE->ue_cap_buffer.len > 0 && UE->ue_cap_buffer.buf != NULL) {
+    cu2du_p = &cu2du;
+    cu2du.uE_CapabilityRAT_ContainerList = UE->ue_cap_buffer.buf;
+    cu2du.uE_CapabilityRAT_ContainerList_length = UE->ue_cap_buffer.len;
+  }
+
   f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
   f1ap_ue_context_modif_req_t ue_context_modif_req = {
-    .gNB_CU_ue_id = UE->rrc_ue_id,
-    .gNB_DU_ue_id = ue_data.secondary_ue,
-    .plmn.mcc = rrc->configuration.mcc[0],
-    .plmn.mnc = rrc->configuration.mnc[0],
-    .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
-    .nr_cellid = rrc->nr_cellid,
-    .servCellId = 0, /* TODO: correct value? */
-    .srbs_to_be_setup_length = nb_srb,
-    .srbs_to_be_setup = srbs,
-    .drbs_to_be_setup_length = nb_drb,
-    .drbs_to_be_setup = drbs,
+      .gNB_CU_ue_id = UE->rrc_ue_id,
+      .gNB_DU_ue_id = ue_data.secondary_ue,
+      .plmn.mcc = rrc->configuration.mcc[0],
+      .plmn.mnc = rrc->configuration.mnc[0],
+      .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
+      .nr_cellid = rrc->nr_cellid,
+      .servCellId = 0, /* TODO: correct value? */
+      .srbs_to_be_setup_length = nb_srb,
+      .srbs_to_be_setup = srbs,
+      .drbs_to_be_setup_length = nb_drb,
+      .drbs_to_be_setup = drbs,
+      .cu_to_du_rrc_information = cu2du_p,
   };
   rrc->mac_rrc.ue_context_modification_request(&ue_context_modif_req);
 }
@@ -2727,18 +2748,30 @@ rrc_gNB_generate_SecurityModeCommand(
   gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id];
   AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n");
 
+  cu_to_du_rrc_information_t cu2du = {0};
+  cu_to_du_rrc_information_t *cu2du_p = NULL;
+  if (ue_p->ue_cap_buffer.len > 0 && ue_p->ue_cap_buffer.buf != NULL) {
+    cu2du_p = &cu2du;
+    cu2du.uE_CapabilityRAT_ContainerList = ue_p->ue_cap_buffer.buf;
+    cu2du.uE_CapabilityRAT_ContainerList_length = ue_p->ue_cap_buffer.len;
+  }
+
+  const nr_rrc_du_container_t *du = rrc->du;
+  DevAssert(du != NULL);
+
   /* the callback will fill the UE context setup request and forward it */
   f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
   f1ap_ue_context_setup_t ue_context_setup_req = {
-    .gNB_CU_ue_id = ue_p->rrc_ue_id,
-    .gNB_DU_ue_id = ue_data.secondary_ue,
-    .plmn.mcc = rrc->configuration.mcc[0],
-    .plmn.mnc = rrc->configuration.mnc[0],
-    .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
-    .nr_cellid = rrc->nr_cellid,
-    .servCellId = 0, /* TODO: correct value? */
-    .srbs_to_be_setup = 0, /* no new SRBs */
-    .drbs_to_be_setup = 0, /* no new DRBs */
+      .gNB_CU_ue_id = ue_p->rrc_ue_id,
+      .gNB_DU_ue_id = ue_data.secondary_ue,
+      .plmn.mcc = rrc->configuration.mcc[0],
+      .plmn.mnc = rrc->configuration.mnc[0],
+      .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0],
+      .nr_cellid = rrc->nr_cellid,
+      .servCellId = 0, /* TODO: correct value? */
+      .srbs_to_be_setup = 0, /* no new SRBs */
+      .drbs_to_be_setup = 0, /* no new DRBs */
+      .cu_to_du_rrc_information = cu2du_p,
   };
   deliver_ue_ctxt_setup_data_t data = {.rrc = rrc, .setup_req = &ue_context_setup_req};
   nr_pdcp_data_req_srb(ctxt_pP->rntiMaybeUEid, DCCH, rrc_gNB_mui++, size, buffer, rrc_deliver_ue_ctxt_setup_req, &data);
-- 
GitLab