Add N2 HO trigger on Bearer Context Setup Response (target NG-RAN)

* add trigger function to (1) add the callbacks and (2) initiate
  handover on the target NG-RAN
* add handover failure callback on target NG-RAN: the Handover
  Failure is a procedure that belongs to the target NG-RAN and
  it is different from the Handover Cancel procedure

Co-authored-by: batuhan duyuler <batuhan.duyuler@firecell.io>
This commit is contained in:
Guido Casati
2025-09-18 11:34:30 +02:00
co-authored by batuhan duyuler
parent e611d092e9
commit 28f926607e
6 changed files with 92 additions and 15 deletions
+43
View File
@@ -87,6 +87,7 @@
#include "NR_HandoverPreparationInformation.h"
#include "NR_HandoverPreparationInformation-IEs.h"
#include "NR_HandoverCommand.h"
#include "NR_UE-CapabilityRAT-ContainerList.h"
#include "common/utils/nr/nr_common.h"
#if defined(NR_Rel16)
#include "NR_SCS-SpecificCarrier.h"
@@ -202,6 +203,48 @@ int xer_nr_sprint(char *string, size_t string_size, asn_TYPE_descriptor_t *td, v
return er.encoded;
}
struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len)
{
if (!buf || len == 0) return NULL;
NR_UE_CapabilityRAT_ContainerList_t *clist = NULL;
asn_dec_rval_t rval = uper_decode(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList,
(void **)&clist, buf, len, 0, 0);
if (rval.code != RC_OK) {
LOG_W(NR_MAC, "UE RNTI %04x: Failed to decode container list\n", rnti);
return NULL;
}
NR_UE_NR_Capability_t *cap = decode_nr_ue_capability(rnti, clist);
ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, clist);
return cap;
}
NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist)
{
if (!clist) return NULL;
for (int i = 0; i < clist->list.count; i++) {
const NR_UE_CapabilityRAT_Container_t *c = clist->list.array[i];
if (c->rat_Type != NR_RAT_Type_nr)
continue;
NR_UE_NR_Capability_t *cap = NULL;
asn_dec_rval_t 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 (rval.code == RC_OK)
return cap;
LOG_W(NR_MAC, "UE RNTI %04x: Failed to decode NR capability (%zu bytes)\n", rnti, rval.consumed);
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, cap);
}
return NULL;
}
//------------------------------------------------------------------------------
int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc)
+4
View File
@@ -51,6 +51,7 @@
#include "NR_SRB-ToAddModList.h"
#include "NR_SecurityConfig.h"
#include "NR_MeasurementTimingConfiguration.h"
#include "NR_UE-CapabilityRAT-ContainerList.h"
#include "ds/seq_arr.h"
#include "ds/byte_array.h"
#include "rrc_messages_types.h"
@@ -167,4 +168,7 @@ byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params);
void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *measConfig, byte_array_t prep_info);
int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, byte_array_t handoverCommand);
struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len);
NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist);
#endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */
+13 -13
View File
@@ -1607,21 +1607,14 @@ static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE,
UE->UE_Capability_nr = 0;
}
asn_dec_rval_t dec_rval = uper_decode(NULL,
&asn_DEF_NR_UE_NR_Capability,
(void **)&UE->UE_Capability_nr,
ue_cap_container->ue_CapabilityRAT_Container.buf,
ue_cap_container->ue_CapabilityRAT_Container.size,
0,
0);
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
UE->UE_Capability_nr = decode_nr_ue_capability(UE->rnti, ue_CapabilityRAT_ContainerList);
if (!UE->UE_Capability_nr) {
LOG_E(NR_RRC, "UE %d: NR capability decoding failed\n", UE->rrc_ue_id);
return;
}
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "UE %d: Failed to decode nr UE capabilities (%zu bytes)\n", UE->rrc_ue_id, dec_rval.consumed);
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
UE->UE_Capability_nr = 0;
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
}
const NR_RedCapParameters_r17_t *redcap_p = get_redcapparam_r17(UE->UE_Capability_nr);
@@ -2557,6 +2550,13 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp
}
}
// If HO Preparation Info is stored, N2 handover is ongoing
if (UE->ho_context) {
LOG_I(NR_RRC, "Received Bearer Context Setup Response for UE %d with valid HO Context\n", UE->rrc_ue_id);
UE->ho_context->target->ho_trigger(rrc, UE);
return;
}
if (!UE->f1_ue_context_active)
rrc_gNB_generate_UeContextSetupRequest(rrc, ue_context_p, resp);
else
+1
View File
@@ -1111,6 +1111,7 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga
return -1;
}
UE->ho_context = alloc_ho_ctx(HO_CTX_TARGET);
UE->ho_context->target->ho_trigger = nr_rrc_trigger_n2_ho_target;
// Store IDs in UE context
UE->amf_ue_ngap_id = msg->amf_ue_ngap_id;
+24 -2
View File
@@ -122,7 +122,8 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
byte_array_t *ho_prep_info,
ho_req_ack_t ack,
ho_success_t success,
ho_cancel_t cancel)
ho_cancel_t cancel,
ho_failure_t failure)
{
DevAssert(rrc != NULL);
DevAssert(ue != NULL);
@@ -145,6 +146,7 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
// response
ho_ctx->target->ho_req_ack = ack;
ho_ctx->target->ho_success = success;
ho_ctx->target->ho_failure = failure;
const f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);
if (source_du != NULL) {
@@ -353,7 +355,7 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain
ho_success_t success = nr_rrc_f1_ho_complete;
ho_cancel_t cancel = nr_rrc_cancel_f1_ho;
byte_array_t hpi = {.buf = buf, .len = size};
nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel);
nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel, NULL);
}
void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue)
@@ -496,6 +498,26 @@ void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_f
return;
}
/** @brief Trigger N2 Handover on the target gNB:
* 1) set up callbacks
* 2) initiate N2 handover on target NG-RAN, which triggers the set up
* of HO context and UE Context Setup procedures */
void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue)
{
ho_req_ack_t ack = nr_rrc_n2_ho_acknowledge;
ho_success_t success = nr_rrc_n2_ho_complete;
ho_failure_t failure = nr_rrc_n2_ho_failure;
const nr_rrc_du_container_t *target_du = get_du_for_ue(rrc, ue->rrc_ue_id);
nr_initiate_handover(rrc, ue, NULL, target_du, &ue->ho_context->target->ue_ho_prep_info, ack, success, NULL, failure);
FREE_AND_ZERO_BYTE_ARRAY(ue->ho_context->target->ue_ho_prep_info);
NR_UE_NR_Capability_t *ue_cap = get_ue_nr_capability(ue->rnti, ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len);
ue->UE_Capability_nr = ue_cap;
ue->UE_Capability_size = ue->ue_cap_buffer.len;
}
/** @brief Trigger N2 handover on source gNB:
* 1) Prepare RRC Container with HandoverPreparationInformation message
* 2) send NGAP Handover Required message */
+7
View File
@@ -54,6 +54,7 @@ typedef struct nr_ho_source_cu {
typedef void (*ho_req_ack_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef void (*ho_success_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef void (*ho_failure_t)(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg);
typedef void (*ho_trigger_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef struct nr_ho_target_cu {
/// pointer to the (target) DU structure
@@ -66,10 +67,14 @@ typedef struct nr_ho_target_cu {
rnti_t new_rnti;
/// Handover Preparation Buffer
byte_array_t ue_ho_prep_info;
/// function pointer to trigger handover on target gNB
ho_trigger_t ho_trigger;
/// function pointer to announce handover request acknowledgment
ho_req_ack_t ho_req_ack;
/// function pointer to announce handover success
ho_success_t ho_success;
/// function pointer to announce the handover failure
ho_failure_t ho_failure;
} nr_ho_target_cu_t;
typedef struct nr_handover_context_s {
@@ -91,4 +96,6 @@ void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc,
void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len);
void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
#endif /* RRC_GNB_MOBILITY_H_ */