From f8299700859284b47804a2dfaa2d1846bf720f3c Mon Sep 17 00:00:00 2001
From: Cedric Roux <cedric.roux@eurecom.fr>
Date: Thu, 20 Feb 2020 15:18:50 +0100
Subject: [PATCH] improve NR UE capabilities request

Seems like the UEs in the CI don't like when asked to report NR UE
capabilities. So let's first ask for eutra capabilities and then
dig into them to check for en-dc support and only then ask for
NR capabilities. We chose to ask for NR capabilities after receiving
RRCConnectionReconfigurationComplete. That may not be the best thing
to do.

Today, we do nothing with those capabilities. We could completely skip
asking them to the UE, but since the code is there and since it may be
useful in the future, let's do it. Hopefully, it should not hurt too much.
---
 openair2/RRC/LTE/MESSAGES/asn1_msg.c |  59 ++++++++++++-
 openair2/RRC/LTE/MESSAGES/asn1_msg.h |   7 ++
 openair2/RRC/LTE/rrc_defs.h          |   1 +
 openair2/RRC/LTE/rrc_eNB.c           | 122 ++++++++++++++++++++++++---
 openair2/RRC/LTE/rrc_proto.h         |   6 ++
 5 files changed, 181 insertions(+), 14 deletions(-)

diff --git a/openair2/RRC/LTE/MESSAGES/asn1_msg.c b/openair2/RRC/LTE/MESSAGES/asn1_msg.c
index 5d733a0efe..8572fcbc15 100644
--- a/openair2/RRC/LTE/MESSAGES/asn1_msg.c
+++ b/openair2/RRC/LTE/MESSAGES/asn1_msg.c
@@ -3510,8 +3510,6 @@ uint8_t do_UECapabilityEnquiry( const protocol_ctxt_t *const ctxt_pP,
 {
   LTE_DL_DCCH_Message_t dl_dcch_msg;
   LTE_RAT_Type_t rat=LTE_RAT_Type_eutra;
-  LTE_RAT_Type_t rat_nr=LTE_RAT_Type_nr;
-  LTE_RAT_Type_t rat_eutra_nr=LTE_RAT_Type_eutra_nr;
   asn_enc_rval_t enc_rval;
   memset(&dl_dcch_msg,0,sizeof(LTE_DL_DCCH_Message_t));
   dl_dcch_msg.message.present           = LTE_DL_DCCH_MessageType_PR_c1;
@@ -3523,6 +3521,57 @@ uint8_t do_UECapabilityEnquiry( const protocol_ctxt_t *const ctxt_pP,
   dl_dcch_msg.message.choice.c1.choice.ueCapabilityEnquiry.criticalExtensions.choice.c1.choice.ueCapabilityEnquiry_r8.ue_CapabilityRequest.list.count=0;
   ASN_SEQUENCE_ADD(&dl_dcch_msg.message.choice.c1.choice.ueCapabilityEnquiry.criticalExtensions.choice.c1.choice.ueCapabilityEnquiry_r8.ue_CapabilityRequest.list,
                    &rat);
+
+  if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) {
+    xer_fprint(stdout, &asn_DEF_LTE_DL_DCCH_Message, (void *)&dl_dcch_msg);
+  }
+
+  enc_rval = uper_encode_to_buffer(&asn_DEF_LTE_DL_DCCH_Message,
+                                   NULL,
+                                   (void *)&dl_dcch_msg,
+                                   buffer,
+                                   100);
+
+  if(enc_rval.encoded == -1) {
+    LOG_I(RRC, "[eNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n",
+          enc_rval.failed_type->name, enc_rval.encoded);
+    return -1;
+  }
+
+  LOG_D(RRC,"[eNB %d] UECapabilityRequest for UE %x Encoded %zd bits (%zd bytes)\n",
+        ctxt_pP->module_id,
+        ctxt_pP->rnti,
+        enc_rval.encoded,
+        (enc_rval.encoded+7)/8);
+
+  if (enc_rval.encoded==-1) {
+    LOG_E(RRC,"[eNB %d] ASN1 : UECapabilityRequest encoding failed for UE %x\n",
+          ctxt_pP->module_id,
+          ctxt_pP->rnti);
+    return(-1);
+  }
+
+  return((enc_rval.encoded+7)/8);
+}
+
+//------------------------------------------------------------------------------
+uint8_t do_NR_UECapabilityEnquiry( const protocol_ctxt_t *const ctxt_pP,
+                                   uint8_t               *const buffer,
+                                   const uint8_t                Transaction_id)
+//------------------------------------------------------------------------------
+{
+  LTE_DL_DCCH_Message_t dl_dcch_msg;
+  LTE_RAT_Type_t rat_nr=LTE_RAT_Type_nr;
+  LTE_RAT_Type_t rat_eutra_nr=LTE_RAT_Type_eutra_nr;
+  asn_enc_rval_t enc_rval;
+  memset(&dl_dcch_msg,0,sizeof(LTE_DL_DCCH_Message_t));
+  dl_dcch_msg.message.present           = LTE_DL_DCCH_MessageType_PR_c1;
+  dl_dcch_msg.message.choice.c1.present = LTE_DL_DCCH_MessageType__c1_PR_ueCapabilityEnquiry;
+  dl_dcch_msg.message.choice.c1.choice.ueCapabilityEnquiry.rrc_TransactionIdentifier = Transaction_id;
+  dl_dcch_msg.message.choice.c1.choice.ueCapabilityEnquiry.criticalExtensions.present = LTE_UECapabilityEnquiry__criticalExtensions_PR_c1;
+  dl_dcch_msg.message.choice.c1.choice.ueCapabilityEnquiry.criticalExtensions.choice.c1.present =
+    LTE_UECapabilityEnquiry__criticalExtensions__c1_PR_ueCapabilityEnquiry_r8;
+  dl_dcch_msg.message.choice.c1.choice.ueCapabilityEnquiry.criticalExtensions.choice.c1.choice.ueCapabilityEnquiry_r8.ue_CapabilityRequest.list.count=0;
   ASN_SEQUENCE_ADD(&dl_dcch_msg.message.choice.c1.choice.ueCapabilityEnquiry.criticalExtensions.choice.c1.choice.ueCapabilityEnquiry_r8.ue_CapabilityRequest.list,
                    &rat_nr);
   ASN_SEQUENCE_ADD(&dl_dcch_msg.message.choice.c1.choice.ueCapabilityEnquiry.criticalExtensions.choice.c1.choice.ueCapabilityEnquiry_r8.ue_CapabilityRequest.list,
@@ -3548,6 +3597,8 @@ uint8_t do_UECapabilityEnquiry( const protocol_ctxt_t *const ctxt_pP,
   r13_10.nonCriticalExtension = &r14_30;
   r14_30.nonCriticalExtension = &r15_10;
 
+  /* TODO: no hardcoded values here */
+
   OCTET_STRING_t req_freq;
   //unsigned char req_freq_buf[5] = { 0x00, 0x20, 0x1a, 0x02, 0x68 };  // bands 7 & nr78
   //unsigned char req_freq_buf[5] = { 0x00, 0x20, 0x1a, 0x08, 0x18 };  // bands 7 & nr260
@@ -3579,14 +3630,14 @@ uint8_t do_UECapabilityEnquiry( const protocol_ctxt_t *const ctxt_pP,
     return -1;
   }
 
-  LOG_D(RRC,"[eNB %d] UECapabilityRequest for UE %x Encoded %zd bits (%zd bytes)\n",
+  LOG_D(RRC,"[eNB %d] NR UECapabilityRequest for UE %x Encoded %zd bits (%zd bytes)\n",
         ctxt_pP->module_id,
         ctxt_pP->rnti,
         enc_rval.encoded,
         (enc_rval.encoded+7)/8);
 
   if (enc_rval.encoded==-1) {
-    LOG_E(RRC,"[eNB %d] ASN1 : UECapabilityRequest encoding failed for UE %x\n",
+    LOG_E(RRC,"[eNB %d] ASN1 : NR UECapabilityRequest encoding failed for UE %x\n",
           ctxt_pP->module_id,
           ctxt_pP->rnti);
     return(-1);
diff --git a/openair2/RRC/LTE/MESSAGES/asn1_msg.h b/openair2/RRC/LTE/MESSAGES/asn1_msg.h
index 925f09416e..34b2909394 100644
--- a/openair2/RRC/LTE/MESSAGES/asn1_msg.h
+++ b/openair2/RRC/LTE/MESSAGES/asn1_msg.h
@@ -362,6 +362,13 @@ do_UECapabilityEnquiry(
   const uint8_t                Transaction_id
 );
 
+uint8_t
+do_NR_UECapabilityEnquiry(
+  const protocol_ctxt_t *const ctxt_pP,
+  uint8_t               *const buffer,
+  const uint8_t                Transaction_id
+);
+
 uint8_t do_SecurityModeCommand(
   const protocol_ctxt_t *const ctxt_pP,
   uint8_t *const buffer,
diff --git a/openair2/RRC/LTE/rrc_defs.h b/openair2/RRC/LTE/rrc_defs.h
index 53ba6d9314..f050733a13 100644
--- a/openair2/RRC/LTE/rrc_defs.h
+++ b/openair2/RRC/LTE/rrc_defs.h
@@ -647,6 +647,7 @@ typedef struct eNB_RRC_UE_s {
   uint8_t                            e_rab_release_command_flag;
   int8_t                             reestablishment_xid;
   int                                does_nr;
+  int                                nr_capabilities_requested;
 } eNB_RRC_UE_t;
 
 typedef uid_t ue_uid_t;
diff --git a/openair2/RRC/LTE/rrc_eNB.c b/openair2/RRC/LTE/rrc_eNB.c
index 051012ce28..aafd1cbf6f 100644
--- a/openair2/RRC/LTE/rrc_eNB.c
+++ b/openair2/RRC/LTE/rrc_eNB.c
@@ -1287,6 +1287,52 @@ rrc_eNB_generate_UECapabilityEnquiry(
     PDCP_TRANSMISSION_MODE_CONTROL);
 }
 
+//-----------------------------------------------------------------------------
+void
+rrc_eNB_generate_NR_UECapabilityEnquiry(
+  const protocol_ctxt_t *const ctxt_pP,
+  rrc_eNB_ue_context_t          *const ue_context_pP
+)
+//-----------------------------------------------------------------------------
+{
+  uint8_t                             buffer[100];
+  uint8_t                             size;
+  T(T_ENB_RRC_UE_CAPABILITY_ENQUIRY, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
+    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
+  size = do_NR_UECapabilityEnquiry(
+           ctxt_pP,
+           buffer,
+           rrc_eNB_get_next_transaction_identifier(ctxt_pP->module_id));
+  LOG_I(RRC,
+        PROTOCOL_RRC_CTXT_UE_FMT" Logical Channel DL-DCCH, Generate NR UECapabilityEnquiry (bytes %d)\n",
+        PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP),
+        size);
+  LOG_D(RRC,
+        PROTOCOL_RRC_CTXT_UE_FMT" --- PDCP_DATA_REQ/%d Bytes (NR UECapabilityEnquiry MUI %d) --->[PDCP][RB %02d]\n",
+        PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP),
+        size,
+        rrc_eNB_mui,
+        DCCH);
+  MSC_LOG_TX_MESSAGE(
+    MSC_RRC_ENB,
+    MSC_RRC_UE,
+    buffer,
+    size,
+    MSC_AS_TIME_FMT" rrcNRUECapabilityEnquiry UE %x MUI %d size %u",
+    MSC_AS_TIME_ARGS(ctxt_pP),
+    ue_context_pP->ue_context.rnti,
+    rrc_eNB_mui,
+    size);
+  rrc_data_req(
+    ctxt_pP,
+    DCCH,
+    rrc_eNB_mui++,
+    SDU_CONFIRM_NO,
+    size,
+    buffer,
+    PDCP_TRANSMISSION_MODE_CONTROL);
+}
+
 //-----------------------------------------------------------------------------
 void
 rrc_eNB_generate_RRCConnectionReject(
@@ -6396,6 +6442,16 @@ rrc_eNB_process_RRCConnectionReconfigurationComplete(
     free(DRB_Release_configList2);
     ue_context_pP->ue_context.DRB_Release_configList2[xid] = NULL;
   }
+
+  /* let's request NR capabilities if the UE supports NR
+   * maybe not the right place/time to request
+   */
+  if (ue_context_pP->ue_context.does_nr &&
+      !ue_context_pP->ue_context.nr_capabilities_requested) {
+    ue_context_pP->ue_context.nr_capabilities_requested = 1;
+    rrc_eNB_generate_NR_UECapabilityEnquiry(ctxt_pP, ue_context_pP);
+  }
+
 }
 
 //-----------------------------------------------------------------------------
@@ -7214,6 +7270,48 @@ rrc_eNB_decode_ccch(
   return rval;
 }
 
+//-----------------------------------------------------------------------------
+static int
+is_en_dc_supported(
+  LTE_UE_EUTRA_Capability_t *c
+)
+//-----------------------------------------------------------------------------
+{
+  /* to be refined - check that the eNB is connected to a gNB, check that
+   * the bands supported by the UE include the band of the gNB
+   */
+#define NCE nonCriticalExtension
+  return c != NULL
+      && c->NCE != NULL
+      && c->NCE->NCE != NULL
+      && c->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->irat_ParametersNR_r15 != NULL
+      && c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->irat_ParametersNR_r15->en_DC_r15 != NULL
+      && *c->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->NCE->irat_ParametersNR_r15->en_DC_r15 == LTE_IRAT_ParametersNR_r15__en_DC_r15_supported;
+#undef NCE
+}
+
 //-----------------------------------------------------------------------------
 int
 rrc_eNB_decode_dcch(
@@ -7743,13 +7841,6 @@ rrc_eNB_decode_dcch(
 
         LOG_I(RRC, "got UE capabilities for UE %x\n", ctxt_pP->rnti);
 
-        if (ue_context_p->ue_context.UE_Capability) {
-          LOG_I(RRC, "freeing old UE capabilities for UE %x\n", ctxt_pP->rnti);
-          ASN_STRUCT_FREE(asn_DEF_LTE_UE_EUTRA_Capability,
-                          ue_context_p->ue_context.UE_Capability);
-          ue_context_p->ue_context.UE_Capability = 0;
-        }
-
         int eutra_index = -1;
 
         for (i = 0; i < ul_dcch_msg->message.choice.c1.choice.ueCapabilityInformation.criticalExtensions.choice.c1.choice.ueCapabilityInformation_r8.ue_CapabilityRAT_ContainerList.list.count; i++) {
@@ -7760,9 +7851,17 @@ rrc_eNB_decode_dcch(
             }
             eutra_index = i;
           }
-          /* todo: improve nr dual connectivity capability checking */
-          if (ul_dcch_msg->message.choice.c1.choice.ueCapabilityInformation.criticalExtensions.choice.c1.choice.ueCapabilityInformation_r8.ue_CapabilityRAT_ContainerList.list.array[i]->rat_Type == LTE_RAT_Type_eutra_nr)
-            ue_context_p->ue_context.does_nr = 1;
+        }
+
+        /* do nothing if no EUTRA capabilities (TODO: may be NR capabilities, to be processed somehow) */
+        if (eutra_index == -1)
+          break;
+
+        if (ue_context_p->ue_context.UE_Capability) {
+          LOG_I(RRC, "freeing old UE capabilities for UE %x\n", ctxt_pP->rnti);
+          ASN_STRUCT_FREE(asn_DEF_LTE_UE_EUTRA_Capability,
+                          ue_context_p->ue_context.UE_Capability);
+          ue_context_p->ue_context.UE_Capability = 0;
         }
 
         dec_rval = uper_decode(NULL,
@@ -7786,6 +7885,9 @@ rrc_eNB_decode_dcch(
           ue_context_p->ue_context.UE_Capability = 0;
         }
 
+        if (dec_rval.code == RC_OK)
+          ue_context_p->ue_context.does_nr = is_en_dc_supported(ue_context_p->ue_context.UE_Capability);
+
         if (EPC_MODE_ENABLED) {
           rrc_eNB_send_S1AP_UE_CAPABILITIES_IND(ctxt_pP,
                                                 ue_context_p,
diff --git a/openair2/RRC/LTE/rrc_proto.h b/openair2/RRC/LTE/rrc_proto.h
index d5009de270..8f6baabcbf 100644
--- a/openair2/RRC/LTE/rrc_proto.h
+++ b/openair2/RRC/LTE/rrc_proto.h
@@ -584,6 +584,12 @@ rrc_eNB_generate_UECapabilityEnquiry(
   rrc_eNB_ue_context_t          *const ue_context_pP
 );
 
+void
+rrc_eNB_generate_NR_UECapabilityEnquiry(
+  const protocol_ctxt_t *const ctxt_pP,
+  rrc_eNB_ue_context_t          *const ue_context_pP
+);
+
 void
 rrc_eNB_generate_SecurityModeCommand(
   const protocol_ctxt_t *const ctxt_pP,
-- 
2.26.2