From 44d309a69da5979c8ec5943aa2650591ea4e07d7 Mon Sep 17 00:00:00 2001
From: luis_pereira87 <lpereira@allbesmart.pt>
Date: Wed, 3 Aug 2022 09:34:27 +0100
Subject: [PATCH] Implementation of RRC NR_Paging message at gNB

---
 doc/FEATURE_SET.md                    |   3 +-
 openair2/COMMON/f1ap_messages_def.h   |   6 +-
 openair2/COMMON/f1ap_messages_types.h |  13 +++
 openair2/F1AP/dummy_enb.c             |   7 ++
 openair2/F1AP/f1ap_cu_paging.c        |  96 ++++++++++++++++++-
 openair2/F1AP/f1ap_cu_paging.h        |  11 ++-
 openair2/F1AP/f1ap_cu_task.c          |   7 ++
 openair2/F1AP/f1ap_decoder.c          |   4 +
 openair2/F1AP/f1ap_du_paging.c        |  68 +++++++++++++-
 openair2/F1AP/f1ap_du_paging.h        |  12 ++-
 openair2/F1AP/f1ap_handlers.c         |   3 +-
 openair2/RRC/LTE/rrc_defs.h           |   4 +-
 openair2/RRC/NR/MESSAGES/asn1_msg.c   |  48 ++++++++++
 openair2/RRC/NR/MESSAGES/asn1_msg.h   |   1 +
 openair2/RRC/NR/nr_rrc_proto.h        |   6 ++
 openair2/RRC/NR/rrc_gNB.c             | 128 ++++++++++++++++++++++++++
 openair2/RRC/NR/rrc_gNB_NGAP.c        |  34 +++++++
 openair2/RRC/NR/rrc_gNB_NGAP.h        |   6 ++
 openair3/UTILS/conversions.h          |  21 +++++
 19 files changed, 463 insertions(+), 15 deletions(-)

diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md
index e69c8328bc1..3f7ffc9fdb2 100644
--- a/doc/FEATURE_SET.md
+++ b/doc/FEATURE_SET.md
@@ -357,7 +357,8 @@ The following features are valid for the gNB and the 5G-NR UE.
   - RRCSetupRequest/RRCSetup/RRCSetupComplete
   - RRC Uplink/Downlink Information transfer carrying NAS messages transparently
   - RRC Reconfiguration/Reconfiguration complete
-  - Support for master cell group configuration 
+  - Paging
+  - Support for master cell group configuration
   - Interface with NGAP for the interactions with the AMF
   - Interface with F1AP for CU/DU split deployment option
   - Periodic RRC measurements of serving cell (no A/B events)
diff --git a/openair2/COMMON/f1ap_messages_def.h b/openair2/COMMON/f1ap_messages_def.h
index f28e0e5ddfc..5db598409fe 100644
--- a/openair2/COMMON/f1ap_messages_def.h
+++ b/openair2/COMMON/f1ap_messages_def.h
@@ -40,7 +40,6 @@ MESSAGE_DEF(F1AP_UL_RRC_MESSAGE                , MESSAGE_PRIORITY_MED, f1ap_ul_r
 MESSAGE_DEF(F1AP_UE_CONTEXT_RELEASE_REQ, MESSAGE_PRIORITY_MED, f1ap_ue_context_release_req_t, f1ap_ue_context_release_req)
 MESSAGE_DEF(F1AP_UE_CONTEXT_RELEASE_CMD, MESSAGE_PRIORITY_MED, f1ap_ue_context_release_cmd_t, f1ap_ue_context_release_cmd)
 
-
 /* RRC -> F1AP  messages */
 MESSAGE_DEF(F1AP_DL_RRC_MESSAGE              , MESSAGE_PRIORITY_MED, f1ap_dl_rrc_message_t              , f1ap_dl_rrc_message )
 //MESSAGE_DEF(F1AP_INITIAL_CONTEXT_SETUP_REQ , MESSAGE_PRIORITY_MED, f1ap_initial_context_setup_req_t , f1ap_initial_context_setup_req )
@@ -49,6 +48,5 @@ MESSAGE_DEF(F1AP_UE_CONTEXT_SETUP_RESP, MESSAGE_PRIORITY_MED, f1ap_ue_context_se
 MESSAGE_DEF(F1AP_UE_CONTEXT_MODIFICATION_REQ,  MESSAGE_PRIORITY_MED, f1ap_ue_context_setup_t, f1ap_ue_context_modification_req)
 MESSAGE_DEF(F1AP_UE_CONTEXT_MODIFICATION_RESP, MESSAGE_PRIORITY_MED, f1ap_ue_context_setup_t, f1ap_ue_context_modification_resp)
 
-
-
-
+/* CU -> DU*/
+MESSAGE_DEF(F1AP_PAGING_IND, MESSAGE_PRIORITY_MED, f1ap_paging_ind_t, f1ap_paging_ind)
diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h
index cbc4d1c6f2d..e6150059919 100644
--- a/openair2/COMMON/f1ap_messages_types.h
+++ b/openair2/COMMON/f1ap_messages_types.h
@@ -50,6 +50,8 @@
 #define F1AP_UE_CONTEXT_RELEASE_REQ(mSGpTR)        (mSGpTR)->ittiMsg.f1ap_ue_context_release_req
 #define F1AP_UE_CONTEXT_RELEASE_CMD(mSGpTR)        (mSGpTR)->ittiMsg.f1ap_ue_context_release_req
 
+#define F1AP_PAGING_IND(mSGpTR)                    (mSGpTR)->ittiMsg.f1ap_paging_ind
+
 /* Length of the transport layer address string
  * 160 bits / 8 bits by char.
  */
@@ -424,4 +426,15 @@ typedef struct f1ap_ue_context_release_s {
 } f1ap_ue_context_release_req_t, f1ap_ue_context_release_cmd_t,
   f1ap_ue_context_release_cplt_t;
 
+typedef struct f1ap_paging_ind_s {
+  uint16_t ueidentityindexvalue;
+  uint64_t fiveg_s_tmsi;
+  uint8_t  fiveg_s_tmsi_length;
+  uint16_t mcc;
+  uint16_t mnc;
+  uint8_t  mnc_digit_length;
+  uint64_t nr_cellid;
+  uint8_t  paging_drx;
+} f1ap_paging_ind_t;
+
 #endif /* F1AP_MESSAGES_TYPES_H_ */
diff --git a/openair2/F1AP/dummy_enb.c b/openair2/F1AP/dummy_enb.c
index 08a5b906eed..afb4a1048af 100644
--- a/openair2/F1AP/dummy_enb.c
+++ b/openair2/F1AP/dummy_enb.c
@@ -52,3 +52,10 @@ abort();
 int dl_rrc_message(module_id_t module_id, const f1ap_dl_rrc_message_t *dl_rrc) {
   abort();
 }
+
+int rrc_gNB_generate_pcch_msg(uint32_t tmsi,
+                              uint8_t paging_drx,
+                              instance_t instance,
+                              uint8_t CC_id) {
+  abort();
+}
diff --git a/openair2/F1AP/f1ap_cu_paging.c b/openair2/F1AP/f1ap_cu_paging.c
index b6bbedba2f0..5bc56643e19 100644
--- a/openair2/F1AP/f1ap_cu_paging.c
+++ b/openair2/F1AP/f1ap_cu_paging.c
@@ -19,8 +19,8 @@
  *      contact@openairinterface.org
  */
 
-/*! \file f1ap_du_interface_management.h
- * \brief f1ap interface management for DU
+/*! \file f1ap_cu_paging.c
+ * \brief f1ap interface paging for CU
  * \author EURECOM/NTUST
  * \date 2018
  * \version 0.1
@@ -28,4 +28,94 @@
  * \email: navid.nikaein@eurecom.fr, bing-kai.hong@eurecom.fr
  * \note
  * \warning
- */
\ No newline at end of file
+ */
+
+#include "f1ap_common.h"
+#include "f1ap_encoder.h"
+#include "f1ap_itti_messaging.h"
+#include "f1ap_cu_paging.h"
+
+extern f1ap_setup_req_t *f1ap_du_data_from_du;
+
+int CU_send_Paging(instance_t instance, f1ap_paging_ind_t *paging) {
+  F1AP_F1AP_PDU_t                 pdu;
+  F1AP_Paging_t    *out;
+  F1AP_PagingIEs_t *ie;
+
+  uint8_t  *buffer;
+  uint32_t  len;
+
+  memset(&pdu, 0, sizeof(pdu));
+  pdu.present = F1AP_F1AP_PDU_PR_initiatingMessage;
+  pdu.choice.initiatingMessage = (F1AP_InitiatingMessage_t *)calloc(1, sizeof(F1AP_InitiatingMessage_t));
+  pdu.choice.initiatingMessage->procedureCode = F1AP_ProcedureCode_id_Paging;
+  pdu.choice.initiatingMessage->criticality   = F1AP_Criticality_reject;
+  pdu.choice.initiatingMessage->value.present = F1AP_InitiatingMessage__value_PR_Paging;
+  out = &pdu.choice.initiatingMessage->value.choice.Paging;
+
+  /* mandatory */
+  /* UEIdentityIndexValue */
+  ie = (F1AP_PagingIEs_t *)calloc(1, sizeof(F1AP_PagingIEs_t));
+  ie->id                             = F1AP_ProtocolIE_ID_id_UEIdentityIndexValue;
+  ie->criticality                    = F1AP_Criticality_reject;
+  ie->value.present                  = F1AP_PagingIEs__value_PR_UEIdentityIndexValue;
+  ie->value.choice.UEIdentityIndexValue.present = F1AP_UEIdentityIndexValue_PR_indexLength10;
+  UEIDENTITYINDEX_TO_BIT_STRING(paging->ueidentityindexvalue, &ie->value.choice.UEIdentityIndexValue.choice.indexLength10);
+  //LOG_D(F1AP, "indexLength10 %d \n", BIT_STRING_to_uint32(&ie->value.choice.UEIdentityIndexValue.choice.indexLength10));
+  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
+
+  /* mandatory */
+  /* PagingIdentity */
+  ie = (F1AP_PagingIEs_t *)calloc(1, sizeof(F1AP_PagingIEs_t));
+  ie->id                             = F1AP_ProtocolIE_ID_id_PagingIdentity;
+  ie->criticality                    = F1AP_Criticality_reject;
+  ie->value.present                  = F1AP_PagingIEs__value_PR_PagingIdentity;
+  ie->value.choice.PagingIdentity.present = F1AP_PagingIdentity_PR_cNUEPagingIdentity;
+  ie->value.choice.PagingIdentity.choice.cNUEPagingIdentity = (F1AP_CNUEPagingIdentity_t*)calloc(1, sizeof(F1AP_CNUEPagingIdentity_t));
+  ie->value.choice.PagingIdentity.choice.cNUEPagingIdentity->present = F1AP_CNUEPagingIdentity_PR_fiveG_S_TMSI;
+  FIVEG_S_TMSI_TO_BIT_STRING(paging->fiveg_s_tmsi,
+                             &ie->value.choice.PagingIdentity.choice.cNUEPagingIdentity->choice.fiveG_S_TMSI);
+  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
+
+  /* optional */
+  /* PagingDRX */
+  ie = (F1AP_PagingIEs_t *)calloc(1, sizeof(F1AP_PagingIEs_t));
+  ie->id                             = F1AP_ProtocolIE_ID_id_PagingDRX;
+  ie->criticality                    = F1AP_Criticality_ignore;
+  ie->value.present                  = F1AP_PagingIEs__value_PR_PagingDRX;
+  ie->value.choice.PagingDRX = paging->paging_drx;
+  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
+
+  /* mandatory */
+  /* PagingCell_list */
+  ie = (F1AP_PagingIEs_t *)calloc(1, sizeof(F1AP_PagingIEs_t));
+  ie->id                             = F1AP_ProtocolIE_ID_id_PagingCell_List;
+  ie->criticality                    = F1AP_Criticality_reject;
+  ie->value.present                  = F1AP_PagingIEs__value_PR_PagingCell_list;
+  F1AP_PagingCell_ItemIEs_t *itemies;
+  F1AP_PagingCell_Item_t item;
+  memset((void *)&item, 0, sizeof(F1AP_PagingCell_Item_t));
+  itemies = (F1AP_PagingCell_ItemIEs_t *)calloc(1, sizeof(F1AP_PagingCell_ItemIEs_t));
+  itemies->id = F1AP_ProtocolIE_ID_id_PagingCell_Item;
+  itemies->criticality = F1AP_Criticality_reject;
+  itemies->value.present = F1AP_PagingCell_ItemIEs__value_PR_PagingCell_Item;
+  F1AP_NRCGI_t nRCGI;
+  MCC_MNC_TO_PLMNID(paging->mcc,
+                    paging->mnc,
+                    paging->mnc_digit_length,
+                    &nRCGI.pLMN_Identity);
+  NR_CELL_ID_TO_BIT_STRING(paging->nr_cellid, &nRCGI.nRCellIdentity);
+  item.nRCGI = nRCGI;
+  itemies->value.choice.PagingCell_Item = item;
+  ASN_SEQUENCE_ADD(&ie->value.choice.PagingCell_list, itemies);
+  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
+
+  /* encode */
+  if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) {
+    LOG_E(F1AP, "Failed to encode F1 Paging failure\n");
+    return -1;
+  }
+
+  f1ap_itti_send_sctp_data_req(true, instance, buffer, len, 0);
+  return 0;
+}
\ No newline at end of file
diff --git a/openair2/F1AP/f1ap_cu_paging.h b/openair2/F1AP/f1ap_cu_paging.h
index b6bbedba2f0..5cff651fcc4 100644
--- a/openair2/F1AP/f1ap_cu_paging.h
+++ b/openair2/F1AP/f1ap_cu_paging.h
@@ -19,8 +19,8 @@
  *      contact@openairinterface.org
  */
 
-/*! \file f1ap_du_interface_management.h
- * \brief f1ap interface management for DU
+/*! \file f1ap_cu_paging.h
+ * \brief f1ap interface paging for CU
  * \author EURECOM/NTUST
  * \date 2018
  * \version 0.1
@@ -28,4 +28,9 @@
  * \email: navid.nikaein@eurecom.fr, bing-kai.hong@eurecom.fr
  * \note
  * \warning
- */
\ No newline at end of file
+ */
+
+#ifndef F1AP_CU_PAGING_H_
+#define F1AP_CU_PAGING_H_
+int CU_send_Paging(instance_t instance, f1ap_paging_ind_t *paging);
+#endif /* F1AP_DU_PAGING_H_ */
diff --git a/openair2/F1AP/f1ap_cu_task.c b/openair2/F1AP/f1ap_cu_task.c
index b521d0c83dc..b2d2c872d1e 100644
--- a/openair2/F1AP/f1ap_cu_task.c
+++ b/openair2/F1AP/f1ap_cu_task.c
@@ -34,6 +34,7 @@
 #include "f1ap_cu_interface_management.h"
 #include "f1ap_cu_rrc_message_transfer.h"
 #include "f1ap_cu_ue_context_management.h"
+#include "f1ap_cu_paging.h"
 #include "f1ap_cu_task.h"
 #include <openair3/ocp-gtpu/gtp_itf.h>
 
@@ -193,6 +194,12 @@ void *F1AP_CU_task(void *arg) {
                                            &F1AP_UE_CONTEXT_RELEASE_CMD(received_msg));
         break;
 
+      case F1AP_PAGING_IND:
+        LOG_I(F1AP, "CU Task Received F1AP_PAGING_IND\n");
+        CU_send_Paging(ITTI_MSG_DESTINATION_INSTANCE(received_msg),
+                       &F1AP_PAGING_IND(received_msg));
+        break;
+
       //    case F1AP_SETUP_RESPONSE: // This is from RRC
       //    CU_send_F1_SETUP_RESPONSE(instance, *f1ap_setup_ind, &(F1AP_SETUP_RESP) f1ap_setup_resp)
       //        break;
diff --git a/openair2/F1AP/f1ap_decoder.c b/openair2/F1AP/f1ap_decoder.c
index 60b3f94310e..6d1c60c67df 100644
--- a/openair2/F1AP/f1ap_decoder.c
+++ b/openair2/F1AP/f1ap_decoder.c
@@ -83,6 +83,10 @@ static int f1ap_decode_initiating_message(F1AP_F1AP_PDU_t *pdu) {
       LOG_I(F1AP, "%s(): F1AP_ProcedureCode_id_UEContextModification\n", __func__);
       break;
 
+    case F1AP_ProcedureCode_id_Paging:
+      LOG_I(F1AP, "%s(): F1AP_ProcedureCode_id_Paging\n", __func__);
+      break;
+
     // case F1AP_ProcedureCode_id_InitialContextSetup:
     //   res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu);
     //   message_id = F1AP_INITIAL_CONTEXT_SETUP_LOG;
diff --git a/openair2/F1AP/f1ap_du_paging.c b/openair2/F1AP/f1ap_du_paging.c
index b6bbedba2f0..46f17fd64d0 100644
--- a/openair2/F1AP/f1ap_du_paging.c
+++ b/openair2/F1AP/f1ap_du_paging.c
@@ -28,4 +28,70 @@
  * \email: navid.nikaein@eurecom.fr, bing-kai.hong@eurecom.fr
  * \note
  * \warning
- */
\ No newline at end of file
+ */
+#include "f1ap_common.h"
+#include "f1ap_du_paging.h"
+#include "conversions.h"
+#include "asn1_conversions.h"
+#include "openair2/RRC/LTE/rrc_proto.h"
+
+int DU_handle_Paging(instance_t       instance,
+                     uint32_t         assoc_id,
+                     uint32_t         stream,
+                     F1AP_F1AP_PDU_t *pdu) {
+  F1AP_Paging_t *paging;
+  F1AP_PagingIEs_t *ie;
+  long tmsi;
+  uint8_t pagingdrx;
+  uint8_t  *fiveg_tmsi_buf = NULL;
+
+  DevAssert(pdu);
+
+  if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) {
+    xer_fprint(stdout, &asn_DEF_F1AP_F1AP_PDU, pdu);
+  }
+
+  paging = &pdu->choice.initiatingMessage->value.choice.Paging;
+  // get UEIdentityIndexValue
+  F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_PagingIEs_t, ie, paging,
+                             F1AP_ProtocolIE_ID_id_UEIdentityIndexValue, true);
+
+  LOG_D(F1AP, "indexLength10 %d\n", BIT_STRING_to_uint32(&ie->value.choice.UEIdentityIndexValue.choice.indexLength10));
+
+  // get PagingIdentity
+  F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_PagingIEs_t, ie, paging,
+                             F1AP_ProtocolIE_ID_id_PagingIdentity, true);
+
+  if(ie != NULL){
+    fiveg_tmsi_buf = ie->value.choice.PagingIdentity.choice.cNUEPagingIdentity->choice.fiveG_S_TMSI.buf;
+
+    tmsi = ((long)fiveg_tmsi_buf[0] << 40) +
+           ((long)fiveg_tmsi_buf[1] << 32) +
+           (fiveg_tmsi_buf[2] << 24) +
+           (fiveg_tmsi_buf[3] << 16) +
+           (fiveg_tmsi_buf[4] << 8) +
+           fiveg_tmsi_buf[5];
+
+    LOG_D(F1AP, "tmsi %ld\n", tmsi);
+  }
+
+  // get PagingDRX
+  F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_PagingIEs_t, ie, paging,
+                             F1AP_ProtocolIE_ID_id_PagingDRX, true);
+
+  if(ie != NULL) {
+    pagingdrx = (uint8_t)ie->value.choice.PagingDRX;
+  }
+
+  LOG_D(F1AP, "pagingdrx %u\n", pagingdrx);
+
+  // get PagingCell_List
+  F1AP_FIND_PROTOCOLIE_BY_ID(F1AP_PagingIEs_t, ie, paging,
+                             F1AP_ProtocolIE_ID_id_PagingCell_List, true);
+
+  for (uint8_t CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
+    rrc_gNB_generate_pcch_msg((uint32_t)tmsi, pagingdrx, instance, CC_id);
+  }
+
+  return 0;
+}
\ No newline at end of file
diff --git a/openair2/F1AP/f1ap_du_paging.h b/openair2/F1AP/f1ap_du_paging.h
index b6bbedba2f0..3e1806ae022 100644
--- a/openair2/F1AP/f1ap_du_paging.h
+++ b/openair2/F1AP/f1ap_du_paging.h
@@ -28,4 +28,14 @@
  * \email: navid.nikaein@eurecom.fr, bing-kai.hong@eurecom.fr
  * \note
  * \warning
- */
\ No newline at end of file
+ */
+
+#ifndef F1AP_DU_PAGING_H_
+#define F1AP_DU_PAGING_H_
+
+int DU_handle_Paging(instance_t       instance,
+                     uint32_t         assoc_id,
+                     uint32_t         stream,
+                     F1AP_F1AP_PDU_t *pdu);
+
+#endif /* F1AP_DU_PAGING_H_ */
diff --git a/openair2/F1AP/f1ap_handlers.c b/openair2/F1AP/f1ap_handlers.c
index 0812a976f63..f3db249e096 100644
--- a/openair2/F1AP/f1ap_handlers.c
+++ b/openair2/F1AP/f1ap_handlers.c
@@ -38,6 +38,7 @@
 #include "f1ap_du_rrc_message_transfer.h"
 #include "f1ap_cu_ue_context_management.h"
 #include "f1ap_du_ue_context_management.h"
+#include "f1ap_du_paging.h"
 
 /* Handlers matrix. Only f1 related procedure present here */
 f1ap_message_processing_t f1ap_messages_processing[][3] = {
@@ -61,7 +62,7 @@ f1ap_message_processing_t f1ap_messages_processing[][3] = {
   { 0, 0, 0 }, /* UEInactivityNotification */
   { 0, 0, 0 }, /* GNBDUResourceCoordination */
   { 0, 0, 0 }, /* SystemInformationDeliveryCommand */
-  { 0, 0, 0 }, /* Paging */
+  { DU_handle_Paging, 0, 0 }, /* Paging */
   { 0, 0, 0 }, /* Notify */
   { 0, 0, 0 }, /* WriteReplaceWarning */
   { 0, 0, 0 }, /* PWSCancel */
diff --git a/openair2/RRC/LTE/rrc_defs.h b/openair2/RRC/LTE/rrc_defs.h
index 4d1dfaf5aa4..f0fc2275ac4 100644
--- a/openair2/RRC/LTE/rrc_defs.h
+++ b/openair2/RRC/LTE/rrc_defs.h
@@ -917,11 +917,13 @@ typedef struct UE_RRC_INST_s {
 } UE_RRC_INST;
 
 typedef struct UE_PF_PO_s {
-  bool      enable_flag;  /* flag indicate whether current object is used */
+  bool enable_flag;  /* flag indicate whether current object is used */
   uint16_t ue_index_value;  /* UE index value */
   uint8_t PF_min;  /* minimal value of Paging Frame (PF) */
   uint8_t PO;  /* Paging Occasion (PO) */
   uint32_t T;  /* DRX cycle */
+  uint8_t PF_offset;
+  uint8_t i_s;
 } UE_PF_PO_t;
 
 #include "rrc_proto.h"
diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c
index 710a984307f..932a768d64f 100755
--- a/openair2/RRC/NR/MESSAGES/asn1_msg.c
+++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c
@@ -74,6 +74,8 @@
 #include "NR_RRCReconfigurationComplete-IEs.h"
 #include "NR_DLInformationTransfer.h"
 #include "NR_RRCReestablishmentRequest.h"
+#include "NR_PCCH-Message.h"
+#include "NR_PagingRecord.h"
 #include "NR_UE-CapabilityRequestFilterNR.h"
 #include "common/utils/nr/nr_common.h"
 #if defined(NR_Rel16)
@@ -136,6 +138,7 @@
 #include "intertask_interface.h"
 
 #include "common/ran_context.h"
+#include "conversions.h"
 
 //#define XER_PRINT
 
@@ -2132,3 +2135,48 @@ NR_MeasConfig_t *get_defaultMeasConfig(const gNB_RrcConfigurationReq *conf)
 
   return mc;
 }
+
+uint8_t do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi) {
+  LOG_D(NR_RRC, "[gNB %d] do_NR_Paging start\n", Mod_id);
+  asn_enc_rval_t enc_rval;
+  NR_PCCH_Message_t pcch_msg;
+  NR_PagingRecord_t *paging_record_p = NULL;
+  pcch_msg.message.present           = NR_PCCH_MessageType_PR_c1;
+  pcch_msg.message.choice.c1          = CALLOC(1, sizeof(struct NR_PCCH_MessageType__c1));
+  pcch_msg.message.choice.c1->present = NR_PCCH_MessageType__c1_PR_paging;
+  pcch_msg.message.choice.c1->choice.paging = CALLOC(1,sizeof(NR_Paging_t));
+  pcch_msg.message.choice.c1->choice.paging->pagingRecordList = CALLOC(1,sizeof(*pcch_msg.message.choice.c1->choice.paging->pagingRecordList));
+  pcch_msg.message.choice.c1->choice.paging->nonCriticalExtension = NULL;
+  asn_set_empty(&pcch_msg.message.choice.c1->choice.paging->pagingRecordList->list);
+  pcch_msg.message.choice.c1->choice.paging->pagingRecordList->list.count = 0;
+
+  if ((paging_record_p = calloc(1, sizeof(NR_PagingRecord_t))) == NULL) {
+    /* Possible error on calloc */
+    return (-1);
+  }
+
+  memset(paging_record_p, 0, sizeof(NR_PagingRecord_t));
+
+  /* convert ue_paging_identity_t to PagingUE_Identity_t */
+  paging_record_p->ue_Identity.present = NR_PagingUE_Identity_PR_ng_5G_S_TMSI;
+  // set ng_5G_S_TMSI
+  INT32_TO_BIT_STRING(tmsi, &paging_record_p->ue_Identity.choice.ng_5G_S_TMSI);
+
+  /* add to list */
+  ASN_SEQUENCE_ADD(&pcch_msg.message.choice.c1->choice.paging->pagingRecordList->list, paging_record_p);
+  LOG_D(NR_RRC, "[gNB %d] do_Paging paging_record: PagingRecordList.count %d\n",
+        Mod_id, pcch_msg.message.choice.c1->choice.paging->pagingRecordList->list.count);
+  enc_rval = uper_encode_to_buffer(&asn_DEF_NR_PCCH_Message, NULL, (void *)&pcch_msg, buffer, RRC_BUF_SIZE);
+
+  if(enc_rval.encoded == -1) {
+    LOG_I(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n",
+          enc_rval.failed_type->name, enc_rval.encoded);
+    return -1;
+  }
+
+  if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) {
+    xer_fprint(stdout, &asn_DEF_NR_PCCH_Message, (void *)&pcch_msg);
+  }
+
+  return((enc_rval.encoded+7)/8);
+}
diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h
index 997adb2bd27..241c2deb483 100644
--- a/openair2/RRC/NR/MESSAGES/asn1_msg.h
+++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h
@@ -193,5 +193,6 @@ do_RRCReestablishmentComplete(
     int64_t rrc_TransactionIdentifier);
 
 NR_MeasConfig_t *get_defaultMeasConfig(const gNB_RrcConfigurationReq *conf);
+uint8_t do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi);
 
 #endif  /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */
diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h
index ea7f07965f5..57aee7a6b2b 100644
--- a/openair2/RRC/NR/nr_rrc_proto.h
+++ b/openair2/RRC/NR/nr_rrc_proto.h
@@ -219,4 +219,10 @@ void nr_pdcp_add_drbs(eNB_flag_t enb_flag,
                       uint8_t *const kUPenc,
                       uint8_t *const kUPint,
                       struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_bearer2add_list);
+
+int rrc_gNB_generate_pcch_msg(uint32_t tmsi,
+                              uint8_t paging_drx,
+                              instance_t instance,
+                              uint8_t CC_id);
+
 #endif
diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c
index d885b961432..edc4a8dcfbe 100755
--- a/openair2/RRC/NR/rrc_gNB.c
+++ b/openair2/RRC/NR/rrc_gNB.c
@@ -4024,6 +4024,10 @@ void *rrc_gnb_task(void *args_p) {
         rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(msg_p, msg_name_p, instance);
         break;
 
+      case NGAP_PAGING_IND:
+        rrc_gNB_process_PAGING_IND(msg_p, msg_name_p, instance);
+        break;
+
       default:
         LOG_E(NR_RRC, "[gNB %ld] Received unexpected message %s\n", instance, msg_name_p);
         break;
@@ -4218,6 +4222,130 @@ rrc_gNB_generate_RRCRelease(
     ue_context_pP->ue_context.ue_release_timer_rrc = 1;
   }
 }
+
+int rrc_gNB_generate_pcch_msg(uint32_t tmsi, uint8_t paging_drx, instance_t instance, uint8_t CC_id){
+  const unsigned int Ttab[4] = {32,64,128,256};
+  uint8_t Tc;
+  uint8_t Tue;
+  uint32_t pfoffset;
+  uint32_t N;  /* N: min(T,nB). total count of PF in one DRX cycle */
+  uint32_t Ns = 0;  /* Ns: max(1,nB/T) */
+  uint8_t i_s;  /* i_s = floor(UE_ID/N) mod Ns */
+  uint32_t T;  /* DRX cycle */
+  uint32_t length;
+  uint8_t buffer[RRC_BUF_SIZE];
+  struct NR_SIB1 *sib1 = RC.nrrrc[instance]->carrier.siblock1->message.choice.c1->choice.systemInformationBlockType1;
+
+  /* get default DRX cycle from configuration */
+  Tc = sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.defaultPagingCycle;
+
+  Tue = paging_drx;
+  /* set T = min(Tc,Tue) */
+  T = Tc < Tue ? Ttab[Tc] : Ttab[Tue];
+  /* set N = PCCH-Config->nAndPagingFrameOffset */
+  switch (sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.nAndPagingFrameOffset.present) {
+    case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneT:
+      N = T;
+      pfoffset = 0;
+      break;
+    case NR_PCCH_Config__nAndPagingFrameOffset_PR_halfT:
+      N = T/2;
+      pfoffset = 1;
+      break;
+    case NR_PCCH_Config__nAndPagingFrameOffset_PR_quarterT:
+      N = T/4;
+      pfoffset = 3;
+      break;
+    case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneEighthT:
+      N = T/8;
+      pfoffset = 7;
+      break;
+    case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneSixteenthT:
+      N = T/16;
+      pfoffset = 15;
+      break;
+    default:
+      LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg:  pfoffset error (pfoffset %d)\n",
+            instance, sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.nAndPagingFrameOffset.present);
+      return (-1);
+
+  }
+
+  switch (sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.ns) {
+    case NR_PCCH_Config__ns_four:
+      if(*sib1->servingCellConfigCommon->downlinkConfigCommon.initialDownlinkBWP.pdcch_ConfigCommon->choice.setup->pagingSearchSpace == 0){
+        LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg:  ns error only 1 or 2 is allowed when pagingSearchSpace is 0\n",
+              instance);
+        return (-1);
+      } else {
+        Ns = 4;
+      }
+      break;
+    case NR_PCCH_Config__ns_two:
+      Ns = 2;
+      break;
+    case NR_PCCH_Config__ns_one:
+      Ns = 1;
+      break;
+    default:
+      LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg: ns error (ns %ld)\n",
+            instance, sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.ns);
+      return (-1);
+  }
+
+  /* insert data to UE_PF_PO or update data in UE_PF_PO */
+  pthread_mutex_lock(&ue_pf_po_mutex);
+  uint8_t i = 0;
+
+  for (i = 0; i < MAX_MOBILES_PER_ENB; i++) {
+    if ((UE_PF_PO[CC_id][i].enable_flag == true && UE_PF_PO[CC_id][i].ue_index_value == (uint16_t)(tmsi%1024))
+        || (UE_PF_PO[CC_id][i].enable_flag != true)) {
+      /* set T = min(Tc,Tue) */
+      UE_PF_PO[CC_id][i].T = T;
+      /* set UE_ID */
+      UE_PF_PO[CC_id][i].ue_index_value = (uint16_t)(tmsi%1024);
+      /* calculate PF and PO */
+      /* set PF_min and PF_offset: (SFN + PF_offset) mod T = (T div N)*(UE_ID mod N) */
+      UE_PF_PO[CC_id][i].PF_min = (T / N) * (UE_PF_PO[CC_id][i].ue_index_value % N);
+      UE_PF_PO[CC_id][i].PF_offset = pfoffset;
+      /* set i_s */
+      /* i_s = floor(UE_ID/N) mod Ns */
+      i_s = (uint8_t)((UE_PF_PO[CC_id][i].ue_index_value / N) % Ns);
+      UE_PF_PO[CC_id][i].i_s = i_s;
+
+      // TODO,set PO
+
+      if (UE_PF_PO[CC_id][i].enable_flag == true) {
+        //paging exist UE log
+        LOG_D(NR_RRC,"[gNB %ld] CC_id %d In rrc_gNB_generate_pcch_msg: Update exist UE %d, T %d, N %d, PF %d, i_s %d, PF_offset %d\n", instance, CC_id, UE_PF_PO[CC_id][i].ue_index_value,
+              T, N, UE_PF_PO[CC_id][i].PF_min, UE_PF_PO[CC_id][i].i_s, UE_PF_PO[CC_id][i].PF_offset);
+      } else {
+        /* set enable_flag */
+        UE_PF_PO[CC_id][i].enable_flag = true;
+        //paging new UE log
+        LOG_D(NR_RRC,"[gNB %ld] CC_id %d In rrc_gNB_generate_pcch_msg: Insert a new UE %d, T %d, N %d, PF %d, i_s %d, PF_offset %d\n", instance, CC_id, UE_PF_PO[CC_id][i].ue_index_value,
+              T, N, UE_PF_PO[CC_id][i].PF_min, UE_PF_PO[CC_id][i].i_s, UE_PF_PO[CC_id][i].PF_offset);
+      }
+      break;
+    }
+  }
+
+  pthread_mutex_unlock(&ue_pf_po_mutex);
+
+  /* Create message for PDCP (DLInformationTransfer_t) */
+  length = do_NR_Paging (instance,
+                         buffer,
+                         tmsi);
+
+  if (length == -1) {
+    LOG_I(NR_RRC, "do_Paging error\n");
+    return -1;
+  }
+  // TODO, send message to pdcp
+
+  return 0;
+}
+
 void nr_rrc_trigger(protocol_ctxt_t *ctxt, int CC_id, int frame, int subframe)
 {
   MessageDef *message_p;
diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c
index 9900da9d66c..8e9d3f7939e 100644
--- a/openair2/RRC/NR/rrc_gNB_NGAP.c
+++ b/openair2/RRC/NR/rrc_gNB_NGAP.c
@@ -1714,3 +1714,37 @@ void nr_rrc_rx_tx(void) {
 
 }
 
+/*------------------------------------------------------------------------------*/
+int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, const char *msg_name, instance_t instance) {
+
+  for (uint16_t tai_size = 0; tai_size < NGAP_PAGING_IND(msg_p).tai_size; tai_size++) {
+    LOG_I(NR_RRC,"[gNB %ld] In NGAP_PAGING_IND: MCC %d, MNC %d, TAC %d\n", instance, NGAP_PAGING_IND(msg_p).plmn_identity[tai_size].mcc,
+          NGAP_PAGING_IND(msg_p).plmn_identity[tai_size].mnc, NGAP_PAGING_IND(msg_p).tac[tai_size]);
+
+    for (uint8_t j = 0; j < RC.nrrrc[instance]->configuration.num_plmn; j++) {
+      if (RC.nrrrc[instance]->configuration.mcc[j] == NGAP_PAGING_IND(msg_p).plmn_identity[tai_size].mcc
+          && RC.nrrrc[instance]->configuration.mnc[j] == NGAP_PAGING_IND(msg_p).plmn_identity[tai_size].mnc
+          && RC.nrrrc[instance]->configuration.tac == NGAP_PAGING_IND(msg_p).tac[tai_size]) {
+        for (uint8_t CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
+          if (NODE_IS_CU(RC.nrrrc[instance]->node_type)) {
+            MessageDef *m = itti_alloc_new_message(TASK_RRC_GNB, 0, F1AP_PAGING_IND);
+            F1AP_PAGING_IND (m).mcc              = RC.nrrrc[j]->configuration.mcc[0];
+            F1AP_PAGING_IND (m).mnc              = RC.nrrrc[j]->configuration.mnc[0];
+            F1AP_PAGING_IND (m).mnc_digit_length = RC.nrrrc[j]->configuration.mnc_digit_length[0];
+            F1AP_PAGING_IND (m).nr_cellid        = RC.nrrrc[j]->nr_cellid;
+            F1AP_PAGING_IND (m).ueidentityindexvalue = (uint16_t)(NGAP_PAGING_IND(msg_p).ue_paging_identity.s_tmsi.m_tmsi%1024);
+            F1AP_PAGING_IND (m).fiveg_s_tmsi = NGAP_PAGING_IND(msg_p).ue_paging_identity.s_tmsi.m_tmsi;
+            F1AP_PAGING_IND (m).paging_drx = NGAP_PAGING_IND(msg_p).paging_drx;
+            LOG_E(F1AP, "ueidentityindexvalue %u fiveg_s_tmsi %ld paging_drx %u\n", F1AP_PAGING_IND (m).ueidentityindexvalue, F1AP_PAGING_IND (m).fiveg_s_tmsi, F1AP_PAGING_IND (m).paging_drx);
+            itti_send_msg_to_task(TASK_CU_F1, instance, m);
+          } else {
+            rrc_gNB_generate_pcch_msg(NGAP_PAGING_IND(msg_p).ue_paging_identity.s_tmsi.m_tmsi,(uint8_t)NGAP_PAGING_IND(msg_p).paging_drx, instance, CC_id);
+          } // end of nodetype check
+        } // end of cc loop
+      } // end of mcc mnc check
+    } // end of num_plmn
+  } // end of tai size
+
+  return 0;
+}
+
diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h
index 5833c8aac21..a03e4df41fb 100644
--- a/openair2/RRC/NR/rrc_gNB_NGAP.h
+++ b/openair2/RRC/NR/rrc_gNB_NGAP.h
@@ -189,4 +189,10 @@ nr_rrc_pdcp_config_security(
     const uint8_t          send_security_mode_command
 );
 
+int
+rrc_gNB_process_PAGING_IND(
+    MessageDef *msg_p,
+    const char *msg_name,
+    instance_t instance);
+
 #endif
diff --git a/openair3/UTILS/conversions.h b/openair3/UTILS/conversions.h
index ad53c6f27d8..6e961781f0f 100644
--- a/openair3/UTILS/conversions.h
+++ b/openair3/UTILS/conversions.h
@@ -544,6 +544,27 @@ do {                                                    \
     (bITsTRING)->bits_unused = 4;                       \
 } while(0)
 
+#define UEIDENTITYINDEX_TO_BIT_STRING(mACRO, bITsTRING)          \
+do {                                                    \
+    (bITsTRING)->buf = calloc(2, sizeof(uint8_t));      \
+    (bITsTRING)->buf[0] = (mACRO) >> 2;                 \
+    (bITsTRING)->buf[1] = ((mACRO) & 0x03)<<6;            \
+    (bITsTRING)->size = 2;                              \
+    (bITsTRING)->bits_unused = 6;                       \
+} while(0)
+
+#define FIVEG_S_TMSI_TO_BIT_STRING(mACRO, bITsTRING)      \
+do {                                                    \
+    (bITsTRING)->buf = calloc(6, sizeof(uint8_t));      \
+    (bITsTRING)->buf[0] = ((mACRO) >> 40) & 0xff;       \
+    (bITsTRING)->buf[1] = ((mACRO) >> 32) & 0xff;       \
+    (bITsTRING)->buf[2] = ((mACRO) >> 24) & 0xff;       \
+    (bITsTRING)->buf[3] = ((mACRO) >> 16) & 0xff;       \
+    (bITsTRING)->buf[4] = ((mACRO) >> 8 ) & 0xff;       \
+    (bITsTRING)->buf[5] = ((mACRO) & 0xff);             \
+    (bITsTRING)->size = 6;                              \
+    (bITsTRING)->bits_unused = 0;                       \
+} while(0)
 
 /* Used to format an uint32_t containing an ipv4 address */
 #define IPV4_ADDR    "%u.%u.%u.%u"
-- 
GitLab