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:
co-authored by
batuhan duyuler
parent
e611d092e9
commit
28f926607e
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
Reference in New Issue
Block a user