Merge branch 'develop' of https://git.aubsebian.net/maki/openairinterface5g into develop
This commit is contained in:
@@ -475,6 +475,14 @@ typedef struct pdu_session_to_mod_s {
|
||||
DRB_nGRAN_to_mod_t DRBnGRanModList[E1AP_MAX_NUM_DRBS];
|
||||
} pdu_session_to_mod_t;
|
||||
|
||||
/** PDU Session Resource To Remove List (3GPP TS 38.463 clause 9.3.3.12) */
|
||||
typedef struct {
|
||||
// PDU Session ID (M)
|
||||
long sessionId;
|
||||
// Cause (O)
|
||||
e1ap_cause_t cause;
|
||||
} pdu_session_to_remove_t;
|
||||
|
||||
/**
|
||||
* Bearer Context Setup Request message, clause 9.2.2.1 of 3GPP TS 38.463
|
||||
*/
|
||||
@@ -534,6 +542,9 @@ typedef struct e1ap_bearer_mod_req_s {
|
||||
// NG-RAN PDU Session Resource To Modify List (O)
|
||||
int numPDUSessionsMod;
|
||||
pdu_session_to_mod_t pduSessionMod[E1AP_MAX_NUM_PDU_SESSIONS];
|
||||
// NG-RAN PDU Session Resource To Remove List (O)
|
||||
int numPDUSessionsRem;
|
||||
pdu_session_to_remove_t pduSessionRem[E1AP_MAX_NUM_PDU_SESSIONS];
|
||||
} e1ap_bearer_mod_req_t;
|
||||
|
||||
typedef struct e1ap_bearer_release_cmd_s {
|
||||
|
||||
@@ -421,6 +421,8 @@ typedef struct ngap_nas_first_req_s {
|
||||
/* PLMN: Selected PLMN Identity (optional)
|
||||
* User Location Information (mandatory) */
|
||||
plmn_id_t plmn;
|
||||
// NR Cell ID for NR CGI (mandatory)
|
||||
uint32_t nr_cell_id;
|
||||
// RRC Establishment Cause (mandatory)
|
||||
ngap_rrc_establishment_cause_t establishment_cause;
|
||||
// NAS-PDU (mandatory)
|
||||
@@ -434,6 +436,10 @@ typedef struct ngap_uplink_nas_s {
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
/* NAS pdu */
|
||||
byte_array_t nas_pdu;
|
||||
/* UserLocationInformation (mandatory) */
|
||||
plmn_id_t plmn; // CGI and TAI
|
||||
uint32_t nr_cell_id; // CGI
|
||||
uint32_t tac; // TAI
|
||||
} ngap_uplink_nas_t;
|
||||
|
||||
typedef struct target_cell_id_s {
|
||||
|
||||
@@ -125,8 +125,8 @@ static void e1_task_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *s
|
||||
int e1ap_send_RESET(bool isCu, sctp_assoc_t assoc_id, E1AP_Reset_t *Reset)
|
||||
{
|
||||
AssertFatal(false, "Not implemented yet\n");
|
||||
E1AP_E1AP_PDU_t pdu = {0};
|
||||
return e1ap_encode_send(isCu, assoc_id, &pdu, 0, __func__);
|
||||
E1AP_E1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu));
|
||||
return e1ap_encode_send(isCu, assoc_id, pdu, 0, __func__);
|
||||
}
|
||||
|
||||
int e1ap_send_RESET_ACKNOWLEDGE(instance_t instance, E1AP_Reset_t *Reset)
|
||||
@@ -304,9 +304,9 @@ static void fill_CONFIGURATION_UPDATE(E1AP_E1AP_PDU_t *pdu)
|
||||
|
||||
void e1apCUUP_send_CONFIGURATION_UPDATE(sctp_assoc_t assoc_id)
|
||||
{
|
||||
E1AP_E1AP_PDU_t pdu = {0};
|
||||
fill_CONFIGURATION_UPDATE(&pdu);
|
||||
e1ap_encode_send(UPtype, assoc_id, &pdu, 0, __func__);
|
||||
E1AP_E1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu));
|
||||
fill_CONFIGURATION_UPDATE(pdu);
|
||||
e1ap_encode_send(UPtype, assoc_id, pdu, 0, __func__);
|
||||
}
|
||||
|
||||
int e1apCUCP_send_gNB_DU_CONFIGURATION_FAILURE(void)
|
||||
|
||||
@@ -226,7 +226,7 @@ int e1ap_encode_send(E1_t type, sctp_assoc_t assoc_id, E1AP_E1AP_PDU_t *pdu, uin
|
||||
|
||||
void *buffer = NULL;
|
||||
ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_E1AP_E1AP_PDU, 0, pdu, &buffer);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_E1AP_E1AP_PDU, pdu);
|
||||
ASN_STRUCT_FREE(asn_DEF_E1AP_E1AP_PDU, pdu);
|
||||
|
||||
if (encoded < 0) {
|
||||
LOG_E(E1AP, "%s: Failed to encode E1AP message\n", func);
|
||||
|
||||
@@ -1631,6 +1631,20 @@ E1AP_E1AP_PDU_t *encode_E1_bearer_context_mod_request(const e1ap_bearer_mod_req_
|
||||
*ieC3_1 = e1_encode_pdu_session_to_mod_item(i);
|
||||
}
|
||||
}
|
||||
|
||||
// NG-RAN PDU Session Resource To Remove List (O)
|
||||
if (in->numPDUSessionsRem) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, remList);
|
||||
E1AP_NG_RAN_BearerContextModificationRequest_t *msgNGRAN = encode_ng_ran_to_mod(remList);
|
||||
msgNGRAN->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Remove_List;
|
||||
msgNGRAN->criticality = E1AP_Criticality_reject;
|
||||
msgNGRAN->value.present = E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Remove_List;
|
||||
E1AP_PDU_Session_Resource_To_Remove_List_t *pdu2Remove = &msgNGRAN->value.choice.PDU_Session_Resource_To_Remove_List;
|
||||
for (const pdu_session_to_remove_t *i = in->pduSessionRem; i < in->pduSessionRem + in->numPDUSessionsRem; i++) {
|
||||
asn1cSequenceAdd(pdu2Remove->list, E1AP_PDU_Session_Resource_To_Remove_Item_t, ieC4_1);
|
||||
ieC4_1->pDU_Session_ID = i->sessionId;
|
||||
}
|
||||
}
|
||||
return pdu;
|
||||
}
|
||||
|
||||
@@ -1854,6 +1868,19 @@ bool decode_E1_bearer_context_mod_request(const E1AP_E1AP_PDU_t *pdu, e1ap_beare
|
||||
CHECK_E1AP_DEC(e1_decode_pdu_session_to_mod_item(out->pduSessionMod + i, modList->list.array[i]));
|
||||
}
|
||||
break;
|
||||
case E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Remove_List:
|
||||
/* PDU Session Resource To Remove List (see 9.3.3.12 of TS 38.463) */
|
||||
_E1_EQ_CHECK_INT(msgNGRAN->value.present,
|
||||
E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Remove_List);
|
||||
E1AP_PDU_Session_Resource_To_Remove_List_t *remList = &msgNGRAN->value.choice.PDU_Session_Resource_To_Remove_List;
|
||||
out->numPDUSessionsRem = remList->list.count;
|
||||
// Loop through all PDU sessions to remove
|
||||
for (int i = 0; i < remList->list.count; i++) {
|
||||
E1AP_PDU_Session_Resource_To_Remove_Item_t *remItem = remList->list.array[i];
|
||||
pdu_session_to_remove_t *pduRem = &out->pduSessionRem[i];
|
||||
pduRem->sessionId = remItem->pDU_Session_ID;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PRINT_ERROR("Unknown msgNGRAN->id in E1 Setup Modification Request\n");
|
||||
return false;
|
||||
@@ -1924,6 +1951,9 @@ e1ap_bearer_mod_req_t cp_bearer_context_mod_request(const e1ap_bearer_mod_req_t
|
||||
cp.pduSession[i] = cp_pdu_session_item(&msg->pduSession[i]);
|
||||
for (int i = 0; i < msg->numPDUSessionsMod; i++)
|
||||
cp.pduSessionMod[i] = cp_pdu_session_to_mod_item(&msg->pduSessionMod[i]);
|
||||
// Copy PDU sessions to remove (simple struct copy, no deep copy needed)
|
||||
for (int i = 0; i < msg->numPDUSessionsRem; i++)
|
||||
cp.pduSessionRem[i] = msg->pduSessionRem[i];
|
||||
return cp;
|
||||
}
|
||||
|
||||
@@ -2037,6 +2067,15 @@ bool eq_bearer_context_mod_request(const e1ap_bearer_mod_req_t *a, const e1ap_be
|
||||
if (!eq_pdu_session_to_mod_item(&a->pduSessionMod[i], &b->pduSessionMod[i]))
|
||||
return false;
|
||||
}
|
||||
// PDU Sessions to Remove
|
||||
_E1_EQ_CHECK_INT(a->numPDUSessionsRem, b->numPDUSessionsRem);
|
||||
for (int i = 0; i < a->numPDUSessionsRem; i++) {
|
||||
const pdu_session_to_remove_t *pduRemA = &a->pduSessionRem[i];
|
||||
const pdu_session_to_remove_t *pduRemB = &b->pduSessionRem[i];
|
||||
_E1_EQ_CHECK_LONG(pduRemA->sessionId, pduRemB->sessionId);
|
||||
_E1_EQ_CHECK_INT(pduRemA->cause.type, pduRemB->cause.type);
|
||||
_E1_EQ_CHECK_INT(pduRemA->cause.value, pduRemB->cause.value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2114,129 +2153,129 @@ struct E1AP_E1AP_PDU *encode_E1_bearer_context_mod_response(const e1ap_bearer_mo
|
||||
ie2->criticality = E1AP_Criticality_reject;
|
||||
ie2->value.present = E1AP_BearerContextModificationResponseIEs__value_PR_GNB_CU_UP_UE_E1AP_ID;
|
||||
ie2->value.choice.GNB_CU_UP_UE_E1AP_ID = msg->gNB_cu_up_ue_id;
|
||||
// NG-RAN PDU Session Resource Modified List (O)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationResponseIEs_t, ie3);
|
||||
ie3->id = E1AP_ProtocolIE_ID_id_System_BearerContextModificationResponse;
|
||||
ie3->criticality = E1AP_Criticality_ignore;
|
||||
// CHOICE System (O)
|
||||
ie3->value.present = E1AP_BearerContextModificationResponseIEs__value_PR_System_BearerContextModificationResponse;
|
||||
E1AP_System_BearerContextModificationResponse_t *sys = &ie3->value.choice.System_BearerContextModificationResponse;
|
||||
// NG-RAN
|
||||
E1AP_ProtocolIE_Container_4932P29_t *ngran = calloc_or_fail(1, sizeof(*ngran));
|
||||
sys->present = E1AP_System_BearerContextModificationResponse_PR_nG_RAN_BearerContextModificationResponse;
|
||||
sys->choice.nG_RAN_BearerContextModificationResponse = (struct E1AP_ProtocolIE_Container *)ngran;
|
||||
asn1cSequenceAdd(ngran->list, E1AP_NG_RAN_BearerContextModificationResponse_t, list);
|
||||
// PDU Session Resource Modified List (O)
|
||||
list->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_Modified_List;
|
||||
list->criticality = E1AP_Criticality_reject;
|
||||
list->value.present = E1AP_NG_RAN_BearerContextModificationResponse__value_PR_PDU_Session_Resource_Modified_List;
|
||||
E1AP_PDU_Session_Resource_Modified_List_t *pdu_mod_l = &list->value.choice.PDU_Session_Resource_Modified_List;
|
||||
for (int i = 0; i < msg->numPDUSessionsMod; ++i) {
|
||||
// PDU Session Resource Modified Item (1..maxnoofPDUSessionResource)
|
||||
const pdu_session_modif_t *pdu = &msg->pduSessionMod[i];
|
||||
asn1cSequenceAdd(pdu_mod_l->list, E1AP_PDU_Session_Resource_Modified_Item_t, iePdu);
|
||||
// PDU Session ID (M)
|
||||
iePdu->pDU_Session_ID = pdu->id;
|
||||
// Security Result (O)
|
||||
if (pdu->confidentialityProtectionIndication || pdu->integrityProtectionIndication) {
|
||||
iePdu->securityResult = calloc_or_fail(1, sizeof(*iePdu->securityResult));
|
||||
if (pdu->confidentialityProtectionIndication)
|
||||
iePdu->securityResult->confidentialityProtectionResult = *pdu->confidentialityProtectionIndication;
|
||||
if (pdu->integrityProtectionIndication)
|
||||
iePdu->securityResult->integrityProtectionResult = *pdu->integrityProtectionIndication;
|
||||
}
|
||||
// NG DL UP Transport Layer Information (O)
|
||||
if (pdu->ng_DL_UP_TL_info) {
|
||||
iePdu->nG_DL_UP_TNL_Information = calloc_or_fail(1, sizeof(*iePdu->nG_DL_UP_TNL_Information));
|
||||
*iePdu->nG_DL_UP_TNL_Information = e1_encode_up_tnl_info(pdu->ng_DL_UP_TL_info);
|
||||
}
|
||||
// DRB Modified List (O)
|
||||
if (pdu->numDRBModified > 0) {
|
||||
iePdu->dRB_Modified_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Modified_List_NG_RAN));
|
||||
E1AP_DRB_Modified_List_NG_RAN_t *drb_mod_l = iePdu->dRB_Modified_List_NG_RAN;
|
||||
for (int j = 0; j < pdu->numDRBModified; ++j) {
|
||||
const DRB_nGRAN_modified_t *drb = &pdu->DRBnGRanModList[j];
|
||||
asn1cSequenceAdd(drb_mod_l->list, E1AP_DRB_Modified_Item_NG_RAN_t, drb_mod);
|
||||
// DRB ID (M)
|
||||
drb_mod->dRB_ID = drb->id;
|
||||
// Flow Setup List (O)
|
||||
if (drb->numQosFlowSetup)
|
||||
drb_mod->flow_Setup_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Setup_List));
|
||||
for (int q = 0; q < drb->numQosFlowSetup; ++q) {
|
||||
asn1cSequenceAdd(drb_mod->flow_Setup_List->list, E1AP_QoS_Flow_Item_t, qos_mod);
|
||||
qos_mod->qoS_Flow_Identifier = drb->qosFlows[q].qfi;
|
||||
}
|
||||
// Flow Failed List (O)
|
||||
if (drb->numQosFlowFailed)
|
||||
drb_mod->flow_Failed_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Failed_List));
|
||||
for (int q = 0; q < drb->numQosFlowFailed; ++q) {
|
||||
asn1cSequenceAdd(drb_mod->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail);
|
||||
encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]);
|
||||
}
|
||||
|
||||
// PDCP Status Information (O)
|
||||
if (drb->pdcp_status) {
|
||||
asn1cCalloc(drb_mod->pDCP_SN_Status_Information, pdcp);
|
||||
*pdcp = encode_pdcp_status_info(drb->pdcp_status);
|
||||
}
|
||||
|
||||
// UL UP Parameters (O)
|
||||
if (drb->numUpParam)
|
||||
drb_mod->uL_UP_Transport_Parameters = calloc_or_fail(1, sizeof(*drb_mod->uL_UP_Transport_Parameters));
|
||||
for (const up_params_t *k = drb->ul_UP_Params; k < drb->ul_UP_Params + drb->numUpParam; k++) {
|
||||
asn1cSequenceAdd(drb_mod->uL_UP_Transport_Parameters->list, E1AP_UP_Parameters_Item_t, up);
|
||||
up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info);
|
||||
up->cell_Group_ID = k->cell_group_id;
|
||||
if (msg->numPDUSessionsMod > 0) {
|
||||
// NG-RAN PDU Session Resource Modified List (O)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationResponseIEs_t, ie3);
|
||||
ie3->id = E1AP_ProtocolIE_ID_id_System_BearerContextModificationResponse;
|
||||
ie3->criticality = E1AP_Criticality_ignore;
|
||||
// CHOICE System (O)
|
||||
ie3->value.present = E1AP_BearerContextModificationResponseIEs__value_PR_System_BearerContextModificationResponse;
|
||||
E1AP_System_BearerContextModificationResponse_t *sys = &ie3->value.choice.System_BearerContextModificationResponse;
|
||||
// NG-RAN
|
||||
E1AP_ProtocolIE_Container_4932P29_t *ngran = calloc_or_fail(1, sizeof(*ngran));
|
||||
sys->present = E1AP_System_BearerContextModificationResponse_PR_nG_RAN_BearerContextModificationResponse;
|
||||
sys->choice.nG_RAN_BearerContextModificationResponse = (struct E1AP_ProtocolIE_Container *)ngran;
|
||||
asn1cSequenceAdd(ngran->list, E1AP_NG_RAN_BearerContextModificationResponse_t, list);
|
||||
// PDU Session Resource Modified List (O)
|
||||
list->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_Modified_List;
|
||||
list->criticality = E1AP_Criticality_reject;
|
||||
list->value.present = E1AP_NG_RAN_BearerContextModificationResponse__value_PR_PDU_Session_Resource_Modified_List;
|
||||
E1AP_PDU_Session_Resource_Modified_List_t *pdu_mod_l = &list->value.choice.PDU_Session_Resource_Modified_List;
|
||||
for (int i = 0; i < msg->numPDUSessionsMod; ++i) {
|
||||
// PDU Session Resource Modified Item (1..maxnoofPDUSessionResource)
|
||||
const pdu_session_modif_t *pdu = &msg->pduSessionMod[i];
|
||||
asn1cSequenceAdd(pdu_mod_l->list, E1AP_PDU_Session_Resource_Modified_Item_t, iePdu);
|
||||
// PDU Session ID (M)
|
||||
iePdu->pDU_Session_ID = pdu->id;
|
||||
// Security Result (O)
|
||||
if (pdu->confidentialityProtectionIndication || pdu->integrityProtectionIndication) {
|
||||
iePdu->securityResult = calloc_or_fail(1, sizeof(*iePdu->securityResult));
|
||||
if (pdu->confidentialityProtectionIndication)
|
||||
iePdu->securityResult->confidentialityProtectionResult = *pdu->confidentialityProtectionIndication;
|
||||
if (pdu->integrityProtectionIndication)
|
||||
iePdu->securityResult->integrityProtectionResult = *pdu->integrityProtectionIndication;
|
||||
}
|
||||
// NG DL UP Transport Layer Information (O)
|
||||
if (pdu->ng_DL_UP_TL_info) {
|
||||
iePdu->nG_DL_UP_TNL_Information = calloc_or_fail(1, sizeof(*iePdu->nG_DL_UP_TNL_Information));
|
||||
*iePdu->nG_DL_UP_TNL_Information = e1_encode_up_tnl_info(pdu->ng_DL_UP_TL_info);
|
||||
}
|
||||
// DRB Modified List (O)
|
||||
if (pdu->numDRBModified > 0) {
|
||||
iePdu->dRB_Modified_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Modified_List_NG_RAN));
|
||||
E1AP_DRB_Modified_List_NG_RAN_t *drb_mod_l = iePdu->dRB_Modified_List_NG_RAN;
|
||||
for (int j = 0; j < pdu->numDRBModified; ++j) {
|
||||
const DRB_nGRAN_modified_t *drb = &pdu->DRBnGRanModList[j];
|
||||
asn1cSequenceAdd(drb_mod_l->list, E1AP_DRB_Modified_Item_NG_RAN_t, drb_mod);
|
||||
// DRB ID (M)
|
||||
drb_mod->dRB_ID = drb->id;
|
||||
// Flow Setup List (O)
|
||||
if (drb->numQosFlowSetup)
|
||||
drb_mod->flow_Setup_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Setup_List));
|
||||
for (int q = 0; q < drb->numQosFlowSetup; ++q) {
|
||||
asn1cSequenceAdd(drb_mod->flow_Setup_List->list, E1AP_QoS_Flow_Item_t, qos_mod);
|
||||
qos_mod->qoS_Flow_Identifier = drb->qosFlows[q].qfi;
|
||||
}
|
||||
// Flow Failed List (O)
|
||||
if (drb->numQosFlowFailed)
|
||||
drb_mod->flow_Failed_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Failed_List));
|
||||
for (int q = 0; q < drb->numQosFlowFailed; ++q) {
|
||||
asn1cSequenceAdd(drb_mod->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail);
|
||||
encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]);
|
||||
}
|
||||
// PDCP Status Information (O)
|
||||
if (drb->pdcp_status) {
|
||||
asn1cCalloc(drb_mod->pDCP_SN_Status_Information, pdcp);
|
||||
*pdcp = encode_pdcp_status_info(drb->pdcp_status);
|
||||
}
|
||||
// UL UP Parameters (O)
|
||||
if (drb->numUpParam)
|
||||
drb_mod->uL_UP_Transport_Parameters = calloc_or_fail(1, sizeof(*drb_mod->uL_UP_Transport_Parameters));
|
||||
for (const up_params_t *k = drb->ul_UP_Params; k < drb->ul_UP_Params + drb->numUpParam; k++) {
|
||||
asn1cSequenceAdd(drb_mod->uL_UP_Transport_Parameters->list, E1AP_UP_Parameters_Item_t, up);
|
||||
up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info);
|
||||
up->cell_Group_ID = k->cell_group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// DRB Failed To Modify List (O)
|
||||
if (pdu->numDRBFailedToMod > 0) {
|
||||
iePdu->dRB_Failed_To_Modify_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_To_Modify_List_NG_RAN));
|
||||
E1AP_DRB_Failed_To_Modify_List_NG_RAN_t *ngran = iePdu->dRB_Failed_To_Modify_List_NG_RAN;
|
||||
for (int j = 0; j < pdu->numDRBFailedToMod; ++j) {
|
||||
const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedModList[j];
|
||||
asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_To_Modify_Item_NG_RAN_t, fail_to_mod);
|
||||
encode_drb_failed_item((E1AP_DRB_Failed_Item_NG_RAN_t *)fail_to_mod, drb);
|
||||
}
|
||||
}
|
||||
// DRB Setup List (O)
|
||||
if (pdu->numDRBSetup > 0) {
|
||||
iePdu->dRB_Setup_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Setup_List_NG_RAN));
|
||||
E1AP_DRB_Setup_List_NG_RAN_t *drb_setup = iePdu->dRB_Setup_List_NG_RAN;
|
||||
for (int j = 0; j < pdu->numDRBModified; ++j) {
|
||||
const DRB_nGRAN_setup_t *drb = &pdu->DRBnGRanSetupList[j];
|
||||
asn1cSequenceAdd(drb_setup->list, E1AP_DRB_Setup_Item_NG_RAN_t, item);
|
||||
// DRB ID (M)
|
||||
item->dRB_ID = drb->id;
|
||||
// UL UP Parameters (M)
|
||||
for (const up_params_t *k = drb->UpParamList; k < drb->UpParamList + drb->numUpParam; k++) {
|
||||
asn1cSequenceAdd(item->uL_UP_Transport_Parameters.list, E1AP_UP_Parameters_Item_t, up);
|
||||
up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info);
|
||||
up->cell_Group_ID = drb->UpParamList->cell_group_id;
|
||||
}
|
||||
// Flow Setup List (M)
|
||||
for (int q = 0; q < drb->numQosFlowSetup; ++q) {
|
||||
asn1cSequenceAdd(item->flow_Setup_List.list, E1AP_QoS_Flow_Item_t, qos);
|
||||
qos->qoS_Flow_Identifier = drb->qosFlows[q].qfi;
|
||||
}
|
||||
// Flow Failed List (O)
|
||||
if (drb->numQosFlowFailed)
|
||||
item->flow_Failed_List = calloc_or_fail(1, sizeof(*item->flow_Failed_List));
|
||||
for (int q = 0; q < drb->numQosFlowFailed; ++q) {
|
||||
asn1cSequenceAdd(item->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail);
|
||||
encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]);
|
||||
// DRB Failed To Modify List (O)
|
||||
if (pdu->numDRBFailedToMod > 0) {
|
||||
iePdu->dRB_Failed_To_Modify_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_To_Modify_List_NG_RAN));
|
||||
E1AP_DRB_Failed_To_Modify_List_NG_RAN_t *ngran = iePdu->dRB_Failed_To_Modify_List_NG_RAN;
|
||||
for (int j = 0; j < pdu->numDRBFailedToMod; ++j) {
|
||||
const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedModList[j];
|
||||
asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_To_Modify_Item_NG_RAN_t, fail_to_mod);
|
||||
encode_drb_failed_item((E1AP_DRB_Failed_Item_NG_RAN_t *)fail_to_mod, drb);
|
||||
}
|
||||
}
|
||||
}
|
||||
// DRB Failed List (O)
|
||||
if (pdu->numDRBFailed) {
|
||||
iePdu->dRB_Failed_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_List_NG_RAN));
|
||||
E1AP_DRB_Failed_List_NG_RAN_t *ngran = iePdu->dRB_Failed_List_NG_RAN;
|
||||
for (int j = 0; j < pdu->numDRBFailed; ++j) {
|
||||
const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedList[j];
|
||||
asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_Item_NG_RAN_t, fail);
|
||||
encode_drb_failed_item(fail, drb);
|
||||
// DRB Setup List (O)
|
||||
if (pdu->numDRBSetup > 0) {
|
||||
iePdu->dRB_Setup_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Setup_List_NG_RAN));
|
||||
E1AP_DRB_Setup_List_NG_RAN_t *drb_setup = iePdu->dRB_Setup_List_NG_RAN;
|
||||
for (int j = 0; j < pdu->numDRBModified; ++j) {
|
||||
const DRB_nGRAN_setup_t *drb = &pdu->DRBnGRanSetupList[j];
|
||||
asn1cSequenceAdd(drb_setup->list, E1AP_DRB_Setup_Item_NG_RAN_t, item);
|
||||
// DRB ID (M)
|
||||
item->dRB_ID = drb->id;
|
||||
// UL UP Parameters (M)
|
||||
for (const up_params_t *k = drb->UpParamList; k < drb->UpParamList + drb->numUpParam; k++) {
|
||||
asn1cSequenceAdd(item->uL_UP_Transport_Parameters.list, E1AP_UP_Parameters_Item_t, up);
|
||||
up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info);
|
||||
up->cell_Group_ID = drb->UpParamList->cell_group_id;
|
||||
}
|
||||
// Flow Setup List (M)
|
||||
for (int q = 0; q < drb->numQosFlowSetup; ++q) {
|
||||
asn1cSequenceAdd(item->flow_Setup_List.list, E1AP_QoS_Flow_Item_t, qos);
|
||||
qos->qoS_Flow_Identifier = drb->qosFlows[q].qfi;
|
||||
}
|
||||
// Flow Failed List (O)
|
||||
if (drb->numQosFlowFailed)
|
||||
item->flow_Failed_List = calloc_or_fail(1, sizeof(*item->flow_Failed_List));
|
||||
for (int q = 0; q < drb->numQosFlowFailed; ++q) {
|
||||
asn1cSequenceAdd(item->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail);
|
||||
encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// DRB Failed List (O)
|
||||
if (pdu->numDRBFailed) {
|
||||
iePdu->dRB_Failed_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_List_NG_RAN));
|
||||
E1AP_DRB_Failed_List_NG_RAN_t *ngran = iePdu->dRB_Failed_List_NG_RAN;
|
||||
for (int j = 0; j < pdu->numDRBFailed; ++j) {
|
||||
const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedList[j];
|
||||
asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_Item_NG_RAN_t, fail);
|
||||
encode_drb_failed_item(fail, drb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
#include "E1AP_DRB-To-Modify-Item-NG-RAN.h"
|
||||
#include "E1AP_PDU-Session-Resource-To-Setup-Mod-Item.h"
|
||||
#include "E1AP_DRB-To-Setup-Mod-Item-NG-RAN.h"
|
||||
#include "E1AP_PDU-Session-Resource-To-Remove-Item.h"
|
||||
// E1 Bearer Context Modification Response
|
||||
#include "E1AP_PDU-Session-Resource-Modified-Item.h"
|
||||
#include "E1AP_DRB-Modified-List-NG-RAN.h"
|
||||
|
||||
@@ -538,6 +538,14 @@ static void test_bearer_context_modification_request(void)
|
||||
.DRBnGRanList[0] = drb_to_setup,
|
||||
};
|
||||
|
||||
// PDU Session to Remove
|
||||
pdu_session_to_remove_t pdu_session_to_remove = {
|
||||
.sessionId = 2,
|
||||
// Note: cause is optional extension IE, currently encoding/decoding only handles sessionId
|
||||
.cause.type = 0,
|
||||
.cause.value = 0,
|
||||
};
|
||||
|
||||
// Initialize the Bearer Context Modification Request
|
||||
e1ap_bearer_mod_req_t orig = {
|
||||
.gNB_cu_cp_ue_id = 0x1234,
|
||||
@@ -548,6 +556,8 @@ static void test_bearer_context_modification_request(void)
|
||||
.pduSession[0] = pdusession_setup_item,
|
||||
.numPDUSessionsMod = 1,
|
||||
.pduSessionMod[0] = pdusession_mod_item,
|
||||
.numPDUSessionsRem = 1,
|
||||
.pduSessionRem[0] = pdu_session_to_remove,
|
||||
};
|
||||
*orig.bearerContextStatus = BEARER_SUSPEND;
|
||||
*orig.inactivityTimer = 1000;
|
||||
|
||||
@@ -38,13 +38,11 @@ ue_id_e2sm_t fill_e2sm_gnb_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, __attr
|
||||
ue_id.gnb.amf_ue_ngap_id = auni < (1LL << 40) ? auni : 0;
|
||||
|
||||
// Mandatory
|
||||
//GUAMI 6.2.3.17
|
||||
ue_id.gnb.guami.plmn_id = (e2sm_plmn_t) {
|
||||
.mcc = rrc_ue_context->ue_guami.mcc,
|
||||
.mnc = rrc_ue_context->ue_guami.mnc,
|
||||
.mnc_digit_len = rrc_ue_context->ue_guami.mnc_len
|
||||
};
|
||||
|
||||
//GUAMI 6.2.3.17
|
||||
ue_id.gnb.guami.plmn_id = (e2sm_plmn_t){.mcc = rrc_ue_context->ue_guami.plmn.mcc,
|
||||
.mnc = rrc_ue_context->ue_guami.plmn.mnc,
|
||||
.mnc_digit_len = rrc_ue_context->ue_guami.plmn.mnc_digit_length};
|
||||
|
||||
ue_id.gnb.guami.amf_region_id = rrc_ue_context->ue_guami.amf_region_id;
|
||||
ue_id.gnb.guami.amf_set_id = rrc_ue_context->ue_guami.amf_set_id;
|
||||
ue_id.gnb.guami.amf_ptr = rrc_ue_context->ue_guami.amf_pointer;
|
||||
|
||||
@@ -54,10 +54,12 @@ typedef struct {
|
||||
ue_id_e2sm_t* ue_id;
|
||||
|
||||
// Optional
|
||||
// only used to retreive MAC/RLC stats
|
||||
// only used to retrieve MAC/RLC stats
|
||||
NR_UE_info_t** ue_info_list;
|
||||
}arr_ue_id_t;
|
||||
|
||||
static e2_node_level_stats_t node_stats[2] = {0}; // [0] node stats collected in previous reporting period; [1] current node stats
|
||||
|
||||
static meas_data_lst_t fill_kpm_meas_data_item(const meas_info_format_1_lst_t* meas_info_lst, const size_t len, const uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
|
||||
{
|
||||
meas_data_lst_t data_item = {0};
|
||||
@@ -74,7 +76,7 @@ static meas_data_lst_t fill_kpm_meas_data_item(const meas_info_format_1_lst_t* m
|
||||
|
||||
char* meas_info_name_str = cp_ba_to_str(meas_info_lst[i].meas_type.name);
|
||||
|
||||
data_item.meas_record_lst[i] = get_kpm_meas_value(meas_info_name_str, gran_period_ms, ue_info, ue_idx);
|
||||
data_item.meas_record_lst[i] = get_kpm_meas_value(meas_info_name_str, gran_period_ms, ue_info, ue_idx, node_stats);
|
||||
|
||||
free(meas_info_name_str);
|
||||
}
|
||||
@@ -153,6 +155,8 @@ static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3(arr_ue_id_t* arr_ue_id, con
|
||||
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = fill_kpm_ind_msg_frm_1(ue_info, i, act_def_fr_1);
|
||||
}
|
||||
|
||||
node_stats[0] = cp_node_level_stats(&node_stats[1]);
|
||||
|
||||
return msg_frm_3;
|
||||
}
|
||||
|
||||
@@ -288,8 +292,10 @@ static arr_ue_id_t filter_ues_by_s_nssai_in_du_or_monolithic(const test_info_lst
|
||||
|
||||
const ngran_node_t node_type = get_e2_node_type();
|
||||
|
||||
gNB_MAC_INST *mac = RC.nrmac[0];
|
||||
NR_SCHED_LOCK(&mac->sched_lock);
|
||||
// Take MAC info
|
||||
UE_iterator(RC.nrmac[0]->UE_info.connected_ue_list, ue) {
|
||||
UE_iterator(mac->UE_info.connected_ue_list, ue) {
|
||||
NR_UE_sched_ctrl_t *sched_ctrl = &ue->UE_sched_ctrl;
|
||||
// UE matches if any of its DRBs matches
|
||||
for (size_t l = 0; l < seq_arr_size(&sched_ctrl->lc_config); ++l) {
|
||||
@@ -316,6 +322,10 @@ static arr_ue_id_t filter_ues_by_s_nssai_in_du_or_monolithic(const test_info_lst
|
||||
AssertFatal(arr_ue_id.sz < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n");
|
||||
}
|
||||
|
||||
node_stats[1].mac_stats.dl.total_prb_aggregate = mac->mac_stats.dl.total_prb_aggregate;
|
||||
node_stats[1].mac_stats.ul.total_prb_aggregate = mac->mac_stats.ul.total_prb_aggregate;
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
|
||||
free(sd); // if NULL, nothing happens
|
||||
|
||||
return arr_ue_id;
|
||||
|
||||
@@ -23,6 +23,18 @@
|
||||
|
||||
#include <search.h>
|
||||
|
||||
e2_node_level_stats_t cp_node_level_stats(const e2_node_level_stats_t *src)
|
||||
{
|
||||
e2_node_level_stats_t dst = {
|
||||
.mac_stats.dl.total_prb_aggregate = src->mac_stats.dl.total_prb_aggregate,
|
||||
.mac_stats.dl.used_prb_aggregate = src->mac_stats.dl.used_prb_aggregate,
|
||||
.mac_stats.ul.total_prb_aggregate = src->mac_stats.ul.total_prb_aggregate,
|
||||
.mac_stats.ul.used_prb_aggregate = src->mac_stats.ul.used_prb_aggregate,
|
||||
};
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* measurements that need to store values from previous reporting period have a limitation
|
||||
when it comes to multiple subscriptions to the same UEs; ric_req_id is unique per subscription */
|
||||
typedef struct uldlcounter {
|
||||
@@ -46,7 +58,7 @@ static nr_pdcp_statistics_t get_pdcp_stats_per_drb(const uint32_t rrc_ue_id, con
|
||||
|
||||
/* 3GPP TS 28.522 - section 5.1.2.1.1.1
|
||||
note: this measurement is calculated as per spec */
|
||||
static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
|
||||
static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, __attribute__((unused))e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
|
||||
@@ -57,7 +69,7 @@ static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_
|
||||
meas_record.value = INTEGER_MEAS_VALUE;
|
||||
|
||||
// Get DL data volume delivered to PDCP layer
|
||||
meas_record.int_val = (pdcp.rxsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].dl)*8/1000; // [kb]
|
||||
meas_record.int_val = (pdcp.rxsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].dl)*8/1000000; // [Mb]
|
||||
last_pdcp_sdu_total_bytes[ue_idx].dl = pdcp.rxsdu_bytes;
|
||||
|
||||
return meas_record;
|
||||
@@ -65,7 +77,7 @@ static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_
|
||||
|
||||
/* 3GPP TS 28.522 - section 5.1.2.1.2.1
|
||||
note: this measurement is calculated as per spec */
|
||||
static meas_record_lst_t fill_DRB_PdcpSduVolumeUL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
|
||||
static meas_record_lst_t fill_DRB_PdcpSduVolumeUL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, __attribute__((unused))e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
|
||||
@@ -76,7 +88,7 @@ static meas_record_lst_t fill_DRB_PdcpSduVolumeUL(__attribute__((unused))uint32_
|
||||
meas_record.value = INTEGER_MEAS_VALUE;
|
||||
|
||||
// Get UL data volume delivered from PDCP layer
|
||||
meas_record.int_val = (pdcp.txsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].ul)*8/1000; // [kb]
|
||||
meas_record.int_val = (pdcp.txsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].ul)*8/1000000; // [Mb]
|
||||
last_pdcp_sdu_total_bytes[ue_idx].ul = pdcp.txsdu_bytes;
|
||||
|
||||
return meas_record;
|
||||
@@ -103,7 +115,7 @@ static nr_rlc_statistics_t get_rlc_stats_per_drb(const rnti_t rnti, const int rb
|
||||
|
||||
/* 3GPP TS 28.522 - section 5.1.3.3.3
|
||||
note: by default this measurement is calculated for previous 100ms (openair2/LAYER2/nr_rlc/nr_rlc_entity.c:118, 173, 213); please, update according to your needs */
|
||||
static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, __attribute__((unused))const size_t ue_idx)
|
||||
static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, __attribute__((unused))const size_t ue_idx, __attribute__((unused))e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
|
||||
@@ -122,7 +134,7 @@ static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused))uint32_t
|
||||
/* 3GPP TS 28.522 - section 5.1.1.3.1
|
||||
note: per spec, average UE throughput in DL (taken into consideration values from all UEs, and averaged)
|
||||
here calculated as: UE specific throughput in DL */
|
||||
static meas_record_lst_t fill_DRB_UEThpDl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
|
||||
static meas_record_lst_t fill_DRB_UEThpDl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, __attribute__((unused))e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
|
||||
@@ -141,7 +153,7 @@ static meas_record_lst_t fill_DRB_UEThpDl(uint32_t gran_period_ms, cudu_ue_info_
|
||||
/* 3GPP TS 28.522 - section 5.1.1.3.3
|
||||
note: per spec, average UE throughput in UL (taken into consideration values from all UEs, and averaged)
|
||||
here calculated as: UE specific throughput in UL */
|
||||
static meas_record_lst_t fill_DRB_UEThpUl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
|
||||
static meas_record_lst_t fill_DRB_UEThpUl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, __attribute__((unused))e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
|
||||
@@ -159,32 +171,30 @@ static meas_record_lst_t fill_DRB_UEThpUl(uint32_t gran_period_ms, cudu_ue_info_
|
||||
}
|
||||
|
||||
/* 3GPP TS 28.522 - section 5.1.1.2.1
|
||||
note: per spec, DL PRB usage [%] = (total used PRBs for DL traffic / total available PRBs for DL traffic) * 100
|
||||
here calculated as: aggregated DL PRBs (t) - aggregated DL PRBs (t-gran_period) */
|
||||
static meas_record_lst_t fill_RRU_PrbTotDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
|
||||
note: this measurement is calculated as per spec */
|
||||
static meas_record_lst_t fill_RRU_PrbTotDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
|
||||
meas_record.value = INTEGER_MEAS_VALUE;
|
||||
|
||||
// Get the number of DL PRBs
|
||||
meas_record.int_val = ue_info.ue->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl; // [PRBs]
|
||||
meas_record.int_val = (ue_info.ue->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl) * 100 / (node_stats[1].mac_stats.dl.total_prb_aggregate - node_stats[0].mac_stats.dl.total_prb_aggregate); // [%]
|
||||
last_total_prbs[ue_idx].dl = ue_info.ue->mac_stats.dl.total_rbs;
|
||||
|
||||
return meas_record;
|
||||
}
|
||||
|
||||
/* 3GPP TS 28.522 - section 5.1.1.2.2
|
||||
note: per spec, UL PRB usage [%] = (total used PRBs for UL traffic / total available PRBs for UL traffic) * 100
|
||||
here calculated as: aggregated UL PRBs (t) - aggregated UL PRBs (t-gran_period) */
|
||||
static meas_record_lst_t fill_RRU_PrbTotUl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
|
||||
note: this measurement is calculated as per spec */
|
||||
static meas_record_lst_t fill_RRU_PrbTotUl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
meas_record_lst_t meas_record = {0};
|
||||
|
||||
meas_record.value = INTEGER_MEAS_VALUE;
|
||||
|
||||
// Get the number of UL PRBs
|
||||
meas_record.int_val = ue_info.ue->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul; // [PRBs]
|
||||
meas_record.int_val = (ue_info.ue->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul) * 100 / (node_stats[1].mac_stats.ul.total_prb_aggregate - node_stats[0].mac_stats.ul.total_prb_aggregate); // [%]
|
||||
last_total_prbs[ue_idx].ul = ue_info.ue->mac_stats.ul.total_rbs;
|
||||
|
||||
return meas_record;
|
||||
@@ -217,7 +227,7 @@ void init_kpm_subs_data(void)
|
||||
}
|
||||
}
|
||||
|
||||
meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
|
||||
meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, e2_node_level_stats_t* node_stats)
|
||||
{
|
||||
assert(kpm_meas_name != NULL);
|
||||
|
||||
@@ -226,7 +236,7 @@ meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_m
|
||||
assert(found_entry != NULL && "Unsupported KPM measurement name");
|
||||
|
||||
kv_measure_t *kv_found = (kv_measure_t *)found_entry->data;
|
||||
meas_record_lst_t meas_record = kv_found->value(gran_period_ms, ue_info, ue_idx);
|
||||
meas_record_lst_t meas_record = kv_found->value(gran_period_ms, ue_info, ue_idx, node_stats);
|
||||
|
||||
return meas_record;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,13 @@ typedef struct {
|
||||
|
||||
} cudu_ue_info_pair_t;
|
||||
|
||||
typedef meas_record_lst_t (*kpm_meas_fp)(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx);
|
||||
typedef struct {
|
||||
dlul_mac_stats_t mac_stats;
|
||||
} e2_node_level_stats_t;
|
||||
|
||||
e2_node_level_stats_t cp_node_level_stats(const e2_node_level_stats_t *src);
|
||||
|
||||
typedef meas_record_lst_t (*kpm_meas_fp)(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, e2_node_level_stats_t* node_stats);
|
||||
|
||||
typedef struct{
|
||||
char* key;
|
||||
@@ -44,6 +50,6 @@ typedef struct{
|
||||
|
||||
void init_kpm_subs_data(void);
|
||||
|
||||
meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx);
|
||||
meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx, e2_node_level_stats_t* node_stats);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -217,7 +217,7 @@ typedef enum {
|
||||
{CONFIG_STRING_RU_NUM_TP_CORES, HLP_RU_NUM_TP_CORES, 0, .uptr=NULL, .defintval=2, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_RU_NUM_INTERFACES, HLP_RU_NUM_INTERFACES, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_RU_HALF_SLOT_PARALLELIZATION, HLP_RU_HALF_SLOT_PARALLELIZATION, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_RU_RU_THREAD_CORE, HLP_RU_RU_THREAD_CORE, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_RU_RU_THREAD_CORE, HLP_RU_RU_THREAD_CORE, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
|
||||
{CONFIG_STRING_RU_GPIO_CONTROL, HLP_RU_GPIO_CONTROL, 0, .strptr=NULL, .defstrval="generic", TYPE_STRING, 0}, \
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
@@ -84,8 +84,8 @@
|
||||
{CONFIG_STRING_L1_PUSCH_DTX_THRESHOLD, NULL, 0, .uptr=NULL, .defintval=50, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_L1_SRS_DTX_THRESHOLD, NULL, 0, .uptr=NULL, .defintval=30, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_L1_MAX_LDPC_ITERATIONS, NULL, 0, .uptr=NULL, .defintval=8, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_L1_RX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_L1_TX_THREAD_CORE, NULL, 0, .uptr=NULL, .defintval=-1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_L1_RX_THREAD_CORE, NULL, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
|
||||
{CONFIG_STRING_L1_TX_THREAD_CORE, NULL, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
|
||||
{CONFIG_STRING_L1_TX_AMP_BACKOFF_dB, HLP_L1TX_BO,0, .uptr=NULL, .defintval=36, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_L1_PHASE_COMP, HLP_L1_PHASE_COMP,PARAMFLAG_BOOL, .uptr=NULL,.defintval=1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_NUM_ANTENNAS_PER_THREAD, HLP_NUM_ARX,0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
#define CONFIG_STRING_MACRLC_BEAM_WEIGHTS_LIST "beam_weights"
|
||||
#define CONFIG_STRING_MACRLC_PUSCH_RSSI_THRESHOLD "pusch_RSSI_Threshold"
|
||||
#define CONFIG_STRING_MACRLC_PUCCH_RSSI_THRESHOLD "pucch_RSSI_Threshold"
|
||||
#define CONFIG_STRING_MACRLC_STATS_MAX_UE "stats_max_ue"
|
||||
|
||||
#define HLP_MACRLC_UL_PRBBLACK "SNR threshold to decide whether a PRB will be blacklisted or not"
|
||||
#define HLP_MACRLC_DL_BLER_UP "Upper threshold of BLER to decrease DL MCS"
|
||||
@@ -98,6 +99,7 @@
|
||||
#define HLP_MACRLC_BEAMS_PERIOD "set of beams that can be simultaneously allocated in a period"
|
||||
#define HLP_MACRLC_PUSCH_RSSI_THRESHOLD "Limits PUSCH TPC commands based on RSSI to prevent ADC railing. Value range [-1280, 0], unit 0.1 dBm/dBFS"
|
||||
#define HLP_MACRLC_PUCCH_RSSI_THRESHOLD "Limits PUCCH TPC commands based on RSSI to prevent ADC railing. Value range [-1280, 0], unit 0.1 dBm/dBFS"
|
||||
#define HLP_MACRLC_STATS_MAX_UE "Maximum number of UEs before disabling periodical output (0 to disable)"
|
||||
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
/* MacRLC configuration parameters */
|
||||
@@ -149,6 +151,7 @@
|
||||
0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||
{CONFIG_STRING_MACRLC_PUCCH_RSSI_THRESHOLD, HLP_MACRLC_PUCCH_RSSI_THRESHOLD, \
|
||||
0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||
{CONFIG_STRING_MACRLC_STATS_MAX_UE, HLP_MACRLC_STATS_MAX_UE, 0, .iptr=NULL, .defintval=8, TYPE_INT, 0}, \
|
||||
}
|
||||
// clang-format off
|
||||
|
||||
@@ -194,6 +197,7 @@
|
||||
#define MACRLC_BEAMWEIGHTS_IDX 39
|
||||
#define MACRLC_PUSCH_RSSI_THRES_IDX 40
|
||||
#define MACRLC_PUCCH_RSSI_THRES_IDX 41
|
||||
#define MACRLC_STATS_MAX_UE_IDX 42
|
||||
|
||||
#define MACRLCPARAMS_CHECK { \
|
||||
{ .s5 = { NULL } }, \
|
||||
@@ -238,6 +242,7 @@
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s2 = { config_check_intrange, {-1280, 0}} }, /* PUSCH RSSI threshold range */ \
|
||||
{ .s2 = { config_check_intrange, {-1280, 0}} }, /* PUCCH RSSI threshold range */ \
|
||||
{ .s5 = { NULL } }, \
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
@@ -707,7 +707,7 @@ void RCconfig_verify(configmodule_interface_t *cfg, ngran_node_t node_type)
|
||||
verify_gnb_param_notset(gnbp, GNB_DISABLE_HARQ_IDX, GNB_CONFIG_STRING_DISABLE_HARQ);
|
||||
verify_gnb_param_notset(gnbp, GNB_NUM_DL_HARQ_IDX, GNB_CONFIG_STRING_NUM_DL_HARQPROCESSES);
|
||||
verify_gnb_param_notset(gnbp, GNB_NUM_UL_HARQ_IDX, GNB_CONFIG_STRING_NUM_UL_HARQPROCESSES);
|
||||
verify_gnb_param_notset(gnbp, GNB_DO_SINR_IDX, GNB_CONFIG_STRING_DOSINR);
|
||||
verify_gnb_param_notset(gnbp, GNB_CONFIG_REP_IDX, GNB_CONFIG_STRING_CONFIG_REP);
|
||||
|
||||
// check for some general sections
|
||||
verify_section_notset(cfg, NULL, CONFIG_STRING_L1_LIST);
|
||||
@@ -1127,7 +1127,7 @@ f1ap_gnb_du_system_info_t *get_sys_info(NR_BCCH_BCH_Message_t *mib, const NR_BCC
|
||||
|
||||
sys_info->mib = calloc_or_fail(buf_len, sizeof(*sys_info->mib));
|
||||
DevAssert(mib != NULL);
|
||||
sys_info->mib_length = encode_MIB_NR(mib, 0, sys_info->mib, buf_len);
|
||||
sys_info->mib_length = encode_MIB_NR_setup(mib->message.choice.mib, 0, sys_info->mib, buf_len);
|
||||
DevAssert(sys_info->mib_length == buf_len);
|
||||
|
||||
DevAssert(sib1 != NULL);
|
||||
@@ -1356,6 +1356,18 @@ static void config_pdcp(configmodule_interface_t *cfg, nr_pdcp_configuration_t *
|
||||
pdcp_config->drb.discard_timer = config_get_processedint(cfg, &pdcp_params[CONFIG_NR_PDCP_DRB_DISCARD_TIMER_IDX]);
|
||||
}
|
||||
|
||||
void nfapi_stop_l1()
|
||||
{
|
||||
if (NFAPI_MODE && (NFAPI_MODE == NFAPI_MODE_AERIAL || NFAPI_MODE == NFAPI_MODE_VNF)) {
|
||||
stop_nr_nfapi_vnf();
|
||||
} else if (NFAPI_MODE && NFAPI_MODE == NFAPI_MODE_PNF) {
|
||||
// PNF should just wait for the STOP.request to come from the L2
|
||||
// If instead it was stopped first, in some cases it makes sense for it to terminate itself (WLS)
|
||||
// Sends a STOP.indication to the VNF
|
||||
stop_nr_nfapi_pnf();
|
||||
}
|
||||
}
|
||||
|
||||
static void get_bwp_config(nr_mac_config_t *configuration)
|
||||
{
|
||||
char path[MAX_OPTNAME_SIZE * 2 + 8];
|
||||
@@ -1432,7 +1444,8 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
|
||||
config.do_CSIRS = *GNBParamList.paramarray[0][GNB_DO_CSIRS_IDX].iptr;
|
||||
config.do_SRS = *GNBParamList.paramarray[0][GNB_DO_SRS_IDX].iptr;
|
||||
config.max_num_rsrp = *GNBParamList.paramarray[0][GNB_LIMIT_RSRP_REPORT_IDX].iptr;
|
||||
config.do_SINR = *GNBParamList.paramarray[0][GNB_DO_SINR_IDX].iptr;
|
||||
const char *report_type_s = *GNBParamList.paramarray[0][GNB_CONFIG_REP_IDX].strptr;
|
||||
config.report_type = config_get_processedint(cfg, &GNBParamList.paramarray[0][GNB_CONFIG_REP_IDX]);
|
||||
config.force_256qam_off = *GNBParamList.paramarray[0][GNB_FORCE256QAMOFF_IDX].iptr;
|
||||
config.force_UL256qam_off = *GNBParamList.paramarray[0][GNB_FORCEUL256QAMOFF_IDX].iptr;
|
||||
config.use_deltaMCS = *GNBParamList.paramarray[0][GNB_USE_DELTA_MCS_IDX].iptr != 0;
|
||||
@@ -1443,10 +1456,11 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
|
||||
if (config.disable_harq)
|
||||
LOG_W(GNB_APP, "\"disable_harq\" is a REL17 feature and is incompatible with REL15 and REL16 UEs!\n");
|
||||
LOG_I(GNB_APP,
|
||||
"CSI-RS %d, SRS %d, SINR:%d, 256 QAM %s, delta_MCS %s, maxMIMO_Layers %d, HARQ feedback %s, num DLHARQ:%d, num ULHARQ:%d\n",
|
||||
"CSI-RS %d, SRS %d, report type %d (%s), 256 QAM %s, delta_MCS %s, maxMIMO_Layers %d, HARQ feedback %s, num DLHARQ:%d, num ULHARQ:%d\n",
|
||||
config.do_CSIRS,
|
||||
config.do_SRS,
|
||||
config.do_SINR,
|
||||
config.report_type,
|
||||
report_type_s,
|
||||
config.force_256qam_off ? "force off" : "may be on",
|
||||
config.use_deltaMCS ? "on" : "off",
|
||||
config.maxMIMO_layers,
|
||||
@@ -1580,6 +1594,8 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
|
||||
AssertFatal(1 == 0, "MACRLC %d: %s unknown southbound midhaul\n", j, *(MacRLC_ParamList.paramarray[j][MACRLC_TRANSPORT_S_PREFERENCE_IDX].strptr));
|
||||
}
|
||||
RC.nrmac[j]->ulsch_max_frame_inactivity = *(MacRLC_ParamList.paramarray[j][MACRLC_ULSCH_MAX_FRAME_INACTIVITY].uptr);
|
||||
RC.nrmac[j]->stats_max_ue = *MacRLC_ParamList.paramarray[j][MACRLC_STATS_MAX_UE_IDX].iptr;
|
||||
RC.nrmac[j]->print_ue_stats = RC.nrmac[j]->stats_max_ue > 0;
|
||||
NR_bler_options_t *dl_bler_options = &RC.nrmac[j]->dl_bler;
|
||||
dl_bler_options->upper = *(MacRLC_ParamList.paramarray[j][MACRLC_DL_BLER_TARGET_UPPER_IDX].dblptr);
|
||||
dl_bler_options->lower = *(MacRLC_ParamList.paramarray[j][MACRLC_DL_BLER_TARGET_LOWER_IDX].dblptr);
|
||||
|
||||
@@ -64,7 +64,7 @@ f1ap_gnb_du_system_info_t *get_sys_info(NR_BCCH_BCH_Message_t *mib, const NR_BCC
|
||||
int gNB_app_handle_f1ap_gnb_cu_configuration_update(f1ap_gnb_cu_configuration_update_t *gnb_cu_cfg_update);
|
||||
MessageDef *RCconfig_NR_CU_E1(const E1_t *entity);
|
||||
ngran_node_t get_node_type(void);
|
||||
|
||||
void nfapi_stop_l1();
|
||||
#ifdef E2_AGENT
|
||||
#include "openair2/E2AP/e2_agent_arg.h"
|
||||
e2_agent_args_t RCconfig_NR_E2agent(void);
|
||||
|
||||
@@ -137,7 +137,7 @@ typedef enum {
|
||||
#define GNB_CONFIG_STRING_UESS_AGG_LEVEL_LIST "uess_agg_levels"
|
||||
#define GNB_CONFIG_STRING_CU_SIB_LIST "cu_sibs"
|
||||
#define GNB_CONFIG_STRING_DU_SIB_LIST "du_sibs"
|
||||
#define GNB_CONFIG_STRING_DOSINR "do_SINR"
|
||||
#define GNB_CONFIG_STRING_CONFIG_REP "CSI_report_type"
|
||||
#define GNB_CONFIG_STRING_1ST_ACTIVE_BWP "first_active_bwp"
|
||||
#define GNB_CONFIG_STRING_LIMIT_RSRP_REPORT "max_num_RSRP_reported"
|
||||
|
||||
@@ -153,7 +153,7 @@ typedef enum {
|
||||
#define GNB_CONFIG_HLP_UESS_AGG_LEVEL_LIST "List of aggregation levels with number of candidates per level. Element 0 - aggregation level 1"
|
||||
#define GNB_CONFIG_HLP_CU_SIBS "List of CU generated SIBs to be transmitted"
|
||||
#define GNB_CONFIG_HLP_DU_SIBS "List of DU generated SIBs to be transmitted"
|
||||
#define GNB_CONFIG_HLP_DOSINR "Enable CSI feedback using SINR measurements on SSB"
|
||||
#define GNB_CONFIG_HLP_CONFIG_REP "Define quantity for CSI report (options: ssb_rsrp, ssb_sinr and cri_rsrp)"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
@@ -201,7 +201,7 @@ typedef enum {
|
||||
GNB_CONFIG_HLP_UESS_AGG_LEVEL_LIST, 0, .iptr=NULL, .defintarrayval=NULL, TYPE_INTARRAY, 0}, \
|
||||
{GNB_CONFIG_STRING_CU_SIB_LIST, GNB_CONFIG_HLP_CU_SIBS, 0, .iptr=NULL, .defintarrayval=0, TYPE_INTARRAY, 0}, \
|
||||
{GNB_CONFIG_STRING_DU_SIB_LIST, GNB_CONFIG_HLP_DU_SIBS, 0, .iptr=NULL, .defintarrayval=0, TYPE_INTARRAY, 0}, \
|
||||
{GNB_CONFIG_STRING_DOSINR, GNB_CONFIG_HLP_DOSINR, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||
{GNB_CONFIG_STRING_CONFIG_REP, GNB_CONFIG_HLP_CONFIG_REP, 0, .strptr=NULL, .defstrval="ssb_rsrp", TYPE_STRING, 0}, \
|
||||
{GNB_CONFIG_STRING_1ST_ACTIVE_BWP, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||
{GNB_CONFIG_STRING_LIMIT_RSRP_REPORT, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||
}
|
||||
@@ -246,7 +246,7 @@ typedef enum {
|
||||
#define GNB_UESS_AGG_LEVEL_LIST_IDX 35
|
||||
#define GNB_CU_SIBS_IDX 36
|
||||
#define GNB_DU_SIBS_IDX 37
|
||||
#define GNB_DO_SINR_IDX 38
|
||||
#define GNB_CONFIG_REP_IDX 38
|
||||
#define GNB_1ST_ACTIVE_BWP_IDX 39
|
||||
#define GNB_LIMIT_RSRP_REPORT_IDX 40
|
||||
|
||||
@@ -293,7 +293,10 @@ typedef enum {
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s3a = { config_checkstr_assign_integer, \
|
||||
{"ssb_rsrp", "ssb_sinr", "cri_rsrp"}, \
|
||||
{SSB_RSRP, SSB_SINR, CRI_RSRP}, \
|
||||
3 } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
}
|
||||
|
||||
@@ -2246,7 +2246,6 @@ int32_t get_l_prime(uint8_t duration_in_symbols,
|
||||
// For PDSCH Mapping TypeA, ld is duration between first OFDM of the slot and last OFDM symbol of the scheduled PUSCH resources
|
||||
// For TypeB, ld is the duration of the scheduled PUSCH resources
|
||||
uint8_t ld = (mapping_type == typeA) ? (duration_in_symbols + start_symbol) : duration_in_symbols;
|
||||
uint8_t l0 = (dmrs_typeA_position == NR_MIB__dmrs_TypeA_Position_pos2) ? 2 : 3 ;
|
||||
|
||||
uint8_t colomn = additional_pos;
|
||||
|
||||
@@ -2260,20 +2259,27 @@ int32_t get_l_prime(uint8_t duration_in_symbols,
|
||||
row = ld - 3;
|
||||
|
||||
uint32_t l_prime;
|
||||
int l0 = (dmrs_typeA_position == NR_MIB__dmrs_TypeA_Position_pos2) ? 2 : 3;
|
||||
int l0_shift;
|
||||
if (pusch_maxLength == pusch_len1) {
|
||||
l_prime = table_6_4_1_1_3_3_pusch_dmrs_positions_l[row][colomn];
|
||||
l0 = 1 << l0;
|
||||
}
|
||||
else {
|
||||
l0_shift = 1 << l0;
|
||||
} else {
|
||||
l_prime = table_6_4_1_1_3_4_pusch_dmrs_positions_l[row][colomn];
|
||||
l0 = 1<<l0 | 1<<(l0+1);
|
||||
l0_shift = 1 << l0 | 1 << (l0 + 1);
|
||||
}
|
||||
AssertFatal(l_prime >= 0, "invalid l_prime < 0\n");
|
||||
|
||||
LOG_D(NR_MAC, "PUSCH - l0:%d, ld:%d,row:%d, column:%d, addpos:%d, maxlen:%d\n", l0, ld, row, colomn, additional_pos, pusch_maxLength);
|
||||
AssertFatal(l_prime>=0,"invalid l_prime < 0\n");
|
||||
|
||||
l_prime = (mapping_type == typeA) ? (l_prime | l0) : (l_prime << start_symbol);
|
||||
LOG_D(MAC, " PUSCH DMRS MASK in HEX:%x\n", l_prime);
|
||||
l_prime = (mapping_type == typeA) ? (l_prime | l0_shift) : (l_prime << start_symbol);
|
||||
LOG_D(NR_MAC,
|
||||
"PUSCH - l0:%d, ld:%d, row:%d, column:%d, addpos:%d, maxlen:%d DMRS MASK in HEX:%x\n",
|
||||
(mapping_type == typeA) ? l0 : 0,
|
||||
ld,
|
||||
row,
|
||||
colomn,
|
||||
additional_pos,
|
||||
pusch_maxLength,
|
||||
l_prime);
|
||||
|
||||
return l_prime;
|
||||
}
|
||||
@@ -2813,11 +2819,13 @@ uint16_t nr_dci_size(const NR_UE_DL_BWP_t *DL_BWP,
|
||||
dci_pdu->harq_pid.nbits = 4;
|
||||
dci_pdu->frequency_domain_assignment.nbits = (uint8_t)ceil(log2((N_RB * (N_RB + 1)) >>1)); // Freq domain assignment -- hopping scenario to be updated
|
||||
size += dci_pdu->frequency_domain_assignment.nbits;
|
||||
if(alt_size >= size)
|
||||
size += alt_size - size; // Padding to match 1_0 size
|
||||
else if (ss_type == NR_SearchSpace__searchSpaceType_PR_common) {
|
||||
dci_pdu->frequency_domain_assignment.nbits -= (size - alt_size);
|
||||
size = alt_size;
|
||||
if (alt_size) {
|
||||
if(alt_size >= size)
|
||||
size += alt_size - size; // Padding to match 1_0 size
|
||||
else if (ss_type == NR_SearchSpace__searchSpaceType_PR_common) {
|
||||
dci_pdu->frequency_domain_assignment.nbits -= (size - alt_size);
|
||||
size = alt_size;
|
||||
}
|
||||
}
|
||||
// UL/SUL indicator assumed to be 0
|
||||
break;
|
||||
@@ -3127,7 +3135,7 @@ uint16_t nr_dci_size(const NR_UE_DL_BWP_t *DL_BWP,
|
||||
default:
|
||||
AssertFatal(1==0, "Invalid NR DCI format %d\n", format);
|
||||
}
|
||||
LOG_D(NR_MAC, "DCI size: %d\n", size);
|
||||
LOG_D(NR_MAC, "DCI format %d size: %d alt_size %d\n", format, size, alt_size);
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -3163,13 +3171,21 @@ int16_t fill_dmrs_mask(const NR_PDSCH_Config_t *pdsch_Config,
|
||||
int dmrs_AdditionalPosition = 0;
|
||||
NR_DMRS_DownlinkConfig_t *dmrs_config = NULL;
|
||||
|
||||
LOG_D(MAC, "NrofSymbols:%d, startSymbol:%d, mappingtype:%d, dmrs_TypeA_Position:%d\n", NrOfSymbols, startSymbol, mappingtype, dmrs_TypeA_Position);
|
||||
LOG_D(NR_MAC,
|
||||
"NrofSymbols:%d, startSymbol:%d, mappingtype:%d, dmrs_TypeA_Position:%d\n",
|
||||
NrOfSymbols,
|
||||
startSymbol,
|
||||
mappingtype,
|
||||
dmrs_TypeA_Position);
|
||||
|
||||
int l0 = 0; // type B
|
||||
if (mappingtype == typeA) {
|
||||
if (dmrs_TypeA_Position == NR_ServingCellConfigCommon__dmrs_TypeA_Position_pos2) l0 = 2;
|
||||
else if (dmrs_TypeA_Position == NR_ServingCellConfigCommon__dmrs_TypeA_Position_pos3) l0 = 3;
|
||||
else AssertFatal(1==0,"Illegal dmrs_TypeA_Position %d\n",(int)dmrs_TypeA_Position);
|
||||
if (dmrs_TypeA_Position == NR_ServingCellConfigCommon__dmrs_TypeA_Position_pos2)
|
||||
l0 = 2;
|
||||
else if (dmrs_TypeA_Position == NR_ServingCellConfigCommon__dmrs_TypeA_Position_pos3)
|
||||
l0 = 3;
|
||||
else
|
||||
AssertFatal(false, "Illegal dmrs_TypeA_Position %d\n", (int)dmrs_TypeA_Position);
|
||||
}
|
||||
// in case of DCI FORMAT 1_0 or dedicated pdsch config not received additionposition = pos2, len1 should be used
|
||||
// referred to section 5.1.6.2 in 38.214
|
||||
@@ -3177,14 +3193,16 @@ int16_t fill_dmrs_mask(const NR_PDSCH_Config_t *pdsch_Config,
|
||||
|
||||
if (pdsch_Config != NULL) {
|
||||
if (mappingtype == typeA) { // Type A
|
||||
if (dci_format != NR_DL_DCI_FORMAT_1_0 &&
|
||||
pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA && pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->present == NR_SetupRelease_DMRS_DownlinkConfig_PR_setup)
|
||||
if (dci_format != NR_DL_DCI_FORMAT_1_0
|
||||
&& pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA
|
||||
&& pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->present == NR_SetupRelease_DMRS_DownlinkConfig_PR_setup)
|
||||
dmrs_config = (NR_DMRS_DownlinkConfig_t *)pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup;
|
||||
} else if (mappingtype == typeB) {
|
||||
if (pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB && pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->present == NR_SetupRelease_DMRS_DownlinkConfig_PR_setup)
|
||||
if (pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB
|
||||
&& pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->present == NR_SetupRelease_DMRS_DownlinkConfig_PR_setup)
|
||||
dmrs_config = (NR_DMRS_DownlinkConfig_t *)pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup;
|
||||
} else {
|
||||
AssertFatal(1==0,"Incorrect Mappingtype\n");
|
||||
AssertFatal(false, "Incorrect Mappingtype\n");
|
||||
}
|
||||
|
||||
// default values of additionalposition is pos2
|
||||
@@ -3192,49 +3210,52 @@ int16_t fill_dmrs_mask(const NR_PDSCH_Config_t *pdsch_Config,
|
||||
dmrs_AdditionalPosition = *dmrs_config->dmrs_AdditionalPosition;
|
||||
}
|
||||
|
||||
uint8_t ld, row, column;
|
||||
int32_t l_prime = -1;
|
||||
|
||||
// columns 0-3 for TypeA, 4-7 for TypeB
|
||||
column = (mappingtype == typeA) ? dmrs_AdditionalPosition : (dmrs_AdditionalPosition + 4);
|
||||
int column = (mappingtype == typeA) ? dmrs_AdditionalPosition : (dmrs_AdditionalPosition + 4);
|
||||
|
||||
// Section 7.4.1.1.2 in Spec 38.211
|
||||
// For PDSCH Mapping TypeA, ld is duration between first OFDM of the slot and last OFDM symbol of the scheduled PDSCH resources
|
||||
// For TypeB, ld is the duration of the scheduled PDSCH resources
|
||||
ld = (mappingtype == typeA) ? (NrOfSymbols + startSymbol) : NrOfSymbols;
|
||||
int ld = (mappingtype == typeA) ? (NrOfSymbols + startSymbol) : NrOfSymbols;
|
||||
|
||||
AssertFatal(ld > 2 && ld < 15,"Illegal NrOfSymbols according to Table 5.1.2.1-1 Spec 38.214 %d\n",ld);
|
||||
AssertFatal((NrOfSymbols + startSymbol) < 15,"Illegal S+L according to Table 5.1.2.1-1 Spec 38.214 S:%d L:%d\n",startSymbol, NrOfSymbols);
|
||||
|
||||
if (mappingtype == typeA) {
|
||||
|
||||
// Section 7.4.1.1.2 in Spec 38.211
|
||||
AssertFatal((l0 == 2) || (l0 == 3 && dmrs_AdditionalPosition != 3),"Wrong config, If dmrs_TypeA_Position POS3, ADD POS cannot be POS3 \n");
|
||||
|
||||
// Table 5.1.2.1-1 in Spec 38.214
|
||||
AssertFatal(startSymbol <= l0, "Wrong config, Start symbol %d cannot be later than dmrs_TypeA_Position %d \n", startSymbol, l0);
|
||||
|
||||
// Section 7.4.1.1.2 in Spec 38.211
|
||||
AssertFatal(l0 == 2 || (l0 == 3 && (ld != 3 && ld != 4)), "ld 3 or 4 symbols only possible with dmrs_TypeA_Position POS2 \n");
|
||||
}
|
||||
|
||||
// number of front loaded symbols
|
||||
int l_prime, row, l0_shift;
|
||||
if (length == 1) {
|
||||
row = ld - 2;
|
||||
l_prime = table_7_4_1_1_2_3_pdsch_dmrs_positions_l[row][column];
|
||||
l0 = 1 << l0;
|
||||
}
|
||||
else {
|
||||
l0_shift = 1 << l0;
|
||||
} else {
|
||||
row = (ld < 4) ? 0 : (ld - 3);
|
||||
l_prime = table_7_4_1_1_2_4_pdsch_dmrs_positions_l[row][column];
|
||||
l0 = 1<<l0 | 1<<(l0+1);
|
||||
l0_shift = 1 << l0 | 1 << (l0 + 1);
|
||||
}
|
||||
AssertFatal(l_prime >= 0,
|
||||
"ERROR in configuration. Check Time Domain allocation of this Grant. l_prime < 1. row:%d, column:%d\n",
|
||||
row,
|
||||
column);
|
||||
|
||||
LOG_D(MAC, "l0:%d, ld:%d,row:%d, column:%d, addpos:%d, maxlen:%d\n", l0, ld, row, column, dmrs_AdditionalPosition, length);
|
||||
AssertFatal(l_prime>=0,"ERROR in configuration.Check Time Domain allocation of this Grant. l_prime < 1. row:%d, column:%d\n", row, column);
|
||||
|
||||
l_prime = (mappingtype == typeA) ? (l_prime | l0) : (l_prime << startSymbol);
|
||||
LOG_D(MAC, " PDSCH DMRS MASK in HEX:%x\n", l_prime);
|
||||
l_prime = (mappingtype == typeA) ? (l_prime | l0_shift) : (l_prime << startSymbol);
|
||||
LOG_D(NR_MAC,
|
||||
"l0:%d, ld:%d, row:%d, column:%d, addpos:%d, maxlen:%d, DMRS MASK in HEX:%x\n",
|
||||
(mappingtype == typeA) ? l0 : 0,
|
||||
ld,
|
||||
row,
|
||||
column,
|
||||
dmrs_AdditionalPosition,
|
||||
length,
|
||||
l_prime);
|
||||
|
||||
return l_prime;
|
||||
}
|
||||
|
||||
@@ -307,8 +307,8 @@ typedef struct {
|
||||
NR_PUCCH_Resource_t *pucch_resource;
|
||||
uint32_t ack_payload;
|
||||
uint8_t sr_payload;
|
||||
uint32_t csi_part1_payload;
|
||||
uint32_t csi_part2_payload;
|
||||
uint64_t csi_part1_payload;
|
||||
uint64_t csi_part2_payload;
|
||||
int n_sr;
|
||||
int n_csi;
|
||||
int n_harq;
|
||||
@@ -431,6 +431,14 @@ typedef struct {
|
||||
float_t ssb_sinr_dB;
|
||||
} NR_SSB_meas_t;
|
||||
|
||||
typedef struct {
|
||||
int rsrp_dBm;
|
||||
uint8_t ri;
|
||||
uint16_t i1;
|
||||
uint8_t i2;
|
||||
uint8_t cqi;
|
||||
} NR_CSIRS_meas_t;
|
||||
|
||||
typedef enum ta_type {
|
||||
no_ta = 0,
|
||||
adjustment_ta,
|
||||
@@ -601,9 +609,6 @@ typedef struct NR_UE_MAC_INST_s {
|
||||
|
||||
nr_csi_report_t csi_report_template[MAX_CSI_REPORTCONFIG];
|
||||
|
||||
/// measurements from SS or CSI-RS
|
||||
fapi_nr_l1_measurements_t l1_measurements;
|
||||
|
||||
//// FAPI-like interface message
|
||||
fapi_nr_ul_config_request_t *ul_config_request;
|
||||
fapi_nr_dl_config_request_t *dl_config_request;
|
||||
@@ -633,6 +638,7 @@ typedef struct NR_UE_MAC_INST_s {
|
||||
int ssb_start_subcarrier;
|
||||
|
||||
NR_SSB_meas_t ssb_measurements[MAX_NB_SSB];
|
||||
NR_CSIRS_meas_t csirs_measurements;
|
||||
|
||||
dci_pdu_rel15_t def_dci_pdu_rel15[NR_MAX_SLOTS_PER_FRAME][8];
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ void release_mac_configuration(NR_UE_MAC_INST_t *mac, NR_UE_MAC_reset_cause_t ca
|
||||
release_ul_BWP(mac, i);
|
||||
|
||||
memset(&mac->ssb_measurements, 0, sizeof(mac->ssb_measurements));
|
||||
memset(&mac->l1_measurements, 0, sizeof(mac->l1_measurements));
|
||||
memset(&mac->csirs_measurements, 0, sizeof(mac->csirs_measurements));
|
||||
memset(&mac->ul_time_alignment, 0, sizeof(mac->ul_time_alignment));
|
||||
for (int i = mac->TAG_list.count; i > 0 ; i--)
|
||||
asn_sequence_del(&mac->TAG_list, i - 1, 1);
|
||||
|
||||
@@ -307,12 +307,7 @@ static void config_dci_pdu(NR_UE_MAC_INST_t *mac,
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sps; i++) {
|
||||
if ((monitoringSymbolsWithinSlot >> (sps - 1 - i)) & 1) {
|
||||
rel15->coreset.StartSymbolIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rel15->coreset.StartSymbolBitmap = monitoringSymbolsWithinSlot;
|
||||
uint32_t Y = 0;
|
||||
if (ss->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific)
|
||||
Y = get_Y(ss, slot, rel15->rnti);
|
||||
|
||||
@@ -1508,13 +1508,18 @@ nr_dci_format_t nr_ue_process_dci_indication_pdu(NR_UE_MAC_INST_t *mac, frame_t
|
||||
|
||||
void nr_ue_process_l1_measurements(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_l1_measurements_t *l1_measurements)
|
||||
{
|
||||
LOG_D(NR_MAC, "(%d.%d) Received CSI-RS measurements\n", frame, slot);
|
||||
memcpy(&mac->l1_measurements, l1_measurements, sizeof(*l1_measurements));
|
||||
LOG_D(NR_MAC, "(%d.%d) Received measurements from L1\n", frame, slot);
|
||||
bool csi_meas = l1_measurements->meas_type == NFAPI_NR_CSI_MEAS;
|
||||
if (!csi_meas && !l1_measurements->is_neighboring_cell) {
|
||||
int ssb_index = l1_measurements->ssb_index;
|
||||
mac->ssb_measurements[ssb_index].ssb_rsrp_dBm = l1_measurements->rsrp_dBm;
|
||||
mac->ssb_measurements[ssb_index].ssb_sinr_dB = l1_measurements->sinr_dB;
|
||||
} else if (csi_meas) {
|
||||
mac->csirs_measurements.rsrp_dBm = l1_measurements->rsrp_dBm;
|
||||
mac->csirs_measurements.i1 = l1_measurements->i1;
|
||||
mac->csirs_measurements.i2 = l1_measurements->i2;
|
||||
mac->csirs_measurements.cqi = l1_measurements->cqi;
|
||||
mac->csirs_measurements.ri = l1_measurements->rank_indicator;
|
||||
}
|
||||
nr_mac_rrc_meas_ind_ue(mac->ue_id,
|
||||
l1_measurements->gNB_index,
|
||||
@@ -2790,13 +2795,13 @@ static csi_payload_t get_ssb_sinr_payload(NR_UE_MAC_INST_t *mac,
|
||||
} else
|
||||
nb_meas = 2;
|
||||
|
||||
struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList SSB_resource;
|
||||
struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList *SSB_resource = NULL;
|
||||
for (int csi_ssb_idx = 0; csi_ssb_idx < csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.count; csi_ssb_idx++) {
|
||||
if (csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceSetId
|
||||
== *(csi_resourceconfig->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->csi_SSB_ResourceSetList->list.array[0])) {
|
||||
SSB_resource = csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList;
|
||||
SSB_resource = &csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList;
|
||||
/// only one SSB resource set from spec 38.331 IE CSI-ResourceConfig
|
||||
nb_ssb = SSB_resource.list.count;
|
||||
nb_ssb = SSB_resource->list.count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2811,7 +2816,7 @@ static csi_payload_t get_ssb_sinr_payload(NR_UE_MAC_INST_t *mac,
|
||||
for (int measured_ssb_idx = 0; measured_ssb_idx < MAX_NB_SSB; measured_ssb_idx++) {
|
||||
// searching for the SSB index in the SSB resource table
|
||||
for (int ssb_resource = 0; ssb_resource < nb_ssb; ssb_resource++) {
|
||||
if (*SSB_resource.list.array[ssb_resource] == measured_ssb_idx) {
|
||||
if (*SSB_resource->list.array[ssb_resource] == measured_ssb_idx) {
|
||||
sorted_sinr_measurements[sorted_idx].ssb_index = ssb_resource;
|
||||
sorted_sinr_measurements[sorted_idx].ssb_rsrp_dBm = mac->ssb_measurements[measured_ssb_idx].ssb_rsrp_dBm;
|
||||
sorted_sinr_measurements[sorted_idx].ssb_sinr_dB = mac->ssb_measurements[measured_ssb_idx].ssb_sinr_dB;
|
||||
@@ -2822,25 +2827,20 @@ static csi_payload_t get_ssb_sinr_payload(NR_UE_MAC_INST_t *mac,
|
||||
}
|
||||
qsort(sorted_sinr_measurements, nb_ssb, sizeof(NR_RSRP_meas_t), compare_ssb_sinr);
|
||||
|
||||
uint8_t ssbi;
|
||||
|
||||
// TS38.212 v16.5.0: Table 6.3.1.1.2-8A
|
||||
if (ssbri_bits > 0) {
|
||||
ssbi = sorted_sinr_measurements[0].ssb_index;
|
||||
temp_payload = reverse_bits(ssbi, ssbri_bits);
|
||||
bits += ssbri_bits;
|
||||
for (int i = 0; i < nb_meas; i++) {
|
||||
if (ssbri_bits > 0) {
|
||||
uint8_t ssbi = sorted_sinr_measurements[i].ssb_index;
|
||||
temp_payload |= (reverse_bits(ssbi, ssbri_bits) << bits);
|
||||
bits += ssbri_bits;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t sinr_idx = get_sinr_index(sorted_sinr_measurements[0].ssb_sinr_dB);
|
||||
temp_payload |= (reverse_bits(sinr_idx, 7) << bits);
|
||||
bits += 7; // 7 bits for highest SINR
|
||||
|
||||
// from the second SSB, differential report
|
||||
for (int i = 1; i < nb_meas; i++) {
|
||||
ssbi = sorted_sinr_measurements[i].ssb_index;
|
||||
temp_payload |= (reverse_bits(ssbi, ssbri_bits) << bits);
|
||||
bits += ssbri_bits;
|
||||
|
||||
sinr_idx = get_sinr_diff_index(sorted_sinr_measurements[0].ssb_sinr_dB, sorted_sinr_measurements[i].ssb_sinr_dB);
|
||||
temp_payload |= (reverse_bits(sinr_idx, 4) << bits);
|
||||
bits += 4; // 4 bits for differential SINR
|
||||
@@ -2931,13 +2931,13 @@ static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
|
||||
} else
|
||||
nb_meas = 2;
|
||||
|
||||
struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList SSB_resource;
|
||||
struct NR_CSI_SSB_ResourceSet__csi_SSB_ResourceList *SSB_resource = NULL;
|
||||
for (int csi_ssb_idx = 0; csi_ssb_idx < csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.count; csi_ssb_idx++) {
|
||||
if (csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceSetId ==
|
||||
*(csi_resourceconfig->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->csi_SSB_ResourceSetList->list.array[0])){
|
||||
SSB_resource = csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList;
|
||||
SSB_resource = &csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList;
|
||||
///only one SSB resource set from spec 38.331 IE CSI-ResourceConfig
|
||||
nb_ssb = SSB_resource.list.count;
|
||||
nb_ssb = SSB_resource->list.count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2952,7 +2952,7 @@ static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
|
||||
for (int measured_ssb_idx = 0; measured_ssb_idx < MAX_NB_SSB; measured_ssb_idx++) {
|
||||
// searching for the SSB index in the SSB resource table
|
||||
for (int ssb_resource = 0; ssb_resource < nb_ssb; ssb_resource++) {
|
||||
if (*SSB_resource.list.array[ssb_resource] == measured_ssb_idx) {
|
||||
if (*SSB_resource->list.array[ssb_resource] == measured_ssb_idx) {
|
||||
sorted_rsrp_measurements[sorted_idx].ssb_index = ssb_resource;
|
||||
sorted_rsrp_measurements[sorted_idx].ssb_rsrp_dBm = mac->ssb_measurements[measured_ssb_idx].ssb_rsrp_dBm;
|
||||
sorted_rsrp_measurements[sorted_idx].ssb_sinr_dB = mac->ssb_measurements[measured_ssb_idx].ssb_sinr_dB;
|
||||
@@ -2963,12 +2963,12 @@ static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
|
||||
}
|
||||
qsort(sorted_rsrp_measurements, nb_ssb, sizeof(NR_RSRP_meas_t), compare_ssb_rsrp);
|
||||
|
||||
uint32_t ssbi;
|
||||
|
||||
if (ssbri_bits > 0) {
|
||||
ssbi = sorted_rsrp_measurements[0].ssb_index;
|
||||
temp_payload = reverse_bits(ssbi, ssbri_bits);
|
||||
bits += ssbri_bits;
|
||||
for (int i = 0; i < nb_meas; i++) {
|
||||
if (ssbri_bits > 0) {
|
||||
uint32_t ssbi = sorted_rsrp_measurements[i].ssb_index;
|
||||
temp_payload |= (reverse_bits(ssbi, ssbri_bits) << bits);
|
||||
bits += ssbri_bits;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t rsrp_idx = get_rsrp_index(sorted_rsrp_measurements[0].ssb_rsrp_dBm);
|
||||
@@ -2977,15 +2977,11 @@ static csi_payload_t get_ssb_rsrp_payload(NR_UE_MAC_INST_t *mac,
|
||||
|
||||
// from the second SSB, differential report
|
||||
for (int i = 1; i < nb_meas; i++) {
|
||||
ssbi = sorted_rsrp_measurements[i].ssb_index;
|
||||
temp_payload |= (reverse_bits(ssbi, ssbri_bits) << bits);
|
||||
bits += ssbri_bits;
|
||||
|
||||
rsrp_idx = get_rsrp_diff_index(sorted_rsrp_measurements[0].ssb_rsrp_dBm,sorted_rsrp_measurements[i].ssb_rsrp_dBm);
|
||||
temp_payload |= (reverse_bits(rsrp_idx, 4) << bits);
|
||||
bits += 4; // 4 bits for subsequent RSRP
|
||||
}
|
||||
break; // resorce found
|
||||
break; // resource found
|
||||
}
|
||||
}
|
||||
int max_bits = sizeof(((csi_payload_t *)0)->part1_payload) * 8;
|
||||
@@ -3026,28 +3022,28 @@ static csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
|
||||
AssertFatal(csi_report, "Couldn't find CSI report with ID %ld\n", csi_reportconfig->reportConfigId);
|
||||
int cri_bitlen = csi_report->csi_meas_bitlen.cri_bitlen;
|
||||
int ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
|
||||
int pmi_x1_bitlen = csi_report->csi_meas_bitlen.pmi_x1_bitlen[mac->l1_measurements.rank_indicator];
|
||||
int pmi_x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[mac->l1_measurements.rank_indicator];
|
||||
int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[mac->l1_measurements.rank_indicator];
|
||||
int pmi_x1_bitlen = csi_report->csi_meas_bitlen.pmi_x1_bitlen[mac->csirs_measurements.ri];
|
||||
int pmi_x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[mac->csirs_measurements.ri];
|
||||
int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[mac->csirs_measurements.ri];
|
||||
int padding_bitlen = 0;
|
||||
// TODO: Improvements will be needed to cri_bitlen>0 and pmi_x1_bitlen>0
|
||||
if (mapping_type == ON_PUSCH) {
|
||||
p1_bits = cri_bitlen + ri_bitlen + cqi_bitlen;
|
||||
p2_bits = pmi_x1_bitlen + pmi_x2_bitlen;
|
||||
temp_payload_1 = (0/*mac->csi_measurements.cri*/ << (cqi_bitlen + ri_bitlen)) |
|
||||
(mac->l1_measurements.rank_indicator << cqi_bitlen) |
|
||||
(mac->l1_measurements.cqi);
|
||||
temp_payload_2 = (mac->l1_measurements.i1 << pmi_x2_bitlen) |
|
||||
mac->l1_measurements.i2;
|
||||
(mac->csirs_measurements.ri << cqi_bitlen) |
|
||||
(mac->csirs_measurements.cqi);
|
||||
temp_payload_2 = (mac->csirs_measurements.i1 << pmi_x2_bitlen) |
|
||||
mac->csirs_measurements.i2;
|
||||
}
|
||||
else {
|
||||
p1_bits = nr_get_csi_bitlen(csi_report);
|
||||
padding_bitlen = p1_bits - (cri_bitlen + ri_bitlen + pmi_x1_bitlen + pmi_x2_bitlen + cqi_bitlen);
|
||||
temp_payload_1 = (0/*mac->csi_measurements.cri*/ << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen + ri_bitlen)) |
|
||||
(mac->l1_measurements.rank_indicator << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen)) |
|
||||
(mac->l1_measurements.i1 << (cqi_bitlen + pmi_x2_bitlen)) |
|
||||
(mac->l1_measurements.i2 << (cqi_bitlen)) |
|
||||
(mac->l1_measurements.cqi);
|
||||
(mac->csirs_measurements.ri << (cqi_bitlen + pmi_x2_bitlen + pmi_x1_bitlen + padding_bitlen)) |
|
||||
(mac->csirs_measurements.i1 << (cqi_bitlen + pmi_x2_bitlen)) |
|
||||
(mac->csirs_measurements.i2 << (cqi_bitlen)) |
|
||||
(mac->csirs_measurements.cqi);
|
||||
}
|
||||
|
||||
temp_payload_1 = reverse_bits(temp_payload_1, p1_bits);
|
||||
@@ -3106,7 +3102,7 @@ static csi_payload_t get_csirs_RSRP_payload(NR_UE_MAC_INST_t *mac,
|
||||
}
|
||||
|
||||
// TODO: Improvements will be needed to cri_ssbri_bitlen>0
|
||||
temp_payload = get_rsrp_index(mac->l1_measurements.rsrp_dBm);
|
||||
temp_payload = get_rsrp_index(mac->csirs_measurements.rsrp_dBm);
|
||||
temp_payload = reverse_bits(temp_payload, n_bits);
|
||||
|
||||
LOG_D(NR_MAC, "cri_ssbri_bitlen = %d\n", cri_ssbri_bitlen);
|
||||
|
||||
@@ -555,6 +555,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac,
|
||||
pusch_config_pdu->tbslbrm = 0;
|
||||
|
||||
} else if (dci) {
|
||||
pusch_config_pdu->subcarrier_spacing = scs;
|
||||
pusch_config_pdu->ulsch_indicator = dci->ulsch_indicator;
|
||||
if (dci->csi_request.nbits > 0 && dci->csi_request.val > 0) {
|
||||
AssertFatal(csi_report, "CSI report needs to be present in case of CSI request\n");
|
||||
@@ -1918,7 +1919,6 @@ uint8_t set_csirs_measurement_bitmap(NR_CSI_MeasConfig_t *csi_measconfig, NR_CSI
|
||||
AssertFatal(false, "Unexpected measurement report type %d\n", report_config->reportQuantity.present);
|
||||
}
|
||||
}
|
||||
AssertFatal(meas_bitmap > 0, "Expected to have at least 1 measurement configured for CSI-RS\n");
|
||||
return meas_bitmap;
|
||||
}
|
||||
|
||||
|
||||
@@ -1099,6 +1099,13 @@ void nr_mac_configure_sib1(gNB_MAC_INST *nrmac, const plmn_id_t *plmn, uint64_t
|
||||
AssertFatal(cc->sib1_bcch_length > 0, "could not encode SIB1\n");
|
||||
}
|
||||
|
||||
static bool process_addmod_bearers_cellGroupConfig(NR_UE_sched_ctrl_t *sched_ctrl, const NR_RLC_BearerConfig_t *conf)
|
||||
{
|
||||
int priority = conf->mac_LogicalChannelConfig->ul_SpecificParameters->priority;
|
||||
nr_lc_config_t c = {.lcid = conf->logicalChannelIdentity, .priority = priority};
|
||||
return nr_mac_add_lcid(sched_ctrl, &c);
|
||||
}
|
||||
|
||||
bool nr_mac_add_test_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t *CellGroup)
|
||||
{
|
||||
/* ideally, instead of this function, "users" of this function should call
|
||||
@@ -1120,7 +1127,9 @@ bool nr_mac_add_test_ue(gNB_MAC_INST *nrmac, uint32_t rnti, NR_CellGroupConfig_t
|
||||
}
|
||||
int ss_type = NR_SearchSpace__searchSpaceType_PR_ue_Specific;
|
||||
configure_UE_BWP(nrmac, nrmac->common_channels[0].ServingCellConfigCommon, UE, false, ss_type, -1, -1);
|
||||
process_addmod_bearers_cellGroupConfig(&UE->UE_sched_ctrl, CellGroup->rlc_BearerToAddModList);
|
||||
const struct NR_CellGroupConfig__rlc_BearerToAddModList *l = CellGroup->rlc_BearerToAddModList;
|
||||
for (int i = 0; l != NULL && i < l->list.count; ++i)
|
||||
process_addmod_bearers_cellGroupConfig(&UE->UE_sched_ctrl, CellGroup->rlc_BearerToAddModList->list.array[i]);
|
||||
AssertFatal(CellGroup->rlc_BearerToReleaseList == NULL, "cannot release bearers while adding new UEs\n");
|
||||
NR_SCHED_UNLOCK(&nrmac->sched_lock);
|
||||
LOG_I(NR_MAC, "Added new UE %x with initial CellGroup\n", rnti);
|
||||
@@ -1139,14 +1148,23 @@ void nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, NR_UE_info_t *UE)
|
||||
uint8_t num_preamble = cfra->resources.choice.ssb->ssb_ResourceList.list.count;
|
||||
ra->preambles.num_preambles = num_preamble;
|
||||
NR_COMMON_channels_t *cc = &nrmac->common_channels[0];
|
||||
char buf[200];
|
||||
int idx = 0;
|
||||
for (int i = 0; i < cc->num_active_ssb; i++) {
|
||||
for (int j = 0; j < num_preamble; j++) {
|
||||
if (cc->ssb_index[i] == cfra->resources.choice.ssb->ssb_ResourceList.list.array[j]->ssb) {
|
||||
// one dedicated preamble for each beam
|
||||
ra->preambles.preamble_list[i] = cfra->resources.choice.ssb->ssb_ResourceList.list.array[j]->ra_PreambleIndex;
|
||||
if (idx < sizeof(buf) - 1)
|
||||
idx += snprintf(buf + idx, sizeof(buf) - idx, " %d", ra->preambles.preamble_list[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_I(NR_MAC, "Added new %s process for UE RNTI %04x with initial CellGroup\n", ra->cfra ? "CFRA" : "CBRA", UE->rnti);
|
||||
LOG_I(NR_MAC,
|
||||
"Added new %s process for UE RNTI %04x with initial CellGroup and %d preamble(s): %s\n",
|
||||
ra->cfra ? "CFRA" : "CBRA",
|
||||
UE->rnti,
|
||||
num_preamble,
|
||||
buf);
|
||||
}
|
||||
|
||||
@@ -59,13 +59,7 @@ uint8_t nr_get_rv(int rel_round)
|
||||
return nr_rv_round_map[rel_round];
|
||||
}
|
||||
|
||||
void clear_nr_nfapi_information(gNB_MAC_INST *gNB,
|
||||
int CC_idP,
|
||||
frame_t frameP,
|
||||
slot_t slotP,
|
||||
nfapi_nr_dl_tti_request_t *DL_req,
|
||||
nfapi_nr_tx_data_request_t *TX_req,
|
||||
nfapi_nr_ul_dci_request_t *UL_dci_req)
|
||||
void clear_nr_nfapi_information(gNB_MAC_INST *gNB, int CC_idP, frame_t frameP, slot_t slotP)
|
||||
{
|
||||
/* called below and in simulators, so we assume a lock but don't require it */
|
||||
const int num_slots = gNB->frame_structure.numb_slots_frame;
|
||||
@@ -75,16 +69,8 @@ void clear_nr_nfapi_information(gNB_MAC_INST *gNB,
|
||||
|
||||
gNB->pdu_index[CC_idP] = 0;
|
||||
|
||||
DL_req[CC_idP].SFN = frameP;
|
||||
DL_req[CC_idP].Slot = slotP;
|
||||
DL_req[CC_idP].dl_tti_request_body.nPDUs = 0;
|
||||
DL_req[CC_idP].dl_tti_request_body.nGroup = 0;
|
||||
memset(pdcch, 0, sizeof(*pdcch) * MAX_NUM_CORESET);
|
||||
|
||||
UL_dci_req[CC_idP].SFN = frameP;
|
||||
UL_dci_req[CC_idP].Slot = slotP;
|
||||
UL_dci_req[CC_idP].numPdus = 0;
|
||||
|
||||
/* advance last round's future UL_tti_req to be ahead of current frame/slot */
|
||||
const int size = gNB->UL_tti_req_ahead_size;
|
||||
const int prev_slot = frameP * num_slots + slotP + size - 1;
|
||||
@@ -100,8 +86,6 @@ void clear_nr_nfapi_information(gNB_MAC_INST *gNB,
|
||||
future_ul_tti_req->n_ulsch = 0;
|
||||
future_ul_tti_req->n_ulcch = 0;
|
||||
future_ul_tti_req->n_group = 0;
|
||||
|
||||
TX_req[CC_idP].Number_of_PDUs = 0;
|
||||
}
|
||||
|
||||
static void clear_beam_information(NR_beam_info_t *beam_info, int frame, int slot, int slots_per_frame)
|
||||
@@ -191,14 +175,27 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
|
||||
uint16_t *vrb_map_UL = cc[CC_id].vrb_map_UL[i];
|
||||
memcpy(&vrb_map_UL[prev_slot % size * MAX_BWP_SIZE], &gNB->ulprbbl, sizeof(uint16_t) * MAX_BWP_SIZE);
|
||||
}
|
||||
clear_nr_nfapi_information(gNB, CC_id, frame, slot, &sched_info->DL_req, &sched_info->TX_req, &sched_info->UL_dci_req);
|
||||
clear_nr_nfapi_information(gNB, CC_id, frame, slot);
|
||||
}
|
||||
|
||||
bool wait_prach_completed = gNB->num_scheduled_prach_rx >= NUM_PRACH_RX_FOR_NOISE_ESTIMATE;
|
||||
if ((wait_prach_completed || get_softmodem_params()->phy_test) && (slot == 0) && (frame & 127) == 0) {
|
||||
if (gNB->print_ue_stats && (wait_prach_completed || get_softmodem_params()->phy_test) && (slot == 0) && (frame & 127) == 0) {
|
||||
char stats_output[32656] = {0};
|
||||
dump_mac_stats(gNB, stats_output, sizeof(stats_output), true);
|
||||
LOG_I(NR_MAC, "Frame.Slot %d.%d\n%s\n", frame, slot, stats_output);
|
||||
|
||||
// TODO: this should be replaced with a size() operation on connected_ue_list
|
||||
int num_ue = 0;
|
||||
UE_iterator(gNB->UE_info.connected_ue_list, it) {
|
||||
(void) it; // not used
|
||||
num_ue++;
|
||||
}
|
||||
if (gNB->print_ue_stats && num_ue > gNB->stats_max_ue) {
|
||||
gNB->print_ue_stats = false;
|
||||
LOG_W(NR_MAC,
|
||||
"periodical UE stats deactivated after reaching %d UEs, please check nrMAC_stats.log, or increase MACRLCs.[0].stats_max_ue\n",
|
||||
gNB->stats_max_ue);
|
||||
}
|
||||
}
|
||||
|
||||
nr_measgap_scheduling(gNB, frame, slot);
|
||||
|
||||
@@ -60,7 +60,7 @@ static int16_t ssb_index_from_prach(module_id_t module_idP,
|
||||
gNB_MAC_INST *gNB = RC.nrmac[module_idP];
|
||||
NR_COMMON_channels_t *cc = &gNB->common_channels[0];
|
||||
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
|
||||
nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0];
|
||||
nfapi_nr_config_request_scf_t *cfg = &gNB->config[0];
|
||||
NR_RACH_ConfigCommon_t *rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
|
||||
uint8_t config_index = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
|
||||
uint8_t fdm = cfg->prach_config.num_prach_fd_occasions.value;
|
||||
@@ -351,10 +351,10 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP)
|
||||
msgacc = initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup;
|
||||
int slots_frame = gNB->frame_structure.numb_slots_frame;
|
||||
int index = ul_buffer_index(frameP, slotP, slots_frame, gNB->UL_tti_req_ahead_size);
|
||||
nfapi_nr_ul_tti_request_t *UL_tti_req = &RC.nrmac[module_idP]->UL_tti_req_ahead[0][index];
|
||||
nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0];
|
||||
nfapi_nr_ul_tti_request_t *UL_tti_req = &gNB->UL_tti_req_ahead[0][index];
|
||||
nfapi_nr_config_request_scf_t *cfg = &gNB->config[0];
|
||||
|
||||
if (is_ul_slot(slotP, &RC.nrmac[module_idP]->frame_structure)) {
|
||||
if (is_ul_slot(slotP, &gNB->frame_structure)) {
|
||||
const NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &rach_ConfigCommon->rach_ConfigGeneric;
|
||||
uint8_t config_index = rach_ConfigGeneric->prach_ConfigurationIndex;
|
||||
int slot_index = 0;
|
||||
@@ -390,16 +390,15 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP)
|
||||
uint32_t N_t_slot = cc->prach_info.N_t_slot;
|
||||
uint32_t start_symb = cc->prach_info.start_symbol;
|
||||
for (int fdm_index = 0; fdm_index < fdm; fdm_index++) { // one structure per frequency domain occasion
|
||||
AssertFatal(UL_tti_req->n_pdus < sizeof(UL_tti_req->pdus_list) / sizeof(UL_tti_req->pdus_list[0]),
|
||||
"Invalid UL_tti_req->n_pdus %d\n",
|
||||
UL_tti_req->n_pdus);
|
||||
|
||||
UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE;
|
||||
UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_prach_pdu_t);
|
||||
nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[UL_tti_req->n_pdus].prach_pdu;
|
||||
memset(prach_pdu, 0, sizeof(nfapi_nr_prach_pdu_t));
|
||||
AssertFatal(UL_tti_req->n_pdus < sizeofArray(UL_tti_req->pdus_list), "Invalid UL_tti_req->n_pdus %d\n", UL_tti_req->n_pdus);
|
||||
nfapi_nr_ul_tti_request_number_of_pdus_t *newpdu = UL_tti_req->pdus_list + UL_tti_req->n_pdus;
|
||||
*newpdu = (nfapi_nr_ul_tti_request_number_of_pdus_t){
|
||||
.pdu_type = NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE,
|
||||
.pdu_size = sizeof(nfapi_nr_prach_pdu_t),
|
||||
};
|
||||
UL_tti_req->n_pdus += 1;
|
||||
int num_td_occ = 0;
|
||||
nfapi_nr_prach_pdu_t *prach_pdu = &newpdu->prach_pdu;
|
||||
for (int td_index = 0; td_index < N_t_slot; td_index++) {
|
||||
uint32_t config_period = cc->prach_info.x;
|
||||
prach_occasion_id = (((frameP % (cc->max_association_period * config_period))/config_period) * cc->total_prach_occasions_per_config_period) +
|
||||
@@ -786,6 +785,7 @@ static void nr_generate_Msg3_retransmission(module_id_t module_idP,
|
||||
{
|
||||
gNB_MAC_INST *nr_mac = RC.nrmac[module_idP];
|
||||
NR_RA_t *ra = UE->ra;
|
||||
DevAssert(!ra->cfra);
|
||||
NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id];
|
||||
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
|
||||
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
|
||||
@@ -1123,7 +1123,7 @@ static void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, slot_
|
||||
const uint16_t mask = SL_to_bitmap(ra->msg3_startsymb, ra->msg3_nbSymb);
|
||||
int slots_frame = mac->frame_structure.numb_slots_frame;
|
||||
int buffer_index = ul_buffer_index(ra->Msg3_frame, ra->Msg3_slot, slots_frame, mac->vrb_map_UL_size);
|
||||
uint16_t *vrb_map_UL = &RC.nrmac[module_idP]->common_channels[CC_id].vrb_map_UL[ra->Msg3_beam.idx][buffer_index * MAX_BWP_SIZE];
|
||||
uint16_t *vrb_map_UL = &mac->common_channels[CC_id].vrb_map_UL[ra->Msg3_beam.idx][buffer_index * MAX_BWP_SIZE];
|
||||
for (int i = 0; i < ra->msg3_nb_rb; ++i) {
|
||||
AssertFatal(!(vrb_map_UL[i + ra->msg3_first_rb + ra->msg3_bwp_start] & mask),
|
||||
"RB %d in %4d.%2d is already taken, cannot allocate Msg3!\n",
|
||||
@@ -1135,7 +1135,7 @@ static void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, slot_
|
||||
|
||||
LOG_D(NR_MAC, "UE %04x: %d.%d RA is active, Msg3 in (%d,%d)\n", UE->rnti, frameP, slotP, ra->Msg3_frame, ra->Msg3_slot);
|
||||
buffer_index = ul_buffer_index(ra->Msg3_frame, ra->Msg3_slot, slots_frame, mac->UL_tti_req_ahead_size);
|
||||
nfapi_nr_ul_tti_request_t *future_ul_tti_req = &RC.nrmac[module_idP]->UL_tti_req_ahead[CC_id][buffer_index];
|
||||
nfapi_nr_ul_tti_request_t *future_ul_tti_req = &mac->UL_tti_req_ahead[CC_id][buffer_index];
|
||||
AssertFatal(future_ul_tti_req->SFN == ra->Msg3_frame
|
||||
&& future_ul_tti_req->Slot == ra->Msg3_slot,
|
||||
"future UL_tti_req's frame.slot %d.%d does not match PUSCH %d.%d\n",
|
||||
@@ -1431,7 +1431,12 @@ static void nr_generate_Msg2(module_id_t module_idP,
|
||||
scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.ra_ResponseWindow;
|
||||
const int n_slots_frame = nr_mac->frame_structure.numb_slots_frame;
|
||||
if (!msg2_in_response_window(ra->preamble_frame, ra->preamble_slot, n_slots_frame, rrc_ra_ResponseWindow, frameP, slotP)) {
|
||||
LOG_E(NR_MAC, "UE RA-RNTI %04x TC-RNTI %04x: exceeded RA window, cannot schedule Msg2\n", ra->RA_rnti, UE->rnti);
|
||||
LOG_E(NR_MAC,
|
||||
"sfn: %d.%d UE RA-RNTI %04x TC-RNTI %04x: exceeded RA window, cannot schedule Msg2\n",
|
||||
frameP,
|
||||
slotP,
|
||||
ra->RA_rnti,
|
||||
UE->rnti);
|
||||
nr_release_ra_UE(nr_mac, UE->rnti);
|
||||
return;
|
||||
}
|
||||
@@ -1447,6 +1452,11 @@ static void nr_generate_Msg2(module_id_t module_idP,
|
||||
return;
|
||||
|
||||
const NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
|
||||
// check the feasibility of Msg3, the actual Msg3 allocation
|
||||
// is further below. In the case of CFRA, we don't need Msg3, but 38.321
|
||||
// §5.1.4 does not clearly exclude Msg3, and UL TA might still be useful.
|
||||
// Before the change in this commit, we used CFRA but required Msg3, which
|
||||
// COTS UE would often (but not always) send.
|
||||
bool ret = get_feasible_msg3_tda(scc,
|
||||
get_delta_for_k2(ul_bwp->scs),
|
||||
ul_bwp->tdaList_Common,
|
||||
@@ -1519,6 +1529,7 @@ static void nr_generate_Msg2(module_id_t module_idP,
|
||||
return;
|
||||
}
|
||||
|
||||
// get an actual Msg3 allocation in CBRA
|
||||
bool msg3_ret = nr_get_Msg3alloc(nr_mac, CC_id, slotP, frameP, UE);
|
||||
if (!msg3_ret) {
|
||||
reset_beam_status(&nr_mac->beam_info, ra->Msg3_frame, ra->Msg3_slot, UE->UE_beam_index, n_slots_frame, ra->Msg3_beam.new_beam);
|
||||
@@ -1605,27 +1616,31 @@ static void nr_generate_Msg2(module_id_t module_idP,
|
||||
// DL TX request
|
||||
nfapi_nr_pdu_t *tx_req = &TX_req->pdu_list[TX_req->Number_of_PDUs];
|
||||
|
||||
// Program UL processing for Msg3
|
||||
// If CFRA: 38.321 §5.1.4 does not clearly say (to me) if UL grant should be
|
||||
// dropped or not, and COTS UE would often send Msg3 if we configured CFRA
|
||||
// but required Msg3. Also, "if RAR includes a MAC subPDU with RAPID: [...]
|
||||
// indicate the reception of an acknowledgement for SI request to upper
|
||||
// layers." which is not the case here.
|
||||
nr_add_msg3(module_idP, CC_id, frameP, slotP, UE, (uint8_t *)&tx_req->TLVs[0].value.direct[0]);
|
||||
|
||||
// Start RA contention resolution timer in Msg3 transmission slot (current slot + K2)
|
||||
// 3GPP TS 38.321 Section 5.1.5 Contention Resolution
|
||||
start_ra_contention_resolution_timer(
|
||||
ra,
|
||||
scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->ra_ContentionResolutionTimer,
|
||||
*ul_bwp->tdaList_Common->list.array[ra->Msg3_tda_id]->k2 + get_NTN_Koffset(scc),
|
||||
ul_bwp->scs);
|
||||
if (!ra->cfra) {
|
||||
LOG_D(NR_MAC,
|
||||
"UE %04x: %d.%d: Setting RA-Msg3 reception for SFN.Slot %d.%d\n",
|
||||
UE->rnti,
|
||||
frameP,
|
||||
slotP,
|
||||
ra->Msg3_frame,
|
||||
ra->Msg3_slot);
|
||||
|
||||
LOG_D(NR_MAC,
|
||||
"UE %04x: %d.%d: Setting RA-Msg3 reception (%s) for SFN.Slot %d.%d\n",
|
||||
UE->rnti,
|
||||
frameP,
|
||||
slotP,
|
||||
ra->cfra ? "CFRA" : "CBRA",
|
||||
ra->Msg3_frame,
|
||||
ra->Msg3_slot);
|
||||
// Start RA contention resolution timer in Msg3 transmission slot (current slot + K2)
|
||||
// 3GPP TS 38.321 Section 5.1.5 Contention Resolution
|
||||
start_ra_contention_resolution_timer(
|
||||
ra,
|
||||
scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->ra_ContentionResolutionTimer,
|
||||
*ul_bwp->tdaList_Common->list.array[ra->Msg3_tda_id]->k2 + get_NTN_Koffset(scc),
|
||||
ul_bwp->scs);
|
||||
|
||||
LOG_A(NR_MAC, "%d.%d Send RAR to RA-RNTI %04x\n", frameP, slotP, ra->RA_rnti);
|
||||
}
|
||||
|
||||
tx_req->PDU_index = pduindex;
|
||||
tx_req->num_TLV = 1;
|
||||
@@ -1650,6 +1665,7 @@ static void nr_generate_Msg2(module_id_t module_idP,
|
||||
vrb_map[bwp_info.bwpStart + rb + rbStart] |= SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols);
|
||||
}
|
||||
|
||||
// In CFRA: in Msg3 handling, will unconditionally mark succeeded
|
||||
ra->ra_state = nrRA_WAIT_Msg3;
|
||||
}
|
||||
|
||||
@@ -1671,6 +1687,7 @@ static void nr_generate_Msg4_MsgB(module_id_t module_idP,
|
||||
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
NR_SearchSpace_t *ss = sched_ctrl->search_space;
|
||||
NR_RA_t *ra = UE->ra;
|
||||
DevAssert(!ra->cfra);
|
||||
const char *ra_type_str = ra->ra_type == RA_2_STEP ? "MsgB" : "Msg4";
|
||||
NR_ControlResourceSet_t *coreset = sched_ctrl->coreset;
|
||||
AssertFatal(coreset != NULL, "Coreset cannot be null for RA %s\n", ra_type_str);
|
||||
@@ -2115,10 +2132,9 @@ static void nr_fill_rar(uint8_t Mod_idP, NR_UE_info_t *UE, uint8_t *dlsch_buffer
|
||||
* The corresponding function to add is add_new_UE_RA(). */
|
||||
void nr_release_ra_UE(gNB_MAC_INST *mac, rnti_t rnti)
|
||||
{
|
||||
NR_SCHED_ENSURE_LOCKED(&mac->sched_lock);
|
||||
NR_UEs_t *UE_info = &mac->UE_info;
|
||||
NR_SCHED_LOCK(&UE_info->mutex);
|
||||
NR_UE_info_t *UE = remove_UE_from_list(NR_NB_RA_PROC_MAX, UE_info->access_ue_list, rnti);
|
||||
NR_SCHED_UNLOCK(&UE_info->mutex);
|
||||
if (UE) {
|
||||
delete_nr_ue_data(UE, mac->common_channels, &UE_info->uid_allocator);
|
||||
} else {
|
||||
|
||||
@@ -274,35 +274,32 @@ void schedule_nr_mib(module_id_t module_idP, frame_t frameP, slot_t slotP, nfapi
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t get_tbs_bch(NR_sched_pdsch_t *pdsch,
|
||||
uint32_t num_total_bytes,
|
||||
uint16_t *vrb_map)
|
||||
static void update_rb_mcs_tbs(NR_sched_pdsch_t *pdsch, uint32_t num_total_bytes, uint16_t *vrb_map)
|
||||
{
|
||||
NR_tda_info_t *tda_info = &pdsch->tda_info;
|
||||
const NR_tda_info_t *tda_info = &pdsch->tda_info;
|
||||
|
||||
// Calculate number of PRB_DMRS
|
||||
uint8_t N_PRB_DMRS = pdsch->dmrs_parms.N_PRB_DMRS;
|
||||
LOG_D(MAC, "dlDmrsSymbPos %x\n", pdsch->dmrs_parms.dl_dmrs_symb_pos);
|
||||
int mcsTableIdx = 0;
|
||||
uint32_t TBS = 0;
|
||||
const uint16_t slbitmap = SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols);
|
||||
int bwpSize = pdsch->bwp_info.bwpSize;
|
||||
int bwpStart = pdsch->bwp_info.bwpStart;
|
||||
int rbStop = bwpSize - 1;
|
||||
int rbStart = 0;
|
||||
uint16_t rbSize = 0;
|
||||
while (rbStart < rbStop) {
|
||||
if (vrb_map[rbStart + bwpStart] & slbitmap)
|
||||
rbStart++;
|
||||
while (pdsch->rbStart < rbStop) {
|
||||
if (vrb_map[pdsch->rbStart + bwpStart] & slbitmap)
|
||||
pdsch->rbStart++;
|
||||
else {
|
||||
int max_rbSize = 0;
|
||||
while (rbStart + max_rbSize <= rbStop && !(vrb_map[rbStart + max_rbSize + bwpStart] & slbitmap))
|
||||
while (pdsch->rbStart + max_rbSize <= rbStop && !(vrb_map[pdsch->rbStart + max_rbSize + bwpStart] & slbitmap))
|
||||
max_rbSize++;
|
||||
|
||||
bool res = false;
|
||||
while (res == false && pdsch->mcs < 10) {
|
||||
res = nr_find_nb_rb(nr_get_Qm_dl(pdsch->mcs, mcsTableIdx),
|
||||
nr_get_code_rate_dl(pdsch->mcs, mcsTableIdx),
|
||||
pdsch->Qm = nr_get_Qm_dl(pdsch->mcs, mcsTableIdx);
|
||||
pdsch->R = nr_get_code_rate_dl(pdsch->mcs, mcsTableIdx);
|
||||
res = nr_find_nb_rb(pdsch->Qm,
|
||||
pdsch->R,
|
||||
1, // no transform precoding for DL
|
||||
1, // single layer
|
||||
tda_info->nrOfSymbols,
|
||||
@@ -310,59 +307,56 @@ static uint32_t get_tbs_bch(NR_sched_pdsch_t *pdsch,
|
||||
num_total_bytes,
|
||||
1, // min_rbSize
|
||||
max_rbSize,
|
||||
&TBS,
|
||||
&rbSize);
|
||||
&pdsch->tb_size,
|
||||
&pdsch->rbSize);
|
||||
if (!res)
|
||||
pdsch->mcs++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
AssertFatal(TBS >= num_total_bytes,
|
||||
AssertFatal(pdsch->tb_size >= num_total_bytes,
|
||||
"Couldn't allocate enough resources for %d bytes in SIB PDSCH (rbStart %d, rbSize %d, bwpSize %d)\n",
|
||||
num_total_bytes,
|
||||
rbStart,
|
||||
rbSize,
|
||||
pdsch->rbStart,
|
||||
pdsch->rbSize,
|
||||
bwpSize);
|
||||
|
||||
pdsch->rbSize = rbSize;
|
||||
pdsch->rbStart = rbStart;
|
||||
|
||||
LOG_D(NR_MAC,
|
||||
"mcs=%i, startSymbolIndex = %i, nrOfSymbols = %i, rbSize = %i, TBS = %i, dmrs_length %d, N_PRB_DMRS = %d, mappingtype = %d\n",
|
||||
pdsch->mcs,
|
||||
tda_info->startSymbolIndex,
|
||||
tda_info->nrOfSymbols,
|
||||
rbSize,
|
||||
TBS,
|
||||
pdsch->rbSize,
|
||||
pdsch->tb_size,
|
||||
pdsch->dmrs_parms.N_DMRS_SLOT,
|
||||
N_PRB_DMRS,
|
||||
tda_info->mapping_type);
|
||||
return TBS;
|
||||
}
|
||||
|
||||
static uint32_t schedule_control_sib1(gNB_MAC_INST *gNB_mac,
|
||||
int CC_id,
|
||||
NR_sched_pdcch_t *pdcch,
|
||||
NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config,
|
||||
int time_domain_allocation,
|
||||
NR_pdsch_dmrs_t *dmrs_parms,
|
||||
NR_tda_info_t *tda_info,
|
||||
uint8_t candidate_idx,
|
||||
int beam,
|
||||
uint16_t num_total_bytes)
|
||||
static NR_sched_pdsch_t schedule_control_sib1(gNB_MAC_INST *gNB_mac,
|
||||
int CC_id,
|
||||
NR_sched_pdcch_t *pdcch,
|
||||
NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config,
|
||||
int time_domain_allocation,
|
||||
NR_pdsch_dmrs_t *dmrs_parms,
|
||||
NR_tda_info_t *tda_info,
|
||||
uint8_t candidate_idx,
|
||||
int beam,
|
||||
uint16_t num_total_bytes)
|
||||
{
|
||||
AssertFatal(gNB_mac->sched_ctrlCommon, "sched_ctrlCommon is NULL\n");
|
||||
NR_COMMON_channels_t *cc = &gNB_mac->common_channels[CC_id];
|
||||
uint16_t *vrb_map = cc->vrb_map[beam];
|
||||
NR_sched_pdsch_t *pdsch = &gNB_mac->sched_ctrlCommon->sched_pdsch;
|
||||
pdsch->bwp_info = get_pdsch_bwp_start_size(gNB_mac, NULL);
|
||||
pdsch->time_domain_allocation = time_domain_allocation;
|
||||
pdsch->dmrs_parms = *dmrs_parms;
|
||||
pdsch->tda_info = *tda_info;
|
||||
pdsch->nrOfLayers = 1;
|
||||
pdsch->pm_index = 0;
|
||||
pdsch->mcs = 0; // starting from mcs 0
|
||||
NR_sched_pdsch_t pdsch = {
|
||||
.bwp_info = get_pdsch_bwp_start_size(gNB_mac, NULL),
|
||||
.time_domain_allocation = time_domain_allocation,
|
||||
.dmrs_parms = *dmrs_parms,
|
||||
.tda_info = *tda_info,
|
||||
.nrOfLayers = 1,
|
||||
.pm_index = 0,
|
||||
.mcs = 0, // starting from mcs 0
|
||||
};
|
||||
gNB_mac->sched_ctrlCommon->num_total_bytes = num_total_bytes;
|
||||
|
||||
uint8_t nr_of_candidates = 0;
|
||||
@@ -383,12 +377,7 @@ static uint32_t schedule_control_sib1(gNB_MAC_INST *gNB_mac,
|
||||
|
||||
AssertFatal(gNB_mac->sched_ctrlCommon->cce_index >= 0, "Could not find CCE for coreset0\n");
|
||||
|
||||
uint32_t TBS = get_tbs_bch(&gNB_mac->sched_ctrlCommon->sched_pdsch,
|
||||
gNB_mac->sched_ctrlCommon->num_total_bytes,
|
||||
vrb_map);
|
||||
|
||||
pdsch->R = nr_get_code_rate_dl(pdsch->mcs, 0);
|
||||
pdsch->Qm = nr_get_Qm_dl(pdsch->mcs, 0);
|
||||
update_rb_mcs_tbs(&pdsch, gNB_mac->sched_ctrlCommon->num_total_bytes, vrb_map);
|
||||
|
||||
// Mark the corresponding RBs as used
|
||||
fill_pdcch_vrb_map(gNB_mac,
|
||||
@@ -397,10 +386,10 @@ static uint32_t schedule_control_sib1(gNB_MAC_INST *gNB_mac,
|
||||
gNB_mac->sched_ctrlCommon->cce_index,
|
||||
gNB_mac->sched_ctrlCommon->aggregation_level,
|
||||
beam);
|
||||
for (int rb = 0; rb < pdsch->rbSize; rb++) {
|
||||
for (int rb = 0; rb < pdsch.rbSize; rb++) {
|
||||
vrb_map[rb + type0_PDCCH_CSS_config->cset_start_rb] |= SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols);
|
||||
}
|
||||
return TBS;
|
||||
return pdsch;
|
||||
}
|
||||
|
||||
static void nr_fill_nfapi_dl_SIB_pdu(gNB_MAC_INST *gNB_mac,
|
||||
@@ -578,22 +567,21 @@ void schedule_nr_sib1(module_id_t module_idP,
|
||||
|
||||
NR_COMMON_channels_t *cc = &gNB_mac->common_channels[0];
|
||||
// Configure sched_ctrlCommon for SIB1
|
||||
uint32_t TBS = schedule_control_sib1(gNB_mac,
|
||||
CC_id,
|
||||
&sched_pdcch,
|
||||
type0_PDCCH_CSS_config,
|
||||
time_domain_allocation,
|
||||
&dmrs_parms,
|
||||
&tda_info,
|
||||
candidate_idx,
|
||||
beam.idx,
|
||||
cc->sib1_bcch_length);
|
||||
NR_sched_pdsch_t sched_pdsch = schedule_control_sib1(gNB_mac,
|
||||
CC_id,
|
||||
&sched_pdcch,
|
||||
type0_PDCCH_CSS_config,
|
||||
time_domain_allocation,
|
||||
&dmrs_parms,
|
||||
&tda_info,
|
||||
candidate_idx,
|
||||
beam.idx,
|
||||
cc->sib1_bcch_length);
|
||||
|
||||
gNB_mac->sched_ctrlCommon->sched_pdsch.tb_size = TBS;
|
||||
nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body;
|
||||
int pdu_index = gNB_mac->pdu_index[0]++;
|
||||
nr_fill_nfapi_dl_SIB_pdu(gNB_mac,
|
||||
&gNB_mac->sched_ctrlCommon->sched_pdsch,
|
||||
&sched_pdsch,
|
||||
&sched_pdcch,
|
||||
gNB_mac->sched_ctrlCommon->search_space,
|
||||
gNB_mac->sched_ctrlCommon->coreset,
|
||||
@@ -609,11 +597,11 @@ void schedule_nr_sib1(module_id_t module_idP,
|
||||
nfapi_nr_pdu_t *tx_req = &TX_req->pdu_list[ntx_req];
|
||||
|
||||
// Data to be transmitted
|
||||
memcpy(tx_req->TLVs[0].value.direct, cc->sib1_bcch_pdu, TBS);
|
||||
memcpy(tx_req->TLVs[0].value.direct, cc->sib1_bcch_pdu, sched_pdsch.tb_size);
|
||||
|
||||
tx_req->PDU_index = pdu_index;
|
||||
tx_req->num_TLV = 1;
|
||||
tx_req->TLVs[0].length = TBS;
|
||||
tx_req->TLVs[0].length = sched_pdsch.tb_size;
|
||||
tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, tx_req->TLVs[0].length);
|
||||
TX_req->Number_of_PDUs++;
|
||||
TX_req->SFN = frameP;
|
||||
@@ -718,9 +706,7 @@ static void other_sib_sched_control(module_id_t module_idP,
|
||||
uint16_t *vrb_map = cc->vrb_map[beam.idx];
|
||||
uint8_t *sib_bcch_pdu = cc->other_sib_bcch_pdu[payload_idx];
|
||||
int num_total_bytes = cc->other_sib_bcch_length[payload_idx];
|
||||
sched_pdsch_otherSI.tb_size = get_tbs_bch(&sched_pdsch_otherSI, num_total_bytes, vrb_map);
|
||||
sched_pdsch_otherSI.R = nr_get_code_rate_dl(sched_pdsch_otherSI.mcs, 0);
|
||||
sched_pdsch_otherSI.Qm = nr_get_Qm_dl(sched_pdsch_otherSI.mcs, 0);
|
||||
update_rb_mcs_tbs(&sched_pdsch_otherSI, num_total_bytes, vrb_map);
|
||||
|
||||
for (int rb = 0; rb < sched_pdsch_otherSI.rbSize; rb++) {
|
||||
vrb_map[rb + type0_PDCCH_CSS_config->cset_start_rb] |= SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols);
|
||||
@@ -747,10 +733,10 @@ static void other_sib_sched_control(module_id_t module_idP,
|
||||
// Data to be transmitted
|
||||
memcpy(tx_req->TLVs[0].value.direct, sib_bcch_pdu, sched_pdsch_otherSI.tb_size);
|
||||
|
||||
tx_req->PDU_length = sched_pdsch_otherSI.tb_size;
|
||||
tx_req->PDU_index = pdu_index;
|
||||
tx_req->num_TLV = 1;
|
||||
tx_req->TLVs[0].length = sched_pdsch_otherSI.tb_size + 2;
|
||||
tx_req->TLVs[0].length = sched_pdsch_otherSI.tb_size;
|
||||
tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, tx_req->TLVs[0].length);
|
||||
TX_req->Number_of_PDUs++;
|
||||
TX_req->SFN = frame;
|
||||
TX_req->Slot = slot;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,9 +55,11 @@ uint32_t target_dl_bw = 50;
|
||||
uint64_t dlsch_slot_bitmap = (1<<1);
|
||||
|
||||
/* schedules whole bandwidth for first user, all the time */
|
||||
void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot)
|
||||
void nr_preprocessor_phytest(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch)
|
||||
{
|
||||
gNB_MAC_INST *mac = RC.nrmac[module_id];
|
||||
frame_t frame = pp_pdsch->frame;
|
||||
slot_t slot = pp_pdsch->slot;
|
||||
|
||||
/* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */
|
||||
int slot_period = slot % mac->frame_structure.numb_slots_period;
|
||||
if (!is_xlsch_in_slot(dlsch_slot_bitmap, slot_period))
|
||||
@@ -86,9 +88,6 @@ void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot)
|
||||
if(!tda_info.valid_tda)
|
||||
return;
|
||||
|
||||
sched_ctrl->sched_pdsch.tda_info = tda_info;
|
||||
sched_ctrl->sched_pdsch.time_domain_allocation = tda;
|
||||
|
||||
/* find largest unallocated chunk */
|
||||
const int bwpSize = dl_bwp->BWPSize;
|
||||
const int BWPStart = dl_bwp->BWPStart;
|
||||
@@ -142,12 +141,11 @@ void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot)
|
||||
0);
|
||||
AssertFatal(CCEIndex >= 0, "Could not find CCE for UE %04x\n", UE->rnti);
|
||||
|
||||
NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch;
|
||||
if (sched_pdsch->dl_harq_pid == -1)
|
||||
sched_pdsch->dl_harq_pid = sched_ctrl->available_dl_harq.head;
|
||||
|
||||
int alloc = -1;
|
||||
if (!get_FeedbackDisabled(UE->sc_info.downlinkHARQ_FeedbackDisabled_r17, sched_pdsch->dl_harq_pid)) {
|
||||
int harq_pid = sched_ctrl->retrans_dl_harq.head;
|
||||
if (harq_pid < 0)
|
||||
harq_pid = sched_ctrl->available_dl_harq.head;
|
||||
if (!get_FeedbackDisabled(UE->sc_info.downlinkHARQ_FeedbackDisabled_r17, harq_pid)) {
|
||||
int r_pucch = nr_get_pucch_resource(sched_ctrl->coreset, UE->current_UL_BWP.pucch_Config, CCEIndex);
|
||||
alloc = nr_acknack_scheduling(mac, UE, frame, slot, 0, r_pucch, 0);
|
||||
if (alloc < 0) {
|
||||
@@ -165,42 +163,38 @@ void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot)
|
||||
sched_ctrl->aggregation_level,
|
||||
beam);
|
||||
|
||||
//AssertFatal(alloc,
|
||||
// "could not find uplink slot for PUCCH (RNTI %04x@%d.%d)!\n",
|
||||
// rnti, frame, slot);
|
||||
|
||||
sched_pdsch->pucch_allocation = alloc;
|
||||
sched_pdsch->rbStart = rbStart;
|
||||
sched_pdsch->rbSize = rbSize;
|
||||
sched_pdsch->bwp_info = get_pdsch_bwp_start_size(mac, UE);
|
||||
sched_pdsch->dmrs_parms = get_dl_dmrs_params(scc,
|
||||
dl_bwp,
|
||||
&tda_info,
|
||||
target_dl_Nl);
|
||||
|
||||
sched_pdsch->mcs = target_dl_mcs;
|
||||
sched_pdsch->nrOfLayers = target_dl_Nl;
|
||||
sched_pdsch->Qm = nr_get_Qm_dl(sched_pdsch->mcs, dl_bwp->mcsTableIdx);
|
||||
sched_pdsch->R = nr_get_code_rate_dl(sched_pdsch->mcs, dl_bwp->mcsTableIdx);
|
||||
NR_sched_pdsch_t sched_pdsch = {
|
||||
.rbSize = rbSize,
|
||||
.rbStart = rbStart,
|
||||
.mcs = target_dl_mcs,
|
||||
.R = nr_get_code_rate_dl(target_dl_mcs, dl_bwp->mcsTableIdx),
|
||||
.Qm = nr_get_Qm_dl(target_dl_mcs, dl_bwp->mcsTableIdx),
|
||||
// tbSize further below
|
||||
.dl_harq_pid = sched_ctrl->retrans_dl_harq.head, // PID of HARQ awaiting retransmission, or -1 otherwise
|
||||
.pucch_allocation = alloc,
|
||||
.pm_index = 0,
|
||||
.nrOfLayers = target_dl_Nl,
|
||||
.bwp_info = get_pdsch_bwp_start_size(mac, UE),
|
||||
.dmrs_parms = get_dl_dmrs_params(scc, dl_bwp, &tda_info, target_dl_Nl),
|
||||
.time_domain_allocation = tda,
|
||||
.tda_info = tda_info,
|
||||
};
|
||||
sched_ctrl->dl_bler_stats.mcs = target_dl_mcs; /* for logging output */
|
||||
sched_pdsch->tb_size = nr_compute_tbs(sched_pdsch->Qm,
|
||||
sched_pdsch->R,
|
||||
sched_pdsch->rbSize,
|
||||
tda_info.nrOfSymbols,
|
||||
sched_pdsch->dmrs_parms.N_PRB_DMRS * sched_pdsch->dmrs_parms.N_DMRS_SLOT,
|
||||
0 /* N_PRB_oh, 0 for initialBWP */,
|
||||
0 /* tb_scaling */,
|
||||
sched_pdsch->nrOfLayers)
|
||||
>> 3;
|
||||
sched_pdsch.tb_size = nr_compute_tbs(sched_pdsch.Qm,
|
||||
sched_pdsch.R,
|
||||
sched_pdsch.rbSize,
|
||||
tda_info.nrOfSymbols,
|
||||
sched_pdsch.dmrs_parms.N_PRB_DMRS * sched_pdsch.dmrs_parms.N_DMRS_SLOT,
|
||||
0 /* N_PRB_oh, 0 for initialBWP */,
|
||||
0 /* tb_scaling */,
|
||||
target_dl_Nl)
|
||||
>> 3;
|
||||
|
||||
/* get the PID of a HARQ process awaiting retransmission, or -1 otherwise */
|
||||
sched_pdsch->dl_harq_pid = sched_ctrl->retrans_dl_harq.head;
|
||||
post_process_dlsch(mac, pp_pdsch, UE, &sched_pdsch);
|
||||
|
||||
/* mark the corresponding RBs as used */
|
||||
for (int rb = 0; rb < sched_pdsch->rbSize; rb++)
|
||||
vrb_map[rb + sched_pdsch->rbStart + BWPStart] = SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols);
|
||||
|
||||
if ((frame&127) == 0) LOG_D(MAC,"phytest: %d.%d DL mcs %d, DL rbStart %d, DL rbSize %d\n", frame, slot, sched_pdsch->mcs, rbStart,rbSize);
|
||||
for (int rb = 0; rb < sched_pdsch.rbSize; rb++)
|
||||
vrb_map[rb + sched_pdsch.rbStart + BWPStart] = SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols);
|
||||
}
|
||||
|
||||
uint32_t target_ul_mcs = 9;
|
||||
|
||||
@@ -125,6 +125,8 @@ static const uint16_t cqi_table3[16][2] = {{0, 0},
|
||||
static void determine_aggregation_level_search_order(int agg_level_search_order[NUM_PDCCH_AGG_LEVELS],
|
||||
float pdcch_cl_adjust);
|
||||
|
||||
static int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots, int slots_per_frame);
|
||||
|
||||
uint8_t get_dl_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format)
|
||||
{
|
||||
// TODO check this but it should be enough for now
|
||||
@@ -2974,9 +2976,6 @@ NR_UE_info_t *get_new_nr_ue_inst(uid_allocator_t *uia, rnti_t rnti, NR_CellGroup
|
||||
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
sched_ctrl->ta_update = 31;
|
||||
|
||||
/* set illegal time domain allocation to force recomputation of all fields */
|
||||
sched_ctrl->sched_pdsch.time_domain_allocation = -1;
|
||||
|
||||
/* Set default BWPs */
|
||||
AssertFatal(UE->sc_info.n_ul_bwp <= NR_MAX_NUM_BWP, "uplinkBWP_ToAddModList has %d BWP!\n", UE->sc_info.n_ul_bwp);
|
||||
|
||||
@@ -3033,18 +3032,17 @@ bool transition_ra_connected_nr_ue(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE)
|
||||
* To remove the UE, use mac_remove_nr_ue(). */
|
||||
bool add_connected_nr_ue(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE)
|
||||
{
|
||||
NR_SCHED_ENSURE_LOCKED(&nr_mac->sched_lock);
|
||||
|
||||
LOG_I(NR_MAC, "Adding new UE context with RNTI 0x%04x\n", UE->rnti);
|
||||
NR_UEs_t *UE_info = &nr_mac->UE_info;
|
||||
dump_nr_list(UE_info->connected_ue_list);
|
||||
AssertFatal(!UE->ra, "UE in connected cannot have RA process\n");
|
||||
|
||||
NR_SCHED_LOCK(&UE_info->mutex);
|
||||
|
||||
bool success = add_UE_to_list(MAX_MOBILES_PER_GNB, UE_info->connected_ue_list, UE);
|
||||
if (!success) {
|
||||
LOG_E(NR_MAC,"Try to add UE %04x but the list is full\n", UE->rnti);
|
||||
delete_nr_ue_data(UE, NULL, &UE_info->uid_allocator);
|
||||
NR_SCHED_UNLOCK(&UE_info->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3057,7 +3055,6 @@ bool add_connected_nr_ue(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE)
|
||||
init_bler_stats(&nr_mac->dl_bler, &sched_ctrl->dl_bler_stats, nr_mac->frame);
|
||||
init_bler_stats(&nr_mac->ul_bler, &sched_ctrl->ul_bler_stats, nr_mac->frame);
|
||||
|
||||
NR_SCHED_UNLOCK(&UE_info->mutex);
|
||||
dump_nr_list(UE_info->connected_ue_list);
|
||||
return true;
|
||||
}
|
||||
@@ -3110,9 +3107,7 @@ void mac_remove_nr_ue(gNB_MAC_INST *nr_mac, rnti_t rnti)
|
||||
/* already mutex protected */
|
||||
NR_SCHED_ENSURE_LOCKED(&nr_mac->sched_lock);
|
||||
NR_UEs_t *UE_info = &nr_mac->UE_info;
|
||||
NR_SCHED_LOCK(&UE_info->mutex);
|
||||
NR_UE_info_t *UE = remove_UE_from_list(MAX_MOBILES_PER_GNB + 1, UE_info->connected_ue_list, rnti);
|
||||
NR_SCHED_UNLOCK(&UE_info->mutex);
|
||||
if (UE)
|
||||
delete_nr_ue_data(UE, nr_mac->common_channels, &UE_info->uid_allocator);
|
||||
else
|
||||
@@ -3442,7 +3437,7 @@ void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot
|
||||
continue;
|
||||
|
||||
const int slots_frame = nr_mac->frame_structure.numb_slots_frame;
|
||||
const frame_t f = (frame + (slot + mgc->n_slots_advance) / slots_frame) % 1024;
|
||||
const frame_t f = (frame + (slot + mgc->n_slots_advance) / slots_frame) % MAX_FRAME_NUMBER;
|
||||
const slot_t s = (slot + mgc->n_slots_advance) % slots_frame;
|
||||
|
||||
// TS 38 331 - Section 5.5.2.9 Measurement gap configuration
|
||||
@@ -3453,7 +3448,7 @@ void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot
|
||||
// start a timer to stop scheduling UE during MeasGap, or extend timer for
|
||||
// duration of measGap with existing follow-up action
|
||||
if (!nr_timer_is_active(t) || nr_timer_remaining_time(t) < mgc->mgl_slots) {
|
||||
nr_mac_interrupt_ue_transmission(nr_mac, UE, mgc->mgl_slots);
|
||||
nr_mac_interrupt_ue_transmission(nr_mac, UE, mgc->mgl_slots, slots_frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3537,7 +3532,7 @@ int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs)
|
||||
return (delay_ms << scs) + sl_ahead;
|
||||
}
|
||||
|
||||
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots)
|
||||
static int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots, int slots_per_frame)
|
||||
{
|
||||
DevAssert(mac != NULL);
|
||||
DevAssert(UE != NULL);
|
||||
@@ -3546,11 +3541,11 @@ int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int sl
|
||||
nr_timer_setup(&UE->UE_sched_ctrl.transm_interrupt, slots, 1);
|
||||
nr_timer_start(&UE->UE_sched_ctrl.transm_interrupt);
|
||||
|
||||
// it might happen that timing advance command should be sent during the UE
|
||||
// inactivity time. To prevent this, set a variable as if we would have just
|
||||
// sent it. This way, another TA command will for sure be sent in some
|
||||
// frames, after the inactivity of the UE.
|
||||
UE->UE_sched_ctrl.ta_frame = (mac->frame - 1 + 1024) % 1024;
|
||||
// it might happen that timing advance command should be sent during the UE inactivity time.
|
||||
// To prevent this, delay next TA command just after the UE inactivity time.
|
||||
const int inactive_frames = slots / slots_per_frame + 1;
|
||||
if ((UE->UE_sched_ctrl.ta_frame - mac->frame + MAX_FRAME_NUMBER) % MAX_FRAME_NUMBER < inactive_frames)
|
||||
UE->UE_sched_ctrl.ta_frame = (mac->frame + inactive_frames) % MAX_FRAME_NUMBER;
|
||||
|
||||
LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots)\n", UE->rnti, slots);
|
||||
return 0;
|
||||
@@ -3968,23 +3963,6 @@ void nr_mac_trigger_reconfiguration(const gNB_MAC_INST *nrmac, const NR_UE_info_
|
||||
free_cellGroupConfig(cellGroup_for_UE);
|
||||
}
|
||||
|
||||
/* \brief add bearers from CellGroupConfig.
|
||||
*
|
||||
* This is a kind of hack, as this should be processed through a F1 UE Context
|
||||
* setup request, but some modes do not use that (NSA/do-ra/phy_test). */
|
||||
void process_addmod_bearers_cellGroupConfig(NR_UE_sched_ctrl_t *sched_ctrl, const struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod)
|
||||
{
|
||||
if (addmod == NULL)
|
||||
return; /* nothing to do */
|
||||
|
||||
for (int i = 0; i < addmod->list.count; ++i) {
|
||||
const NR_RLC_BearerConfig_t *conf = addmod->list.array[i];
|
||||
int lcid = conf->logicalChannelIdentity;
|
||||
nr_lc_config_t c = {.lcid = lcid};
|
||||
nr_mac_add_lcid(sched_ctrl, &c);
|
||||
}
|
||||
}
|
||||
|
||||
long get_lcid_from_drbid(int drb_id)
|
||||
{
|
||||
return drb_id + 3; /* LCID is DRB + 3 */
|
||||
|
||||
@@ -589,11 +589,13 @@ static void evaluate_rsrp_report(gNB_MAC_INST *nrmac,
|
||||
|
||||
rsrp_report->nr_reports = csi_report->CSI_report_bitlen.nb_ssbri_cri;
|
||||
int bitlen = csi_report->CSI_report_bitlen.cri_ssbri_bitlen;
|
||||
uint8_t curr_payload = pickandreverse_bits(payload, bitlen, *cumul_bits);
|
||||
rsrp_report->resource_id[0] = *(index_list[bitlen > 0 ? ((curr_payload) & ~(~1U << (bitlen - 1))) : bitlen]);
|
||||
*cumul_bits += bitlen;
|
||||
for (int i = 0; i < rsrp_report->nr_reports; i++) {
|
||||
uint8_t idx_payload = pickandreverse_bits(payload, bitlen, *cumul_bits);
|
||||
rsrp_report->resource_id[i] = *(index_list[bitlen > 0 ? ((idx_payload) & ~(~1U << (bitlen - 1))) : bitlen]);
|
||||
*cumul_bits += bitlen;
|
||||
}
|
||||
|
||||
curr_payload = pickandreverse_bits(payload, 7, *cumul_bits);
|
||||
uint8_t curr_payload = pickandreverse_bits(payload, 7, *cumul_bits);
|
||||
int rsrp = curr_payload & 0x7f;
|
||||
*cumul_bits += 7;
|
||||
csi_report->nb_of_csi_ssb_report++;
|
||||
@@ -605,10 +607,6 @@ static void evaluate_rsrp_report(gNB_MAC_INST *nrmac,
|
||||
}
|
||||
|
||||
for (int i = 1; i < rsrp_report->nr_reports; i++) {
|
||||
curr_payload = pickandreverse_bits(payload, bitlen, *cumul_bits);
|
||||
rsrp_report->resource_id[i] = *(index_list[bitlen > 0 ? ((curr_payload) & ~(~1U << (bitlen - 1))) : bitlen]);
|
||||
*cumul_bits += bitlen;
|
||||
|
||||
curr_payload = pickandreverse_bits(payload, 4, *cumul_bits);
|
||||
csi_report->nb_of_csi_ssb_report++;
|
||||
rsrp_report->RSRP[i] = get_diff_rsrp(curr_payload & 0x0f, rsrp_report->RSRP[0]);
|
||||
@@ -680,6 +678,8 @@ static void evaluate_cqi_report(uint8_t *payload,
|
||||
const int cqi_idx = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb;
|
||||
const int mcs_table = UE->current_DL_BWP.mcsTableIdx;
|
||||
sched_ctrl->dl_max_mcs = get_mcs_from_cqi(mcs_table, cqi_Table, cqi_idx);
|
||||
|
||||
LOG_D(MAC, "Reported CQI = %d, dl_max_mcs %d\n", temp_cqi, sched_ctrl->dl_max_mcs);
|
||||
}
|
||||
|
||||
static uint8_t evaluate_pmi_report(uint8_t *payload,
|
||||
|
||||
@@ -725,13 +725,6 @@ static void handle_nr_ul_harq(gNB_MAC_INST *nrmac,
|
||||
|
||||
static void handle_msg3_failed_rx(gNB_MAC_INST *mac, NR_RA_t *ra, rnti_t rnti, int harq_round_max)
|
||||
{
|
||||
// for CFRA (NSA) do not schedule retransmission of msg3
|
||||
if (ra->cfra) {
|
||||
LOG_W(NR_MAC, "UE %04x RA failed at state %s (NSA msg3 reception failed)\n", rnti, nrra_text[ra->ra_state]);
|
||||
nr_release_ra_UE(mac, rnti);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ra->msg3_round >= harq_round_max - 1) {
|
||||
LOG_W(NR_MAC, "UE %04x RA failed at state %s (Reached msg3 max harq rounds)\n", rnti, nrra_text[ra->ra_state]);
|
||||
nr_release_ra_UE(mac, rnti);
|
||||
@@ -756,6 +749,7 @@ static void nr_rx_ra_sdu(const module_id_t mod_id,
|
||||
const uint16_t rssi)
|
||||
{
|
||||
gNB_MAC_INST *mac = RC.nrmac[mod_id];
|
||||
NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
|
||||
NR_UE_info_t *UE = find_ra_UE(&mac->UE_info, rnti);
|
||||
if (!UE) {
|
||||
LOG_E(NR_MAC, "UL SDU discarded. Couldn't finde UE with RNTI %04x \n", rnti);
|
||||
@@ -768,8 +762,36 @@ static void nr_rx_ra_sdu(const module_id_t mod_id,
|
||||
return;
|
||||
}
|
||||
|
||||
// CFRA: we scheduled Msg3 (which does not exist in CFRA, see also
|
||||
// nr_generate_Msg2()). We did not mark RA as complete right away, as the
|
||||
// DLSCH scheduler might schedule in the same slot as Msg2 if RLC has data
|
||||
// (which can only happen in do-ra), so we mark it as complete now.
|
||||
bool cfra = ra->cfra;
|
||||
if (ra->cfra) {
|
||||
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
nr_mac_reset_ul_failure(sched_ctrl);
|
||||
reset_dl_harq_list(sched_ctrl);
|
||||
reset_ul_harq_list(sched_ctrl);
|
||||
// we configure the UE using common search space with DCIX0 while waiting for a reconfiguration in SA
|
||||
// in NSA (or do-ra) there is no reconfiguration in NR
|
||||
int ss_type = IS_SA_MODE(get_softmodem_params()) ? NR_SearchSpace__searchSpaceType_PR_common
|
||||
: NR_SearchSpace__searchSpaceType_PR_ue_Specific;
|
||||
configure_UE_BWP(mac, scc, UE, false, ss_type, -1, -1);
|
||||
// initialize ta_frame in case there is no Msg3 received
|
||||
UE->UE_sched_ctrl.ta_frame = (frame + 100) % MAX_FRAME_NUMBER;
|
||||
if (!transition_ra_connected_nr_ue(mac, UE)) {
|
||||
LOG_E(NR_MAC, "cannot add UE %04x: list is full\n", UE->rnti);
|
||||
delete_nr_ue_data(UE, NULL, &mac->UE_info.uid_allocator);
|
||||
} else {
|
||||
LOG_A(NR_MAC, "(rnti 0x%04x) CFRA procedure succeeded!\n", UE->rnti);
|
||||
}
|
||||
}
|
||||
|
||||
const int target_snrx10 = mac->pusch_target_snrx10;
|
||||
if (!sdu) { // NACK
|
||||
if (cfra) // no Msg3 on CFRA, no problem
|
||||
return;
|
||||
|
||||
if (ra->ra_state != nrRA_WAIT_Msg3)
|
||||
return;
|
||||
|
||||
@@ -799,10 +821,11 @@ static void nr_rx_ra_sdu(const module_id_t mod_id,
|
||||
|
||||
if (no_sig) {
|
||||
LOG_W(NR_MAC, "MSG3 ULSCH with no signal\n");
|
||||
handle_msg3_failed_rx(mac, ra, rnti, mac->ul_bler.harq_round_max);
|
||||
if (!cfra)
|
||||
handle_msg3_failed_rx(mac, ra, rnti, mac->ul_bler.harq_round_max);
|
||||
return;
|
||||
}
|
||||
if (ra->ra_type == RA_2_STEP) {
|
||||
if (!cfra && ra->ra_type == RA_2_STEP) {
|
||||
// random access pusch with RA-RNTI
|
||||
if (ra->RA_rnti != rnti) {
|
||||
LOG_E(NR_MAC, "expected TC_RNTI %04x to match current RNTI %04x\n", ra->RA_rnti, rnti);
|
||||
@@ -811,7 +834,7 @@ static void nr_rx_ra_sdu(const module_id_t mod_id,
|
||||
}
|
||||
|
||||
// re-initialize ta update variables after RA procedure completion
|
||||
UE->UE_sched_ctrl.ta_frame = frame;
|
||||
UE->UE_sched_ctrl.ta_frame = (frame + 100) % MAX_FRAME_NUMBER;
|
||||
|
||||
LOG_A(NR_MAC, "%4d.%2d PUSCH with TC_RNTI 0x%04x received correctly\n", frame, slot, rnti);
|
||||
|
||||
@@ -826,94 +849,76 @@ static void nr_rx_ra_sdu(const module_id_t mod_id,
|
||||
UE_scheduling_control->ta_update = timing_advance;
|
||||
UE_scheduling_control->raw_rssi = rssi;
|
||||
LOG_D(NR_MAC, "[UE %04x] PUSCH TPC %d and TA %d\n", UE->rnti, UE_scheduling_control->tpc0, UE_scheduling_control->ta_update);
|
||||
NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
|
||||
if (ra->cfra) {
|
||||
LOG_A(NR_MAC, "(rnti 0x%04x) CFRA procedure succeeded!\n", UE->rnti);
|
||||
nr_mac_reset_ul_failure(UE_scheduling_control);
|
||||
reset_dl_harq_list(UE_scheduling_control);
|
||||
reset_ul_harq_list(UE_scheduling_control);
|
||||
process_addmod_bearers_cellGroupConfig(&UE->UE_sched_ctrl, UE->CellGroup->rlc_BearerToAddModList);
|
||||
int ss_type;
|
||||
// we configure the UE using common search space with DCIX0 while waiting for a reconfiguration in SA
|
||||
// in NSA (or do-ra) there is no reconfiguration in NR
|
||||
if (IS_SA_MODE(get_softmodem_params()))
|
||||
ss_type = NR_SearchSpace__searchSpaceType_PR_common;
|
||||
else
|
||||
ss_type = NR_SearchSpace__searchSpaceType_PR_ue_Specific;
|
||||
configure_UE_BWP(mac, scc, UE, false, ss_type, -1, -1);
|
||||
if (!transition_ra_connected_nr_ue(mac, UE)) {
|
||||
LOG_E(NR_MAC, "cannot add UE %04x: list is full\n", UE->rnti);
|
||||
delete_nr_ue_data(UE, NULL, &mac->UE_info.uid_allocator);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
LOG_D(NR_MAC, "[RAPROC] Received %s:\n", ra->ra_type == RA_2_STEP ? "MsgA-PUSCH" : "Msg3");
|
||||
for (uint32_t k = 0; k < sdu_len; k++) {
|
||||
LOG_D(NR_MAC, "(%i): 0x%x\n", k, sdu[k]);
|
||||
}
|
||||
|
||||
// 3GPP TS 38.321 Section 5.4.3 Multiplexing and assembly
|
||||
// Logical channels shall be prioritised in accordance with the following order (highest priority listed first):
|
||||
// - MAC CE for C-RNTI, or data from UL-CCCH;
|
||||
// This way, we need to process MAC CE for C-RNTI if RA is active and it is present in the MAC PDU
|
||||
// Search for MAC CE for C-RNTI
|
||||
rnti_t crnti = lcid_crnti_lookahead(sdu, sdu_len);
|
||||
if (crnti != 0) { // 3GPP TS 38.321 Table 7.1-1: RNTI values, RNTI 0x0000: N/A
|
||||
// Replace the current UE by the UE identified by C-RNTI
|
||||
NR_UE_info_t *old_UE = find_nr_UE(&mac->UE_info, crnti);
|
||||
if (!old_UE) {
|
||||
// The UE identified by C-RNTI no longer exists at the gNB
|
||||
// Let's abort the current RA, so the UE will trigger a new RA later but using RRCSetupRequest instead. A better
|
||||
// solution may be implemented
|
||||
LOG_W(NR_MAC, "No UE found with C-RNTI %04x, ignoring Msg3 to have UE come back with new RA attempt\n", UE->rnti);
|
||||
nr_release_ra_UE(mac, rnti);
|
||||
return;
|
||||
}
|
||||
// in case UE beam has changed
|
||||
old_UE->UE_beam_index = UE->UE_beam_index;
|
||||
// Reset UL failure for old UE
|
||||
nr_mac_reset_ul_failure(&old_UE->UE_sched_ctrl);
|
||||
// Reset HARQ processes
|
||||
reset_dl_harq_list(&old_UE->UE_sched_ctrl);
|
||||
reset_ul_harq_list(&old_UE->UE_sched_ctrl);
|
||||
LOG_D(NR_MAC, "[RAPROC] Received %s:\n", ra->ra_type == RA_2_STEP ? "MsgA-PUSCH" : "Msg3");
|
||||
for (uint32_t k = 0; k < sdu_len; k++) {
|
||||
LOG_D(NR_MAC, "(%i): 0x%x\n", k, sdu[k]);
|
||||
}
|
||||
|
||||
// Only trigger RRCReconfiguration if UE is not performing RRCReestablishment
|
||||
// The RRCReconfiguration will be triggered by the RRCReestablishmentComplete
|
||||
if (!old_UE->reconfigSpCellConfig) {
|
||||
LOG_I(NR_MAC, "Received UL_SCH_LCID_C_RNTI with C-RNTI 0x%04x, triggering RRC Reconfiguration\n", crnti);
|
||||
// Trigger RRCReconfiguration
|
||||
nr_mac_trigger_reconfiguration(mac, old_UE, -1);
|
||||
// we configure the UE using common search space with DCIX0 while waiting for a reconfiguration
|
||||
configure_UE_BWP(mac, scc, old_UE, false, NR_SearchSpace__searchSpaceType_PR_common, -1, -1);
|
||||
}
|
||||
// 3GPP TS 38.321 Section 5.4.3 Multiplexing and assembly
|
||||
// Logical channels shall be prioritised in accordance with the following order (highest priority listed first):
|
||||
// - MAC CE for C-RNTI, or data from UL-CCCH;
|
||||
// This way, we need to process MAC CE for C-RNTI if RA is active and it is present in the MAC PDU
|
||||
// Search for MAC CE for C-RNTI
|
||||
rnti_t crnti = lcid_crnti_lookahead(sdu, sdu_len);
|
||||
if (crnti != 0) { // 3GPP TS 38.321 Table 7.1-1: RNTI values, RNTI 0x0000: N/A
|
||||
// Replace the current UE by the UE identified by C-RNTI
|
||||
NR_UE_info_t *old_UE = find_nr_UE(&mac->UE_info, crnti);
|
||||
if (!old_UE) {
|
||||
// The UE identified by C-RNTI no longer exists at the gNB
|
||||
// Let's abort the current RA, so the UE will trigger a new RA later but using RRCSetupRequest instead. A better
|
||||
// solution may be implemented
|
||||
LOG_W(NR_MAC, "No UE found with C-RNTI %04x, ignoring Msg3 to have UE come back with new RA attempt\n", UE->rnti);
|
||||
nr_release_ra_UE(mac, rnti);
|
||||
LOG_A(NR_MAC, "%4d.%2d RA with C-RNTI %04x complete\n", frame, slot, crnti);
|
||||
|
||||
// Decode the entire MAC PDU
|
||||
// It may have multiple MAC subPDUs, for example, a MAC subPDU with LCID 1 caring a RRCReestablishmentComplete
|
||||
nr_process_mac_pdu(mod_id, old_UE, CC_id, frame, slot, sdu, sdu_len, -1);
|
||||
return;
|
||||
}
|
||||
// in case UE beam has changed
|
||||
old_UE->UE_beam_index = UE->UE_beam_index;
|
||||
// Reset UL failure for old UE
|
||||
nr_mac_reset_ul_failure(&old_UE->UE_sched_ctrl);
|
||||
// Reset HARQ processes
|
||||
reset_dl_harq_list(&old_UE->UE_sched_ctrl);
|
||||
reset_ul_harq_list(&old_UE->UE_sched_ctrl);
|
||||
|
||||
// UE Contention Resolution Identity
|
||||
// Store the first 48 bits belonging to the uplink CCCH SDU within Msg3 to fill in Msg4
|
||||
// First byte corresponds to R/LCID MAC sub-header
|
||||
memcpy(ra->cont_res_id, &sdu[1], sizeof(uint8_t) * 6);
|
||||
// Only trigger RRCReconfiguration if UE is not performing RRCReestablishment
|
||||
// The RRCReconfiguration will be triggered by the RRCReestablishmentComplete
|
||||
if (!old_UE->reconfigSpCellConfig) {
|
||||
LOG_I(NR_MAC, "Received UL_SCH_LCID_C_RNTI with C-RNTI 0x%04x, triggering RRC Reconfiguration\n", crnti);
|
||||
// Trigger RRCReconfiguration
|
||||
nr_mac_trigger_reconfiguration(mac, old_UE, -1);
|
||||
// we configure the UE using common search space with DCIX0 while waiting for a reconfiguration
|
||||
configure_UE_BWP(mac, scc, old_UE, false, NR_SearchSpace__searchSpaceType_PR_common, -1, -1);
|
||||
}
|
||||
nr_release_ra_UE(mac, rnti);
|
||||
LOG_A(NR_MAC, "%4d.%2d RA with C-RNTI %04x complete\n", frame, slot, crnti);
|
||||
|
||||
// Decode MAC PDU
|
||||
// the function is only called to decode the contention resolution sub-header
|
||||
// harq_pid set a non-valid value because it is not used in this call
|
||||
nr_process_mac_pdu(mod_id, UE, CC_id, frame, slot, sdu, sdu_len, -1);
|
||||
|
||||
LOG_I(NR_MAC,
|
||||
"Activating scheduling %s for TC_RNTI 0x%04x (state %s)\n",
|
||||
ra->ra_type == RA_2_STEP ? "MsgB" : "Msg4",
|
||||
UE->rnti,
|
||||
nrra_text[ra->ra_state]);
|
||||
ra->ra_state = ra->ra_type == RA_2_STEP ? nrRA_MsgB : nrRA_Msg4;
|
||||
LOG_D(NR_MAC, "TC_RNTI 0x%04x next RA state %s\n", UE->rnti, nrra_text[ra->ra_state]);
|
||||
// Decode the entire MAC PDU
|
||||
// It may have multiple MAC subPDUs, for example, a MAC subPDU with LCID 1 caring a RRCReestablishmentComplete
|
||||
nr_process_mac_pdu(mod_id, old_UE, CC_id, frame, slot, sdu, sdu_len, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfra)
|
||||
return; // rest not relevant for CFRA
|
||||
|
||||
// UE Contention Resolution Identity
|
||||
// Store the first 48 bits belonging to the uplink CCCH SDU within Msg3 to fill in Msg4
|
||||
// First byte corresponds to R/LCID MAC sub-header
|
||||
memcpy(ra->cont_res_id, &sdu[1], sizeof(uint8_t) * 6);
|
||||
|
||||
// Decode MAC PDU
|
||||
// the function is only called to decode the contention resolution sub-header
|
||||
// harq_pid set a non-valid value because it is not used in this call
|
||||
nr_process_mac_pdu(mod_id, UE, CC_id, frame, slot, sdu, sdu_len, -1);
|
||||
|
||||
LOG_I(NR_MAC,
|
||||
"Activating scheduling %s for TC_RNTI 0x%04x (state %s)\n",
|
||||
ra->ra_type == RA_2_STEP ? "MsgB" : "Msg4",
|
||||
UE->rnti,
|
||||
nrra_text[ra->ra_state]);
|
||||
ra->ra_state = ra->ra_type == RA_2_STEP ? nrRA_MsgB : nrRA_Msg4;
|
||||
LOG_D(NR_MAC, "TC_RNTI 0x%04x next RA state %s\n", UE->rnti, nrra_text[ra->ra_state]);
|
||||
return;
|
||||
}
|
||||
|
||||
static void _nr_rx_sdu(const module_id_t gnb_mod_idP,
|
||||
@@ -1834,6 +1839,10 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac,
|
||||
const uint16_t slbitmap = SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols);
|
||||
while (rbStart < bwpSize && (rballoc_mask[rbStart + bwpStart] & slbitmap))
|
||||
rbStart++;
|
||||
if (rbStart >= bwpSize) {
|
||||
LOG_D(NR_MAC, "[UE %04x][%4d.%2d] could not allocate UL retransmission: no resources\n", UE->rnti, frame, slot);
|
||||
return false;
|
||||
}
|
||||
int rbSize = 0;
|
||||
while (rbStart + rbSize < bwpSize && !(rballoc_mask[rbStart + bwpStart + rbSize] & slbitmap))
|
||||
rbSize++;
|
||||
@@ -1909,7 +1918,6 @@ static bool allocate_ul_retransmission(gNB_MAC_INST *nrmac,
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t ul_pf_tbs[5][29]; // pre-computed, approximate TBS values for PF coefficient
|
||||
typedef struct UEsched_s {
|
||||
float coef;
|
||||
bool sched_inactive;
|
||||
@@ -2065,8 +2073,8 @@ static int pf_ul(gNB_MAC_INST *nrmac,
|
||||
const int max_mcs_table = (current_BWP->mcs_table == 0 || current_BWP->mcs_table == 2) ? 28 : 27;
|
||||
const int max_mcs = min(bo->max_mcs, max_mcs_table); /* no per-user maximum MCS yet */
|
||||
int selected_mcs;
|
||||
int nrOfLayers = get_ul_nrOfLayers(sched_ctrl, current_BWP->dci_format);
|
||||
if (bo->harq_round_max == 1) {
|
||||
int nrOfLayers = get_ul_nrOfLayers(sched_ctrl, current_BWP->dci_format);
|
||||
selected_mcs = get_mcs_from_SINRx10(current_BWP->mcs_table, sched_ctrl->pusch_snrx10, nrOfLayers);
|
||||
selected_mcs = min(max_mcs, selected_mcs);
|
||||
selected_mcs = max(bo->min_mcs, selected_mcs);
|
||||
@@ -2078,7 +2086,17 @@ static int pf_ul(gNB_MAC_INST *nrmac,
|
||||
|
||||
/* Create UE_sched for UEs eligibale for new data transmission*/
|
||||
/* Calculate coefficient*/
|
||||
const uint32_t tbs = ul_pf_tbs[current_BWP->mcs_table][selected_mcs];
|
||||
const uint8_t Qm = nr_get_Qm_ul(selected_mcs, current_BWP->mcs_table);
|
||||
const uint16_t R = nr_get_code_rate_ul(selected_mcs, current_BWP->mcs_table);
|
||||
const uint32_t tbs = nr_compute_tbs(Qm,
|
||||
R,
|
||||
1, /* rbSize */
|
||||
10, /* hypothetical number of slots */
|
||||
0, /* N_PRB_DMRS * N_DMRS_SLOT */
|
||||
0 /* N_PRB_oh, 0 for initialBWP */,
|
||||
0 /* tb_scaling */,
|
||||
nrOfLayers)
|
||||
>> 3;
|
||||
float coeff_ue = (float) tbs / UE->ul_thr_ue;
|
||||
bool sched_inactive = B == 0 && do_sched;
|
||||
LOG_D(NR_MAC, "[UE %04x][%4d.%2d] b %d, ul_thr_ue %f, tbs %d, coeff_ue %f, sched_inactive %d\n",
|
||||
@@ -2155,13 +2173,16 @@ static int pf_ul(gNB_MAC_INST *nrmac,
|
||||
bwp_info_t bi = get_pusch_bwp_start_size(iterator->UE);
|
||||
while (rbStart < bi.bwpSize && (rballoc_mask[rbStart + bi.bwpStart] & slbitmap))
|
||||
rbStart++;
|
||||
|
||||
/* if it's for inactivity, min_grant_prb is enough, otherwise check what
|
||||
* would be the maximum */
|
||||
uint16_t max_rbSize = iterator->sched_inactive ? min_rb : bi.bwpSize;
|
||||
/* uint16_t max_rbSize = 10; */
|
||||
/* uint16_t max_rbSize = iterator->sched_inactive ? min_rb : bi.bwpSize; */
|
||||
uint16_t max_rbSize = min_rb;
|
||||
uint16_t available_rb = 1;
|
||||
while (rbStart + available_rb < bi.bwpSize && !(rballoc_mask[rbStart + bi.bwpStart + available_rb] & slbitmap) && available_rb < max_rbSize)
|
||||
available_rb++;
|
||||
available_rb++;
|
||||
|
||||
/* LOG_I(NR_MAC, "[UE %04x] RB size: %u\n", iterator->UE->rnti, available_rb); */
|
||||
|
||||
if (rbStart + min_rb > bi.bwpSize || available_rb < min_rb) {
|
||||
reset_beam_status(&nrmac->beam_info, frame, slot, iterator->UE->UE_beam_index, slots_per_frame, dci_beam.new_beam);
|
||||
@@ -2363,10 +2384,9 @@ nfapi_nr_pusch_pdu_t *prepare_pusch_pdu(nfapi_nr_ul_tti_request_t *future_ul_tti
|
||||
long *maxMIMO_Layers = UE->sc_info.maxMIMO_Layers_PUSCH;
|
||||
if (!maxMIMO_Layers && ul_bwp && ul_bwp->pusch_Config)
|
||||
maxMIMO_Layers = ul_bwp->pusch_Config->maxRank;
|
||||
long lbrm_layers = maxMIMO_Layers ? *maxMIMO_Layers : ue_supported_ul_layers(UE->capability);
|
||||
AssertFatal (maxMIMO_Layers != NULL,"Option with max MIMO layers not configured is not supported\n");
|
||||
pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(ul_bwp->mcs_table,
|
||||
UE->sc_info.ul_bw_tbslbrm,
|
||||
*maxMIMO_Layers);
|
||||
pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes = nr_compute_tbslbrm(ul_bwp->mcs_table, UE->sc_info.ul_bw_tbslbrm, lbrm_layers);
|
||||
}
|
||||
/* PUSCH PTRS */
|
||||
if (sched_pusch->dmrs_info.ptrsConfig) {
|
||||
@@ -2443,6 +2463,7 @@ void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE
|
||||
}
|
||||
UE->mac_stats.ul.current_bytes = sched_pusch->tb_size;
|
||||
UE->mac_stats.ul.current_rbs = sched_pusch->rbSize;
|
||||
nr_mac->mac_stats.ul.used_prb_aggregate += sched_pusch->rbSize;
|
||||
sched_ctrl->last_ul_frame = sched_pusch->frame;
|
||||
sched_ctrl->last_ul_slot = sched_pusch->slot;
|
||||
|
||||
@@ -2685,6 +2706,7 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp
|
||||
int tda = seq_arr_dist(&nr_mac->ul_tda, seq_arr_front(&nr_mac->ul_tda), tda_info);
|
||||
AssertFatal(tda >= 0 && tda < 16, "illegal TDA index %d\n", tda);
|
||||
|
||||
nr_mac->mac_stats.ul.total_prb_aggregate += bw;
|
||||
int len[num_beams];
|
||||
for (int i = 0; i < num_beams; i++)
|
||||
len[i] = rb_len;
|
||||
@@ -2704,32 +2726,6 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp
|
||||
|
||||
nr_pp_impl_ul nr_init_ulsch_preprocessor(int CC_id)
|
||||
{
|
||||
/* during initialization: no mutex needed */
|
||||
/* in the PF algorithm, we have to use the TBsize to compute the coefficient.
|
||||
* This would include the number of DMRS symbols, which in turn depends on
|
||||
* the time domain allocation. In case we are in a mixed slot, we do not want
|
||||
* to recalculate all these values, and therefore we provide a look-up table
|
||||
* which should approximately(!) give us the TBsize. In particular, the
|
||||
* number of symbols, the number of DMRS symbols, and the exact Qm and R, are
|
||||
* not correct*/
|
||||
for (int mcsTableIdx = 0; mcsTableIdx < 5; ++mcsTableIdx) {
|
||||
for (int mcs = 0; mcs < 29; ++mcs) {
|
||||
if (mcs > 27 && (mcsTableIdx == 1 || mcsTableIdx == 3 || mcsTableIdx == 4))
|
||||
continue;
|
||||
const uint8_t Qm = nr_get_Qm_ul(mcs, mcsTableIdx);
|
||||
const uint16_t R = nr_get_code_rate_ul(mcs, mcsTableIdx);
|
||||
/* note: we do not update R/Qm based on low MCS or pi2BPSK */
|
||||
ul_pf_tbs[mcsTableIdx][mcs] = nr_compute_tbs(Qm,
|
||||
R,
|
||||
1, /* rbSize */
|
||||
10, /* hypothetical number of slots */
|
||||
0, /* N_PRB_DMRS * N_DMRS_SLOT */
|
||||
0 /* N_PRB_oh, 0 for initialBWP */,
|
||||
0 /* tb_scaling */,
|
||||
1 /* nrOfLayers */)
|
||||
>> 3;
|
||||
}
|
||||
}
|
||||
return nr_ulsch_preprocessor;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,16 +65,9 @@ void nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, NR_UE_info_t *UE);
|
||||
bool add_new_UE_RA(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE);
|
||||
int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs);
|
||||
|
||||
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots);
|
||||
int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info);
|
||||
|
||||
void clear_nr_nfapi_information(gNB_MAC_INST *gNB,
|
||||
int CC_idP,
|
||||
frame_t frameP,
|
||||
slot_t slotP,
|
||||
nfapi_nr_dl_tti_request_t *DL_req,
|
||||
nfapi_nr_tx_data_request_t *TX_req,
|
||||
nfapi_nr_ul_dci_request_t *UL_dci_req);
|
||||
void clear_nr_nfapi_information(gNB_MAC_INST *gNB, int CC_idP, frame_t frameP, slot_t slotP);
|
||||
|
||||
void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot);
|
||||
|
||||
@@ -145,7 +138,7 @@ uint16_t nr_mac_compute_RIV(uint16_t N_RB_DL, uint16_t RBstart, uint16_t Lcrbs);
|
||||
|
||||
/* \brief preprocessor for phytest: schedules UE_id 0 with fixed MCS on all
|
||||
* freq resources */
|
||||
void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, slot_t slot);
|
||||
void nr_preprocessor_phytest(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch);
|
||||
/* \brief UL preprocessor for phytest: schedules UE_id 0 with fixed MCS on a
|
||||
* fixed set of resources */
|
||||
void nr_ul_preprocessor_phytest(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp_pusch);
|
||||
@@ -487,9 +480,6 @@ bool nr_mac_check_ul_failure(gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *
|
||||
|
||||
void nr_mac_trigger_reconfiguration(const gNB_MAC_INST *nrmac, const NR_UE_info_t *UE, int new_bwp_id);
|
||||
|
||||
void process_addmod_bearers_cellGroupConfig(NR_UE_sched_ctrl_t *sched_ctrl,
|
||||
const struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod);
|
||||
|
||||
bool nr_mac_add_lcid(NR_UE_sched_ctrl_t *sched_ctrl, const nr_lc_config_t *c);
|
||||
nr_lc_config_t *nr_mac_get_lc_config(NR_UE_sched_ctrl_t* sched_ctrl, int lcid);
|
||||
bool nr_mac_remove_lcid(NR_UE_sched_ctrl_t *sched_ctrl, long lcid);
|
||||
@@ -504,5 +494,6 @@ void prepare_du_configuration_update(gNB_MAC_INST *mac,
|
||||
|
||||
void nr_mac_clean_cellgroup(NR_CellGroupConfig_t *cell_group);
|
||||
|
||||
void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE_info_t *UE, NR_sched_pdsch_t *sched_pdsch);
|
||||
void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE_info_t *UE, NR_sched_pusch_t *sched_pusch);
|
||||
#endif /*__LAYER2_NR_MAC_PROTO_H__*/
|
||||
|
||||
@@ -329,7 +329,7 @@ static int handle_ue_context_drbs_setup(NR_UE_info_t *UE,
|
||||
*rlc_BearerConfig->rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength = NR_SN_FieldLengthAM_size12;
|
||||
*rlc_BearerConfig->rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength = NR_SN_FieldLengthAM_size12;
|
||||
}
|
||||
|
||||
AssertFatal(rlc_BearerConfig->rlc_Config, "We expect rlc-Config to be always present when we configure a DRB\n");
|
||||
nr_rlc_add_drb(UE->rnti, drb->id, rlc_BearerConfig);
|
||||
|
||||
nr_lc_config_t c = {.lcid = rlc_BearerConfig->logicalChannelIdentity, .nssai = drb->nr.nssai};
|
||||
@@ -381,12 +381,9 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE,
|
||||
{
|
||||
DevAssert(req_drbs != NULL && cellGroupConfig != NULL);
|
||||
instance_t f1inst = get_f1_gtp_instance();
|
||||
|
||||
cellGroupConfig->rlc_BearerToReleaseList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToReleaseList));
|
||||
AssertFatal(cellGroupConfig->rlc_BearerToReleaseList != NULL, "out of memory\n");
|
||||
|
||||
/* Note: the actual GTP tunnels are already removed in the F1AP message
|
||||
* decoding */
|
||||
for (int i = 0; i < drbs_len; i++) {
|
||||
const f1ap_drb_to_release_t *drb = &req_drbs[i];
|
||||
|
||||
@@ -401,7 +398,7 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE,
|
||||
if (idx < cellGroupConfig->rlc_BearerToAddModList->list.count) {
|
||||
nr_mac_remove_lcid(&UE->UE_sched_ctrl, lcid);
|
||||
nr_rlc_release_entity(UE->rnti, lcid);
|
||||
if (f1inst >= 0)
|
||||
if (f1inst >= 0) /* Delete F1 tunnel */
|
||||
newGtpuDeleteOneTunnel(f1inst, UE->rnti, drb->id);
|
||||
asn_sequence_del(&cellGroupConfig->rlc_BearerToAddModList->list, idx, 1);
|
||||
long *plcid = malloc(sizeof(*plcid));
|
||||
@@ -684,11 +681,9 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
/* new UE: tell the UE to reestablish RLC */
|
||||
struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod = new_CellGroup->rlc_BearerToAddModList;
|
||||
for (int i = 0; i < addmod->list.count; ++i) {
|
||||
asn1cCallocOne(addmod->list.array[i]->reestablishRLC, NR_RLC_BearerConfig__reestablishRLC_true);
|
||||
for (int i = 1; i < seq_arr_size(&UE->UE_sched_ctrl.lc_config); ++i) {
|
||||
nr_lc_config_t *lc_config = seq_arr_at(&UE->UE_sched_ctrl.lc_config, i);
|
||||
nr_rlc_reestablish_entity(UE->rnti, lc_config->lcid);
|
||||
}
|
||||
NR_RLC_BearerConfig_t *bc = addmod->list.array[i];
|
||||
asn1cCallocOne(bc->reestablishRLC, NR_RLC_BearerConfig__reestablishRLC_true);
|
||||
nr_rlc_reestablish_entity(UE->rnti, bc->logicalChannelIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -967,6 +962,8 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc)
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup);
|
||||
UE->CellGroup = oldUE->CellGroup;
|
||||
oldUE->CellGroup = NULL;
|
||||
UE->capability = oldUE->capability;
|
||||
oldUE->capability = NULL;
|
||||
UE->mac_stats = oldUE->mac_stats;
|
||||
UE->measgap_config = oldUE->measgap_config;
|
||||
/* 38.331 5.3.7.2 says that the UE releases the spCellConfig, so we drop it
|
||||
@@ -978,8 +975,15 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc)
|
||||
pthread_mutex_unlock(&mac->sched_lock);
|
||||
nr_rlc_remove_ue(dl_rrc->gNB_DU_ue_id);
|
||||
nr_rlc_update_id(*dl_rrc->old_gNB_DU_ue_id, dl_rrc->gNB_DU_ue_id);
|
||||
/* 38.331 clause 5.3.7.4: apply the specified configuration defined in 9.2.1 for SRB1 */
|
||||
nr_rlc_reconfigure_entity(dl_rrc->gNB_DU_ue_id, 1, NULL);
|
||||
/* 38.331 clause 5.3.7.4: apply gNB RLC configuration for SRB1 to match the UE RLC configuration defined in 9.2.1 */
|
||||
nr_rlc_configuration_t rlc_configuration = mac->rlc_config; // use configuration file values for timers t_poll_retransmit, t_reassembly and t_status_prohibit
|
||||
rlc_configuration.srb.poll_pdu = -1;
|
||||
rlc_configuration.srb.poll_byte = -1;
|
||||
rlc_configuration.srb.max_retx_threshold = 8;
|
||||
rlc_configuration.srb.sn_field_length = 12;
|
||||
NR_RLC_Config_t *rlc_Config = nr_srb_config(&rlc_configuration);
|
||||
nr_rlc_reconfigure_entity(dl_rrc->gNB_DU_ue_id, 1, rlc_Config);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_RLC_Config, rlc_Config);
|
||||
instance_t f1inst = get_f1_gtp_instance();
|
||||
if (f1inst >= 0) // we actually use F1-U
|
||||
gtpv1u_update_ue_id(f1inst, *dl_rrc->old_gNB_DU_ue_id, dl_rrc->gNB_DU_ue_id);
|
||||
|
||||
@@ -87,7 +87,6 @@ void *nrmac_stats_thread(void *arg) {
|
||||
char *p = output;
|
||||
NR_SCHED_LOCK(&gNB->sched_lock);
|
||||
p += dump_mac_stats(gNB, p, end - p, false);
|
||||
NR_SCHED_UNLOCK(&gNB->sched_lock);
|
||||
p += snprintf(p, end - p, "\n");
|
||||
p += print_meas_log(&gNB->gNB_scheduler, "gNB_scheduler", NULL, NULL, p, end - p);
|
||||
p += print_meas_log(&gNB->rx_ulsch_sdu, "rx_ulsch_sdu", NULL, NULL, p, end - p);
|
||||
@@ -97,10 +96,17 @@ void *nrmac_stats_thread(void *arg) {
|
||||
p += print_meas_log(&gNB->rlc_data_req, "rlc_data_req", NULL, NULL, p, end - p);
|
||||
p += print_meas_log(&gNB->nr_srs_ri_computation_timer, "UL-RI computation time", NULL, NULL, p, end - p);
|
||||
p += print_meas_log(&gNB->nr_srs_tpmi_computation_timer, "UL-TPMI computation time", NULL, NULL, p, end - p);
|
||||
fwrite(output, p - output, 1, file);
|
||||
fflush(file);
|
||||
NR_SCHED_UNLOCK(&gNB->sched_lock);
|
||||
size_t len = p - output;
|
||||
if (fwrite(output, len, 1, file) != 1 || fflush(file) != 0) {
|
||||
LOG_E(NR_MAC, "error while writing nrMAC_stats.log: %d, %s\n", errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
fseek(file,0,SEEK_SET);
|
||||
if (ftruncate(fileno(file), 0) != 0 || fseek(file, 0, SEEK_SET) != 0) {
|
||||
LOG_E(NR_MAC, "error while writing nrMAC_stats.log: %d, %s\n", errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
return NULL;
|
||||
@@ -112,6 +118,19 @@ void clear_mac_stats(gNB_MAC_INST *gNB) {
|
||||
}
|
||||
}
|
||||
|
||||
static char *st_append(char *start, const char *end, const char *format, ...)
|
||||
{
|
||||
size_t space = end - start;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
size_t written = vsnprintf(start, space, format, args);
|
||||
va_end(args);
|
||||
if (written <= space)
|
||||
return start + written;
|
||||
else
|
||||
return (char *)end;
|
||||
}
|
||||
|
||||
size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset_rsrp)
|
||||
{
|
||||
const char *begin = output;
|
||||
@@ -121,46 +140,45 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset
|
||||
* scheduler to be locked*/
|
||||
NR_SCHED_ENSURE_LOCKED(&gNB->sched_lock);
|
||||
|
||||
NR_SCHED_LOCK(&gNB->UE_info.mutex);
|
||||
UE_iterator(gNB->UE_info.connected_ue_list, UE) {
|
||||
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
NR_mac_stats_t *stats = &UE->mac_stats;
|
||||
const int avg_rsrp = stats->num_rsrp_meas > 0 ? stats->cumul_rsrp / stats->num_rsrp_meas : 0;
|
||||
const int avg_sinrx10 = stats->num_sinr_meas > 0 ? stats->cumul_sinrx10 / stats->num_sinr_meas : 0;
|
||||
|
||||
output += snprintf(output, end - output, "UE RNTI %04x CU-UE-ID ", UE->rnti);
|
||||
output = st_append(output, end, "UE RNTI %04x CU-UE-ID ", UE->rnti);
|
||||
if (du_exists_f1_ue_data(UE->rnti)) {
|
||||
f1_ue_data_t ued = du_get_f1_ue_data(UE->rnti);
|
||||
output += snprintf(output, end - output, "%d", ued.secondary_ue);
|
||||
output = st_append(output, end, "%d", ued.secondary_ue);
|
||||
} else {
|
||||
output += snprintf(output, end - output, "(none)");
|
||||
output = st_append(output, end, "(none)");
|
||||
}
|
||||
|
||||
bool in_sync = !sched_ctrl->ul_failure;
|
||||
output += snprintf(output,
|
||||
end - output,
|
||||
output = st_append(output,
|
||||
end,
|
||||
" %s PH %d dB PCMAX %d dBm",
|
||||
in_sync ? "in-sync" : "out-of-sync",
|
||||
sched_ctrl->ph,
|
||||
sched_ctrl->pcmax);
|
||||
|
||||
if (stats->num_rsrp_meas)
|
||||
output += snprintf(output, end - output, ", average RSRP %d (%d meas)", avg_rsrp, stats->num_rsrp_meas);
|
||||
output = st_append(output, end, ", average RSRP %d (%d meas)", avg_rsrp, stats->num_rsrp_meas);
|
||||
|
||||
if (stats->num_sinr_meas) {
|
||||
output += snprintf(output,
|
||||
end - output,
|
||||
output = st_append(output,
|
||||
end,
|
||||
", average SINR %d.%d (%d meas)",
|
||||
avg_sinrx10 / 10,
|
||||
avg_sinrx10 % 10,
|
||||
stats->num_sinr_meas);
|
||||
}
|
||||
|
||||
output += snprintf(output, end - output, "\n");
|
||||
output = st_append(output, end, "\n");
|
||||
|
||||
if(sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.print_report)
|
||||
output += snprintf(output,
|
||||
end - output,
|
||||
output = st_append(output,
|
||||
end,
|
||||
"UE %04x: CQI %d, RI %d, PMI (%d,%d)\n",
|
||||
UE->rnti,
|
||||
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb,
|
||||
@@ -169,18 +187,18 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset
|
||||
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2);
|
||||
|
||||
if (stats->srs_stats[0] != '\0') {
|
||||
output += snprintf(output, end - output, "UE %04x: %s\n", UE->rnti, stats->srs_stats);
|
||||
output = st_append(output, end, "UE %04x: %s\n", UE->rnti, stats->srs_stats);
|
||||
}
|
||||
|
||||
output += snprintf(output,
|
||||
end - output,
|
||||
output = st_append(output,
|
||||
end,
|
||||
"UE %04x: dlsch_rounds ", UE->rnti);
|
||||
output += snprintf(output, end - output, "%"PRIu64, stats->dl.rounds[0]);
|
||||
output = st_append(output, end, "%"PRIu64, stats->dl.rounds[0]);
|
||||
for (int i = 1; i < gNB->dl_bler.harq_round_max; i++)
|
||||
output += snprintf(output, end - output, "/%"PRIu64, stats->dl.rounds[i]);
|
||||
output = st_append(output, end, "/%"PRIu64, stats->dl.rounds[i]);
|
||||
|
||||
output += snprintf(output,
|
||||
end - output,
|
||||
output = st_append(output,
|
||||
end,
|
||||
", dlsch_errors %"PRIu64", pucch0_DTX %d, BLER %.5f MCS (%d) %d CCE fail %d\n",
|
||||
stats->dl.errors,
|
||||
stats->pucch0_DTX,
|
||||
@@ -194,15 +212,15 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset
|
||||
stats->num_sinr_meas = 0;
|
||||
stats->cumul_sinrx10 = 0;
|
||||
}
|
||||
output += snprintf(output,
|
||||
end - output,
|
||||
output = st_append(output,
|
||||
end,
|
||||
"UE %04x: ulsch_rounds ", UE->rnti);
|
||||
output += snprintf(output, end - output, "%"PRIu64, stats->ul.rounds[0]);
|
||||
output = st_append(output, end, "%"PRIu64, stats->ul.rounds[0]);
|
||||
for (int i = 1; i < gNB->ul_bler.harq_round_max; i++)
|
||||
output += snprintf(output, end - output, "/%"PRIu64, stats->ul.rounds[i]);
|
||||
output = st_append(output, end, "/%"PRIu64, stats->ul.rounds[i]);
|
||||
|
||||
output += snprintf(output,
|
||||
end - output,
|
||||
output = st_append(output,
|
||||
end,
|
||||
", ulsch_errors %"PRIu64", ulsch_DTX %d, BLER %.5f MCS (%d) %d (Qm %d deltaMCS %d dB) NPRB %d SNR %d.%d dB CCE fail %d\n",
|
||||
stats->ul.errors,
|
||||
stats->ulsch_DTX,
|
||||
@@ -215,15 +233,15 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset
|
||||
sched_ctrl->pusch_snrx10 / 10,
|
||||
sched_ctrl->pusch_snrx10 % 10,
|
||||
sched_ctrl->ul_cce_fail);
|
||||
output += snprintf(output,
|
||||
end - output,
|
||||
output = st_append(output,
|
||||
end,
|
||||
"UE %04x: MAC: TX %14"PRIu64" RX %14"PRIu64" bytes\n",
|
||||
UE->rnti, stats->dl.total_bytes, stats->ul.total_bytes);
|
||||
|
||||
for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); i++) {
|
||||
const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i);
|
||||
output += snprintf(output,
|
||||
end - output,
|
||||
output = st_append(output,
|
||||
end,
|
||||
"UE %04x: LCID %d: TX %14"PRIu64" RX %14"PRIu64" bytes\n",
|
||||
UE->rnti,
|
||||
c->lcid,
|
||||
@@ -231,7 +249,7 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset
|
||||
stats->ul.lc_bytes[c->lcid]);
|
||||
}
|
||||
}
|
||||
NR_SCHED_UNLOCK(&gNB->UE_info.mutex);
|
||||
DevAssert(output <= end);
|
||||
return output - begin;
|
||||
}
|
||||
|
||||
@@ -296,9 +314,10 @@ void mac_top_init_gNB(ngran_node_t node_type,
|
||||
RC.nrmac[i]->cset0_bwp_start = 0;
|
||||
RC.nrmac[i]->cset0_bwp_size = 0;
|
||||
|
||||
RC.nrmac[i]->print_ue_stats = true;
|
||||
|
||||
pthread_mutex_init(&RC.nrmac[i]->sched_lock, NULL);
|
||||
|
||||
pthread_mutex_init(&RC.nrmac[i]->UE_info.mutex, NULL);
|
||||
uid_linear_allocator_init(&RC.nrmac[i]->UE_info.uid_allocator);
|
||||
|
||||
if (get_softmodem_params()->phy_test) {
|
||||
|
||||
@@ -179,6 +179,12 @@ typedef struct {
|
||||
int location_and_bw;
|
||||
} nr_bwp_config_t;
|
||||
|
||||
typedef enum {
|
||||
SSB_RSRP,
|
||||
CRI_RSRP,
|
||||
SSB_SINR,
|
||||
} nr_config_report_type_t;
|
||||
|
||||
typedef struct nr_mac_config_t {
|
||||
int sib1_tda;
|
||||
nr_pdsch_AntennaPorts_t pdsch_AntennaPorts;
|
||||
@@ -207,7 +213,7 @@ typedef struct nr_mac_config_t {
|
||||
int num_agg_level_candidates[NUM_PDCCH_AGG_LEVELS];
|
||||
nr_redcap_config_t *redcap;
|
||||
nr_ptrs_config_t *ptrs;
|
||||
bool do_SINR;
|
||||
nr_config_report_type_t report_type;
|
||||
} nr_mac_config_t;
|
||||
|
||||
typedef struct NR_preamble_ue {
|
||||
@@ -633,8 +639,6 @@ typedef struct {
|
||||
/// PHR info: nominal UE transmit power levels (dBm)
|
||||
int pcmax;
|
||||
|
||||
/// Sched PDSCH: scheduling decisions, copied into HARQ and cleared every TTI
|
||||
NR_sched_pdsch_t sched_pdsch;
|
||||
/// UE-estimated maximum MCS (from CSI-RS)
|
||||
uint8_t dl_max_mcs;
|
||||
|
||||
@@ -797,7 +801,6 @@ typedef struct {
|
||||
typedef struct {
|
||||
/// scheduling control info
|
||||
// last element always NULL
|
||||
pthread_mutex_t mutex;
|
||||
NR_UE_info_t *connected_ue_list[MAX_MOBILES_PER_GNB + 1];
|
||||
NR_UE_info_t *access_ue_list[NR_NB_RA_PROC_MAX + 1];
|
||||
// bitmap of CSI-RS already scheduled in current slot
|
||||
@@ -820,7 +823,18 @@ typedef struct {
|
||||
nr_beam_mode_t beam_mode;
|
||||
} NR_beam_info_t;
|
||||
|
||||
#define UE_iterator(BaSe, VaR) NR_UE_info_t ** VaR##pptr=BaSe, *VaR; while ((VaR=*(VaR##pptr++)))
|
||||
#define UE_iterator(BaSe, VaR) for (NR_UE_info_t **VaR##pptr=BaSe, *VaR=*VaR##pptr; VaR; VaR=*(++VaR##pptr))
|
||||
|
||||
typedef struct {
|
||||
/// current frame
|
||||
frame_t frame;
|
||||
/// current slot
|
||||
slot_t slot;
|
||||
/// FAPI DL req in which allocations are made
|
||||
nfapi_nr_dl_tti_request_body_t *dl_req;
|
||||
/// TX_data request holds the actual data
|
||||
nfapi_nr_tx_data_request_t *TX_req;
|
||||
} post_process_pdsch_t;
|
||||
|
||||
typedef struct {
|
||||
/// current frame for DCI
|
||||
@@ -837,7 +851,7 @@ typedef struct {
|
||||
struct gNB_MAC_INST_s;
|
||||
typedef struct gNB_MAC_INST_s gNB_MAC_INST;
|
||||
|
||||
typedef void (*nr_pp_impl_dl)(module_id_t mod_id, frame_t frame, slot_t slot);
|
||||
typedef void (*nr_pp_impl_dl)(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pp_pdsch);
|
||||
typedef void (*nr_pp_impl_ul)(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp_pusch);
|
||||
|
||||
typedef struct f1_config_t {
|
||||
@@ -856,6 +870,11 @@ typedef struct {
|
||||
uint64_t used_prb_aggregate;
|
||||
} mac_stats_t;
|
||||
|
||||
typedef struct dlul_mac_stats {
|
||||
mac_stats_t dl;
|
||||
mac_stats_t ul;
|
||||
} dlul_mac_stats_t;
|
||||
|
||||
/// helper type to encapsulate a frame/slot combination in a single type.
|
||||
/// Currently only used in the UL preprocessor. Note: if you use this type
|
||||
/// further, please refactor it into a common type first.
|
||||
@@ -975,9 +994,14 @@ typedef struct gNB_MAC_INST_s {
|
||||
f1_config_t f1_config;
|
||||
int16_t frame;
|
||||
|
||||
/// number of UEs to exceed to disable stats
|
||||
int stats_max_ue;
|
||||
/// if stats are currently enabled
|
||||
bool print_ue_stats;
|
||||
|
||||
pthread_mutex_t sched_lock;
|
||||
|
||||
mac_stats_t mac_stats;
|
||||
dlul_mac_stats_t mac_stats;
|
||||
uint64_t num_scheduled_prach_rx;
|
||||
} gNB_MAC_INST;
|
||||
|
||||
|
||||
@@ -585,22 +585,13 @@ static void config_csiim(int do_csirs,
|
||||
}
|
||||
|
||||
|
||||
void set_dl_maxmimolayers(NR_PDSCH_ServingCellConfig_t *pdsch_servingcellconfig,
|
||||
const NR_ServingCellConfigCommon_t *scc,
|
||||
const NR_UE_NR_Capability_t *uecap,
|
||||
int maxMIMO_layers)
|
||||
long ue_supported_dl_layers(const NR_ServingCellConfigCommon_t *scc, const NR_UE_NR_Capability_t *uecap)
|
||||
{
|
||||
if(!pdsch_servingcellconfig->ext1)
|
||||
pdsch_servingcellconfig->ext1=calloc(1,sizeof(*pdsch_servingcellconfig->ext1));
|
||||
if(!pdsch_servingcellconfig->ext1->maxMIMO_Layers)
|
||||
pdsch_servingcellconfig->ext1->maxMIMO_Layers = calloc(1,sizeof(*pdsch_servingcellconfig->ext1->maxMIMO_Layers));
|
||||
|
||||
NR_SCS_SpecificCarrier_t *scs_carrier = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0];
|
||||
int band = *scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0];
|
||||
const frequency_range_t freq_range = get_freq_range_from_band(band);
|
||||
const int scs = scs_carrier->subcarrierSpacing;
|
||||
const int bw_size = scs_carrier->carrierBandwidth;
|
||||
|
||||
NR_FeatureSets_t *fs = uecap ? uecap->featureSets : NULL;
|
||||
if (fs) {
|
||||
const int bw_mhz = get_supported_bw_mhz(freq_range, get_supported_band_index(scs, freq_range, bw_size));
|
||||
@@ -610,16 +601,29 @@ void set_dl_maxmimolayers(NR_PDSCH_ServingCellConfig_t *pdsch_servingcellconfig,
|
||||
if (scs == dl_fs->supportedSubcarrierSpacingDL &&
|
||||
supported_bw_comparison(bw_mhz, &dl_fs->supportedBandwidthDL, dl_fs->channelBW_90mhz) &&
|
||||
dl_fs->maxNumberMIMO_LayersPDSCH) {
|
||||
long ue_supported_layers = (2 << *dl_fs->maxNumberMIMO_LayersPDSCH);
|
||||
if (maxMIMO_layers == -1)
|
||||
*pdsch_servingcellconfig->ext1->maxMIMO_Layers = NR_MAX_SUPPORTED_DL_LAYERS < ue_supported_layers ? NR_MAX_SUPPORTED_DL_LAYERS : ue_supported_layers;
|
||||
else
|
||||
*pdsch_servingcellconfig->ext1->maxMIMO_Layers = maxMIMO_layers < ue_supported_layers ? maxMIMO_layers : ue_supported_layers;
|
||||
return;
|
||||
return (2 << *dl_fs->maxNumberMIMO_LayersPDSCH);
|
||||
}
|
||||
}
|
||||
}
|
||||
*pdsch_servingcellconfig->ext1->maxMIMO_Layers = 2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void set_dl_maxmimolayers(NR_PDSCH_ServingCellConfig_t *pdsch_servingcellconfig,
|
||||
const NR_ServingCellConfigCommon_t *scc,
|
||||
const NR_UE_NR_Capability_t *uecap,
|
||||
int maxMIMO_layers)
|
||||
{
|
||||
if(!pdsch_servingcellconfig->ext1)
|
||||
pdsch_servingcellconfig->ext1 = calloc(1, sizeof(*pdsch_servingcellconfig->ext1));
|
||||
if(!pdsch_servingcellconfig->ext1->maxMIMO_Layers)
|
||||
pdsch_servingcellconfig->ext1->maxMIMO_Layers = calloc(1, sizeof(*pdsch_servingcellconfig->ext1->maxMIMO_Layers));
|
||||
|
||||
long l = ue_supported_dl_layers(scc, uecap);
|
||||
long ue_supported_layers = l > 1 ? l : 2; // min UE supported layers = 2
|
||||
if (maxMIMO_layers == -1)
|
||||
*pdsch_servingcellconfig->ext1->maxMIMO_Layers = min(NR_MAX_SUPPORTED_DL_LAYERS, ue_supported_layers);
|
||||
else
|
||||
*pdsch_servingcellconfig->ext1->maxMIMO_Layers = min(maxMIMO_layers, ue_supported_layers);
|
||||
}
|
||||
|
||||
// TODO: Implement to b_SRS = 1 and b_SRS = 2
|
||||
@@ -2144,7 +2148,7 @@ static void config_rsrp_meas_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
for (int csi_list = 0; csi_list < csi_MeasConfig->csi_ResourceConfigToAddModList->list.count; csi_list++) {
|
||||
NR_CSI_ResourceConfig_t *csires = csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_list];
|
||||
if (csires->csi_RS_ResourceSetList.present == NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_nzp_CSI_RS_SSB) {
|
||||
if (configuration->do_CSIRS && num_antenna_ports < 4) {
|
||||
if (configuration->report_type == CRI_RSRP && configuration->do_CSIRS && num_antenna_ports < 4) {
|
||||
if (csires->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList)
|
||||
resource_id = csires->csi_ResourceConfigId;
|
||||
} else {
|
||||
@@ -2166,20 +2170,19 @@ static void config_rsrp_meas_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
csirep->reportConfigType.choice.periodic = calloc(1, sizeof(*csirep->reportConfigType.choice.periodic));
|
||||
set_csi_meas_periodicity(servingcellconfigcommon, csirep, uid, curr_bwp, true);
|
||||
asn1cSeqAdd(&csirep->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list, pucchcsires);
|
||||
if (configuration->do_CSIRS && num_antenna_ports < 4) {
|
||||
if (configuration->report_type == SSB_SINR) {
|
||||
csirep->reportQuantity.present = NR_CSI_ReportConfig__reportQuantity_PR_none;
|
||||
csirep->reportQuantity.choice.none = (NULL_t)0;
|
||||
csirep->ext2 = calloc(1, sizeof(*csirep->ext2));
|
||||
csirep->ext2->reportQuantity_r16 = calloc(1, sizeof(*csirep->ext2->reportQuantity_r16));
|
||||
csirep->ext2->reportQuantity_r16->present = NR_CSI_ReportConfig__ext2__reportQuantity_r16_PR_ssb_Index_SINR_r16;
|
||||
csirep->ext2->reportQuantity_r16->choice.ssb_Index_SINR_r16 = (NULL_t)0;
|
||||
} else if (configuration->report_type == CRI_RSRP && configuration->do_CSIRS && num_antenna_ports < 4) {
|
||||
csirep->reportQuantity.present = NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP;
|
||||
csirep->reportQuantity.choice.cri_RSRP = (NULL_t)0;
|
||||
} else {
|
||||
csirep->reportQuantity.present = NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP;
|
||||
csirep->reportQuantity.choice.ssb_Index_RSRP = (NULL_t)0;
|
||||
if (configuration->do_SINR) {
|
||||
csirep->reportQuantity.present = NR_CSI_ReportConfig__reportQuantity_PR_none;
|
||||
csirep->reportQuantity.choice.none = (NULL_t)0;
|
||||
csirep->ext2 = calloc(1, sizeof(*csirep->ext2));
|
||||
csirep->ext2->reportQuantity_r16 = calloc(1, sizeof(*csirep->ext2->reportQuantity_r16));
|
||||
csirep->ext2->reportQuantity_r16->present = NR_CSI_ReportConfig__ext2__reportQuantity_r16_PR_ssb_Index_SINR_r16;
|
||||
csirep->ext2->reportQuantity_r16->choice.ssb_Index_SINR_r16 = (NULL_t)0;
|
||||
}
|
||||
}
|
||||
csirep->groupBasedBeamReporting.present = NR_CSI_ReportConfig__groupBasedBeamReporting_PR_disabled;
|
||||
csirep->groupBasedBeamReporting.choice.disabled = calloc(1, sizeof(*csirep->groupBasedBeamReporting.choice.disabled));
|
||||
@@ -3539,6 +3542,24 @@ static NR_SpCellConfig_t *get_initial_SpCellConfig(int uid,
|
||||
return SpCellConfig;
|
||||
}
|
||||
|
||||
struct NR_RLC_Config *nr_srb_config(const nr_rlc_configuration_t *default_rlc_config)
|
||||
{
|
||||
NR_RLC_Config_t *rlc_Config = calloc(1, sizeof(NR_RLC_Config_t));
|
||||
rlc_Config->present = NR_RLC_Config_PR_am;
|
||||
rlc_Config->choice.am = calloc(1, sizeof(*rlc_Config->choice.am));
|
||||
rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t));
|
||||
*(rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength) = encode_sn_field_length_am(default_rlc_config->srb.sn_field_length);
|
||||
rlc_Config->choice.am->dl_AM_RLC.t_Reassembly = encode_t_reassembly(default_rlc_config->srb.t_reassembly);
|
||||
rlc_Config->choice.am->dl_AM_RLC.t_StatusProhibit = encode_t_status_prohibit(default_rlc_config->srb.t_status_prohibit);
|
||||
rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t));
|
||||
*(rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength) = encode_sn_field_length_am(default_rlc_config->srb.sn_field_length);
|
||||
rlc_Config->choice.am->ul_AM_RLC.t_PollRetransmit = encode_t_poll_retransmit(default_rlc_config->srb.t_poll_retransmit);
|
||||
rlc_Config->choice.am->ul_AM_RLC.pollPDU = encode_poll_pdu(default_rlc_config->srb.poll_pdu);
|
||||
rlc_Config->choice.am->ul_AM_RLC.pollByte = encode_poll_byte(default_rlc_config->srb.poll_byte);
|
||||
rlc_Config->choice.am->ul_AM_RLC.maxRetxThreshold = encode_max_retx_threshold(default_rlc_config->srb.max_retx_threshold);
|
||||
return rlc_Config;
|
||||
}
|
||||
|
||||
NR_RLC_BearerConfig_t *get_SRB_RLC_BearerConfig(long channelId,
|
||||
long priority,
|
||||
long bucketSizeDuration,
|
||||
@@ -3552,20 +3573,7 @@ NR_RLC_BearerConfig_t *get_SRB_RLC_BearerConfig(long channelId,
|
||||
rlc_BearerConfig->servedRadioBearer->choice.srb_Identity = channelId;
|
||||
rlc_BearerConfig->reestablishRLC = NULL;
|
||||
|
||||
NR_RLC_Config_t *rlc_Config = calloc(1, sizeof(NR_RLC_Config_t));
|
||||
rlc_Config->present = NR_RLC_Config_PR_am;
|
||||
rlc_Config->choice.am = calloc(1, sizeof(*rlc_Config->choice.am));
|
||||
rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t));
|
||||
*(rlc_Config->choice.am->dl_AM_RLC.sn_FieldLength) = encode_sn_field_length_am(default_rlc_config->srb.sn_field_length);
|
||||
rlc_Config->choice.am->dl_AM_RLC.t_Reassembly = encode_t_reassembly(default_rlc_config->srb.t_reassembly);
|
||||
rlc_Config->choice.am->dl_AM_RLC.t_StatusProhibit = encode_t_status_prohibit(default_rlc_config->srb.t_status_prohibit);
|
||||
rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength = calloc(1, sizeof(NR_SN_FieldLengthAM_t));
|
||||
*(rlc_Config->choice.am->ul_AM_RLC.sn_FieldLength) = encode_sn_field_length_am(default_rlc_config->srb.sn_field_length);
|
||||
rlc_Config->choice.am->ul_AM_RLC.t_PollRetransmit = encode_t_poll_retransmit(default_rlc_config->srb.t_poll_retransmit);
|
||||
rlc_Config->choice.am->ul_AM_RLC.pollPDU = encode_poll_pdu(default_rlc_config->srb.poll_pdu);
|
||||
rlc_Config->choice.am->ul_AM_RLC.pollByte = encode_poll_byte(default_rlc_config->srb.poll_byte);
|
||||
rlc_Config->choice.am->ul_AM_RLC.maxRetxThreshold = encode_max_retx_threshold(default_rlc_config->srb.max_retx_threshold);
|
||||
rlc_BearerConfig->rlc_Config = rlc_Config;
|
||||
rlc_BearerConfig->rlc_Config = nr_srb_config(default_rlc_config);
|
||||
|
||||
NR_LogicalChannelConfig_t *logicalChannelConfig = calloc(1, sizeof(NR_LogicalChannelConfig_t));
|
||||
logicalChannelConfig->ul_SpecificParameters = calloc(1, sizeof(*logicalChannelConfig->ul_SpecificParameters));
|
||||
@@ -3585,10 +3593,9 @@ NR_RLC_BearerConfig_t *get_SRB_RLC_BearerConfig(long channelId,
|
||||
return rlc_BearerConfig;
|
||||
}
|
||||
|
||||
static void nr_drb_config(struct NR_RLC_Config *rlc_Config,
|
||||
NR_RLC_Config_PR rlc_config_pr,
|
||||
const nr_rlc_configuration_t *default_rlc_config)
|
||||
struct NR_RLC_Config *nr_drb_config(NR_RLC_Config_PR rlc_config_pr, const nr_rlc_configuration_t *default_rlc_config)
|
||||
{
|
||||
NR_RLC_Config_t *rlc_Config = calloc(1, sizeof(NR_RLC_Config_t));
|
||||
switch (rlc_config_pr) {
|
||||
case NR_RLC_Config_PR_um_Bi_Directional:
|
||||
// RLC UM Bi-directional Bearer configuration
|
||||
@@ -3621,6 +3628,7 @@ static void nr_drb_config(struct NR_RLC_Config *rlc_Config,
|
||||
break;
|
||||
}
|
||||
rlc_Config->present = rlc_config_pr;
|
||||
return rlc_Config;
|
||||
}
|
||||
|
||||
NR_RLC_BearerConfig_t *get_DRB_RLC_BearerConfig(long lcChannelId,
|
||||
@@ -3636,9 +3644,7 @@ NR_RLC_BearerConfig_t *get_DRB_RLC_BearerConfig(long lcChannelId,
|
||||
rlc_BearerConfig->servedRadioBearer->choice.drb_Identity = drbId;
|
||||
rlc_BearerConfig->reestablishRLC = NULL;
|
||||
|
||||
NR_RLC_Config_t *rlc_Config = calloc(1, sizeof(NR_RLC_Config_t));
|
||||
nr_drb_config(rlc_Config, rlc_conf, default_rlc_config);
|
||||
rlc_BearerConfig->rlc_Config = rlc_Config;
|
||||
rlc_BearerConfig->rlc_Config = nr_drb_config(rlc_conf, default_rlc_config);
|
||||
|
||||
NR_LogicalChannelConfig_t *logicalChannelConfig = calloc(1, sizeof(NR_LogicalChannelConfig_t));
|
||||
logicalChannelConfig->ul_SpecificParameters = calloc(1, sizeof(*logicalChannelConfig->ul_SpecificParameters));
|
||||
@@ -3733,10 +3739,11 @@ NR_CellGroupConfig_t *get_initial_cellGroupConfig(int uid,
|
||||
return cellGroupConfig;
|
||||
}
|
||||
|
||||
static long set_ul_max_layers(const nr_mac_config_t *configuration, const NR_UE_NR_Capability_t *uecap)
|
||||
long ue_supported_ul_layers(const NR_UE_NR_Capability_t *uecap)
|
||||
{
|
||||
long ul_max_layers = 1;
|
||||
if (uecap && uecap->featureSets
|
||||
if (uecap
|
||||
&& uecap->featureSets
|
||||
&& uecap->featureSets->featureSetsUplinkPerCC
|
||||
&& uecap->featureSets->featureSetsUplinkPerCC->list.count > 0) {
|
||||
NR_FeatureSetUplinkPerCC_t *ul_feature_setup_per_cc = uecap->featureSets->featureSetsUplinkPerCC->list.array[0];
|
||||
@@ -3752,11 +3759,15 @@ static long set_ul_max_layers(const nr_mac_config_t *configuration, const NR_UE_
|
||||
ul_max_layers = 1;
|
||||
}
|
||||
}
|
||||
ul_max_layers = min(ul_max_layers, configuration->pusch_AntennaPorts);
|
||||
}
|
||||
return ul_max_layers;
|
||||
}
|
||||
|
||||
static long set_ul_max_layers(const nr_mac_config_t *configuration, const NR_UE_NR_Capability_t *uecap)
|
||||
{
|
||||
return min(ue_supported_ul_layers(uecap), configuration->pusch_AntennaPorts);
|
||||
}
|
||||
|
||||
NR_CellGroupConfig_t * update_cellGroupConfig_for_BWP_switch(NR_CellGroupConfig_t *cellGroupConfig,
|
||||
const nr_mac_config_t *configuration,
|
||||
const NR_UE_NR_Capability_t *uecap,
|
||||
@@ -3867,8 +3878,15 @@ void update_cellGroupConfig(NR_CellGroupConfig_t *cellGroupConfig,
|
||||
NR_CSI_MeasConfig_t *csi_MeasConfig = spCellConfigDedicated->csi_MeasConfig->choice.setup;
|
||||
for (int report = 0; report < csi_MeasConfig->csi_ReportConfigToAddModList->list.count; report++) {
|
||||
NR_CSI_ReportConfig_t *csirep = csi_MeasConfig->csi_ReportConfigToAddModList->list.array[report];
|
||||
if(csirep->codebookConfig)
|
||||
if (csirep->codebookConfig)
|
||||
config_csi_codebook(&configuration->pdsch_AntennaPorts, *pdsch_servingcellconfig->ext1->maxMIMO_Layers, csirep->codebookConfig);
|
||||
if (csirep->groupBasedBeamReporting.present == NR_CSI_ReportConfig__groupBasedBeamReporting_PR_disabled
|
||||
&& csirep->groupBasedBeamReporting.choice.disabled
|
||||
&& csirep->groupBasedBeamReporting.choice.disabled->nrofReportedRS)
|
||||
*csirep->groupBasedBeamReporting.choice.disabled->nrofReportedRS = config_nrofReportedRS(uecap,
|
||||
get_ssb_bitmap(scc),
|
||||
scc,
|
||||
configuration->max_num_rsrp);
|
||||
}
|
||||
|
||||
NR_UplinkConfig_t *uplinkConfig = spCellConfigDedicated->uplinkConfig;
|
||||
|
||||
@@ -112,6 +112,9 @@ NR_CellGroupConfig_t *get_default_secondaryCellGroup(const NR_ServingCellConfigC
|
||||
|
||||
NR_ReconfigurationWithSync_t *get_reconfiguration_with_sync(rnti_t rnti, uid_t uid, const NR_ServingCellConfigCommon_t *scc, int frame);
|
||||
|
||||
struct NR_RLC_Config *nr_srb_config(const nr_rlc_configuration_t *default_rlc_config);
|
||||
struct NR_RLC_Config *nr_drb_config(NR_RLC_Config_PR rlc_config_pr, const nr_rlc_configuration_t *default_rlc_config);
|
||||
|
||||
NR_RLC_BearerConfig_t *get_SRB_RLC_BearerConfig(long channelId,
|
||||
long priority,
|
||||
long bucketSizeDuration,
|
||||
@@ -131,5 +134,6 @@ NR_CellGroupConfig_t * update_cellGroupConfig_for_BWP_switch(NR_CellGroupConfig_
|
||||
NR_MeasurementTimingConfiguration_t *get_nr_mtc(uint8_t *buf, uint32_t len);
|
||||
measgap_config_t create_measgap_config(const NR_MeasurementTimingConfiguration_t *mtc, int scs, int min_rxtxtime);
|
||||
int encode_measgap_config(const measgap_config_t *c, uint8_t *buf);
|
||||
|
||||
long ue_supported_ul_layers(const NR_UE_NR_Capability_t *uecap);
|
||||
long ue_supported_dl_layers(const NR_ServingCellConfigCommon_t *scc, const NR_UE_NR_Capability_t *uecap);
|
||||
#endif
|
||||
|
||||
@@ -141,6 +141,24 @@ static instance_t get_f1_gtp_instance(void)
|
||||
return inst->gtpInst;
|
||||
}
|
||||
|
||||
/** @brief Add bearer in PDCP/SDAP for 5GC association (SA) */
|
||||
static void e1_add_bearers(const int ue_id, const NR_DRB_ToAddModList_t *addMod, const nr_pdcp_entity_security_keys_and_algos_t *sp)
|
||||
{
|
||||
for (int i = 0; i < addMod->list.count; i++) {
|
||||
NR_DRB_ToAddMod_t *drb = addMod->list.array[i];
|
||||
DevAssert(drb->cnAssociation);
|
||||
DevAssert(drb->cnAssociation->present != NR_DRB_ToAddMod__cnAssociation_PR_NOTHING);
|
||||
DevAssert(drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config);
|
||||
DevAssert(drb->cnAssociation->choice.sdap_Config);
|
||||
// get SDAP config
|
||||
sdap_config_t sdap = nr_sdap_get_config(GNB_FLAG_YES, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity);
|
||||
// add SDAP entity
|
||||
nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, &sdap);
|
||||
// add PDCP entity
|
||||
nr_pdcp_add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp);
|
||||
}
|
||||
}
|
||||
|
||||
void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req)
|
||||
{
|
||||
bool need_ue_id_mgmt = e1_used();
|
||||
@@ -208,11 +226,10 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req)
|
||||
security_parameters.integrity_algorithm = req->secInfo.integrityProtectionAlgorithm;
|
||||
memcpy(security_parameters.ciphering_key, req->secInfo.encryptionKey, NR_K_KEY_SIZE);
|
||||
memcpy(security_parameters.integrity_key, req->secInfo.integrityProtectionKey, NR_K_KEY_SIZE);
|
||||
nr_pdcp_add_drbs(true, // set this to notify PDCP that his not UE
|
||||
cu_up_ue_id,
|
||||
&DRB_configList,
|
||||
&security_parameters);
|
||||
|
||||
e1_add_bearers(cu_up_ue_id, &DRB_configList, &security_parameters);
|
||||
ASN_STRUCT_RESET(asn_DEF_NR_DRB_ToAddModList, &DRB_configList.list);
|
||||
|
||||
if (f1inst >= 0) { /* we have F1(-U) */
|
||||
teid_t dummy_teid = 0xffff; // we will update later with answer from DU
|
||||
in_addr_t dummy_address = {0}; // IPv4, updated later with answer from DU
|
||||
@@ -242,17 +259,58 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req)
|
||||
get_e1_if()->bearer_setup_response(&resp);
|
||||
}
|
||||
|
||||
static void release_gtpu_tunnel(uint32_t ue_id, int pdusession_id)
|
||||
{
|
||||
instance_t n3inst = get_n3_gtp_instance();
|
||||
|
||||
// Handle N3 tunnel (1 tunnel per PDU session)
|
||||
if (n3inst >= 0) {
|
||||
LOG_I(E1AP, "UE %u: Deleting N3 tunnel for PDU session %d\n", ue_id, pdusession_id);
|
||||
int result = newGtpuDeleteOneTunnel(n3inst, ue_id, pdusession_id);
|
||||
if (result == GTPNOK) {
|
||||
LOG_E(E1AP, "UE %u: Failed to delete N3 tunnel for PDU session %d\n", ue_id, pdusession_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void release_f1_drbs(uint32_t ue_id, int pdusession_id)
|
||||
{
|
||||
instance_t f1inst = get_f1_gtp_instance();
|
||||
// Handle F1 tunnels (1 tunnel per DRB, fetch PDCP entities and get DRB IDs)
|
||||
int drb_ids[MAX_DRBS_PER_UE];
|
||||
int n_drbs = nr_pdcp_get_drb_ids_for_pdusession(ue_id, pdusession_id, drb_ids);
|
||||
if (n_drbs > 0) {
|
||||
LOG_I(E1AP, "UE %u: Deleting %d F1 tunnels for PDU session %d\n", ue_id, n_drbs, pdusession_id);
|
||||
// Delete each F1 tunnel using the DRB IDs
|
||||
for (int i = 0; i < n_drbs; i++) {
|
||||
int drb_id = drb_ids[i];
|
||||
if (f1inst >= 0) {
|
||||
int result = newGtpuDeleteOneTunnel(f1inst, ue_id, drb_id);
|
||||
if (result == GTPNOK) {
|
||||
LOG_E(E1AP, "UE %u: Failed to delete F1 tunnel for DRB %d\n", ue_id, drb_id);
|
||||
}
|
||||
}
|
||||
// Release PDCP entity
|
||||
nr_pdcp_release_drb(ue_id, drb_id);
|
||||
}
|
||||
} else {
|
||||
LOG_W(E1AP, "UE %u: No DRBs found for PDU session %d\n", ue_id, pdusession_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fill Bearer Context Modification Response and send to callback
|
||||
*/
|
||||
void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
|
||||
{
|
||||
AssertFatal(req->numPDUSessionsMod > 0, "need at least one PDU session to modify\n");
|
||||
AssertFatal(req->numPDUSessionsMod > 0 || req->numPDUSessionsRem > 0,
|
||||
"need at least one PDU session to modify or release (mod=%u, rel=%u)\n",
|
||||
req->numPDUSessionsMod,
|
||||
req->numPDUSessionsRem);
|
||||
|
||||
e1ap_bearer_modif_resp_t modif = {
|
||||
.gNB_cu_cp_ue_id = req->gNB_cu_cp_ue_id,
|
||||
.gNB_cu_up_ue_id = req->gNB_cu_up_ue_id,
|
||||
.numPDUSessionsMod = req->numPDUSessionsMod,
|
||||
};
|
||||
|
||||
instance_t f1inst = get_f1_gtp_instance();
|
||||
@@ -267,6 +325,7 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
|
||||
req->pduSessionMod[i].numDRB2Modify);
|
||||
modif.pduSessionMod[i].id = req->pduSessionMod[i].sessionId;
|
||||
modif.pduSessionMod[i].numDRBModified = req->pduSessionMod[i].numDRB2Modify;
|
||||
modif.numPDUSessionsMod++;
|
||||
/* DRBs to modify */
|
||||
for (int j = 0; j < req->pduSessionMod[i].numDRB2Modify; j++) {
|
||||
const DRB_nGRAN_to_mod_t *to_modif = &req->pduSessionMod[i].DRBnGRanModList[j];
|
||||
@@ -330,6 +389,15 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
|
||||
}
|
||||
}
|
||||
|
||||
/* PDU Session Resource To Remove List (see 9.3.3.12 of TS 38.463) */
|
||||
for (int i = 0; i < req->numPDUSessionsRem; i++) {
|
||||
modif.pduSessionMod[i].id = req->pduSessionRem[i].sessionId;
|
||||
modif.numPDUSessionsMod++;
|
||||
release_gtpu_tunnel(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId);
|
||||
release_f1_drbs(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId);
|
||||
nr_sdap_delete_entity(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId);
|
||||
}
|
||||
|
||||
get_e1_if()->bearer_modif_response(&modif);
|
||||
}
|
||||
|
||||
|
||||
@@ -575,75 +575,32 @@ void add_srb(int is_gnb,
|
||||
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
|
||||
}
|
||||
|
||||
void add_drb(int is_gnb,
|
||||
ue_id_t UEid,
|
||||
struct NR_DRB_ToAddMod *s,
|
||||
const nr_pdcp_entity_security_keys_and_algos_t *security_parameters)
|
||||
void nr_pdcp_add_drb(int is_gnb,
|
||||
const ue_id_t UEid,
|
||||
const NR_PDCP_Config_t *pdcp,
|
||||
const sdap_config_t *sdap,
|
||||
const nr_pdcp_entity_security_keys_and_algos_t *security_parameters)
|
||||
{
|
||||
nr_pdcp_entity_t *pdcp_drb;
|
||||
nr_pdcp_ue_t *ue;
|
||||
|
||||
int drb_id = s->drb_Identity;
|
||||
if (!s->pdcp_Config || !s->pdcp_Config->drb) {
|
||||
LOG_E(PDCP, "DRB field mandatory present at setup. DRB %d not configured.\n", drb_id);
|
||||
if (!pdcp->drb) {
|
||||
LOG_E(PDCP, "Missing PDCP config. DRB %d not configured.\n", sdap->drb_id);
|
||||
return;
|
||||
}
|
||||
|
||||
struct NR_PDCP_Config__drb *drb = s->pdcp_Config->drb;
|
||||
struct NR_PDCP_Config__drb *drb = pdcp->drb;
|
||||
int sn_size_ul = decode_sn_size_ul(*drb->pdcp_SN_SizeUL);
|
||||
int sn_size_dl = decode_sn_size_dl(*drb->pdcp_SN_SizeDL);
|
||||
int discard_timer = decode_discard_timer(*drb->discardTimer);
|
||||
|
||||
int has_integrity;
|
||||
int has_ciphering;
|
||||
|
||||
/* if pdcp_Config->t_Reordering is not present, it means infinity (-1) */
|
||||
int t_reordering = -1;
|
||||
if (s->pdcp_Config->t_Reordering != NULL) {
|
||||
t_reordering = decode_t_reordering(*s->pdcp_Config->t_Reordering);
|
||||
if (pdcp->t_Reordering != NULL) {
|
||||
t_reordering = decode_t_reordering(*pdcp->t_Reordering);
|
||||
}
|
||||
|
||||
if (drb->integrityProtection != NULL)
|
||||
has_integrity = 1;
|
||||
else
|
||||
has_integrity = 0;
|
||||
|
||||
if (s->pdcp_Config->ext1 != NULL && s->pdcp_Config->ext1->cipheringDisabled != NULL)
|
||||
has_ciphering = 0;
|
||||
else
|
||||
has_ciphering = 1;
|
||||
|
||||
if ((!s->cnAssociation) || s->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_NOTHING) {
|
||||
LOG_E(PDCP, "fatal, cnAssociation is missing or present is NR_DRB_ToAddMod__cnAssociation_PR_NOTHING\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int pdusession_id;
|
||||
bool has_sdap_rx = false;
|
||||
bool has_sdap_tx = false;
|
||||
bool is_sdap_DefaultDRB = false;
|
||||
NR_QFI_t *mappedQFIs2Add = NULL;
|
||||
uint8_t mappedQFIs2AddCount=0;
|
||||
if (s->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity)
|
||||
pdusession_id = s->cnAssociation->choice.eps_BearerIdentity;
|
||||
else {
|
||||
if (!s->cnAssociation->choice.sdap_Config) {
|
||||
LOG_E(PDCP,"fatal, sdap_Config is null");
|
||||
exit(-1);
|
||||
}
|
||||
pdusession_id = s->cnAssociation->choice.sdap_Config->pdu_Session;
|
||||
has_sdap_rx = is_sdap_rx(is_gnb, s->cnAssociation->choice.sdap_Config);
|
||||
has_sdap_tx = is_sdap_tx(is_gnb, s->cnAssociation->choice.sdap_Config);
|
||||
is_sdap_DefaultDRB = s->cnAssociation->choice.sdap_Config->defaultDRB == true ? 1 : 0;
|
||||
mappedQFIs2Add = (NR_QFI_t*)s->cnAssociation->choice.sdap_Config->mappedQoS_FlowsToAdd->list.array[0];
|
||||
mappedQFIs2AddCount = s->cnAssociation->choice.sdap_Config->mappedQoS_FlowsToAdd->list.count;
|
||||
LOG_D(SDAP, "Captured mappedQoS_FlowsToAdd from RRC: %ld \n", *mappedQFIs2Add);
|
||||
}
|
||||
/* TODO(?): accept different UL and DL SN sizes? */
|
||||
if (sn_size_ul != sn_size_dl) {
|
||||
LOG_E(PDCP, "fatal, bad SN sizes, must be same. ul=%d, dl=%d\n", sn_size_ul, sn_size_dl);
|
||||
exit(1);
|
||||
}
|
||||
int has_integrity = (drb->integrityProtection != NULL) ? 1 : 0;
|
||||
int has_ciphering = (pdcp->ext1 != NULL && pdcp->ext1->cipheringDisabled != NULL) ? 0 : 1;
|
||||
|
||||
/* get actual ciphering and integrity algorithm based on pdcp_Config */
|
||||
nr_pdcp_entity_security_keys_and_algos_t actual_security_parameters = *security_parameters;
|
||||
@@ -652,29 +609,33 @@ void add_drb(int is_gnb,
|
||||
|
||||
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
|
||||
ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, UEid);
|
||||
if (nr_pdcp_get_rb(ue, drb_id, false) != NULL) {
|
||||
LOG_W(PDCP, "warning DRB %d already exist for UE ID %ld, do nothing\n", drb_id, UEid);
|
||||
if (nr_pdcp_get_rb(ue, sdap->drb_id, false) != NULL) {
|
||||
LOG_W(PDCP, "warning DRB %d already exist for UE ID %ld, do nothing\n", sdap->drb_id, UEid);
|
||||
} else {
|
||||
pdcp_drb = new_nr_pdcp_entity(NR_PDCP_DRB_AM, is_gnb, drb_id, pdusession_id,
|
||||
has_sdap_rx, has_sdap_tx, deliver_sdu_drb, ue,
|
||||
is_gnb ?
|
||||
deliver_pdu_drb_gnb : deliver_pdu_drb_ue,
|
||||
ue,
|
||||
sn_size_dl, t_reordering, discard_timer,
|
||||
&actual_security_parameters);
|
||||
nr_pdcp_ue_add_drb_pdcp_entity(ue, drb_id, pdcp_drb);
|
||||
// code assumption: same SN size for both DL and UL
|
||||
if (sn_size_ul != sn_size_dl) {
|
||||
LOG_E(PDCP, "fatal, bad SN sizes, must be same. ul=%d, dl=%d\n", sn_size_ul, sn_size_dl);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
LOG_I(PDCP, "added drb %d to UE ID %ld\n", drb_id, UEid);
|
||||
/* add new SDAP entity for the PDU session the DRB belongs to */
|
||||
new_nr_sdap_entity(is_gnb,
|
||||
has_sdap_rx,
|
||||
has_sdap_tx,
|
||||
UEid,
|
||||
pdusession_id,
|
||||
is_sdap_DefaultDRB,
|
||||
drb_id,
|
||||
mappedQFIs2Add,
|
||||
mappedQFIs2AddCount);
|
||||
// add PDCP entity
|
||||
nr_pdcp_entity_t *pdcp_drb = new_nr_pdcp_entity(NR_PDCP_DRB_AM,
|
||||
is_gnb,
|
||||
sdap->drb_id,
|
||||
sdap->pdusession_id,
|
||||
(sdap->role & (SDAP_UL_RX | SDAP_DL_RX)) != 0,
|
||||
(sdap->role & (SDAP_UL_TX | SDAP_DL_TX)) != 0,
|
||||
deliver_sdu_drb,
|
||||
ue,
|
||||
is_gnb ? deliver_pdu_drb_gnb : deliver_pdu_drb_ue,
|
||||
ue,
|
||||
sn_size_dl,
|
||||
t_reordering,
|
||||
discard_timer,
|
||||
&actual_security_parameters);
|
||||
nr_pdcp_ue_add_drb_pdcp_entity(ue, sdap->drb_id, pdcp_drb);
|
||||
|
||||
LOG_I(PDCP, "Added DRB %d to UE ID %ld\n", sdap->drb_id, UEid);
|
||||
}
|
||||
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
|
||||
}
|
||||
@@ -692,20 +653,28 @@ void nr_pdcp_add_srbs(eNB_flag_t enb_flag,
|
||||
LOG_W(PDCP, "nr_pdcp_add_srbs() with void list\n");
|
||||
}
|
||||
|
||||
void nr_pdcp_add_drbs(eNB_flag_t enb_flag,
|
||||
ue_id_t UEid,
|
||||
NR_DRB_ToAddModList_t *const drb2add_list,
|
||||
const nr_pdcp_entity_security_keys_and_algos_t *security_parameters)
|
||||
/** @brief Get DRB IDs for a specific PDU session */
|
||||
int nr_pdcp_get_drb_ids_for_pdusession(ue_id_t ue_id, long pdusession_id, int *drb_ids)
|
||||
{
|
||||
if (drb2add_list != NULL) {
|
||||
for (int i = 0; i < drb2add_list->list.count; i++) {
|
||||
add_drb(enb_flag,
|
||||
UEid,
|
||||
drb2add_list->list.array[i],
|
||||
security_parameters);
|
||||
int num_found = 0;
|
||||
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
|
||||
nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id);
|
||||
if (ue) {
|
||||
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
|
||||
nr_pdcp_entity_t *drb = ue->drb[i];
|
||||
if (drb && drb->pdusession_id == pdusession_id) {
|
||||
if (num_found < MAX_DRBS_PER_UE) {
|
||||
drb_ids[num_found] = drb->rb_id;
|
||||
num_found++;
|
||||
} else {
|
||||
LOG_W(PDCP, "Found more DRBs than MAX_DRBS_PER_UE=%d\n", MAX_DRBS_PER_UE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
LOG_W(PDCP, "nr_pdcp_add_drbs() with void list\n");
|
||||
}
|
||||
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
|
||||
return num_found;
|
||||
}
|
||||
|
||||
uint64_t get_pdcp_optmask(void)
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "nr_pdcp_ue_manager.h"
|
||||
struct NR_DRB_ToAddMod;
|
||||
struct NR_SRB_ToAddMod;
|
||||
struct sdap_configuration_s;
|
||||
|
||||
void nr_pdcp_layer_init(void);
|
||||
|
||||
@@ -42,20 +43,16 @@ bool nr_pdcp_data_ind(const protocol_ctxt_t *const ctxt_pP,
|
||||
const sdu_size_t sdu_buffer_size,
|
||||
uint8_t *const sdu_buffer);
|
||||
|
||||
void nr_pdcp_add_drbs(eNB_flag_t enb_flag,
|
||||
ue_id_t UEid,
|
||||
NR_DRB_ToAddModList_t *const drb2add_list,
|
||||
const nr_pdcp_entity_security_keys_and_algos_t *security_parameters);
|
||||
|
||||
void nr_pdcp_add_srbs(eNB_flag_t enb_flag,
|
||||
ue_id_t UEid,
|
||||
NR_SRB_ToAddModList_t *const srb2add_list,
|
||||
const nr_pdcp_entity_security_keys_and_algos_t *security_parameters);
|
||||
|
||||
void add_drb(int is_gnb,
|
||||
ue_id_t UEid,
|
||||
struct NR_DRB_ToAddMod *s,
|
||||
const nr_pdcp_entity_security_keys_and_algos_t *security_parameters);
|
||||
void nr_pdcp_add_drb(int is_gnb,
|
||||
const ue_id_t UEid,
|
||||
const NR_PDCP_Config_t *pdcp,
|
||||
const struct sdap_configuration_s *sdap,
|
||||
const nr_pdcp_entity_security_keys_and_algos_t *security_parameters);
|
||||
|
||||
void nr_pdcp_remove_UE(ue_id_t ue_id);
|
||||
void nr_pdcp_reestablishment(ue_id_t ue_id,
|
||||
@@ -69,6 +66,7 @@ void nr_pdcp_reconfigure_srb(ue_id_t ue_id, int srb_id, long t_Reordering);
|
||||
void nr_pdcp_reconfigure_drb(ue_id_t ue_id, int drb_id, NR_PDCP_Config_t *pdcp_config);
|
||||
void nr_pdcp_release_srb(ue_id_t ue_id, int srb_id);
|
||||
void nr_pdcp_release_drb(ue_id_t ue_id, int drb_id);
|
||||
int nr_pdcp_get_drb_ids_for_pdusession(ue_id_t ue_id, long pdusession_id, int *drb_ids);
|
||||
|
||||
void add_srb(int is_gnb,
|
||||
ue_id_t UEid,
|
||||
|
||||
@@ -178,7 +178,7 @@ void nr_pdcp_ue_add_srb_pdcp_entity(nr_pdcp_ue_t *ue, int srb_id, nr_pdcp_entity
|
||||
/* must be called with lock acquired */
|
||||
void nr_pdcp_ue_add_drb_pdcp_entity(nr_pdcp_ue_t *ue, int drb_id, nr_pdcp_entity_t *entity)
|
||||
{
|
||||
if (drb_id < 1 || drb_id > 5) {
|
||||
if (drb_id < 1 || drb_id > MAX_DRBS_PER_UE) {
|
||||
LOG_E(PDCP, "%s:%d:%s: fatal, bad drb id\n", __FILE__, __LINE__, __FUNCTION__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -672,7 +672,7 @@ void nr_rlc_reconfigure_entity(int ue_id, int lc_id, NR_RLC_Config_t *rlc_Config
|
||||
|
||||
void nr_rlc_add_srb(int ue_id, int srb_id, const NR_RLC_BearerConfig_t *rlc_BearerConfig)
|
||||
{
|
||||
struct NR_RLC_Config *r = rlc_BearerConfig->rlc_Config;
|
||||
NR_RLC_Config_t *r = rlc_BearerConfig->rlc_Config;
|
||||
int t_status_prohibit;
|
||||
int t_poll_retransmit;
|
||||
int poll_pdu;
|
||||
@@ -682,8 +682,7 @@ void nr_rlc_add_srb(int ue_id, int srb_id, const NR_RLC_BearerConfig_t *rlc_Bear
|
||||
int sn_field_length;
|
||||
|
||||
LOG_D(RLC, "Trying to add SRB %d\n", srb_id);
|
||||
AssertFatal(srb_id > 0 && srb_id < 4,
|
||||
"Invalid srb id %d\n", srb_id);
|
||||
AssertFatal(srb_id > 0 && srb_id < 4, "Invalid srb id %d\n", srb_id);
|
||||
|
||||
if (r && r->present == NR_RLC_Config_PR_am) {
|
||||
struct NR_RLC_Config__am *am;
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "nfapi/oai_integration/gnb_ind_vars.h"
|
||||
#include "nfapi/oai_integration/vendor_ext.h"
|
||||
#include "nfapi_interface.h"
|
||||
#include "openair2/NR_PHY_INTERFACE/nr_sched_response.h"
|
||||
#include "openair2/PHY_INTERFACE/queue_t.h"
|
||||
#include "utils.h"
|
||||
#include "nfapi/oai_integration/nfapi_pnf.h"
|
||||
@@ -379,52 +378,25 @@ static void match_crc_rx_pdu(nfapi_nr_rx_data_indication_t *rx_ind, nfapi_nr_crc
|
||||
}
|
||||
}
|
||||
|
||||
extern void handle_nr_slot_ind(uint16_t sfn, uint16_t slot);
|
||||
static void pnf_send_slot_ind(module_id_t module_id, int CC_id, int frame, int slot)
|
||||
extern void handle_nr_slot_ind(uint16_t sfn, uint16_t slot, NR_Sched_Rsp_t *sched_response);
|
||||
static void pnf_send_slot_ind(const nfapi_nr_slot_indication_scf_t *ind, NR_Sched_Rsp_t *rsp)
|
||||
{
|
||||
(void)module_id;
|
||||
(void)CC_id;
|
||||
handle_nr_slot_ind(frame, slot);
|
||||
module_id_t module_id = 0;
|
||||
int CC_id = 0;
|
||||
reset_sched_response(rsp, ind->sfn, ind->slot, module_id, CC_id);
|
||||
handle_nr_slot_ind(ind->sfn, ind->slot, rsp);
|
||||
}
|
||||
|
||||
static void run_scheduler_monolithic(module_id_t module_id, int CC_id, int frame, int slot)
|
||||
static void run_scheduler_monolithic(const nfapi_nr_slot_indication_scf_t *ind, NR_Sched_Rsp_t *rsp)
|
||||
{
|
||||
NR_IF_Module_t *ifi = nr_if_inst[module_id];
|
||||
|
||||
LOG_D(NR_MAC, "Calling scheduler for %d.%d\n", frame, slot);
|
||||
NR_Sched_Rsp_t *sched_info = allocate_sched_response();
|
||||
|
||||
// clear UL DCI prior to handling ULSCH
|
||||
sched_info->UL_dci_req.numPdus = 0;
|
||||
gNB_dlsch_ulsch_scheduler(module_id, frame, slot, sched_info);
|
||||
ifi->CC_mask = 0;
|
||||
sched_info->module_id = module_id;
|
||||
sched_info->CC_id = CC_id;
|
||||
sched_info->frame = frame;
|
||||
sched_info->slot = slot;
|
||||
/*
|
||||
sched_info->DL_req = &mac->DL_req[CC_id];
|
||||
sched_info->UL_dci_req = &mac->UL_dci_req[CC_id];
|
||||
|
||||
sched_info->UL_tti_req = mac->UL_tti_req[CC_id];
|
||||
|
||||
sched_info->TX_req = &mac->TX_req[CC_id];
|
||||
*/
|
||||
#ifdef DUMP_FAPI
|
||||
dump_dl(sched_info);
|
||||
#endif
|
||||
|
||||
AssertFatal(ifi->NR_Schedule_response != NULL, "nr_schedule_response is null (mod %d, cc %d)\n", module_id, CC_id);
|
||||
ifi->NR_Schedule_response(sched_info);
|
||||
|
||||
LOG_D(NR_PHY,
|
||||
"NR_Schedule_response: SFN SLOT:%d %d dl_pdus:%d\n",
|
||||
sched_info->frame,
|
||||
sched_info->slot,
|
||||
sched_info->DL_req.dl_tti_request_body.nPDUs);
|
||||
module_id_t module_id = 0;
|
||||
int CC_id = 0;
|
||||
reset_sched_response(rsp, ind->sfn, ind->slot, module_id, CC_id);
|
||||
gNB_dlsch_ulsch_scheduler(rsp->module_id, ind->sfn, ind->slot, rsp);
|
||||
}
|
||||
|
||||
void NR_UL_indication(NR_UL_IND_t *UL_info) {
|
||||
static void NR_UL_indication(NR_UL_IND_t *UL_info)
|
||||
{
|
||||
AssertFatal(UL_info!=NULL,"UL_info is null\n");
|
||||
module_id_t module_id = UL_info->module_id;
|
||||
int CC_id = UL_info->CC_id;
|
||||
@@ -501,17 +473,47 @@ NR_IF_Module_t *NR_IF_Module_init(int Mod_id) {
|
||||
|
||||
nr_if_inst[Mod_id]->CC_mask=0;
|
||||
nr_if_inst[Mod_id]->NR_UL_indication = NR_UL_indication;
|
||||
if (NFAPI_MODE == NFAPI_MONOLITHIC)
|
||||
if (NFAPI_MODE == NFAPI_MONOLITHIC) {
|
||||
nr_if_inst[Mod_id]->NR_slot_indication = run_scheduler_monolithic;
|
||||
else if (NFAPI_MODE == NFAPI_MODE_PNF)
|
||||
} else if (NFAPI_MODE == NFAPI_MODE_PNF) {
|
||||
nr_if_inst[Mod_id]->NR_slot_indication = pnf_send_slot_ind;
|
||||
else // NFAPI_MODE_VNF
|
||||
NULL;
|
||||
} else { // NFAPI_MODE_VNF
|
||||
DevAssert(NFAPI_MODE == NFAPI_MODE_VNF || NFAPI_MODE == NFAPI_MODE_AERIAL);
|
||||
nr_if_inst[Mod_id]->NR_slot_indication = run_scheduler_monolithic;
|
||||
}
|
||||
AssertFatal(pthread_mutex_init(&nr_if_inst[Mod_id]->if_mutex,NULL)==0,
|
||||
"allocation of nr_if_inst[%d]->if_mutex fails\n",Mod_id);
|
||||
}
|
||||
|
||||
init_sched_response();
|
||||
|
||||
return nr_if_inst[Mod_id];
|
||||
}
|
||||
|
||||
void reset_sched_response(NR_Sched_Rsp_t *sched_response, int frame, int slot, int module_id, int CC_id)
|
||||
{
|
||||
sched_response->module_id = module_id;
|
||||
sched_response->CC_id = CC_id;
|
||||
sched_response->frame = frame;
|
||||
sched_response->slot = slot;
|
||||
|
||||
|
||||
nfapi_nr_dl_tti_request_t *DL_req = &sched_response->DL_req;
|
||||
DL_req->SFN = frame;
|
||||
DL_req->Slot = slot;
|
||||
DL_req->dl_tti_request_body.nPDUs = 0;
|
||||
DL_req->dl_tti_request_body.nGroup = 0;
|
||||
|
||||
nfapi_nr_ul_dci_request_t *UL_dci_req = &sched_response->UL_dci_req;
|
||||
UL_dci_req->SFN = frame;
|
||||
UL_dci_req->Slot = slot;
|
||||
UL_dci_req->numPdus = 0;
|
||||
|
||||
nfapi_nr_ul_tti_request_t *UL_tti_req = &sched_response->UL_tti_req;
|
||||
UL_tti_req->SFN = frame;
|
||||
UL_tti_req->Slot = slot;
|
||||
UL_tti_req->n_pdus = 0;
|
||||
|
||||
nfapi_nr_tx_data_request_t *TX_req = &sched_response->TX_req;
|
||||
TX_req->SFN = frame;
|
||||
TX_req->Slot = slot;
|
||||
TX_req->Number_of_PDUs = 0;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ typedef struct {
|
||||
// Downlink slot P7
|
||||
|
||||
|
||||
typedef struct {
|
||||
typedef struct NR_Sched_Rsp {
|
||||
/// the ID of this sched_response - used by sched_reponse memory management
|
||||
int sched_response_id;
|
||||
/// Module ID
|
||||
@@ -111,6 +111,7 @@ typedef struct {
|
||||
/// Pointers to DL SDUs
|
||||
nfapi_nr_tx_data_request_t TX_req;
|
||||
} NR_Sched_Rsp_t;
|
||||
void reset_sched_response(NR_Sched_Rsp_t *sched_response, int frame, int slot, int module_id, int CC_id);
|
||||
|
||||
typedef struct {
|
||||
uint8_t Mod_id;
|
||||
@@ -121,8 +122,7 @@ typedef struct {
|
||||
typedef struct NR_IF_Module_s {
|
||||
//define the function pointer
|
||||
void (*NR_UL_indication)(NR_UL_IND_t *UL_INFO);
|
||||
void (*NR_Schedule_response)(NR_Sched_Rsp_t *Sched_INFO);
|
||||
void (*NR_slot_indication)(module_id_t module_idP, int CC_id, int frame, int slot);
|
||||
void (*NR_slot_indication)(const nfapi_nr_slot_indication_scf_t *ind, NR_Sched_Rsp_t *rsp);
|
||||
void (*NR_PHY_config_req)(NR_PHY_Config_t *config_INFO);
|
||||
uint32_t CC_mask;
|
||||
uint16_t current_frame;
|
||||
@@ -136,9 +136,4 @@ NR_IF_Module_t *NR_IF_Module_init(int Mod_id);
|
||||
|
||||
void NR_IF_Module_kill(int Mod_id);
|
||||
|
||||
void NR_UL_indication(NR_UL_IND_t *UL_INFO);
|
||||
|
||||
/*Interface for Downlink, transmitting the DLSCH SDU, DCI SDU*/
|
||||
void NR_Schedule_Response(NR_Sched_Rsp_t *Sched_INFO);
|
||||
|
||||
#endif /*_NFAPI_INTERFACE_NR_H_*/
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/* sched response memory management */
|
||||
|
||||
/* A system with reference counting is implemented.
|
||||
* It is needed because several threads will access the structure,
|
||||
* which has to be released only when all the threads are done with it.
|
||||
* So the main thread allocates a sched_response with allocate_sched_response()
|
||||
* which allocates a sched_response with a reference counter == 1
|
||||
* and then for each other thread that will access sched_response, a
|
||||
* call to inc_ref_sched_response() is done. When a thread has finished
|
||||
* using the sched_response, it calls deref_sched_response() which will
|
||||
* in turn call release_sched_response() when the reference counter becomes 0.
|
||||
*
|
||||
* The several threads in question are accessing the _same_ sched_response,
|
||||
* it has not to be confused with the various TX processes that may run in
|
||||
* parallel and which are accessing _different_ sched_response (the maximum
|
||||
* number of parallel processes is N_RESP).
|
||||
*/
|
||||
|
||||
#include "nr_sched_response.h"
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "NR_PHY_INTERFACE/NR_IF_Module.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "common/utils/assertions.h"
|
||||
|
||||
#define N_RESP 3
|
||||
static NR_Sched_Rsp_t resp[N_RESP];
|
||||
static int resp_refcount[N_RESP];
|
||||
static int resp_freelist_next[N_RESP];
|
||||
static int resp_freelist_head;
|
||||
static pthread_mutex_t resp_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static bool resp_freelist_inited = false;
|
||||
|
||||
void init_sched_response(void)
|
||||
{
|
||||
/* init only once */
|
||||
if (pthread_mutex_lock(&resp_mutex))
|
||||
AssertFatal(0, "pthread_mutex_lock failed\n");
|
||||
if (resp_freelist_inited) {
|
||||
if (pthread_mutex_unlock(&resp_mutex))
|
||||
AssertFatal(0, "pthread_mutex_unlock failed\n");
|
||||
return;
|
||||
}
|
||||
resp_freelist_inited = true;
|
||||
if (pthread_mutex_unlock(&resp_mutex))
|
||||
AssertFatal(0, "pthread_mutex_unlock failed\n");
|
||||
|
||||
int i;
|
||||
for (i = 0; i < N_RESP - 1; i++)
|
||||
resp_freelist_next[i] = i + 1;
|
||||
resp_freelist_next[N_RESP - 1] = -1;
|
||||
resp_freelist_head = 0;
|
||||
}
|
||||
|
||||
NR_Sched_Rsp_t *allocate_sched_response(void)
|
||||
{
|
||||
NR_Sched_Rsp_t *ret;
|
||||
int new_head;
|
||||
|
||||
if (pthread_mutex_lock(&resp_mutex))
|
||||
AssertFatal(0, "pthread_mutex_lock failed\n");
|
||||
|
||||
AssertFatal(resp_freelist_inited, "sched_response used before init\n");
|
||||
|
||||
if (resp_freelist_head == -1) {
|
||||
LOG_E(PHY, "fatal: sched_response cannot be allocated, increase N_RESP\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = &resp[resp_freelist_head];
|
||||
ret->sched_response_id = resp_freelist_head;
|
||||
resp_refcount[resp_freelist_head] = 1;
|
||||
|
||||
new_head = resp_freelist_next[resp_freelist_head];
|
||||
resp_freelist_next[resp_freelist_head] = -1;
|
||||
resp_freelist_head = new_head;
|
||||
|
||||
if (pthread_mutex_unlock(&resp_mutex))
|
||||
AssertFatal(0, "pthread_mutex_unlock failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void release_sched_response(int sched_response_id)
|
||||
{
|
||||
LOG_D(NR_MAC,"Releasing sched_response %d\n",sched_response_id);
|
||||
resp_freelist_next[sched_response_id] = resp_freelist_head;
|
||||
resp_freelist_head = sched_response_id;
|
||||
}
|
||||
|
||||
void deref_sched_response(int sched_response_id)
|
||||
{
|
||||
/* simulators (ulsim/dlsim) deal with their own sched_response but call
|
||||
* functions that call this one, let's handle this case with a special
|
||||
* value -1 where we do nothing (yes it's a hack)
|
||||
*/
|
||||
if (sched_response_id == -1)
|
||||
return;
|
||||
|
||||
if (pthread_mutex_lock(&resp_mutex))
|
||||
AssertFatal(0, "pthread_mutex_lock failed\n");
|
||||
|
||||
AssertFatal(resp_freelist_inited, "sched_response used before init\n");
|
||||
AssertFatal(resp_refcount[sched_response_id] > 0, "sched_reponse decreased too much\n");
|
||||
|
||||
LOG_D(NR_MAC,"resp_refcount[%d] %d\n",sched_response_id,resp_refcount[sched_response_id]);
|
||||
resp_refcount[sched_response_id]--;
|
||||
if (resp_refcount[sched_response_id] == 0)
|
||||
release_sched_response(sched_response_id);
|
||||
|
||||
if (pthread_mutex_unlock(&resp_mutex))
|
||||
AssertFatal(0, "pthread_mutex_unlock failed\n");
|
||||
}
|
||||
|
||||
void inc_ref_sched_response(int sched_response_id)
|
||||
{
|
||||
/* simulators (ulsim/dlsim) deal with their own sched_response but call
|
||||
* functions that call this one, let's handle this case with a special
|
||||
* value -1 where we do nothing (yes it's a hack)
|
||||
*/
|
||||
if (sched_response_id == -1)
|
||||
return;
|
||||
|
||||
LOG_D(NR_MAC,"Incrementing sched_resp resp_refounct[%d] = %d\n",
|
||||
sched_response_id,resp_refcount[sched_response_id]);
|
||||
if (pthread_mutex_lock(&resp_mutex))
|
||||
AssertFatal(0, "pthread_mutex_lock failed\n");
|
||||
|
||||
AssertFatal(resp_freelist_inited, "sched_response used before init\n");
|
||||
|
||||
resp_refcount[sched_response_id]++;
|
||||
|
||||
if (pthread_mutex_unlock(&resp_mutex))
|
||||
AssertFatal(0, "pthread_mutex_unlock failed\n");
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef OPENAIR2_NR_PHY_INTERFACE_NR_SCHED_RESPONSE_H
|
||||
#define OPENAIR2_NR_PHY_INTERFACE_NR_SCHED_RESPONSE_H
|
||||
|
||||
#include "NR_IF_Module.h"
|
||||
|
||||
void init_sched_response(void);
|
||||
NR_Sched_Rsp_t *allocate_sched_response(void);
|
||||
void deref_sched_response(int sched_response_id);
|
||||
void inc_ref_sched_response(int sched_response_id);
|
||||
|
||||
#endif /* OPENAIR2_NR_PHY_INTERFACE_NR_SCHED_RESPONSE_H */
|
||||
@@ -82,7 +82,7 @@ void print_ue_mac_stats(const module_id_t mod, const int frame_rx, const int slo
|
||||
for (int i = 1; i < nb + 1; i++)
|
||||
cur += snprintf(cur, end - cur, "/%lu", mac->stats.dl.rounds[i]);
|
||||
|
||||
cur += snprintf(cur, end - cur, "\n Ul harq: %lu", mac->stats.ul.rounds[0]);
|
||||
cur += snprintf(cur, end - cur, "\n UL harq: %lu", mac->stats.ul.rounds[0]);
|
||||
for (nb = NR_MAX_HARQ_ROUNDS_FOR_STATS - 1; nb > 1; nb--)
|
||||
if (mac->stats.ul.rounds[nb])
|
||||
break;
|
||||
|
||||
@@ -519,7 +519,13 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re
|
||||
cfg->drb_ToAddModList = params->drb_config_list;
|
||||
cfg->securityConfig = params->security_config;
|
||||
cfg->srb3_ToRelease = NULL;
|
||||
cfg->drb_ToReleaseList = params->drb_release_list;
|
||||
if (params->n_drb_rel) {
|
||||
asn1cCalloc(cfg->drb_ToReleaseList, to_release);
|
||||
for (int i = 0; i < params->n_drb_rel; i++) {
|
||||
asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release);
|
||||
*DRB_release = params->drb_rel[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* measConfig */
|
||||
@@ -1020,7 +1026,7 @@ int do_RRCReestablishment(int8_t nh_ncc, uint8_t *const buffer, size_t buffer_si
|
||||
rrcReestablishment->criticalExtensions.choice.rrcReestablishment = CALLOC(1, sizeof(NR_RRCReestablishment_IEs_t));
|
||||
|
||||
// 3GPP TS 33.501 Section 6.11 Security handling for RRC connection re-establishment procedure
|
||||
rrcReestablishment->criticalExtensions.choice.rrcReestablishment->nextHopChainingCount = nh_ncc >= 0 ? nh_ncc : 0;
|
||||
rrcReestablishment->criticalExtensions.choice.rrcReestablishment->nextHopChainingCount = nh_ncc;
|
||||
rrcReestablishment->criticalExtensions.choice.rrcReestablishment->lateNonCriticalExtension = NULL;
|
||||
rrcReestablishment->criticalExtensions.choice.rrcReestablishment->nonCriticalExtension = NULL;
|
||||
|
||||
|
||||
@@ -64,7 +64,8 @@ typedef struct {
|
||||
uint8_t transaction_id;
|
||||
NR_SRB_ToAddModList_t *srb_config_list;
|
||||
NR_DRB_ToAddModList_t *drb_config_list;
|
||||
NR_DRB_ToReleaseList_t *drb_release_list;
|
||||
int *drb_rel;
|
||||
int n_drb_rel;
|
||||
NR_SecurityConfig_t *security_config;
|
||||
NR_MeasConfig_t *meas_config;
|
||||
byte_array_t dedicated_NAS_msg_list[MAX_DRBS_PER_UE];
|
||||
|
||||
@@ -88,7 +88,6 @@ TEST(nr_asn1, paging)
|
||||
void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params)
|
||||
{
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_MeasConfig, params.meas_config);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, params.drb_release_list);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, params.drb_config_list);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, params.srb_config_list);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_SecurityConfig, params.security_config);
|
||||
|
||||
@@ -97,7 +97,6 @@ typedef enum pdu_session_satus_e {
|
||||
PDU_SESSION_STATUS_TOMODIFY, // ENDC NSA
|
||||
PDU_SESSION_STATUS_FAILED,
|
||||
PDU_SESSION_STATUS_TORELEASE, // to release DRB between eNB and UE
|
||||
PDU_SESSION_STATUS_RELEASED
|
||||
} pdu_session_status_t;
|
||||
|
||||
typedef struct pdusession_s {
|
||||
@@ -149,6 +148,12 @@ typedef enum {
|
||||
RRC_UECAPABILITY_ENQUIRY,
|
||||
} rrc_action_t;
|
||||
|
||||
/* Small state for delaying NG-triggered actions (setup/release) */
|
||||
typedef struct {
|
||||
int max_delays;
|
||||
bool ongoing_transaction;
|
||||
} delayed_action_state_t;
|
||||
|
||||
typedef struct nr_redcap_ue_cap {
|
||||
bool support_of_redcap_r17;
|
||||
bool support_of_16drb_redcap_r17;
|
||||
@@ -167,8 +172,6 @@ typedef struct nr_handover_context_s nr_handover_context_t;
|
||||
typedef struct gNB_RRC_UE_s {
|
||||
time_t last_seen; // last time this UE has been accessed
|
||||
|
||||
NR_DRB_ToReleaseList_t *DRB_ReleaseList;
|
||||
|
||||
NR_SRB_INFO_TABLE_ENTRY Srb[NR_NUM_SRB];
|
||||
NR_MeasConfig_t *measConfig;
|
||||
nr_handover_context_t *ho_context;
|
||||
@@ -188,7 +191,6 @@ typedef struct gNB_RRC_UE_s {
|
||||
|
||||
/* KgNB as derived from KASME received from EPC */
|
||||
uint8_t kgnb[32];
|
||||
int8_t kgnb_ncc;
|
||||
uint8_t nh[32];
|
||||
int8_t nh_ncc;
|
||||
|
||||
@@ -209,6 +211,8 @@ typedef struct gNB_RRC_UE_s {
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// Globally Unique AMF Identifier
|
||||
nr_guami_t ue_guami;
|
||||
// Serving PLMN of the UE
|
||||
plmn_id_t serving_plmn;
|
||||
|
||||
ngap_security_capabilities_t security_capabilities;
|
||||
//NSA block
|
||||
@@ -243,8 +247,7 @@ typedef struct gNB_RRC_UE_s {
|
||||
byte_array_t nas_pdu;
|
||||
|
||||
/* hack, see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() for more info */
|
||||
int max_delays_pdu_session;
|
||||
bool ongoing_pdusession_setup_request;
|
||||
delayed_action_state_t delayed_action;
|
||||
|
||||
nr_redcap_ue_cap_t *redcap_cap;
|
||||
} gNB_RRC_UE_t;
|
||||
|
||||
@@ -66,12 +66,6 @@ void *rrc_gnb_task(void *args_p);
|
||||
|
||||
int nr_rrc_reconfiguration_req(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, const int dl_bwp_id, const int ul_bwp_id);
|
||||
|
||||
void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc,
|
||||
gNB_RRC_UE_t *ue_p,
|
||||
uint8_t xid,
|
||||
uint32_t nas_length,
|
||||
uint8_t *nas_buffer);
|
||||
|
||||
NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid);
|
||||
|
||||
bool ue_associated_to_cuup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue);
|
||||
@@ -128,5 +122,6 @@ NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish);
|
||||
NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do_integrity, bool do_ciphering);
|
||||
void activate_srb(gNB_RRC_UE_t *UE, int srb_id);
|
||||
void e1_notify_pdcp_status(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const ngap_drb_status_t *drb_status);
|
||||
void init_delayed_action(delayed_action_state_t *delayed_action);
|
||||
|
||||
#endif
|
||||
|
||||
+196
-87
@@ -115,6 +115,97 @@
|
||||
|
||||
mui_t rrc_gNB_mui = 0;
|
||||
|
||||
/* Per-transaction max_delays counter to limit retry attempts */
|
||||
#define MAX_DELAYS 100
|
||||
|
||||
/** @brief clone and re-enqueue an NGAP message after delaying
|
||||
* delays the ongoing transaction (in msg_p) by setting a timer to wait
|
||||
* 10ms; upon expiry, delivers to RRC, which sends the message to itself */
|
||||
static void delay_transaction(MessageDef *msg_p, int wait_us)
|
||||
{
|
||||
MessagesIds id = ITTI_MSG_ID(msg_p);
|
||||
AssertFatal(id == NGAP_PDUSESSION_SETUP_REQ || id == NGAP_PDUSESSION_RELEASE_COMMAND,
|
||||
"delay_transaction(): unsupported message id %d\n",
|
||||
id);
|
||||
|
||||
MessageDef *new = itti_alloc_new_message(TASK_RRC_GNB, 0, id);
|
||||
|
||||
// Copy only the specific message struct, not the entire union.
|
||||
// The union (msg_t) contains all message types and is much larger than
|
||||
// the allocated space (which is sized for the specific message type only).
|
||||
if (id == NGAP_PDUSESSION_SETUP_REQ) {
|
||||
NGAP_PDUSESSION_SETUP_REQ(new) = NGAP_PDUSESSION_SETUP_REQ(msg_p);
|
||||
} else if (id == NGAP_PDUSESSION_RELEASE_COMMAND) {
|
||||
NGAP_PDUSESSION_RELEASE_COMMAND(new) = NGAP_PDUSESSION_RELEASE_COMMAND(msg_p);
|
||||
}
|
||||
|
||||
int instance = msg_p->ittiMsgHeader.originInstance;
|
||||
long timer_id;
|
||||
timer_setup(0, wait_us, TASK_RRC_GNB, instance, TIMER_ONE_SHOT, new, &timer_id);
|
||||
}
|
||||
|
||||
static void reset_delayed_action(delayed_action_state_t *delayed_action)
|
||||
{
|
||||
delayed_action->ongoing_transaction = false;
|
||||
delayed_action->max_delays = 0;
|
||||
}
|
||||
|
||||
void init_delayed_action(delayed_action_state_t *delayed_action)
|
||||
{
|
||||
delayed_action->ongoing_transaction = true;
|
||||
delayed_action->max_delays = MAX_DELAYS;
|
||||
}
|
||||
|
||||
/* \brief checks if any transaction is ongoing for any xid of this UE */
|
||||
static bool transaction_ongoing(const gNB_RRC_UE_t *UE)
|
||||
{
|
||||
for (int xid = 0; xid < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++xid) {
|
||||
if (UE->xids[xid] != RRC_ACTION_NONE)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @brief delay control: returns true if delayed, false if should proceed
|
||||
* This is a hack. We observed that with some UEs, PDU session requests might
|
||||
* come in quick succession, faster than the RRC reconfiguration for the PDU
|
||||
* session requests can be carried out (UE is doing reconfig, and second PDU
|
||||
* session request arrives). We don't have currently the means to "queue up"
|
||||
* these transactions, which would probably involve some rework of the RRC.
|
||||
* To still allow these requests to come in and succeed, we below check and delay transactions
|
||||
* for 10ms. However, to not accidentally end up in infinite loops, the
|
||||
* maximum number is capped on a per-UE basis as indicated in variable
|
||||
* max_delays_pdu_session. See commit 277f8da0 for more details. */
|
||||
static bool rrc_delay_transaction(instance_t instance, MessageDef *msg_p)
|
||||
{
|
||||
uint32_t cu_ue_id = 0;
|
||||
if (ITTI_MSG_ID(msg_p) == NGAP_PDUSESSION_SETUP_REQ) {
|
||||
cu_ue_id = NGAP_PDUSESSION_SETUP_REQ(msg_p).gNB_ue_ngap_id;
|
||||
} else if (ITTI_MSG_ID(msg_p) == NGAP_PDUSESSION_RELEASE_COMMAND) {
|
||||
cu_ue_id = NGAP_PDUSESSION_RELEASE_COMMAND(msg_p).gNB_ue_ngap_id;
|
||||
}
|
||||
AssertFatal(cu_ue_id > 0, "cu_ue_id not found in message %s\n", ITTI_MSG_NAME(msg_p));
|
||||
|
||||
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], cu_ue_id);
|
||||
DevAssert(ue_context_p);
|
||||
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
bool delay = UE->delayed_action.ongoing_transaction && UE->delayed_action.max_delays > 0;
|
||||
/* Check if any PDU session action is ongoing */
|
||||
if (delay || transaction_ongoing(UE)) {
|
||||
int wait_us = 10000;
|
||||
LOG_I(NR_RRC,
|
||||
"UE %d: ongoing transaction, delaying incoming transaction by %d us\n",
|
||||
UE->rrc_ue_id,
|
||||
wait_us);
|
||||
delay_transaction(msg_p, wait_us);
|
||||
UE->delayed_action.max_delays--;
|
||||
return true; /* delayed */
|
||||
}
|
||||
LOG_D(NR_RRC, "UE %d: no delayed action ongoing, proceeding with incoming transaction\n", UE->rrc_ue_id);
|
||||
return false; /* not delayed */
|
||||
}
|
||||
|
||||
typedef struct deliver_ue_ctxt_release_data_t {
|
||||
gNB_RRC_INST *rrc;
|
||||
f1ap_ue_context_rel_cmd_t *release_cmd;
|
||||
@@ -366,6 +457,10 @@ NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do
|
||||
LOG_D(NR_RRC, "PDU Session %d not found, skip\n", drb->pdusession_id);
|
||||
continue;
|
||||
}
|
||||
if (pduSession->status > PDU_SESSION_STATUS_TOMODIFY) {
|
||||
LOG_D(NR_RRC, "PDU Session %d is not to add/mod\n", drb->pdusession_id);
|
||||
continue;
|
||||
}
|
||||
pdusession_t *session = &pduSession->param;
|
||||
NR_DRB_ToAddMod_t *drb_ToAddMod = calloc_or_fail(1, sizeof(*drb_ToAddMod));
|
||||
drb_ToAddMod->drb_Identity = drb->drb_id;
|
||||
@@ -625,10 +720,10 @@ static NR_ReportConfigToAddMod_t *prepare_a3_event_report(const nr_a3_event_t *a
|
||||
|
||||
void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params)
|
||||
{
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, params.drb_release_list);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, params.drb_config_list);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, params.srb_config_list);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_SecurityConfig, params.security_config);
|
||||
free(params.drb_rel);
|
||||
for (int i = 0; i < params.num_nas_msg; i++)
|
||||
FREE_AND_ZERO_BYTE_ARRAY(params.dedicated_NAS_msg_list[i]);
|
||||
}
|
||||
@@ -706,19 +801,37 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC
|
||||
nr_rrc_reconfig_param_t params = {.cell_group_config = UE->masterCellGroup,
|
||||
.transaction_id = xid,
|
||||
.drb_config_list = DRBs,
|
||||
.drb_release_list = UE->DRB_ReleaseList,
|
||||
.meas_config = UE->measConfig,
|
||||
.srb_config_list = SRBs};
|
||||
UE->DRB_ReleaseList = NULL; // pointer transferred to params
|
||||
|
||||
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &UE->pduSessions) {
|
||||
pdusession_t *session = &item->param;
|
||||
// Collect NAS PDUs
|
||||
if (session->nas_pdu.len > 0) {
|
||||
params.dedicated_NAS_msg_list[params.num_nas_msg++] = session->nas_pdu;
|
||||
session->nas_pdu.buf = NULL;
|
||||
session->nas_pdu.len = 0;
|
||||
LOG_D(NR_RRC, "Transfer NAS info with size %ld to RRCReconfiguration params\n", session->nas_pdu.len);
|
||||
}
|
||||
// Collect DRBs to release for PDU sessions marked for release
|
||||
if (item->status == PDU_SESSION_STATUS_TORELEASE) {
|
||||
if (!params.drb_rel)
|
||||
params.drb_rel = calloc_or_fail(MAX_DRBS_PER_UE, sizeof(int));
|
||||
FOR_EACH_SEQ_ARR (drb_t *, drb, &UE->drbs) {
|
||||
if (drb->pdusession_id == session->pdusession_id) {
|
||||
if (params.n_drb_rel >= MAX_DRBS_PER_UE) {
|
||||
LOG_E(NR_RRC, "UE %d: Too many DRBs to release (max %d)\n", UE->rrc_ue_id, MAX_DRBS_PER_UE);
|
||||
break;
|
||||
}
|
||||
params.drb_rel[params.n_drb_rel++] = drb->drb_id;
|
||||
LOG_D(NR_RRC,
|
||||
"UE %d: Added DRB %d to release list for PDU session %d\n",
|
||||
UE->rrc_ue_id,
|
||||
drb->drb_id,
|
||||
session->pdusession_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (UE->nas_pdu.len > 0) {
|
||||
@@ -746,21 +859,24 @@ static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_
|
||||
{
|
||||
/* do not re-establish PDCP for any bearer */
|
||||
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue_p, 0, false);
|
||||
ue_p->xids[params.transaction_id] = RRC_PDUSESSION_ESTABLISH;
|
||||
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) {
|
||||
if (item->status == PDU_SESSION_STATUS_NEW) {
|
||||
item->status = PDU_SESSION_STATUS_DONE;
|
||||
continue;
|
||||
}
|
||||
// Set xid for all PDU sessions
|
||||
item->xid = params.transaction_id;
|
||||
}
|
||||
|
||||
// Set xid for RRC transaction
|
||||
ue_p->xids[params.transaction_id] = params.n_drb_rel > 0 ? RRC_PDUSESSION_RELEASE : RRC_PDUSESSION_ESTABLISH;
|
||||
|
||||
byte_array_t msg = rrc_gNB_encode_RRCReconfiguration(rrc, ue_p, params);
|
||||
if (msg.len <= 0) {
|
||||
LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
|
||||
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) {
|
||||
if (item->param.nas_pdu.buf)
|
||||
item->xid = params.transaction_id;
|
||||
if (item->status < PDU_SESSION_STATUS_ESTABLISHED) {
|
||||
item->status = PDU_SESSION_STATUS_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_UE_DL_EVENT(ue_p, "Generate RRCReconfiguration (bytes %ld, xid %d)\n", msg.len, params.transaction_id);
|
||||
const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration;
|
||||
nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len);
|
||||
@@ -831,44 +947,27 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t
|
||||
free_byte_array(msg);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc,
|
||||
gNB_RRC_UE_t *ue_p,
|
||||
uint8_t xid,
|
||||
uint32_t nas_length,
|
||||
uint8_t *nas_buffer)
|
||||
//-----------------------------------------------------------------------------
|
||||
static void rrc_gNB_send_f1_drb_release_request(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, int *drb_to_release, int n_drb_to_release)
|
||||
{
|
||||
nr_rrc_reconfig_param_t params = {.transaction_id = xid};
|
||||
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
|
||||
RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id);
|
||||
f1ap_ue_context_mod_req_t req = {
|
||||
.gNB_CU_ue_id = ue_p->rrc_ue_id,
|
||||
.gNB_DU_ue_id = ue_data.secondary_ue,
|
||||
.servCellIndex = 0,
|
||||
};
|
||||
req.plmn = malloc_or_fail(sizeof(*req.plmn));
|
||||
*req.plmn = rrc->configuration.plmn[0];
|
||||
req.nr_cellid = malloc_or_fail(sizeof(*req.nr_cellid));
|
||||
*req.nr_cellid = rrc->nr_cellid;
|
||||
req.drbs_rel = malloc_or_fail(sizeof(*req.drbs_rel));
|
||||
req.drbs_rel_len = n_drb_to_release;
|
||||
memcpy(req.drbs_rel, drb_to_release, n_drb_to_release * sizeof(int));
|
||||
|
||||
NR_DRB_ToReleaseList_t *to_release = CALLOC(sizeof(*to_release), 1);
|
||||
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) {
|
||||
if ((item->status != PDU_SESSION_STATUS_TORELEASE) && item->xid != xid)
|
||||
continue;
|
||||
FOR_EACH_SEQ_ARR(drb_t *, drb, &ue_p->drbs) {
|
||||
if (drb->pdusession_id == item->param.pdusession_id) {
|
||||
asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release);
|
||||
*DRB_release = drb->drb_id; // DRB ID
|
||||
}
|
||||
}
|
||||
}
|
||||
params.drb_release_list = to_release;
|
||||
|
||||
if (nas_length > 0) {
|
||||
params.dedicated_NAS_msg_list[params.num_nas_msg].buf = nas_buffer;
|
||||
params.dedicated_NAS_msg_list[params.num_nas_msg++].len = nas_length;
|
||||
}
|
||||
byte_array_t msg = do_RRCReconfiguration(¶ms);
|
||||
if (msg.len <= 0) {
|
||||
LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
LOG_DUMPMSG(NR_RRC, DEBUG_RRC, msg.buf, msg.len, "[MSG] RRC Reconfiguration\n");
|
||||
LOG_I(NR_RRC, "UE %d: Generate NR_RRCReconfiguration (bytes %ld)\n", ue_p->rrc_ue_id, msg.len);
|
||||
const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration;
|
||||
nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len);
|
||||
free_RRCReconfiguration_params(params);
|
||||
free_byte_array(msg);
|
||||
/* send UE Context Modification to DU without attaching any RRC container */
|
||||
rrc->mac_rrc.ue_context_modification_request(ue_data.du_assoc_id, &req);
|
||||
LOG_I(NR_RRC, "UE %d: send F1 UE Context Modification Request with DRB release (%d DRBs)\n", ue_p->rrc_ue_id, req.drbs_rel_len);
|
||||
free_ue_context_mod_req(&req);
|
||||
}
|
||||
|
||||
static void fill_security_info(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, security_information_t *secInfo)
|
||||
@@ -964,15 +1063,10 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* According to current understanding of E1 specifications, it is not needed
|
||||
* to send security information because this does not change.
|
||||
* But let's keep the code here in case it's needed.
|
||||
*/
|
||||
// Always send security information
|
||||
/* During reestablishment, for DRB integrity protection security keys change (KgNB* is derived),
|
||||
* so security information MUST be sent to CU-UP to update DRB security keys. */
|
||||
req.secInfo = malloc_or_fail(sizeof(*req.secInfo));
|
||||
fill_security_info(rrc, ue_p, req.secInfo);
|
||||
#endif
|
||||
|
||||
/* Send E1 Bearer Context Modification Request (3GPP TS 38.463) */
|
||||
sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, ue_p);
|
||||
@@ -998,8 +1092,7 @@ static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context
|
||||
const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
|
||||
uint32_t ssb_arfcn = get_ssb_arfcn(du);
|
||||
LOG_I(NR_RRC, "Reestablishment update key pci=%d, earfcn_dl=%u\n", cell_info->nr_pci, ssb_arfcn);
|
||||
nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, ue_p->nh_ncc >= 0 ? ue_p->nh : ue_p->kgnb, ue_p->kgnb);
|
||||
ue_p->kgnb_ncc = 0;
|
||||
nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, ue_p->nh_ncc > 0 ? ue_p->nh : ue_p->kgnb, ue_p->kgnb);
|
||||
int size = do_RRCReestablishment(ue_context_pP->ue_context.nh_ncc, buffer, NR_RRC_BUF_SIZE, xid);
|
||||
|
||||
LOG_A(NR_RRC, "Send RRCReestablishment [%d bytes] to RNTI %04x\n", size, ue_p->rnti);
|
||||
@@ -1284,6 +1377,20 @@ static rrc_gNB_ue_context_t *rrc_gNB_get_ue_context_source_cell(gNB_RRC_INST *rr
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Rollback F1-U DL TL and TEID in RRC
|
||||
*/
|
||||
static void f1u_dl_gtp_rollback(gNB_RRC_UE_t *UE)
|
||||
{
|
||||
DevAssert(UE != NULL);
|
||||
DevAssert(UE->ho_context->source != NULL);
|
||||
|
||||
FOR_EACH_SEQ_ARR(drb_t *, drb, &UE->drbs) {
|
||||
drb->du_tunnel_config = UE->ho_context->source->old_du_tunnel_config;
|
||||
LOG_W(NR_RRC, "DRB id %d rollback to tunnel TEID %x\n", drb->drb_id, drb->du_tunnel_config.teid);
|
||||
}
|
||||
}
|
||||
|
||||
static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc,
|
||||
sctp_assoc_t assoc_id,
|
||||
const NR_RRCReestablishmentRequest_IEs_t *req,
|
||||
@@ -1362,6 +1469,7 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc,
|
||||
}
|
||||
|
||||
source_ctx->ho_cancel(rrc, UE);
|
||||
f1u_dl_gtp_rollback(UE);
|
||||
|
||||
/* we need the original CellGroupConfig */
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
|
||||
@@ -1833,10 +1941,8 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
|
||||
|
||||
switch (UE->xids[xid]) {
|
||||
case RRC_PDUSESSION_RELEASE: {
|
||||
gtpv1u_gnb_delete_tunnel_req_t req = {0};
|
||||
gtpv1u_delete_ngu_tunnel(rrc->module_id, &req);
|
||||
// NGAP_PDUSESSION_RELEASE_RESPONSE
|
||||
rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid);
|
||||
reset_delayed_action(&UE->delayed_action);
|
||||
} break;
|
||||
case RRC_PDUSESSION_ESTABLISH:
|
||||
if (UE->n_initial_pdu > 0) {
|
||||
@@ -1851,6 +1957,7 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
|
||||
LOG_W(NR_RRC,
|
||||
"UE %d: RRC Reconfiguration Complete for PDU session establishment, but no PDU sessions were setup\n",
|
||||
UE->rrc_ue_id);
|
||||
reset_delayed_action(&UE->delayed_action);
|
||||
break;
|
||||
case RRC_PDUSESSION_MODIFY:
|
||||
rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(rrc, UE, xid);
|
||||
@@ -1867,9 +1974,6 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
|
||||
break;
|
||||
}
|
||||
|
||||
if (UE->xids[xid] == RRC_PDUSESSION_ESTABLISH)
|
||||
UE->ongoing_pdusession_setup_request = false;
|
||||
|
||||
UE->xids[xid] = RRC_ACTION_NONE;
|
||||
for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) {
|
||||
if (UE->xids[i] != RRC_ACTION_NONE) {
|
||||
@@ -2714,15 +2818,19 @@ void rrc_gNB_process_e1_bearer_context_setup_failure(e1ap_bearer_context_setup_f
|
||||
*/
|
||||
void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t *resp)
|
||||
{
|
||||
LOG_I(NR_RRC, "Received E1AP Bearer Context Modification Response for UE CU-CP ID %d\n", resp->gNB_cu_cp_ue_id);
|
||||
gNB_RRC_INST *rrc = RC.nrrrc[0];
|
||||
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id);
|
||||
if (ue_context_p == NULL) {
|
||||
LOG_E(RRC, "no UE with CU-CP UE ID %d found\n", resp->gNB_cu_cp_ue_id);
|
||||
return;
|
||||
}
|
||||
gNB_RRC_UE_t *ue = &ue_context_p->ue_context;
|
||||
|
||||
int n_drb_mod = 0;
|
||||
int drb_ids[MAX_DRBS_PER_UE] = {0};
|
||||
int drb_to_release[MAX_DRBS_PER_UE] = {0};
|
||||
int n_drb_to_release = 0;
|
||||
e1_pdcp_status_info_t pdcp_status[MAX_DRBS_PER_UE] = {0};
|
||||
for (int i = 0; i < resp->numPDUSessionsMod; ++i) {
|
||||
const pdu_session_modif_t *pdu = &resp->pduSessionMod[i];
|
||||
@@ -2735,13 +2843,32 @@ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t
|
||||
pdcp_status[n_drb_mod++] = *pdu->DRBnGRanModList[j].pdcp_status;
|
||||
}
|
||||
}
|
||||
// Collect DRBs to release for PDU sessions marked for release
|
||||
rrc_pdu_session_param_t *pdu_session = find_pduSession(&ue->pduSessions, pdu->id);
|
||||
if (pdu_session && pdu_session->status == PDU_SESSION_STATUS_TORELEASE) {
|
||||
FOR_EACH_SEQ_ARR(drb_t *, drb, &ue->drbs) {
|
||||
if (drb->pdusession_id == pdu->id) {
|
||||
DevAssert(n_drb_to_release < MAX_DRBS_PER_UE);
|
||||
drb_to_release[n_drb_to_release++] = drb->drb_id;
|
||||
}
|
||||
}
|
||||
if (n_drb_to_release == 0) {
|
||||
LOG_E(NR_RRC, "UE %d: no DRBs to release for PDU session %ld\n", ue->rrc_ue_id, pdu->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n_drb_mod) {
|
||||
LOG_I(NR_RRC, "UE %d: received PDU Status Info - send UL RAN Status Transfer\n", resp->gNB_cu_cp_ue_id);
|
||||
gNB_RRC_UE_t *ue = &ue_context_p->ue_context;
|
||||
if (ue->ho_context && ue->ho_context->source)
|
||||
ue->ho_context->source->ho_status_transfer(rrc, ue, n_drb_mod, drb_ids, pdcp_status);
|
||||
}
|
||||
|
||||
// Send F1 UE Context Modification Request with DRB release
|
||||
if (n_drb_to_release) {
|
||||
LOG_I(NR_RRC, "Send F1 UE Context Modification Request with DRB to release\n");
|
||||
rrc_gNB_send_f1_drb_release_request(rrc, ue, drb_to_release, n_drb_to_release);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief E1AP Bearer Context Modification Failure processing on CU-CP */
|
||||
@@ -2838,7 +2965,6 @@ static const char *get_pdusession_status_text(pdu_session_status_t status)
|
||||
case PDU_SESSION_STATUS_TOMODIFY: return "to-modify";
|
||||
case PDU_SESSION_STATUS_FAILED: return "failed";
|
||||
case PDU_SESSION_STATUS_TORELEASE: return "to-release";
|
||||
case PDU_SESSION_STATUS_RELEASED: return "released";
|
||||
default: AssertFatal(false, "illegal PDU status code %d\n", status); return "illegal";
|
||||
}
|
||||
return "illegal";
|
||||
@@ -2964,7 +3090,8 @@ void *rrc_gnb_task(void *args_p) {
|
||||
break;
|
||||
|
||||
case NGAP_PDUSESSION_SETUP_REQ:
|
||||
rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(msg_p, instance);
|
||||
if (!rrc_delay_transaction(instance, msg_p))
|
||||
rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(msg_p, instance);
|
||||
break;
|
||||
|
||||
case NGAP_PDUSESSION_MODIFY_REQ:
|
||||
@@ -2972,7 +3099,8 @@ void *rrc_gnb_task(void *args_p) {
|
||||
break;
|
||||
|
||||
case NGAP_PDUSESSION_RELEASE_COMMAND:
|
||||
rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(msg_p, instance);
|
||||
if (!rrc_delay_transaction(instance, msg_p))
|
||||
rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(&NGAP_PDUSESSION_RELEASE_COMMAND(msg_p), RC.nrrrc[instance]);
|
||||
break;
|
||||
|
||||
case NGAP_DL_RAN_STATUS_TRANSFER:
|
||||
@@ -3274,26 +3402,7 @@ void rrc_gNB_generate_UeContextSetupRequest(const gNB_RRC_INST *rrc,
|
||||
}
|
||||
|
||||
nr_rrc_du_container_t *du = get_du_for_ue((gNB_RRC_INST *)rrc, ue_p->rrc_ue_id);
|
||||
byte_array_t *meas_timing_config = NULL;
|
||||
if (du->mtc && ue_p->measConfig && ue_p->measConfig->measObjectToAddModList) {
|
||||
NR_MeasObjectToAddModList_t *mo_list = ue_p->measConfig->measObjectToAddModList;
|
||||
NR_ARFCN_ValueNR_t ssbFrequency0;
|
||||
for (int i = 0; i < mo_list->list.count; i++) {
|
||||
NR_MeasObjectToAddMod_t *mo = mo_list->list.array[i];
|
||||
if (mo->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) {
|
||||
NR_MeasObjectNR_t *monr = mo->measObject.choice.measObjectNR;
|
||||
if (i == 0) {
|
||||
ssbFrequency0 = *monr->ssbFrequency;
|
||||
} else if (ssbFrequency0 != *monr->ssbFrequency) {
|
||||
meas_timing_config = calloc_or_fail(1, sizeof(*meas_timing_config));
|
||||
meas_timing_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE);
|
||||
meas_timing_config->len = do_NR_MeasurementTimingConfiguration(du->mtc, meas_timing_config->buf, NR_RRC_BUF_SIZE);
|
||||
cu2du.meas_timing_config = meas_timing_config;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cu2du.meas_timing_config = get_meas_timing_config(du->mtc, ue_p->measConfig);
|
||||
|
||||
int nb_srb = 1;
|
||||
f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs));
|
||||
|
||||
+104
-90
@@ -110,6 +110,24 @@ static const uint16_t NGAP_INTEGRITY_NIA3_MASK = 0x2000;
|
||||
|
||||
static void set_UE_security_algos(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const ngap_security_capabilities_t *cap);
|
||||
|
||||
/** @brief Validates PLMN against allowed PLMN list and returns pointer to matching PLMN
|
||||
* @param rrc RRC instance
|
||||
* @param plmn PLMN to validate
|
||||
* @return pointer to matching PLMN in configuration, or NULL if not found */
|
||||
static const plmn_id_t *get_serving_plmn(gNB_RRC_INST *rrc, const plmn_id_t *plmn)
|
||||
{
|
||||
// Find matching PLMN in configuration
|
||||
for (int idx = 0; idx < rrc->configuration.num_plmn; idx++) {
|
||||
const plmn_id_t *allowed = &rrc->configuration.plmn[idx];
|
||||
if (allowed->mcc == plmn->mcc && allowed->mnc == plmn->mnc && allowed->mnc_digit_length == plmn->mnc_digit_length) {
|
||||
LOG_D(NR_RRC, "PLMN (MCC:%d, MNC:%*d) matched with allowed PLMN[%d]\n", plmn->mcc, plmn->mnc_digit_length, plmn->mnc, idx);
|
||||
return allowed;
|
||||
}
|
||||
}
|
||||
LOG_W(NR_RRC, "PLMN (MCC:%d, MNC:%*d) not found in allowed PLMN list\n", plmn->mcc, plmn->mnc_digit_length, plmn->mnc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*!
|
||||
*\brief save security key.
|
||||
*\param UE UE context.
|
||||
@@ -122,7 +140,9 @@ static void set_UE_security_key(gNB_RRC_UE_t *UE, uint8_t *security_key_pP)
|
||||
/* Saves the security key */
|
||||
memcpy(UE->kgnb, security_key_pP, SECURITY_KEY_LENGTH);
|
||||
memset(UE->nh, 0, SECURITY_KEY_LENGTH);
|
||||
UE->nh_ncc = -1;
|
||||
/* 3GPP TS 33.501 §6.9.2.1.1: On Initial Context Setup, AMF does not send NH;
|
||||
* gNB shall initialize NCC to 0. */
|
||||
UE->nh_ncc = 0;
|
||||
|
||||
char ascii_buffer[65];
|
||||
for (i = 0; i < 32; i++) {
|
||||
@@ -165,9 +185,7 @@ static nr_guami_t get_guami(const uint32_t amf_Id, const plmn_id_t plmn)
|
||||
guami.amf_region_id = (amf_Id >> 16) & 0xff;
|
||||
guami.amf_set_id = (amf_Id >> 6) & 0x3ff;
|
||||
guami.amf_pointer = amf_Id & 0x3f;
|
||||
guami.mcc = plmn.mcc;
|
||||
guami.mnc = plmn.mnc;
|
||||
guami.mnc_len = plmn.mnc_digit_length;
|
||||
guami.plmn = plmn;
|
||||
return guami;
|
||||
}
|
||||
|
||||
@@ -223,17 +241,22 @@ void rrc_gNB_send_NGAP_NAS_FIRST_REQ(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, NR_RRC
|
||||
req->nas_pdu = create_byte_array(rrcSetupComplete->dedicatedNAS_Message.size, rrcSetupComplete->dedicatedNAS_Message.buf);
|
||||
|
||||
/* Selected PLMN Identity (Optional)
|
||||
* selectedPLMN-Identity in RRCSetupComplete: Index of the PLMN selected by the UE from the plmn-IdentityInfoList (SIB1)
|
||||
* Selected PLMN Identity in INITIAL UE MESSAGE: Indicates the selected PLMN id for the non-3GPP access.*/
|
||||
if (rrcSetupComplete->selectedPLMN_Identity > rrc->configuration.num_plmn) {
|
||||
* selectedPLMN-Identity in RRCSetupComplete: Index of the PLMN selected by the UE from the plmn-IdentityInfoList (SIB1) */
|
||||
int idx = rrcSetupComplete->selectedPLMN_Identity - 1; // Convert 1-based PLMN Identity IE to 0-based index
|
||||
if (idx < 0 || idx >= rrc->configuration.num_plmn) {
|
||||
LOG_E(NGAP,
|
||||
"Failed to send Initial UE Message: selected PLMN (%ld) identity is out of bounds (%d)\n",
|
||||
rrcSetupComplete->selectedPLMN_Identity,
|
||||
"Failed to send Initial UE Message: selected PLMN index (%d) is out of bounds [0..%d)\n",
|
||||
idx,
|
||||
rrc->configuration.num_plmn);
|
||||
return;
|
||||
}
|
||||
int selected_plmn_identity = rrcSetupComplete->selectedPLMN_Identity - 1; // Convert 1-based PLMN Identity IE to 0-based index
|
||||
req->plmn = rrc->configuration.plmn[selected_plmn_identity]; // Select from the stored list
|
||||
req->plmn = rrc->configuration.plmn[idx]; // Select from the stored list
|
||||
/* UE is connected to only one PLMN at a time: store this as the serving PLMN */
|
||||
UE->serving_plmn = req->plmn;
|
||||
|
||||
// Cell ID (NR CGI)
|
||||
req->nr_cell_id = UE->nr_cellid;
|
||||
// PLMN (NR CGI and TAI)
|
||||
plmn_id_t *p = &req->plmn;
|
||||
LOG_I(NGAP, "Selected PLMN in the NG Initial UE Message: MCC=%03d MNC=%0*d\n", p->mcc, p->mnc_digit_length, p->mnc);
|
||||
|
||||
@@ -721,6 +744,10 @@ void rrc_gNB_send_NGAP_UPLINK_NAS(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_
|
||||
NGAP_UPLINK_NAS(msg_p).gNB_ue_ngap_id = UE->rrc_ue_id;
|
||||
NGAP_UPLINK_NAS(msg_p).nas_pdu.len = nas->size;
|
||||
NGAP_UPLINK_NAS(msg_p).nas_pdu.buf = buf;
|
||||
/* Fill PLMN and location info: use serving PLMN of the UE */
|
||||
NGAP_UPLINK_NAS(msg_p).plmn = UE->serving_plmn;
|
||||
NGAP_UPLINK_NAS(msg_p).nr_cell_id = rrc->nr_cellid;
|
||||
NGAP_UPLINK_NAS(msg_p).tac = rrc->configuration.tac;
|
||||
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
|
||||
}
|
||||
|
||||
@@ -762,29 +789,6 @@ void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE
|
||||
return;
|
||||
}
|
||||
|
||||
/* \brief checks if any transaction is ongoing for any xid of this UE */
|
||||
static bool transaction_ongoing(const gNB_RRC_UE_t *UE)
|
||||
{
|
||||
for (int xid = 0; xid < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++xid) {
|
||||
if (UE->xids[xid] != RRC_ACTION_NONE)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* \brief delays the ongoing transaction (in msg_p) by setting a timer to wait
|
||||
* 10ms; upon expiry, delivers to RRC, which sends the message to itself */
|
||||
static void delay_transaction(MessageDef *msg_p, int wait_us)
|
||||
{
|
||||
MessageDef *new = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_SETUP_REQ);
|
||||
ngap_pdusession_setup_req_t *n = &NGAP_PDUSESSION_SETUP_REQ(new);
|
||||
*n = NGAP_PDUSESSION_SETUP_REQ(msg_p);
|
||||
|
||||
int instance = msg_p->ittiMsgHeader.originInstance;
|
||||
long timer_id;
|
||||
timer_setup(0, wait_us, TASK_RRC_GNB, instance, TIMER_ONE_SHOT, new, &timer_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fill PDU Session Resource Setup Response with a list of PDU Session Resources Failed to Setup
|
||||
* and send ITTI message to TASK_NGAP
|
||||
@@ -856,25 +860,6 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins
|
||||
|
||||
UE->amf_ue_ngap_id = msg->amf_ue_ngap_id;
|
||||
|
||||
/* This is a hack. We observed that with some UEs, PDU session requests might
|
||||
* come in quick succession, faster than the RRC reconfiguration for the PDU
|
||||
* session requests can be carried out (UE is doing reconfig, and second PDU
|
||||
* session request arrives). We don't have currently the means to "queue up"
|
||||
* these transactions, which would probably involve some rework of the RRC.
|
||||
* To still allow these requests to come in and succeed, we below check and delay transactions
|
||||
* for 10ms. However, to not accidentally end up in infinite loops, the
|
||||
* maximum number is capped on a per-UE basis as indicated in variable
|
||||
* max_delays_pdu_session. */
|
||||
if (!UE->ongoing_pdusession_setup_request)
|
||||
UE->max_delays_pdu_session = 100;
|
||||
|
||||
if (UE->max_delays_pdu_session > 0 && (transaction_ongoing(UE) || UE->ongoing_pdusession_setup_request)) {
|
||||
int wait_us = 10000;
|
||||
LOG_I(RRC, "UE %d: delay PDU session setup by %d us, pending %d retries\n", UE->rrc_ue_id, wait_us, UE->max_delays_pdu_session);
|
||||
delay_transaction(msg_p, wait_us);
|
||||
UE->max_delays_pdu_session--;
|
||||
return;
|
||||
}
|
||||
|
||||
pdusession_t to_setup[NGAP_MAX_PDU_SESSION] = {0};
|
||||
for (int i = 0; i < msg->nb_pdusessions_tosetup; ++i)
|
||||
@@ -889,7 +874,8 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins
|
||||
send_ngap_pdu_session_setup_resp_fail(instance, msg, cause);
|
||||
rrc_forward_ue_nas_message(rrc, UE);
|
||||
} else {
|
||||
UE->ongoing_pdusession_setup_request = true;
|
||||
// Set ongoing_transaction flag to true
|
||||
init_delayed_action(&UE->delayed_action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1154,6 +1140,20 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga
|
||||
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Validate PLMN from GUAMI against allowed PLMN list
|
||||
const plmn_id_t *serving_plmn = get_serving_plmn(rrc, &msg->guami.plmn);
|
||||
if (!serving_plmn) {
|
||||
LOG_E(NR_RRC, "PLMN from GUAMI not supported - rejecting handover\n");
|
||||
ngap_handover_failure_t fail = {
|
||||
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
|
||||
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
|
||||
.cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_TARGET_NOT_ALLOWED,
|
||||
};
|
||||
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint16_t pci = du->setup_req->cell[0].info.nr_pci;
|
||||
LOG_I(NR_RRC, "Received Handover Request (on NR Cell ID=%lu, PCI=%u) \n", msg->nr_cell_id, pci);
|
||||
|
||||
@@ -1169,6 +1169,9 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga
|
||||
// Store IDs in UE context
|
||||
UE->amf_ue_ngap_id = msg->amf_ue_ngap_id;
|
||||
UE->ue_guami = msg->guami;
|
||||
// Store the serving PLMN
|
||||
UE->serving_plmn = *serving_plmn;
|
||||
|
||||
UE->ho_context->target->ue_ho_prep_info = copy_byte_array(msg->ue_ho_prep_info);
|
||||
// store the received UE Security Capabilities in the UE context
|
||||
FREE_AND_ZERO_BYTE_ARRAY(UE->ue_cap_buffer);
|
||||
@@ -1189,7 +1192,11 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga
|
||||
// Activate SRBs
|
||||
activate_srb(UE, SRB1);
|
||||
activate_srb(UE, SRB2);
|
||||
nr_rrc_pdcp_config_security(UE, false);
|
||||
// During N2 handover, the UE continues using the old security context from the source gNB
|
||||
// until it receives and processes the RRC Reconfiguration with masterKeyUpdate. The first
|
||||
// PDUs sent after CFRA are still ciphered with the old keys. The target gNB must enable
|
||||
// ciphering during handover setup to correctly decipher and verify integrity of these PDUs.
|
||||
nr_rrc_pdcp_config_security(UE, true);
|
||||
|
||||
// Process all PDU Session Resource Setup items from handover request
|
||||
DevAssert(msg->nb_of_pdusessions <= NGAP_MAX_PDU_SESSION);
|
||||
@@ -1518,68 +1525,75 @@ void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE
|
||||
DevAssert(resp->nb_of_pdusessions_released < NGAP_MAX_PDU_SESSION);
|
||||
resp->pdusession_release[resp->nb_of_pdusessions_released++].pdusession_id = pdusession->pdusession_id;
|
||||
}
|
||||
session->status = PDU_SESSION_STATUS_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < resp->nb_of_pdusessions_released; ++i) {
|
||||
rm_pduSession(&UE->pduSessions, &UE->drbs, resp->pdusession_release[i].pdusession_id);
|
||||
}
|
||||
|
||||
LOG_I(NR_RRC, "NGAP PDUSESSION RELEASE RESPONSE: rrc_ue_id %u release_pdu_sessions %d\n", resp->gNB_ue_ngap_id, resp->nb_of_pdusessions_released);
|
||||
itti_send_msg_to_task (TASK_NGAP, rrc->module_id, msg_p);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance)
|
||||
//------------------------------------------------------------------------------
|
||||
/** @brief Process NG PDU Session Resource Release command (8.2.2 of 3GPP TS 38.413)
|
||||
* upon reception the NG-RAN node shall execute the release of the requested PDU sessions.
|
||||
* For each PDU session to be released the NG-RAN node shall release the corresponding
|
||||
* resources over Uu and over NG, if any. */
|
||||
int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(ngap_pdusession_release_command_t *cmd, gNB_RRC_INST *rrc)
|
||||
{
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
ngap_pdusession_release_command_t *cmd = &NGAP_PDUSESSION_RELEASE_COMMAND(msg_p);
|
||||
gNB_ue_ngap_id = cmd->gNB_ue_ngap_id;
|
||||
gNB_RRC_INST *rrc = RC.nrrrc[instance];
|
||||
uint32_t gNB_ue_ngap_id = cmd->gNB_ue_ngap_id;
|
||||
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, gNB_ue_ngap_id);
|
||||
|
||||
if (!ue_context_p) {
|
||||
LOG_E(NR_RRC, "[gNB %ld] not found ue context gNB_ue_ngap_id %u \n", instance, gNB_ue_ngap_id);
|
||||
LOG_E(NR_RRC, "[gNB %d] UE context not found for gNB_ue_ngap_id %u \n", rrc->module_id, gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_I(NR_RRC, "[gNB %ld] gNB_ue_ngap_id %u \n", instance, gNB_ue_ngap_id);
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
LOG_I(
|
||||
NR_RRC, "PDU Session Release Command: AMF_UE_NGAP_ID %lu rrc_ue_id %u release_pdusessions %d \n", cmd->amf_ue_ngap_id, gNB_ue_ngap_id, cmd->nb_pdusessions_torelease);
|
||||
bool found = false;
|
||||
uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
|
||||
UE->xids[xid] = RRC_PDUSESSION_RELEASE;
|
||||
LOG_I(NR_RRC, "NG PDU Session Release command: AMF_UE_NGAP_ID=%lu, rrc_ue_id=%u, nb_pdusessions_torelease=%d \n",
|
||||
cmd->amf_ue_ngap_id,
|
||||
gNB_ue_ngap_id,
|
||||
cmd->nb_pdusessions_torelease);
|
||||
e1ap_bearer_mod_req_t req = {0};
|
||||
for (int pdusession = 0; pdusession < cmd->nb_pdusessions_torelease; pdusession++) {
|
||||
rrc_pdu_session_param_t *pduSession = find_pduSession(&UE->pduSessions, cmd->pdusession_release_params[pdusession].pdusession_id);
|
||||
if (!pduSession) {
|
||||
LOG_E(NR_RRC, "Failed to release non-existing PDU Session %d\n", cmd->pdusession_release_params[pdusession].pdusession_id);
|
||||
continue;
|
||||
}
|
||||
if (pduSession->status == PDU_SESSION_STATUS_FAILED) {
|
||||
pduSession->xid = xid;
|
||||
if (pduSession->status == PDU_SESSION_STATUS_TORELEASE) {
|
||||
LOG_W(NR_RRC, "PDU Session %d already set to be released\n", pduSession->param.pdusession_id);
|
||||
continue;
|
||||
}
|
||||
if (pduSession->status == PDU_SESSION_STATUS_ESTABLISHED) {
|
||||
found = true;
|
||||
LOG_I(NR_RRC, "RELEASE pdusession %d \n", pduSession->param.pdusession_id);
|
||||
pduSession->status = PDU_SESSION_STATUS_TORELEASE;
|
||||
pduSession->xid = xid;
|
||||
}
|
||||
// Set PDU session to release, regardless of the status
|
||||
LOG_I(NR_RRC, "Set PDU Session %d to release\n", pduSession->param.pdusession_id);
|
||||
pdu_session_to_remove_t *release = &req.pduSessionRem[req.numPDUSessionsRem++];
|
||||
release->sessionId = pduSession->param.pdusession_id;
|
||||
release->cause.type = E1AP_CAUSE_RADIO_NETWORK;
|
||||
release->cause.value = E1AP_RADIO_CAUSE_NORMAL_RELEASE;
|
||||
pduSession->status = PDU_SESSION_STATUS_TORELEASE;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
// TODO RRCReconfiguration To UE
|
||||
LOG_I(NR_RRC, "Send RRCReconfiguration To UE \n");
|
||||
rrc_gNB_generate_dedicatedRRCReconfiguration_release(rrc, UE, xid, cmd->nas_pdu.len, cmd->nas_pdu.buf);
|
||||
if (req.numPDUSessionsRem == 0) {
|
||||
LOG_E(NR_RRC, "Received NG PDU Session Release Command but no PDU Sessions to release\n");
|
||||
return -1;
|
||||
} else if (!ue_associated_to_cuup(rrc, UE)) {
|
||||
LOG_E(NR_RRC, "UE %d is not associated to CU-UP\n", UE->rrc_ue_id);
|
||||
// TODO handle, e.g., only trigger F1 release
|
||||
return 0;
|
||||
} else {
|
||||
// gtp tunnel delete
|
||||
LOG_I(NR_RRC, "gtp tunnel delete all tunnels for UE %04x\n", UE->rnti);
|
||||
gtpv1u_gnb_delete_tunnel_req_t req = {0};
|
||||
req.ue_id = UE->rnti;
|
||||
gtpv1u_delete_ngu_tunnel(rrc->module_id, &req);
|
||||
// NGAP_PDUSESSION_RELEASE_RESPONSE
|
||||
rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid);
|
||||
LOG_I(NR_RRC, "Send PDU Session Release Response \n");
|
||||
/* If present, store NAS-PDU in the UE context for later inclusion in RRCReconfiguration */
|
||||
if (cmd->nas_pdu.len > 0) {
|
||||
UE->nas_pdu = copy_byte_array(cmd->nas_pdu);
|
||||
}
|
||||
LOG_I(NR_RRC, "Send F1 Bearer Context Modification Request with PDU Session release \n");
|
||||
req.gNB_cu_cp_ue_id = UE->rrc_ue_id;
|
||||
req.gNB_cu_up_ue_id = UE->rrc_ue_id;
|
||||
sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE);
|
||||
rrc->cucp_cuup.bearer_context_mod(assoc_id, &req);
|
||||
}
|
||||
init_delayed_action(&UE->delayed_action);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, uint32_t
|
||||
|
||||
void rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_UECapabilityInformation_t *const ue_cap_info);
|
||||
|
||||
int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance);
|
||||
int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(ngap_pdusession_release_command_t *cmd, gNB_RRC_INST *rrc);
|
||||
|
||||
void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid);
|
||||
|
||||
|
||||
@@ -218,8 +218,6 @@ rrc_gNB_ue_context_t *rrc_gNB_create_ue_context(sctp_assoc_t assoc_id,
|
||||
ue->rrc_ue_id);
|
||||
bool success = cu_add_f1_ue_data(ue->rrc_ue_id, &ue_data);
|
||||
DevAssert(success);
|
||||
ue->max_delays_pdu_session = 20; /* see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() */
|
||||
ue->ongoing_pdusession_setup_request = false;
|
||||
|
||||
// Initialise setup PDU Sessions list
|
||||
seq_arr_init(&ue->pduSessions, sizeof(rrc_pdu_session_param_t));
|
||||
|
||||
@@ -160,6 +160,12 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
||||
ho_ctx->source->du_ue_id = ue_data.secondary_ue;
|
||||
ho_ctx->source->old_rnti = ue->rnti;
|
||||
|
||||
// Save the GTP-U tunnel info for source DU before process UE context setup request/response
|
||||
// since tunnel info will be updated by calling store_du_f1u_tunnel() in rrc_CU_process_ue_context_setup_response()
|
||||
FOR_EACH_SEQ_ARR(drb_t *, drb, &ue->drbs) {
|
||||
ho_ctx->source->old_du_tunnel_config = drb->du_tunnel_config;
|
||||
}
|
||||
|
||||
int result = asn_copy(&asn_DEF_NR_CellGroupConfig, (void **)&ho_ctx->source->old_cellGroupConfig, ue->masterCellGroup);
|
||||
AssertFatal(result == 0, "error during asn_copy() of CellGroupConfig\n");
|
||||
}
|
||||
@@ -186,12 +192,7 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
||||
meas_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE);
|
||||
meas_config->len = do_NR_MeasConfig(ue->measConfig, meas_config->buf, NR_RRC_BUF_SIZE);
|
||||
|
||||
byte_array_t *meas_timing_config = NULL;
|
||||
if (target_du->mtc) {
|
||||
meas_timing_config = calloc_or_fail(1, sizeof(*meas_timing_config));
|
||||
meas_timing_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE);
|
||||
meas_timing_config->len = do_NR_MeasurementTimingConfiguration(target_du->mtc, meas_timing_config->buf, NR_RRC_BUF_SIZE);
|
||||
}
|
||||
byte_array_t *meas_timing_config = get_meas_timing_config(target_du->mtc, ue->measConfig);
|
||||
|
||||
byte_array_t *hpi = malloc_or_fail(sizeof(*hpi));
|
||||
*hpi = copy_byte_array(*ho_prep_info);
|
||||
@@ -612,3 +613,31 @@ void nr_HO_N2_trigger_telnet(gNB_RRC_INST *rrc, uint32_t neighbour_pci, uint32_t
|
||||
|
||||
nr_rrc_trigger_n2_ho(rrc, UE, scell_du->nr_pci, neighbour);
|
||||
}
|
||||
|
||||
// This function detects if there are at least two different ssbFrequency values, and if so, returns meas_timing_config;
|
||||
// otherwise, it returns NULL. When you return NULL, the measurement gaps will not be configured.
|
||||
byte_array_t *get_meas_timing_config(const NR_MeasurementTimingConfiguration_t *mtc, const NR_MeasConfig_t *measConfig)
|
||||
{
|
||||
if (!mtc || !measConfig || !measConfig->measObjectToAddModList)
|
||||
return NULL;
|
||||
|
||||
byte_array_t *meas_timing_config = NULL;
|
||||
NR_MeasObjectToAddModList_t *mo_list = measConfig->measObjectToAddModList;
|
||||
NR_ARFCN_ValueNR_t ssbFrequency0 = 0;
|
||||
for (int i = 0; i < mo_list->list.count; i++) {
|
||||
NR_MeasObjectToAddMod_t *mo = mo_list->list.array[i];
|
||||
if (mo->measObject.present != NR_MeasObjectToAddMod__measObject_PR_measObjectNR)
|
||||
continue;
|
||||
NR_MeasObjectNR_t *monr = mo->measObject.choice.measObjectNR;
|
||||
if (i == 0) {
|
||||
ssbFrequency0 = *monr->ssbFrequency;
|
||||
} else if (ssbFrequency0 != *monr->ssbFrequency) {
|
||||
meas_timing_config = calloc_or_fail(1, sizeof(*meas_timing_config));
|
||||
meas_timing_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE);
|
||||
meas_timing_config->len = do_NR_MeasurementTimingConfiguration(mtc, meas_timing_config->buf, NR_RRC_BUF_SIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return meas_timing_config;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ typedef struct nr_ho_source_cu {
|
||||
ho_cancel_t ho_cancel;
|
||||
/// status transfer
|
||||
ho_status_transfer_t ho_status_transfer;
|
||||
/// old (source) downlink tunnel
|
||||
gtpu_tunnel_t old_du_tunnel_config;
|
||||
} nr_ho_source_cu_t;
|
||||
|
||||
/* acknowledgement of handover request. buf+len is the RRC Reconfiguration */
|
||||
@@ -106,4 +108,6 @@ void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_
|
||||
|
||||
void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
|
||||
|
||||
byte_array_t *get_meas_timing_config(const NR_MeasurementTimingConfiguration_t *mtc, const NR_MeasConfig_t *measConfig);
|
||||
|
||||
#endif /* RRC_GNB_MOBILITY_H_ */
|
||||
|
||||
@@ -161,6 +161,28 @@ static NR_RadioBearerConfig_t *get_default_rbconfig(int eps_bearer_id,
|
||||
return rbconfig;
|
||||
}
|
||||
|
||||
static void rrc_nsa_add_drb(const int ue_id,
|
||||
const NR_DRB_ToAddModList_t *addMod,
|
||||
const nr_pdcp_entity_security_keys_and_algos_t *sp)
|
||||
{
|
||||
for (int i = 0; i < addMod->list.count; i++) {
|
||||
NR_DRB_ToAddMod_t *drb = addMod->list.array[i];
|
||||
DevAssert(drb->cnAssociation);
|
||||
DevAssert(drb->cnAssociation->present != NR_DRB_ToAddMod__cnAssociation_PR_NOTHING);
|
||||
DevAssert(drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity);
|
||||
// get SDAP config
|
||||
sdap_config_t sdap = {0};
|
||||
// EPC association
|
||||
sdap.pdusession_id = drb->cnAssociation->choice.eps_BearerIdentity;
|
||||
sdap.drb_id = drb->drb_Identity;
|
||||
sdap.defaultDRB = true;
|
||||
// add SDAP entity (terminated at gNB, since it's EPC)
|
||||
nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, &sdap);
|
||||
// add PDCP entity
|
||||
nr_pdcp_add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp);
|
||||
}
|
||||
}
|
||||
|
||||
/* generate prototypes for the tree management functions (RB_INSERT used in rrc_add_nsa_user) */
|
||||
RB_PROTOTYPE(rrc_nr_ue_tree_s, rrc_gNB_ue_context_s, entries,
|
||||
rrc_gNB_compare_ue_rnti_id);
|
||||
@@ -304,10 +326,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc, x2ap_ENDC_sgnb_addition_req_t *m, sctp_
|
||||
}
|
||||
|
||||
DevAssert(UE->rb_config != NULL);
|
||||
nr_pdcp_add_drbs(GNB_FLAG_YES,
|
||||
UE->rrc_ue_id,
|
||||
UE->rb_config->drb_ToAddModList,
|
||||
&security_parameters);
|
||||
rrc_nsa_add_drb(UE->rrc_ue_id, UE->rb_config->drb_ToAddModList, &security_parameters);
|
||||
|
||||
/* assumption: only a single bearer, see above */
|
||||
NR_DRB_ToAddModList_t *rb_list = UE->rb_config->drb_ToAddModList;
|
||||
|
||||
@@ -141,6 +141,62 @@ rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusessio
|
||||
return added;
|
||||
}
|
||||
|
||||
static bool eq_drb_pdu_session_id(const void *vval, const void *vit)
|
||||
{
|
||||
const int *id = (const int *)vval;
|
||||
const drb_t *elem = (const drb_t *)vit;
|
||||
return elem->pdusession_id == *id;
|
||||
}
|
||||
|
||||
/** @brief Finds the first DRB with the given PDU session ID.
|
||||
* @return Pointer to matching drb_t or NULL if not found. */
|
||||
static drb_t *find_drb_by_pdusession_id(seq_arr_t *seq, int pdusession_id)
|
||||
{
|
||||
DevAssert(seq);
|
||||
DevAssert(pdusession_id > 0 && pdusession_id <= NGAP_MAX_PDU_SESSION);
|
||||
elm_arr_t elm = find_if(seq, &pdusession_id, eq_drb_pdu_session_id);
|
||||
if (elm.found)
|
||||
return (drb_t *)elm.it;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @brief Removes a DRB from the list
|
||||
* @param drbs The DRB list
|
||||
* @param drb Pointer to the DRB to remove */
|
||||
static void nr_rrc_rm_drb(seq_arr_t *drbs, drb_t *drb)
|
||||
{
|
||||
DevAssert(drbs);
|
||||
DevAssert(drb);
|
||||
LOG_I(NR_RRC, "Removing DRB ID %d (PDU Session ID=%d)\n", drb->drb_id, drb->pdusession_id);
|
||||
seq_arr_erase_deep(drbs, drb, free_drb);
|
||||
}
|
||||
|
||||
/** @brief Removes a PDU Session from the list by ID
|
||||
* Also removes all associated DRBs for this PDU session.
|
||||
* @return true if successfully removed, false if not found */
|
||||
bool rm_pduSession(seq_arr_t *sessions, seq_arr_t *drbs, int pdusession_id)
|
||||
{
|
||||
DevAssert(sessions);
|
||||
DevAssert(drbs);
|
||||
|
||||
rrc_pdu_session_param_t *session = find_pduSession(sessions, pdusession_id);
|
||||
|
||||
if (session) {
|
||||
LOG_I(NR_RRC, "Removing PDU Session %d from RRC setup list\n", pdusession_id);
|
||||
// Remove all associated DRBs first
|
||||
drb_t *drb;
|
||||
while ((drb = find_drb_by_pdusession_id(drbs, pdusession_id))) {
|
||||
nr_rrc_rm_drb(drbs, drb);
|
||||
}
|
||||
// Then remove the PDU session
|
||||
seq_arr_erase_deep(sessions, session, free_pdusession);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_W(NR_RRC, "pdusession_id=%d not found to remove\n", pdusession_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @brief Add drb_t item in the UE context list for @param pdusession_id */
|
||||
drb_t *nr_rrc_add_drb(seq_arr_t *drb_ptr, int pdusession_id, nr_pdcp_configuration_t *pdcp)
|
||||
{
|
||||
|
||||
@@ -39,6 +39,10 @@ rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusessio
|
||||
/// @brief get PDU session of UE ue through the DRB drb_id
|
||||
rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id);
|
||||
|
||||
/// @brief Remove PDU Session from RRC list
|
||||
/// Also removes all associated DRBs for this PDU session.
|
||||
bool rm_pduSession(seq_arr_t *sessions, seq_arr_t *drbs, int pdusession_id);
|
||||
|
||||
/// @brief set PDCP configuration in E1 Bearer Context Management message
|
||||
bearer_context_pdcp_config_t set_bearer_context_pdcp_config(const nr_pdcp_configuration_t pdcp,
|
||||
bool um_on_default_drb,
|
||||
|
||||
+158
-75
@@ -210,7 +210,7 @@ static void get_epochtime_from_sib19scheduling(NR_UE_RRC_SI_INFO *SI_info, int *
|
||||
slot);
|
||||
}
|
||||
|
||||
static int eval_epoch_time(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int frame)
|
||||
static int eval_epoch_time(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int frame, bool is_targetcell)
|
||||
{
|
||||
int epoch_frame = 0, epoch_subframe = 0;
|
||||
int diff_frames = 0;
|
||||
@@ -229,11 +229,19 @@ static int eval_epoch_time(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntnc
|
||||
ntncfg->epochTime_r17->sfn_r17 = epoch_frame;
|
||||
ntncfg->epochTime_r17->subFrameNR_r17 = epoch_subframe;
|
||||
}
|
||||
// For serving cell, the field sfn indicates the current SFN or the next upcoming SFN
|
||||
// after the frame where the message indicating the epochTime is received
|
||||
// i.e. Epochframe can be present or future SFN
|
||||
diff_frames = (epoch_frame - frame + 1024) % 1024; // According to 38.331 Epochtime is defined for serving cell like this
|
||||
LOG_I(NR_RRC, "Epoch frame %d, ahead by %d frames\n", epoch_frame, diff_frames);
|
||||
if (is_targetcell) {
|
||||
// For Target cell,the SFN nearest to the frame needs to be considered
|
||||
// i.e. Epochframe can be in the past or future
|
||||
int diff1 = (frame - epoch_frame + 1024) % 1024;
|
||||
int diff2 = (epoch_frame - frame + 1024) % 1024;
|
||||
diff_frames = (diff1 < diff2) ? -diff1 : diff2;
|
||||
} else {
|
||||
// For serving cell, the field sfn indicates the current SFN or the next upcoming SFN
|
||||
// after the frame where the message indicating the epochTime is received
|
||||
// i.e. Epochframe can be present or future SFN
|
||||
diff_frames = (epoch_frame - frame + 1024) % 1024; // According to 38.331 Epochtime is defined for serving cell like this
|
||||
}
|
||||
LOG_I(NR_RRC, "Epoch frame %d ahead by %d frames\n", epoch_frame, diff_frames);
|
||||
return diff_frames;
|
||||
}
|
||||
|
||||
@@ -257,11 +265,11 @@ static int get_ntn_timervalues(NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *
|
||||
return sib19_timer_ms;
|
||||
}
|
||||
|
||||
static void nr_rrc_process_ntnconfig(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int frame)
|
||||
static void nr_rrc_process_ntnconfig(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_info, NR_NTN_Config_r17_t *ntncfg, int frame, bool is_targetcell)
|
||||
{
|
||||
SI_info->SInfo_r17.sib19_validity = true;
|
||||
// Check if Epochtime is sent or not
|
||||
int diff_frames = eval_epoch_time(SI_info, ntncfg, frame);
|
||||
int diff_frames = eval_epoch_time(SI_info, ntncfg, frame, is_targetcell);
|
||||
|
||||
if (ntncfg->ntn_UlSyncValidityDuration_r17) { // ulsyncvalidity duration configured
|
||||
int val430_ms = 0, sib19_timer_ms = 0;
|
||||
@@ -361,7 +369,7 @@ static void nr_decode_SI(NR_UE_RRC_SI_INFO *SI_info, NR_SystemInformation_t *si,
|
||||
sib19 = typeandinfo->choice.sib19_v1700;
|
||||
if (g_log->log_component[NR_RRC].level >= OAILOG_DEBUG)
|
||||
xer_fprint(stdout, &asn_DEF_NR_SIB19_r17, (const void *)sib19);
|
||||
nr_rrc_process_ntnconfig(rrc, SI_info, sib19->ntn_Config_r17, frame);
|
||||
nr_rrc_process_ntnconfig(rrc, SI_info, sib19->ntn_Config_r17, frame, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -599,6 +607,31 @@ static void nr_rrc_process_dedicatedNAS_MessageList(NR_UE_RRC_INST_t *rrc, NR_RR
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Add bearer in PDCP/SDAP for 5GC association (SA) */
|
||||
static void rrc_ue_add_bearer(const int ue_id, const NR_DRB_ToAddMod_t *drb, const nr_pdcp_entity_security_keys_and_algos_t *sp)
|
||||
{
|
||||
DevAssert(drb->cnAssociation);
|
||||
DevAssert(drb->cnAssociation->present != NR_DRB_ToAddMod__cnAssociation_PR_NOTHING);
|
||||
// get SDAP config
|
||||
sdap_config_t sdap = {0};
|
||||
if (drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity) {
|
||||
DevAssert(drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity);
|
||||
|
||||
// EPC association
|
||||
sdap.pdusession_id = drb->cnAssociation->choice.eps_BearerIdentity;
|
||||
sdap.drb_id = drb->drb_Identity;
|
||||
sdap.defaultDRB = true;
|
||||
} else {
|
||||
DevAssert(drb->cnAssociation->present == NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config);
|
||||
DevAssert(drb->cnAssociation->choice.sdap_Config);
|
||||
sdap = nr_sdap_get_config(GNB_FLAG_NO, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity);
|
||||
}
|
||||
// add SDAP entity
|
||||
nr_sdap_addmod_entity(GNB_FLAG_NO, ue_id, &sdap);
|
||||
// add PDCP entity
|
||||
nr_pdcp_add_drb(GNB_FLAG_NO, ue_id, drb->pdcp_Config, &sdap, sp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add, modify and release SRBs and/or DRBs
|
||||
* @ref 3GPP TS 38.331
|
||||
@@ -706,10 +739,7 @@ static void nr_rrc_ue_process_RadioBearerConfig(NR_UE_RRC_INST_t *ue_rrc, NR_Rad
|
||||
nr_reconfigure_sdap_entity(sdap_Config, ue_rrc->ue_id, sdap_Config->pdu_Session, DRB_id);
|
||||
} else {
|
||||
set_DRB_status(ue_rrc ,DRB_id, RB_ESTABLISHED);
|
||||
add_drb(false,
|
||||
ue_rrc->ue_id,
|
||||
radioBearerConfig->drb_ToAddModList->list.array[cnt],
|
||||
&security_up_parameters);
|
||||
rrc_ue_add_bearer(ue_rrc->ue_id, radioBearerConfig->drb_ToAddModList->list.array[cnt], &security_up_parameters);
|
||||
}
|
||||
}
|
||||
} // drb_ToAddModList //
|
||||
@@ -725,12 +755,15 @@ static void nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc,
|
||||
{
|
||||
AssertFatal(!fullConfig, "fullConfig not supported yet\n");
|
||||
NR_CellGroupConfig_t *cellGroupConfig = NULL;
|
||||
uper_decode(NULL,
|
||||
&asn_DEF_NR_CellGroupConfig, //might be added prefix later
|
||||
(void **)&cellGroupConfig,
|
||||
(uint8_t *)masterCellGroup->buf,
|
||||
masterCellGroup->size, 0, 0);
|
||||
|
||||
asn_dec_rval_t dec_rval = uper_decode(NULL,
|
||||
&asn_DEF_NR_CellGroupConfig, //might be added prefix later
|
||||
(void **)&cellGroupConfig,
|
||||
(uint8_t *)masterCellGroup->buf,
|
||||
masterCellGroup->size, 0, 0);
|
||||
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
|
||||
LOG_E(NR_RRC, "CellGroupConfig decode error\n");
|
||||
return;
|
||||
}
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
|
||||
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig);
|
||||
}
|
||||
@@ -801,28 +834,30 @@ static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCRe
|
||||
NR_RRCReconfiguration_v1540_IEs_t *rec_1540 = rec_1530->nonCriticalExtension;
|
||||
if (rec_1540) {
|
||||
NR_RRCReconfiguration_v1560_IEs_t *rec_1560 = rec_1540->nonCriticalExtension;
|
||||
if (rec_1560->sk_Counter) {
|
||||
// TODO perform AS security key update procedure as specified in 5.3.5.7
|
||||
LOG_E(NR_RRC, "RRCReconfiguration includes sk-Counter but this is not implemented yet\n");
|
||||
}
|
||||
if (rec_1560->mrdc_SecondaryCellGroupConfig) {
|
||||
// TODO perform handling of mrdc-SecondaryCellGroupConfig as specified in 5.3.5.3
|
||||
LOG_E(NR_RRC, "RRCReconfiguration includes mrdc-SecondaryCellGroupConfig but this is not handled yet\n");
|
||||
}
|
||||
if (rec_1560->radioBearerConfig2) {
|
||||
NR_RadioBearerConfig_t *RadioBearerConfig = NULL;
|
||||
asn_dec_rval_t dec_rval = uper_decode(NULL,
|
||||
&asn_DEF_NR_RadioBearerConfig,
|
||||
(void **)&RadioBearerConfig,
|
||||
(uint8_t *)rec_1560->radioBearerConfig2->buf,
|
||||
rec_1560->radioBearerConfig2->size,
|
||||
0,
|
||||
0);
|
||||
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
|
||||
LOG_E(NR_RRC, "radioBearerConfig2 decode error\n");
|
||||
SEQUENCE_free(&asn_DEF_NR_RadioBearerConfig, RadioBearerConfig, 1);
|
||||
} else
|
||||
nr_rrc_ue_process_RadioBearerConfig(rrc, RadioBearerConfig);
|
||||
if (rec_1560) {
|
||||
if (rec_1560->sk_Counter) {
|
||||
// TODO perform AS security key update procedure as specified in 5.3.5.7
|
||||
LOG_E(NR_RRC, "RRCReconfiguration includes sk-Counter but this is not implemented yet\n");
|
||||
}
|
||||
if (rec_1560->mrdc_SecondaryCellGroupConfig) {
|
||||
// TODO perform handling of mrdc-SecondaryCellGroupConfig as specified in 5.3.5.3
|
||||
LOG_E(NR_RRC, "RRCReconfiguration includes mrdc-SecondaryCellGroupConfig but this is not handled yet\n");
|
||||
}
|
||||
if (rec_1560->radioBearerConfig2) {
|
||||
NR_RadioBearerConfig_t *RadioBearerConfig = NULL;
|
||||
asn_dec_rval_t dec_rval = uper_decode(NULL,
|
||||
&asn_DEF_NR_RadioBearerConfig,
|
||||
(void **)&RadioBearerConfig,
|
||||
(uint8_t *)rec_1560->radioBearerConfig2->buf,
|
||||
rec_1560->radioBearerConfig2->size,
|
||||
0,
|
||||
0);
|
||||
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
|
||||
LOG_E(NR_RRC, "radioBearerConfig2 decode error\n");
|
||||
SEQUENCE_free(&asn_DEF_NR_RadioBearerConfig, RadioBearerConfig, 1);
|
||||
} else
|
||||
nr_rrc_ue_process_RadioBearerConfig(rrc, RadioBearerConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1136,20 +1171,19 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_
|
||||
LOG_E(NR_RRC, "\n");
|
||||
// free the memory
|
||||
SEQUENCE_free(&asn_DEF_NR_CellGroupConfig, (void *)cellGroupConfig, 1);
|
||||
} else {
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig);
|
||||
|
||||
nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index);
|
||||
AssertFatal(!IS_SA_MODE(get_softmodem_params()), "secondaryCellGroup only used in NSA for now\n");
|
||||
nr_mac_rrc_message_t rrc_msg = {0};
|
||||
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_CG;
|
||||
nr_mac_rrc_config_cg_t *config_cg = &rrc_msg.payload.config_cg;
|
||||
config_cg->cellGroupConfig = cellGroupConfig;
|
||||
config_cg->UE_NR_Capability = rrc->UECap.UE_NR_Capability;
|
||||
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
|
||||
}
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig);
|
||||
|
||||
nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index);
|
||||
|
||||
AssertFatal(!IS_SA_MODE(get_softmodem_params()), "secondaryCellGroup only used in NSA for now\n");
|
||||
nr_mac_rrc_message_t rrc_msg = {0};
|
||||
rrc_msg.payload_type = NR_MAC_RRC_CONFIG_CG;
|
||||
nr_mac_rrc_config_cg_t *config_cg = &rrc_msg.payload.config_cg;
|
||||
config_cg->cellGroupConfig = cellGroupConfig;
|
||||
config_cg->UE_NR_Capability = rrc->UECap.UE_NR_Capability;
|
||||
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
|
||||
}
|
||||
if (ie->measConfig) {
|
||||
LOG_I(NR_RRC, "RRCReconfiguration includes Measurement Configuration\n");
|
||||
@@ -1428,6 +1462,11 @@ static void nr_rrc_ue_decode_NR_BCCH_BCH_Message(NR_UE_RRC_INST_t *rrc,
|
||||
uint8_t *const bufferP,
|
||||
const uint8_t buffer_len)
|
||||
{
|
||||
// MIB received on the target cell. Now process target ntncfg once new timing is received.
|
||||
if (phycellid == rrc->phyCellID && rrc->target_ntncfg) {
|
||||
rrc->process_target_ntncfg = true;
|
||||
}
|
||||
|
||||
NR_BCCH_BCH_Message_t *bcch_message = NULL;
|
||||
rrc->phyCellID = phycellid;
|
||||
rrc->arfcn_ssb = ssb_arfcn;
|
||||
@@ -1629,13 +1668,29 @@ static void nr_rrc_signal_maxrtxindication(int ue_id)
|
||||
itti_send_msg_to_task(TASK_RRC_NRUE, ue_id, msg);
|
||||
}
|
||||
|
||||
/** @brief Release all active RLC entities for a UE and set to inactive.
|
||||
* This is typically used when tearing down all DRBs at UE side,
|
||||
* such as after a PDU session release or full reconfiguration.
|
||||
* @param rrc Pointer to NR_UE_RRC_INST_t structure
|
||||
* @param id Logical Channel ID (must be in range [0, NR_MAX_NUM_LCID-1]) */
|
||||
static void nr_rrc_release_rlc_entity(NR_UE_RRC_INST_t *rrc, int id)
|
||||
{
|
||||
DevAssert(rrc);
|
||||
DevAssert(id >= 0 && id < NR_MAX_NUM_LCID);
|
||||
if (rrc->active_RLC_entity[id]) {
|
||||
rrc->active_RLC_entity[id] = false;
|
||||
nr_rlc_release_entity(rrc->ue_id, id);
|
||||
LOG_I(RLC, "Released RLC entity: ue_id=%ld, lc_id=%d\n", rrc->ue_id, id);
|
||||
}
|
||||
}
|
||||
|
||||
static void nr_rrc_manage_rlc_bearers(NR_UE_RRC_INST_t *rrc, const NR_CellGroupConfig_t *cellGroupConfig)
|
||||
{
|
||||
if (cellGroupConfig->rlc_BearerToReleaseList != NULL) {
|
||||
for (int i = 0; i < cellGroupConfig->rlc_BearerToReleaseList->list.count; i++) {
|
||||
NR_LogicalChannelIdentity_t *lcid = cellGroupConfig->rlc_BearerToReleaseList->list.array[i];
|
||||
AssertFatal(lcid, "LogicalChannelIdentity shouldn't be null here\n");
|
||||
nr_rlc_release_entity(rrc->ue_id, *lcid);
|
||||
nr_rrc_release_rlc_entity(rrc, *lcid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1659,8 +1714,13 @@ static void nr_rrc_manage_rlc_bearers(NR_UE_RRC_INST_t *rrc, const NR_CellGroupC
|
||||
nr_rlc_set_rlf_handler(rrc->ue_id, nr_rrc_signal_maxrtxindication);
|
||||
} else { // DRB
|
||||
NR_DRB_Identity_t drb_id = rlc_bearer->servedRadioBearer->choice.drb_Identity;
|
||||
nr_rlc_add_drb(rrc->ue_id, drb_id, rlc_bearer);
|
||||
nr_rlc_set_rlf_handler(rrc->ue_id, nr_rrc_signal_maxrtxindication);
|
||||
if (!rlc_bearer->rlc_Config) {
|
||||
LOG_E(RLC, "RLC-Config not present but is mandatory for setup\n");
|
||||
rrc->active_RLC_entity[lcid] = false;
|
||||
} else {
|
||||
nr_rlc_add_drb(rrc->ue_id, drb_id, rlc_bearer);
|
||||
nr_rlc_set_rlf_handler(rrc->ue_id, nr_rrc_signal_maxrtxindication);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1688,7 +1748,10 @@ static void nr_rrc_process_reconfigurationWithSync(NR_UE_RRC_INST_t *rrc,
|
||||
rrc->arfcn_ssb = *dcc->frequencyInfoDL->absoluteFrequencySSB;
|
||||
|
||||
// consider the target SpCell to be one with a physical cell identity indicated by the physCellId
|
||||
rrc->phyCellID = *reconfigurationWithSync->spCellConfigCommon->physCellId;
|
||||
if (!reconfigurationWithSync->spCellConfigCommon->physCellId)
|
||||
LOG_E(NR_RRC, "physCellId absent but should be mandatory present upon cell change and cell addition\n");
|
||||
else
|
||||
rrc->phyCellID = *reconfigurationWithSync->spCellConfigCommon->physCellId;
|
||||
}
|
||||
|
||||
NR_UE_Timers_Constants_t *tac = &rrc->timers_and_constants;
|
||||
@@ -1704,13 +1767,20 @@ static void nr_rrc_process_reconfigurationWithSync(NR_UE_RRC_INST_t *rrc,
|
||||
|
||||
// 3GPP TS38.331 section 5.3.5.5.2
|
||||
nr_timer_stop(&tac->T430);
|
||||
|
||||
if (rrc->target_ntncfg) {
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg);
|
||||
rrc->target_ntncfg = NULL;
|
||||
rrc->process_target_ntncfg = false;
|
||||
}
|
||||
if (reconfigurationWithSync->spCellConfigCommon &&
|
||||
reconfigurationWithSync->spCellConfigCommon->ext2 &&
|
||||
reconfigurationWithSync->spCellConfigCommon->ext2->ntn_Config_r17) {
|
||||
NR_NTN_Config_r17_t *ntncfg = reconfigurationWithSync->spCellConfigCommon->ext2->ntn_Config_r17;
|
||||
// EPOCH time is always sent if NTN config is sent through DCCH
|
||||
AssertFatal(ntncfg->epochTime_r17, "NTN-CONFIG sent in dedicated mode should have EPOCHTIME\n");
|
||||
nr_rrc_process_ntnconfig(rrc, &rrc->perNB[gNB_index].SInfo, ntncfg, rrc->current_frame);
|
||||
const int copy_result = asn_copy(&asn_DEF_NR_NTN_Config_r17, (void **)&rrc->target_ntncfg, ntncfg);
|
||||
AssertFatal(copy_result == 0, "unable to copy NR_NTN_Config_r17_t\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1808,10 +1878,7 @@ static void nr_rrc_rrcsetup_fallback(NR_UE_RRC_INST_t *rrc)
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < NR_MAX_NUM_LCID; i++) {
|
||||
if (rrc->active_RLC_entity[i]) {
|
||||
rrc->active_RLC_entity[i] = false;
|
||||
nr_rlc_release_entity(rrc->ue_id, i);
|
||||
}
|
||||
nr_rrc_release_rlc_entity(rrc, i);
|
||||
}
|
||||
nr_sdap_delete_ue_entities(rrc->ue_id);
|
||||
|
||||
@@ -2213,13 +2280,7 @@ static void nr_rrc_ue_process_ueCapabilityEnquiry(NR_UE_RRC_INST_t *rrc, NR_UECa
|
||||
AssertFatal (enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
|
||||
rrc->UECap.sdu_size = (enc_rval.encoded + 7) / 8;
|
||||
LOG_I(PHY, "[RRC]UE NR Capability encoded, %d bytes (%zd bits)\n", rrc->UECap.sdu_size, enc_rval.encoded + 7);
|
||||
/* RAT Container */
|
||||
NR_UE_CapabilityRAT_Container_t *ue_CapabilityRAT_Container = CALLOC(1, sizeof(NR_UE_CapabilityRAT_Container_t));
|
||||
ue_CapabilityRAT_Container->rat_Type = NR_RAT_Type_nr;
|
||||
OCTET_STRING_fromBuf(&ue_CapabilityRAT_Container->ue_CapabilityRAT_Container, (const char *)rrc->UECap.sdu, rrc->UECap.sdu_size);
|
||||
NR_UECapabilityEnquiry_IEs_t *ueCapabilityEnquiry_ie = UECapabilityEnquiry->criticalExtensions.choice.ueCapabilityEnquiry;
|
||||
// ue_CapabilityRAT_Container.ueCapabilityRAT_Container.buf = UE_rrc_inst[ue_mod_idP].UECapability;
|
||||
// ue_CapabilityRAT_Container.ueCapabilityRAT_Container.size = UE_rrc_inst[ue_mod_idP].UECapability_size;
|
||||
AssertFatal(UECapabilityEnquiry->criticalExtensions.present == NR_UECapabilityEnquiry__criticalExtensions_PR_ueCapabilityEnquiry,
|
||||
"UECapabilityEnquiry->criticalExtensions.present (%d) != UECapabilityEnquiry__criticalExtensions_PR_c1 (%d)\n",
|
||||
UECapabilityEnquiry->criticalExtensions.present,NR_UECapabilityEnquiry__criticalExtensions_PR_ueCapabilityEnquiry);
|
||||
@@ -2232,6 +2293,10 @@ static void nr_rrc_ue_process_ueCapabilityEnquiry(NR_UE_RRC_INST_t *rrc, NR_UECa
|
||||
|
||||
for (int i = 0; i < ueCapabilityEnquiry_ie->ue_CapabilityRAT_RequestList.list.count; i++) {
|
||||
if (ueCapabilityEnquiry_ie->ue_CapabilityRAT_RequestList.list.array[i]->rat_Type == NR_RAT_Type_nr) {
|
||||
/* RAT Container */
|
||||
NR_UE_CapabilityRAT_Container_t *ue_CapabilityRAT_Container = CALLOC(1, sizeof(NR_UE_CapabilityRAT_Container_t));
|
||||
ue_CapabilityRAT_Container->rat_Type = NR_RAT_Type_nr;
|
||||
OCTET_STRING_fromBuf(&ue_CapabilityRAT_Container->ue_CapabilityRAT_Container, (const char *)rrc->UECap.sdu, rrc->UECap.sdu_size);
|
||||
asn1cSeqAdd(&UEcapList->list, ue_CapabilityRAT_Container);
|
||||
uint8_t buffer[500];
|
||||
asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, NULL, (void *)&ul_dcch_msg, buffer, 500);
|
||||
@@ -2245,8 +2310,7 @@ static void nr_rrc_ue_process_ueCapabilityEnquiry(NR_UE_RRC_INST_t *rrc, NR_UECa
|
||||
nr_pdcp_data_req_srb(rrc->ue_id, srb_id, 0, (enc_rval.encoded + 7) / 8, buffer, deliver_pdu_srb_rlc, NULL);
|
||||
}
|
||||
}
|
||||
/* Free struct members after it's done
|
||||
including locally allocated ue_CapabilityRAT_Container */
|
||||
/* Free struct members after it's done including locally allocated ue_CapabilityRAT_Container */
|
||||
ASN_STRUCT_RESET(asn_DEF_NR_UL_DCCH_Message, &ul_dcch_msg);
|
||||
}
|
||||
|
||||
@@ -2582,6 +2646,13 @@ void *rrc_nrue(void *notUsed)
|
||||
nr_rrc_handle_timers(rrc);
|
||||
NR_UE_RRC_SI_INFO *SInfo = &rrc->perNB[NRRRC_FRAME_PROCESS(msg_p).gnb_id].SInfo;
|
||||
nr_rrc_SI_timers(SInfo);
|
||||
if (rrc->process_target_ntncfg) {
|
||||
// Process target NTNCFG as the target cells timing is acquired
|
||||
nr_rrc_process_ntnconfig(rrc, &rrc->perNB[NRRRC_FRAME_PROCESS(msg_p).gnb_id].SInfo, rrc->target_ntncfg, rrc->current_frame, true);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg);
|
||||
rrc->target_ntncfg = NULL;
|
||||
rrc->process_target_ntncfg = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case NR_RRC_MAC_INAC_IND:
|
||||
@@ -2762,6 +2833,14 @@ static void nr_rrc_initiate_rrcReestablishment(NR_UE_RRC_INST_t *rrc, NR_Reestab
|
||||
set_DRB_status(rrc, i, RB_SUSPENDED);
|
||||
}
|
||||
}
|
||||
|
||||
// Free Target NTNcfg is stored
|
||||
if (rrc->target_ntncfg) {
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg);
|
||||
rrc->target_ntncfg = NULL;
|
||||
rrc->process_target_ntncfg = false;
|
||||
}
|
||||
|
||||
// release the MCG SCell(s), if configured
|
||||
// no SCell configured in our implementation
|
||||
|
||||
@@ -2916,10 +2995,7 @@ void nr_rrc_going_to_IDLE(NR_UE_RRC_INST_t *rrc,
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < NR_MAX_NUM_LCID; i++) {
|
||||
if (rrc->active_RLC_entity[i]) {
|
||||
rrc->active_RLC_entity[i] = false;
|
||||
nr_rlc_release_entity(rrc->ue_id, i);
|
||||
}
|
||||
nr_rrc_release_rlc_entity(rrc, i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NB_CNX_UE; i++) {
|
||||
@@ -2955,6 +3031,13 @@ void nr_rrc_going_to_IDLE(NR_UE_RRC_INST_t *rrc,
|
||||
asn1cFreeStruc(asn_DEF_NR_UE_TimersAndConstants, tac->sib1_TimersAndConstants);
|
||||
}
|
||||
|
||||
// Free Target NTNcfg is stored
|
||||
if (rrc->target_ntncfg) {
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_NTN_Config_r17, rrc->target_ntncfg);
|
||||
rrc->target_ntncfg = NULL;
|
||||
rrc->process_target_ntncfg = false;
|
||||
}
|
||||
|
||||
// reset MAC
|
||||
NR_UE_MAC_reset_cause_t cause = (rrc->nrRrcState == RRC_STATE_DETACH_NR) ? DETACH : GO_TO_IDLE;
|
||||
nr_mac_rrc_message_t rrc_msg = {0};
|
||||
|
||||
@@ -268,6 +268,8 @@ typedef struct NR_UE_RRC_INST_s {
|
||||
NR_SL_PreconfigurationNR_r16_t *sl_preconfig;
|
||||
// NTN params
|
||||
bool is_NTN_UE;
|
||||
NR_NTN_Config_r17_t *target_ntncfg;
|
||||
bool process_target_ntncfg;
|
||||
notifiedFIFO_t *mac_input_nf;
|
||||
} NR_UE_RRC_INST_t;
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ static void reblock_tun_socket(int fd)
|
||||
bool sdap_data_req(protocol_ctxt_t *ctxt_p,
|
||||
const ue_id_t ue_id,
|
||||
const srb_flag_t srb_flag,
|
||||
const rb_id_t rb_id,
|
||||
const mui_t mui,
|
||||
const confirm_t confirm,
|
||||
const sdu_size_t sdu_buffer_size,
|
||||
@@ -69,7 +68,6 @@ bool sdap_data_req(protocol_ctxt_t *ctxt_p,
|
||||
bool ret = sdap_entity->tx_entity(sdap_entity,
|
||||
ctxt_p,
|
||||
srb_flag,
|
||||
rb_id,
|
||||
mui,
|
||||
confirm,
|
||||
sdu_buffer_size,
|
||||
@@ -82,7 +80,7 @@ bool sdap_data_req(protocol_ctxt_t *ctxt_p,
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sdap_data_ind(rb_id_t pdcp_entity,
|
||||
void sdap_data_ind(int pdcp_entity,
|
||||
int is_gnb,
|
||||
bool has_sdap_rx,
|
||||
int pdusession_id,
|
||||
@@ -100,7 +98,6 @@ void sdap_data_ind(rb_id_t pdcp_entity,
|
||||
sdap_entity->rx_entity(sdap_entity,
|
||||
pdcp_entity,
|
||||
is_gnb,
|
||||
has_sdap_rx,
|
||||
pdusession_id,
|
||||
ue_id,
|
||||
buf,
|
||||
@@ -116,8 +113,6 @@ static void *sdap_tun_read_thread(void *arg)
|
||||
int len;
|
||||
reblock_tun_socket(entity->pdusession_sock);
|
||||
|
||||
int rb_id = 1;
|
||||
|
||||
while (!entity->stop_thread) {
|
||||
len = read(entity->pdusession_sock, &rx_buf, NL_MAX_PAYLOAD);
|
||||
if (len == -1) {
|
||||
@@ -148,7 +143,6 @@ static void *sdap_tun_read_thread(void *arg)
|
||||
entity->tx_entity(entity,
|
||||
&ctxt,
|
||||
SRB_FLAG_NO,
|
||||
rb_id,
|
||||
RLC_MUI_UNDEFINED,
|
||||
RLC_SDU_CONFIRM_NO,
|
||||
len,
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
bool sdap_data_req(protocol_ctxt_t *ctxt_p,
|
||||
const ue_id_t ue_id,
|
||||
const srb_flag_t srb_flag,
|
||||
const rb_id_t rb_id,
|
||||
const mui_t mui,
|
||||
const confirm_t confirm,
|
||||
const sdu_size_t sdu_buffer_size,
|
||||
@@ -53,7 +52,7 @@ bool sdap_data_req(protocol_ctxt_t *ctxt_p,
|
||||
* Uplink - gNB
|
||||
* Downlink - nrUE
|
||||
*/
|
||||
void sdap_data_ind(rb_id_t pdcp_entity,
|
||||
void sdap_data_ind(int pdcp_entity,
|
||||
int is_gnb,
|
||||
bool has_sdap_rx,
|
||||
int pdusession_id,
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include "tun_if.h"
|
||||
#include "nr_sdap.h"
|
||||
|
||||
#define NO_SDAP_HEADER 0
|
||||
|
||||
typedef struct {
|
||||
nr_sdap_entity_t *sdap_entity_llist;
|
||||
} nr_sdap_entity_info;
|
||||
@@ -42,35 +44,22 @@ static nr_sdap_entity_info sdap_info;
|
||||
|
||||
instance_t *N3GTPUInst = NULL;
|
||||
|
||||
/**
|
||||
* @brief indicates whether it is a receiving SDAP entity
|
||||
* i.e. for UE, header for DL data is present
|
||||
* for gNB, header for UL data is present
|
||||
*/
|
||||
bool is_sdap_rx(bool is_gnb, NR_SDAP_Config_t *sdap_config)
|
||||
/** @brief Returns a bitmap indicating the SDAP entity role,
|
||||
* i.e. for UL transmission, header for UL data is present in RX/TX
|
||||
* for DL transmission, header for DL data is present in RX/TX */
|
||||
static int get_sdap_role(bool is_gnb, const NR_SDAP_Config_t *sdap_config)
|
||||
{
|
||||
if (is_gnb) {
|
||||
return sdap_config->sdap_HeaderUL == NR_SDAP_Config__sdap_HeaderUL_present;
|
||||
} else {
|
||||
return sdap_config->sdap_HeaderDL == NR_SDAP_Config__sdap_HeaderDL_present;
|
||||
}
|
||||
sdap_role_t role_ul = is_gnb ? SDAP_UL_RX : SDAP_UL_TX;
|
||||
sdap_role_t role_dl = is_gnb ? SDAP_DL_TX : SDAP_DL_RX;
|
||||
int role = NO_SDAP_HEADER;
|
||||
if (sdap_config->sdap_HeaderUL == NR_SDAP_Config__sdap_HeaderUL_present)
|
||||
role |= role_ul;
|
||||
if (sdap_config->sdap_HeaderDL == NR_SDAP_Config__sdap_HeaderDL_present)
|
||||
role |= role_dl;
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief indicates whether it is a transmitting SDAP entity
|
||||
* i.e. for UE, header for UL data is present
|
||||
* for gNB, header for DL data is present
|
||||
*/
|
||||
bool is_sdap_tx(bool is_gnb, NR_SDAP_Config_t *sdap_config)
|
||||
{
|
||||
if (is_gnb) {
|
||||
return sdap_config->sdap_HeaderDL == NR_SDAP_Config__sdap_HeaderDL_present;
|
||||
} else {
|
||||
return sdap_config->sdap_HeaderUL == NR_SDAP_Config__sdap_HeaderUL_present;
|
||||
}
|
||||
}
|
||||
|
||||
void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu)
|
||||
void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu)
|
||||
{
|
||||
|
||||
protocol_ctxt_t ctxt = { .rntiMaybeUEid = ue_id };
|
||||
@@ -84,7 +73,7 @@ void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_s
|
||||
PDCP_TRANSMISSION_MODE_UNKNOWN,
|
||||
NULL,
|
||||
NULL);
|
||||
LOG_D(SDAP, "Control PDU - Submitting Control PDU to DRB ID: %ld\n", sdap_ctrl_pdu_drb);
|
||||
LOG_D(SDAP, "Control PDU - Submitting Control PDU to DRB ID: %d\n", sdap_ctrl_pdu_drb);
|
||||
LOG_D(SDAP, "QFI: %u\n R: %u\n D/C: %u\n", ctrl_pdu.QFI, ctrl_pdu.R, ctrl_pdu.DC);
|
||||
return;
|
||||
}
|
||||
@@ -92,7 +81,6 @@ void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_s
|
||||
static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity,
|
||||
protocol_ctxt_t *ctxt_p,
|
||||
const srb_flag_t srb_flag,
|
||||
const rb_id_t rb_id,
|
||||
const mui_t mui,
|
||||
const confirm_t confirm,
|
||||
const sdu_size_t sdu_buffer_size,
|
||||
@@ -105,9 +93,8 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity,
|
||||
/* The offset of the SDAP header, it might be 0 if has_sdap_tx is not true in the pdcp entity. */
|
||||
int offset=0;
|
||||
bool ret = false;
|
||||
/*Hardcode DRB ID given from upper layer (ue/gnb_tun_read_thread rb_id), it will change if we have SDAP*/
|
||||
rb_id_t sdap_drb_id = rb_id;
|
||||
int pdcp_ent_has_sdap = 0;
|
||||
bool sdap_ul_tx = false;
|
||||
bool sdap_dl_tx = false;
|
||||
|
||||
if(sdu_buffer == NULL) {
|
||||
LOG_E(SDAP, "%s:%d:%s: NULL sdu_buffer \n", __FILE__, __LINE__, __FUNCTION__);
|
||||
@@ -115,19 +102,22 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity,
|
||||
}
|
||||
|
||||
uint8_t sdap_buf[SDAP_MAX_PDU];
|
||||
rb_id_t pdcp_entity = entity->qfi2drb_map(entity, qfi);
|
||||
int drb_id = entity->qfi2drb_map(entity, qfi);
|
||||
|
||||
if(pdcp_entity){
|
||||
sdap_drb_id = pdcp_entity;
|
||||
pdcp_ent_has_sdap = entity->qfi2drb_table[qfi].has_sdap_tx;
|
||||
LOG_D(SDAP, "TX - QFI: %u is mapped to DRB ID: %ld\n", qfi, entity->qfi2drb_table[qfi].drb_id);
|
||||
if (drb_id != SDAP_MAP_RULE_EMPTY) {
|
||||
sdap_ul_tx = entity->qfi2drb_table[qfi].entity_role & SDAP_UL_TX; // UE TX entity
|
||||
sdap_dl_tx = entity->qfi2drb_table[qfi].entity_role & SDAP_DL_TX; // gNB TX entity
|
||||
LOG_D(SDAP, "TX - QFI: %u is mapped to DRB ID: %d\n", qfi, entity->qfi2drb_table[qfi].drb_id);
|
||||
} else {
|
||||
LOG_E(SDAP, "QFI %u not mapped to any DRB - dropping SDU of size %d\n", qfi, sdu_buffer_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!pdcp_ent_has_sdap){
|
||||
LOG_D(SDAP, "TX - DRB ID: %ld does not have SDAP\n", entity->qfi2drb_table[qfi].drb_id);
|
||||
if (!sdap_ul_tx && !sdap_dl_tx) {
|
||||
LOG_D(SDAP, "TX - DRB ID: %d does not have SDAP header\n", entity->qfi2drb_table[qfi].drb_id);
|
||||
ret = nr_pdcp_data_req_drb(ctxt_p,
|
||||
srb_flag,
|
||||
sdap_drb_id,
|
||||
drb_id,
|
||||
mui,
|
||||
confirm,
|
||||
sdu_buffer_size,
|
||||
@@ -147,7 +137,7 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(ctxt_p->enb_flag) { // gNB
|
||||
if (sdap_dl_tx) { // create DL Data PDU with SDAP header
|
||||
offset = SDAP_HDR_LENGTH;
|
||||
/*
|
||||
* TS 37.324 4.4 Functions
|
||||
@@ -165,7 +155,7 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity,
|
||||
LOG_D(SDAP, "TX Entity QFI: %u \n", sdap_hdr.QFI);
|
||||
LOG_D(SDAP, "TX Entity RQI: %u \n", sdap_hdr.RQI);
|
||||
LOG_D(SDAP, "TX Entity RDI: %u \n", sdap_hdr.RDI);
|
||||
} else { // nrUE
|
||||
} else if (sdap_ul_tx) { // create UL Data PDU with SDAP header
|
||||
offset = SDAP_HDR_LENGTH;
|
||||
/*
|
||||
* TS 37.324 4.4 Functions
|
||||
@@ -195,7 +185,7 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity,
|
||||
*/
|
||||
ret = nr_pdcp_data_req_drb(ctxt_p,
|
||||
srb_flag,
|
||||
sdap_drb_id,
|
||||
drb_id,
|
||||
mui,
|
||||
confirm,
|
||||
sdu_buffer_size + offset,
|
||||
@@ -211,9 +201,8 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity,
|
||||
}
|
||||
|
||||
static void nr_sdap_rx_entity(nr_sdap_entity_t *entity,
|
||||
rb_id_t pdcp_entity,
|
||||
int pdcp_entity,
|
||||
int is_gnb,
|
||||
bool has_sdap_rx,
|
||||
int pdusession_id,
|
||||
ue_id_t ue_id,
|
||||
char *buf,
|
||||
@@ -221,9 +210,23 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity,
|
||||
{
|
||||
/* The offset of the SDAP header, it might be 0 if has_sdap_rx is not true in the pdcp entity. */
|
||||
int offset=0;
|
||||
bool sdap_ul_rx = false;
|
||||
bool sdap_dl_rx = false;
|
||||
/* If SDAP header is disabled for this entity, bypass header parsing */
|
||||
if (entity->enable_sdap) {
|
||||
uint8_t qfi = buf[0] & 0x3F; // QFI is always the first 6 bits in the first octet
|
||||
if (qfi >= SDAP_MAX_QFI) {
|
||||
LOG_E(SDAP, "Invalid QFI %d received in SDAP header\n", qfi);
|
||||
return;
|
||||
}
|
||||
// Fetch entity role from the qfi2drb_table
|
||||
sdap_ul_rx = entity->qfi2drb_table[qfi].entity_role & SDAP_UL_RX; // gNB RX entity
|
||||
sdap_dl_rx = entity->qfi2drb_table[qfi].entity_role & SDAP_DL_RX; // UE RX entity
|
||||
}
|
||||
|
||||
|
||||
if (is_gnb) { // gNB
|
||||
if (has_sdap_rx) { // Handling the SDAP Header
|
||||
if (sdap_ul_rx) { // UL Data/Control PDU with SDAP header
|
||||
offset = SDAP_HDR_LENGTH;
|
||||
nr_sdap_ul_hdr_t *sdap_hdr = (nr_sdap_ul_hdr_t *)buf;
|
||||
LOG_D(SDAP, "RX Entity Received QFI: %u\n", sdap_hdr->QFI);
|
||||
@@ -255,7 +258,7 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity,
|
||||
* 5.2.2 Downlink
|
||||
* if the DRB from which this SDAP data PDU is received is configured by RRC with the presence of SDAP header.
|
||||
*/
|
||||
if (has_sdap_rx) { // Handling the SDAP Header
|
||||
if (sdap_dl_rx) { // DL Data/Control PDU with SDAP header
|
||||
offset = SDAP_HDR_LENGTH;
|
||||
/*
|
||||
* TS 37.324 5.2 Data transfer
|
||||
@@ -281,7 +284,7 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity,
|
||||
*/
|
||||
if(!entity->qfi2drb_table[sdap_hdr->QFI].drb_id && entity->default_drb){
|
||||
nr_sdap_ul_hdr_t sdap_ctrl_pdu = entity->sdap_construct_ctrl_pdu(sdap_hdr->QFI);
|
||||
rb_id_t sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, pdcp_entity, SDAP_CTRL_PDU_MAP_DEF_DRB, sdap_hdr->QFI);
|
||||
int sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, SDAP_CTRL_PDU_MAP_DEF_DRB, sdap_hdr->QFI);
|
||||
entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu);
|
||||
}
|
||||
|
||||
@@ -296,7 +299,7 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity,
|
||||
*/
|
||||
if (pdcp_entity != entity->qfi2drb_table[sdap_hdr->QFI].drb_id) {
|
||||
nr_sdap_ul_hdr_t sdap_ctrl_pdu = entity->sdap_construct_ctrl_pdu(sdap_hdr->QFI);
|
||||
rb_id_t sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, pdcp_entity, SDAP_CTRL_PDU_MAP_RULE_DRB, sdap_hdr->QFI);
|
||||
int sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, SDAP_CTRL_PDU_MAP_RULE_DRB, sdap_hdr->QFI);
|
||||
entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu);
|
||||
}
|
||||
|
||||
@@ -333,51 +336,52 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief update QFI to DRB mapping rules
|
||||
*/
|
||||
void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, uint8_t qfi, rb_id_t drb, bool has_sdap_rx, bool has_sdap_tx)
|
||||
static void nr_sdap_qfi2drb_map_add(nr_sdap_entity_t *entity,
|
||||
const uint8_t qfi,
|
||||
const uint8_t drb_id,
|
||||
const uint8_t role)
|
||||
{
|
||||
if(qfi < SDAP_MAX_QFI &&
|
||||
qfi > SDAP_MAP_RULE_EMPTY &&
|
||||
drb > 0 &&
|
||||
drb <= AVLBL_DRB){
|
||||
entity->qfi2drb_table[qfi].drb_id = drb;
|
||||
entity->qfi2drb_table[qfi].has_sdap_rx = has_sdap_rx;
|
||||
entity->qfi2drb_table[qfi].has_sdap_tx = has_sdap_tx;
|
||||
LOG_D(SDAP, "Updated mapping: QFI %u -> DRB %ld \n", qfi, entity->qfi2drb_table[qfi].drb_id);
|
||||
} else {
|
||||
LOG_D(SDAP, "Map updated failed, QFI: %u, DRB: %ld\n", qfi, drb);
|
||||
}
|
||||
qfi2drb_t *qfi2drb = &entity->qfi2drb_table[qfi];
|
||||
LOG_D(SDAP, "%s mapping: QFI %u -> DRB %d \n", qfi2drb->drb_id == SDAP_NO_MAPPING_RULE ? "Add" : "Update", qfi, drb_id);
|
||||
qfi2drb->drb_id = drb_id;
|
||||
qfi2drb->entity_role = role;
|
||||
}
|
||||
|
||||
void nr_sdap_qfi2drb_map_del(nr_sdap_entity_t *entity, uint8_t qfi){
|
||||
entity->qfi2drb_table[qfi].drb_id = SDAP_NO_MAPPING_RULE;
|
||||
LOG_D(SDAP, "Deleted mapping for QFI: %u \n", qfi);
|
||||
static void nr_sdap_qfi2drb_map_del(nr_sdap_entity_t *entity, const uint8_t qfi)
|
||||
{
|
||||
qfi2drb_t *qfi2drb = &entity->qfi2drb_table[qfi];
|
||||
qfi2drb->drb_id = SDAP_NO_MAPPING_RULE;
|
||||
LOG_D(SDAP, "Deleted mapping for QFI=%d, DRB=%d\n", qfi, qfi2drb->drb_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief maps the QFIs to the default DRB if not mapping rule exists
|
||||
* @return DRB that is mapped to the QFI, 0 if no mapping and no default DRB exists for that QFI
|
||||
*/
|
||||
rb_id_t nr_sdap_qfi2drb_map(nr_sdap_entity_t *entity, uint8_t qfi){
|
||||
rb_id_t pdcp_entity;
|
||||
|
||||
pdcp_entity = entity->qfi2drb_table[qfi].drb_id;
|
||||
|
||||
if(pdcp_entity){
|
||||
LOG_D(SDAP, "Mapping rule exists for QFI: %u\n", qfi);
|
||||
return pdcp_entity;
|
||||
} else if(entity->default_drb) {
|
||||
LOG_D(SDAP, "Mapping QFI: %u to Default DRB: %ld\n", qfi, entity->default_drb);
|
||||
entity->qfi2drb_map_update(entity, qfi, entity->default_drb, entity->qfi2drb_table[qfi].has_sdap_rx, entity->qfi2drb_table[qfi].has_sdap_tx);
|
||||
* @brief get the DRB ID mapped to the QFI, for both DL and UL
|
||||
* @return DRB that is mapped to the QFI
|
||||
* or the default DRB if no mapping rule exists
|
||||
* or 0 if no mapping and no default DRB exists for that QFI
|
||||
* @ref TS 37.324, 5.2.1 Uplink
|
||||
* If there is no stored QoS flow to DRB mapping rule for the
|
||||
* QoS flow as specified in the subclause 5.3, and a default DRB is configured,
|
||||
* map the SDAP SDU to the default DRB else, map the SDAP SDU to the DRB according
|
||||
* to the stored QoS flow to DRB mapping rule. */
|
||||
static int nr_sdap_qfi2drb(nr_sdap_entity_t *entity, uint8_t qfi)
|
||||
{
|
||||
/* Fetch DRB ID mapped to QFI */
|
||||
int drb_id = entity->qfi2drb_table[qfi].drb_id;
|
||||
if (drb_id) {
|
||||
/* QoS flow to DRB mapping rule exists, return corresponding DRB ID */
|
||||
LOG_D(SDAP, "Existing QoS flow to DRB mapping rule: QFI %u to DRB %d\n", qfi, drb_id);
|
||||
return drb_id;
|
||||
} else if (entity->default_drb) {
|
||||
/* QoS flow to DRB mapping rule does not exist, map SDAP SDU to default DRB, e.g. return default DRB of the SDAP entity */
|
||||
LOG_D(SDAP, "QoS flow to DRB mapping rule does not exists! mapping SDU to Default DRB: %d\n", entity->default_drb);
|
||||
return entity->default_drb;
|
||||
} else {
|
||||
LOG_D(SDAP, "Mapping rule and default DRB do not exist for QFI:%u\n", qfi);
|
||||
/* Note: UE undefined behaviour when neither a default DRB
|
||||
nor a stored QoS flow to DRB mapping rule exists */
|
||||
LOG_E(SDAP, "Mapping rule and default DRB do not exist for QFI:%u\n", qfi);
|
||||
return SDAP_MAP_RULE_EMPTY;
|
||||
}
|
||||
|
||||
return pdcp_entity;
|
||||
}
|
||||
|
||||
nr_sdap_ul_hdr_t nr_sdap_construct_ctrl_pdu(uint8_t qfi){
|
||||
@@ -391,15 +395,16 @@ nr_sdap_ul_hdr_t nr_sdap_construct_ctrl_pdu(uint8_t qfi){
|
||||
return sdap_end_marker_hdr;
|
||||
}
|
||||
|
||||
rb_id_t nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, rb_id_t pdcp_entity, int map_type, uint8_t dl_qfi){
|
||||
rb_id_t drb_of_endmarker = 0;
|
||||
int nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, int map_type, uint8_t dl_qfi)
|
||||
{
|
||||
int drb_of_endmarker = 0;
|
||||
if(map_type == SDAP_CTRL_PDU_MAP_DEF_DRB){
|
||||
drb_of_endmarker = entity->default_drb;
|
||||
LOG_D(SDAP, "Mapping Control PDU QFI: %u to Default DRB: %ld\n", dl_qfi, drb_of_endmarker);
|
||||
LOG_D(SDAP, "Mapping Control PDU QFI: %u to Default DRB: %d\n", dl_qfi, drb_of_endmarker);
|
||||
}
|
||||
if(map_type == SDAP_CTRL_PDU_MAP_RULE_DRB){
|
||||
drb_of_endmarker = entity->qfi2drb_map(entity, dl_qfi);
|
||||
LOG_D(SDAP, "Mapping Control PDU QFI: %u to DRB: %ld\n", dl_qfi, drb_of_endmarker);
|
||||
LOG_D(SDAP, "Mapping Control PDU QFI: %u to DRB: %d\n", dl_qfi, drb_of_endmarker);
|
||||
}
|
||||
return drb_of_endmarker;
|
||||
}
|
||||
@@ -407,7 +412,7 @@ rb_id_t nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, rb_id_t pdcp_entity, int
|
||||
/**
|
||||
* @brief Submit the end-marker control PDU to PDCP according to TS 37.324, clause 5.3
|
||||
*/
|
||||
void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu)
|
||||
void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu)
|
||||
{
|
||||
if(sdap_ctrl_pdu_drb){
|
||||
nr_pdcp_submit_sdap_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, ctrl_pdu);
|
||||
@@ -415,116 +420,114 @@ void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_u
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UL QoS flow to DRB mapping configuration for an existing SDAP entity
|
||||
* according to TS 37.324, 5.3 QoS flow to DRB Mapping, clause 5.3.1 Configuration Procedures
|
||||
*/
|
||||
static void nr_sdap_ue_qfi2drb_config(nr_sdap_entity_t *existing_sdap_entity,
|
||||
rb_id_t pdcp_entity,
|
||||
ue_id_t ue_id,
|
||||
NR_QFI_t *mapped_qfi_2_add,
|
||||
uint8_t mappedQFIs2AddCount,
|
||||
uint8_t drb_identity,
|
||||
bool has_sdap_rx,
|
||||
bool has_sdap_tx)
|
||||
/** @brief UL QoS flow to DRB mapping configuration for a SDAP entity has already been established
|
||||
* according to TS 37.324, 5.3 QoS flow to DRB Mapping, 5.3.1 Configuration Procedures. */
|
||||
static void nr_sdap_ue_control_pdu_config(nr_sdap_entity_t *entity, const ue_id_t ue_id, const sdap_config_t *sdap)
|
||||
{
|
||||
LOG_D(SDAP, "RRC Configuring SDAP Entity\n");
|
||||
for(int i = 0; i < mappedQFIs2AddCount; i++){
|
||||
uint8_t qfi = mapped_qfi_2_add[i];
|
||||
/* a default DRB exists and there is no QFI to DRB mapping rule for the QFI */
|
||||
if (existing_sdap_entity->default_drb && existing_sdap_entity->qfi2drb_table[qfi].drb_id == SDAP_NO_MAPPING_RULE) {
|
||||
nr_sdap_ul_hdr_t sdap_ctrl_pdu = existing_sdap_entity->sdap_construct_ctrl_pdu(qfi);
|
||||
rb_id_t sdap_ctrl_pdu_drb =
|
||||
existing_sdap_entity->sdap_map_ctrl_pdu(existing_sdap_entity, pdcp_entity, SDAP_CTRL_PDU_MAP_DEF_DRB, qfi);
|
||||
existing_sdap_entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu);
|
||||
// handle QFIs to DRB mapping rule to add
|
||||
for (int i = 0; i < sdap->mappedQFIs2AddCount; i++) {
|
||||
uint8_t qfi = sdap->mappedQFIs2Add[i];
|
||||
/* a default DRB exists and there is no stored QFI to DRB mapping rule for the QFI */
|
||||
if (entity->default_drb && entity->qfi2drb_table[qfi].drb_id == SDAP_NO_MAPPING_RULE) {
|
||||
// construct an end-marker control PDU (6.2.3 TS 37.324)
|
||||
nr_sdap_ul_hdr_t sdap_ctrl_pdu = entity->sdap_construct_ctrl_pdu(qfi);
|
||||
// map the end-marker control PDU to the default DRB
|
||||
int sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, SDAP_CTRL_PDU_MAP_DEF_DRB, qfi);
|
||||
// submit the end-marker control PDU to the lower layers
|
||||
entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu);
|
||||
}
|
||||
/* the stored UL QFI to DRB mapping rule is different from the configured one and has UL SDAP header*/
|
||||
if (existing_sdap_entity->qfi2drb_table[qfi].drb_id != drb_identity && existing_sdap_entity->qfi2drb_table[qfi].has_sdap_tx) {
|
||||
nr_sdap_ul_hdr_t sdap_ctrl_pdu = existing_sdap_entity->sdap_construct_ctrl_pdu(qfi);
|
||||
rb_id_t sdap_ctrl_pdu_drb =
|
||||
existing_sdap_entity->sdap_map_ctrl_pdu(existing_sdap_entity, pdcp_entity, SDAP_CTRL_PDU_MAP_RULE_DRB, qfi);
|
||||
existing_sdap_entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu);
|
||||
/* the stored UL QFI to DRB mapping rule is different from the configured one and has UL SDAP header */
|
||||
bool ul_sdap_header = (entity->qfi2drb_table[qfi].entity_role & SDAP_UL_TX) != 0;
|
||||
if (entity->qfi2drb_table[qfi].drb_id != sdap->drb_id && ul_sdap_header) {
|
||||
// construct an end-marker control PDU (6.2.3 TS 37.324)
|
||||
nr_sdap_ul_hdr_t sdap_ctrl_pdu = entity->sdap_construct_ctrl_pdu(qfi);
|
||||
// map the end-marker control PDU to the DRB according to the stored QoS flow to DRB mapping rule
|
||||
int sdap_ctrl_pdu_drb = entity->sdap_map_ctrl_pdu(entity, SDAP_CTRL_PDU_MAP_RULE_DRB, qfi);
|
||||
// submit the end-marker control PDU to the lower layers
|
||||
entity->sdap_submit_ctrl_pdu(ue_id, sdap_ctrl_pdu_drb, sdap_ctrl_pdu);
|
||||
}
|
||||
/* update QFI to DRB mapping rules */
|
||||
LOG_D(SDAP, "Storing the configured QoS flow to DRB mapping rule\n");
|
||||
existing_sdap_entity->qfi2drb_map_update(existing_sdap_entity, qfi, drb_identity, has_sdap_rx, has_sdap_tx);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Add QoS flows to a DRB */
|
||||
static void nr_sdap_add_qos_flows_to_drb(nr_sdap_entity_t *entity, const sdap_config_t *sdap)
|
||||
{
|
||||
for (int i = 0; i < sdap->mappedQFIs2AddCount; i++) {
|
||||
uint8_t qfi = sdap->mappedQFIs2Add[i];
|
||||
LOG_D(SDAP, "Adding QFI to DRB mapping rules: %d mapped QFIs for DRB %d\n", sdap->mappedQFIs2AddCount, sdap->drb_id);
|
||||
if (qfi < SDAP_MAX_QFI && sdap->drb_id > SDAP_MAP_RULE_EMPTY && sdap->drb_id <= MAX_DRBS_PER_UE) {
|
||||
entity->qfi2drb_map_add(entity, qfi, sdap->drb_id, sdap->role);
|
||||
} else {
|
||||
LOG_E(SDAP, "Failed to add qfi2drb mapping: QFI=%d, DRB=%d\n", qfi, sdap->drb_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Remove QoS flows from a DRB */
|
||||
static void nr_sdap_rm_qos_flows_from_drb(nr_sdap_entity_t *entity, const sdap_config_t *sdap)
|
||||
{
|
||||
for (int i = 0; i < sdap->mappedQFIs2ReleaseCount; i++) {
|
||||
uint8_t qfi = sdap->mappedQFIs2Release[i];
|
||||
LOG_D(SDAP, "Removing QFI from DRB mapping rules: QFI=%d for DRB=%d\n", qfi, sdap->drb_id);
|
||||
entity->qfi2drb_map_delete(entity, qfi);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Update QFI to DRB mapping rules
|
||||
* @param qfi the QoS Flow index, used as unique index of the qfi2drb mapping table
|
||||
* @param drb the DRB ID to be mapped */
|
||||
static void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, const sdap_config_t *sdap)
|
||||
{
|
||||
if (!entity->is_gnb) { // UE control PDU configuration
|
||||
nr_sdap_ue_control_pdu_config(entity, entity->ue_id, sdap);
|
||||
}
|
||||
nr_sdap_add_qos_flows_to_drb(entity, sdap);
|
||||
nr_sdap_rm_qos_flows_from_drb(entity, sdap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add a new SDAP entity according to 5.1.1. of 3GPP TS 37.324
|
||||
* @note there is one SDAP entity per PDU session
|
||||
*
|
||||
* @param is_gnb, indicates whether it is for gNB or UE
|
||||
* @param has_sdap_rx, indicates whether it is a receiving SDAP entity
|
||||
* @param has_sdap_tx, indicates whether it is a transmitting SDAP entity
|
||||
* @param ue_id, UE ID
|
||||
* @param pdusession_id, PDU session ID
|
||||
* @param is_defaultDRB, indicates whether the entity has a default DRB
|
||||
* @param mapped_qfi_2_add, list of QoS flows to add/update
|
||||
* @param mappedQFIs2AddCount, number of QoS flows to add/update
|
||||
*/
|
||||
nr_sdap_entity_t *new_nr_sdap_entity(int is_gnb,
|
||||
bool has_sdap_rx,
|
||||
bool has_sdap_tx,
|
||||
ue_id_t ue_id,
|
||||
int pdusession_id,
|
||||
bool is_defaultDRB,
|
||||
uint8_t drb_identity,
|
||||
NR_QFI_t *mapped_qfi_2_add,
|
||||
uint8_t mappedQFIs2AddCount)
|
||||
* @param sdap, SDAP configuration */
|
||||
static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t *sdap)
|
||||
{
|
||||
/* check whether the SDAP entity already exists and
|
||||
update QFI to DRB mapping rules in that case */
|
||||
if (nr_sdap_get_entity(ue_id, pdusession_id)) {
|
||||
LOG_E(SDAP, "SDAP Entity for UE already exists with RNTI/UE ID: %lu and PDU SESSION ID: %d\n", ue_id, pdusession_id);
|
||||
nr_sdap_entity_t *existing_sdap_entity = nr_sdap_get_entity(ue_id, pdusession_id);
|
||||
rb_id_t pdcp_entity = existing_sdap_entity->default_drb;
|
||||
if(!is_gnb)
|
||||
nr_sdap_ue_qfi2drb_config(existing_sdap_entity,
|
||||
pdcp_entity,
|
||||
ue_id,
|
||||
mapped_qfi_2_add,
|
||||
mappedQFIs2AddCount,
|
||||
drb_identity,
|
||||
has_sdap_rx,
|
||||
has_sdap_tx);
|
||||
return existing_sdap_entity;
|
||||
}
|
||||
|
||||
nr_sdap_entity_t *sdap_entity;
|
||||
sdap_entity = calloc(1, sizeof(nr_sdap_entity_t));
|
||||
|
||||
if(sdap_entity == NULL) {
|
||||
LOG_E(SDAP, "SDAP Entity creation failed, out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
nr_sdap_entity_t *sdap_entity = calloc_or_fail(1, sizeof(*sdap_entity));
|
||||
|
||||
// SDAP entity ids
|
||||
sdap_entity->ue_id = ue_id;
|
||||
sdap_entity->pdusession_id = pdusession_id;
|
||||
sdap_entity->pdusession_id = sdap->pdusession_id;
|
||||
sdap_entity->is_gnb = is_gnb;
|
||||
sdap_entity->enable_sdap = (sdap->role != NO_SDAP_HEADER);
|
||||
|
||||
// rx/tx entities
|
||||
sdap_entity->tx_entity = nr_sdap_tx_entity;
|
||||
sdap_entity->rx_entity = nr_sdap_rx_entity;
|
||||
|
||||
// control pdu function pointers
|
||||
sdap_entity->sdap_construct_ctrl_pdu = nr_sdap_construct_ctrl_pdu;
|
||||
sdap_entity->sdap_map_ctrl_pdu = nr_sdap_map_ctrl_pdu;
|
||||
sdap_entity->sdap_submit_ctrl_pdu = nr_sdap_submit_ctrl_pdu;
|
||||
|
||||
// QFI to DRB mapping functions pointers
|
||||
sdap_entity->qfi2drb_map_update = nr_sdap_qfi2drb_map_update;
|
||||
sdap_entity->qfi2drb_map_add = nr_sdap_qfi2drb_map_add;
|
||||
sdap_entity->qfi2drb_map_delete = nr_sdap_qfi2drb_map_del;
|
||||
sdap_entity->qfi2drb_map = nr_sdap_qfi2drb_map;
|
||||
|
||||
sdap_entity->qfi2drb_map = nr_sdap_qfi2drb;
|
||||
sdap_entity->pdusession_sock = -1;
|
||||
|
||||
if(is_defaultDRB) {
|
||||
sdap_entity->default_drb = drb_identity;
|
||||
LOG_I(SDAP, "Default DRB for the created SDAP entity: %ld \n", sdap_entity->default_drb);
|
||||
LOG_D(SDAP, "RRC updating mapping rules: %d\n", mappedQFIs2AddCount);
|
||||
for (int i = 0; i < mappedQFIs2AddCount; i++)
|
||||
sdap_entity->qfi2drb_map_update(sdap_entity, mapped_qfi_2_add[i], sdap_entity->default_drb, has_sdap_rx, has_sdap_tx);
|
||||
// set default DRB
|
||||
if (sdap->defaultDRB) {
|
||||
sdap_entity->default_drb = sdap->drb_id;
|
||||
LOG_I(SDAP, "Default DRB for the created SDAP entity: DRB %d \n", sdap_entity->default_drb);
|
||||
}
|
||||
|
||||
// Add QoS flows to the DRB (initial configuration)
|
||||
nr_sdap_add_qos_flows_to_drb(sdap_entity, sdap);
|
||||
|
||||
// update SDAP entity list pointers
|
||||
sdap_entity->next_entity = sdap_info.sdap_entity_llist;
|
||||
sdap_info.sdap_entity_llist = sdap_entity;
|
||||
|
||||
@@ -533,7 +536,17 @@ nr_sdap_entity_t *new_nr_sdap_entity(int is_gnb,
|
||||
// PDCP SDUs to/from the TUN interface.
|
||||
start_sdap_tun_gnb_first_ue_default_pdu_session(ue_id);
|
||||
}
|
||||
return sdap_entity;
|
||||
}
|
||||
|
||||
/** @brief Add or modify an SDAP entity if it already exists */
|
||||
void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t *sdap)
|
||||
{
|
||||
nr_sdap_entity_t *sdap_entity = nr_sdap_get_entity(ue_id, sdap->pdusession_id);
|
||||
if (sdap_entity) {
|
||||
sdap_entity->qfi2drb_map_update(sdap_entity, sdap);
|
||||
} else {
|
||||
nr_sdap_add_entity(is_gnb, ue_id, sdap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -583,14 +596,13 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id)
|
||||
LOG_E(SDAP, "SDAP entities not established or Invalid range of pdusession_id [0, 256].\n");
|
||||
return false;
|
||||
}
|
||||
LOG_D(SDAP, "Deleting SDAP entity for UE %lx and PDU Session id %d\n", ue_id, entityPtr->pdusession_id);
|
||||
|
||||
if (entityPtr->ue_id == ue_id && entityPtr->pdusession_id == pdusession_id) {
|
||||
sdap_info.sdap_entity_llist = sdap_info.sdap_entity_llist->next_entity;
|
||||
if (entityPtr->pdusession_sock != -1)
|
||||
remove_ip_if(entityPtr);
|
||||
free(entityPtr);
|
||||
LOG_D(SDAP, "Successfully deleted Entity.\n");
|
||||
LOG_D(SDAP, "Successfully deleted SDAP entity for UE %lx and PDU Session id %d\n", ue_id, pdusession_id);
|
||||
return true;
|
||||
} else {
|
||||
while ((entityPtr->ue_id != ue_id || entityPtr->pdusession_id != pdusession_id) && entityPtr->next_entity != NULL
|
||||
@@ -606,7 +618,7 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id)
|
||||
remove_ip_if(entityPtr);
|
||||
}
|
||||
free(entityPtr);
|
||||
LOG_D(SDAP, "Successfully deleted Entity.\n");
|
||||
LOG_D(SDAP, "Successfully deleted Entity for UE %lx and PDU Session id %d\n", ue_id, pdusession_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -653,6 +665,40 @@ bool nr_sdap_delete_ue_entities(ue_id_t ue_id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @brief This function gets the relevant SDAP config from the received SDAP-Config */
|
||||
sdap_config_t nr_sdap_get_config(const int is_gnb, const NR_SDAP_Config_t *sdap_Config, const int drb_id)
|
||||
{
|
||||
DevAssert(sdap_Config);
|
||||
sdap_config_t sdapConfig = {0};
|
||||
sdapConfig.drb_id = drb_id;
|
||||
sdapConfig.role = get_sdap_role(is_gnb, sdap_Config);
|
||||
LOG_D(SDAP, "SDAP headers %s\n", sdapConfig.role == NO_SDAP_HEADER ? "absent" : "present");
|
||||
sdapConfig.defaultDRB = sdap_Config->defaultDRB;
|
||||
// 3GPP TS 38.331 The network sets sdap-HeaderUL to present if the field defaultDRB is set to true
|
||||
if (sdapConfig.defaultDRB && (sdap_Config->sdap_HeaderUL != NR_SDAP_Config__sdap_HeaderUL_present))
|
||||
LOG_D(SDAP, "Received SDAP-Config with defaultDRB but sdap-HeaderUL is not present\n");
|
||||
if (sdap_Config->mappedQoS_FlowsToAdd) {
|
||||
sdapConfig.mappedQFIs2AddCount = sdap_Config->mappedQoS_FlowsToAdd->list.count;
|
||||
DevAssert(sdapConfig.mappedQFIs2AddCount <= SDAP_MAX_QFI);
|
||||
LOG_D(SDAP, "DRB %d: mapped QFIs = %d \n", sdapConfig.drb_id, sdapConfig.mappedQFIs2AddCount);
|
||||
for (int i = 0; i < sdapConfig.mappedQFIs2AddCount; i++){
|
||||
DevAssert(i < SDAP_MAX_QFI);
|
||||
sdapConfig.mappedQFIs2Add[i] = *sdap_Config->mappedQoS_FlowsToAdd->list.array[i];
|
||||
LOG_D(SDAP, "Captured mappedQoS_FlowsToAdd[%d] from RRC: %ld\n", i, sdapConfig.mappedQFIs2Add[i]);
|
||||
}
|
||||
}
|
||||
sdapConfig.pdusession_id = sdap_Config->pdu_Session;
|
||||
if (sdap_Config->mappedQoS_FlowsToRelease) {
|
||||
sdapConfig.mappedQFIs2ReleaseCount = sdap_Config->mappedQoS_FlowsToRelease->list.count;
|
||||
DevAssert(sdapConfig.mappedQFIs2ReleaseCount <= SDAP_MAX_QFI);
|
||||
for (int i = 0; i < sdapConfig.mappedQFIs2ReleaseCount; i++) {
|
||||
DevAssert(i < SDAP_MAX_QFI);
|
||||
sdapConfig.mappedQFIs2Release[i] = *sdap_Config->mappedQoS_FlowsToRelease->list.array[i];
|
||||
}
|
||||
}
|
||||
return sdapConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SDAP Entity reconfiguration at UE according to TS 37.324
|
||||
* and triggered by RRC reconfiguration events according to clause 5.3.5.6.5 of TS 38.331.
|
||||
@@ -670,27 +716,8 @@ void nr_reconfigure_sdap_entity(NR_SDAP_Config_t *sdap_config, ue_id_t ue_id, in
|
||||
ue_id,
|
||||
pdusession_id);
|
||||
/* QFI to DRB mapping */
|
||||
NR_QFI_t *mappedQFIs2Add = (NR_QFI_t *)sdap_config->mappedQoS_FlowsToAdd->list.array[0];
|
||||
uint8_t mappedQFIs2AddCount = sdap_config->mappedQoS_FlowsToAdd->list.count;
|
||||
bool has_sdap_rx = is_sdap_rx(is_gnb, sdap_config);
|
||||
bool has_sdap_tx = is_sdap_tx(is_gnb, sdap_config);
|
||||
nr_sdap_ue_qfi2drb_config(sdap_entity,
|
||||
sdap_entity->default_drb,
|
||||
ue_id,
|
||||
mappedQFIs2Add,
|
||||
mappedQFIs2AddCount,
|
||||
drb_id,
|
||||
has_sdap_rx,
|
||||
has_sdap_tx);
|
||||
/* handle QFIs to DRB mapping rule to release */
|
||||
if (sdap_config->mappedQoS_FlowsToRelease) {
|
||||
NR_QFI_t *mappedQFIs2release = (NR_QFI_t *)sdap_config->mappedQoS_FlowsToRelease->list.array[0];
|
||||
uint8_t mappedQFIs2RemoveCount = sdap_config->mappedQoS_FlowsToRelease->list.count;
|
||||
for(int i = 0; i < mappedQFIs2RemoveCount; i++){
|
||||
uint8_t qfi = mappedQFIs2release[i];
|
||||
sdap_entity->qfi2drb_map_delete(sdap_entity, qfi);
|
||||
}
|
||||
}
|
||||
sdap_config_t sdap = nr_sdap_get_config(is_gnb, sdap_config, drb_id);
|
||||
sdap_entity->qfi2drb_map_update(sdap_entity, &sdap);
|
||||
}
|
||||
|
||||
void set_qfi(uint8_t qfi, uint8_t pduid, ue_id_t ue_id)
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#define SDAP_HDR_LENGTH (1)
|
||||
#define SDAP_MAX_QFI (64)
|
||||
#define SDAP_MAP_RULE_EMPTY (0)
|
||||
#define AVLBL_DRB (5)
|
||||
#define SDAP_NO_MAPPING_RULE (0)
|
||||
#define SDAP_REFLECTIVE_MAPPING (1)
|
||||
#define SDAP_RQI_HANDLING (1)
|
||||
@@ -69,20 +68,38 @@ typedef struct nr_sdap_ul_hdr_s {
|
||||
uint8_t DC:1;
|
||||
} __attribute__((packed)) nr_sdap_ul_hdr_t;
|
||||
|
||||
typedef enum {
|
||||
SDAP_UL_TX = (1 << 0),
|
||||
SDAP_UL_RX = (1 << 1),
|
||||
SDAP_DL_TX = (1 << 2),
|
||||
SDAP_DL_RX = (1 << 3)
|
||||
} sdap_role_t;
|
||||
|
||||
typedef struct qfi2drb_s {
|
||||
rb_id_t drb_id;
|
||||
bool has_sdap_rx;
|
||||
bool has_sdap_tx;
|
||||
int drb_id;
|
||||
int entity_role;
|
||||
} qfi2drb_t;
|
||||
|
||||
void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu);
|
||||
void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu);
|
||||
|
||||
typedef struct sdap_configuration_s {
|
||||
int pdusession_id;
|
||||
int drb_id;
|
||||
int role;
|
||||
bool defaultDRB;
|
||||
NR_QFI_t mappedQFIs2Add[SDAP_MAX_QFI];
|
||||
uint8_t mappedQFIs2AddCount;
|
||||
NR_QFI_t mappedQFIs2Release[SDAP_MAX_QFI];
|
||||
uint8_t mappedQFIs2ReleaseCount;
|
||||
} sdap_config_t;
|
||||
|
||||
typedef struct nr_sdap_entity_s {
|
||||
ue_id_t ue_id;
|
||||
rb_id_t default_drb;
|
||||
int default_drb;
|
||||
/// sdap_tun_read_thread needs to know if we are gNB/UE, so for noS1 mode,
|
||||
/// store which one we are
|
||||
bool is_gnb;
|
||||
bool enable_sdap;
|
||||
int pdusession_id;
|
||||
int pdusession_sock;
|
||||
pthread_t pdusession_thread;
|
||||
@@ -91,18 +108,21 @@ typedef struct nr_sdap_entity_s {
|
||||
|
||||
qfi2drb_t qfi2drb_table[SDAP_MAX_QFI];
|
||||
|
||||
void (*qfi2drb_map_update)(struct nr_sdap_entity_s *entity, uint8_t qfi, rb_id_t drb, bool has_sdap_rx, bool has_sdap_tx);
|
||||
void (*qfi2drb_map_delete)(struct nr_sdap_entity_s *entity, uint8_t qfi);
|
||||
rb_id_t (*qfi2drb_map)(struct nr_sdap_entity_s *entity, uint8_t qfi);
|
||||
void (*qfi2drb_map_update)(struct nr_sdap_entity_s *entity, const sdap_config_t *sdap);
|
||||
void (*qfi2drb_map_delete)(struct nr_sdap_entity_s *entity, const uint8_t qfi);
|
||||
void (*qfi2drb_map_add)(struct nr_sdap_entity_s *entity,
|
||||
const uint8_t qfi,
|
||||
const uint8_t drb_id,
|
||||
const uint8_t role);
|
||||
int (*qfi2drb_map)(struct nr_sdap_entity_s *entity, uint8_t qfi);
|
||||
|
||||
nr_sdap_ul_hdr_t (*sdap_construct_ctrl_pdu)(uint8_t qfi);
|
||||
rb_id_t (*sdap_map_ctrl_pdu)(struct nr_sdap_entity_s *entity, rb_id_t pdcp_entity, int map_type, uint8_t dl_qfi);
|
||||
void (*sdap_submit_ctrl_pdu)(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu);
|
||||
int (*sdap_map_ctrl_pdu)(struct nr_sdap_entity_s *entity, int map_type, uint8_t dl_qfi);
|
||||
void (*sdap_submit_ctrl_pdu)(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu);
|
||||
|
||||
bool (*tx_entity)(struct nr_sdap_entity_s *entity,
|
||||
protocol_ctxt_t *ctxt_p,
|
||||
const srb_flag_t srb_flag,
|
||||
const rb_id_t rb_id,
|
||||
const mui_t mui,
|
||||
const confirm_t confirm,
|
||||
const sdu_size_t sdu_buffer_size,
|
||||
@@ -114,9 +134,8 @@ typedef struct nr_sdap_entity_s {
|
||||
const bool rqi);
|
||||
|
||||
void (*rx_entity)(struct nr_sdap_entity_s *entity,
|
||||
rb_id_t pdcp_entity,
|
||||
int pdcp_entity,
|
||||
int is_gnb,
|
||||
bool has_sdap_rx,
|
||||
int pdusession_id,
|
||||
ue_id_t ue_id,
|
||||
char *buf,
|
||||
@@ -126,23 +145,6 @@ typedef struct nr_sdap_entity_s {
|
||||
struct nr_sdap_entity_s *next_entity;
|
||||
} nr_sdap_entity_t;
|
||||
|
||||
/* QFI to DRB Mapping Related Function */
|
||||
void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, uint8_t qfi, rb_id_t drb, bool has_sdap_rx, bool has_sdap_tx);
|
||||
|
||||
/* QFI to DRB Mapping Related Function */
|
||||
void nr_sdap_qfi2drb_map_del(nr_sdap_entity_t *entity, uint8_t qfi);
|
||||
|
||||
/*
|
||||
* TS 37.324
|
||||
* 4.4 Functions
|
||||
* Mapping between a QoS flow and a DRB for both DL and UL.
|
||||
*
|
||||
* 5.2.1 Uplink
|
||||
* If there is no stored QoS flow to DRB mapping rule for the QoS flow as specified in the subclause 5.3, map the SDAP SDU to the default DRB
|
||||
* else, map the SDAP SDU to the DRB according to the stored QoS flow to DRB mapping rule.
|
||||
*/
|
||||
rb_id_t nr_sdap_qfi2drb_map(nr_sdap_entity_t *entity, uint8_t qfi);
|
||||
|
||||
/*
|
||||
* TS 37.324 5.3 QoS flow to DRB Mapping
|
||||
* construct an end-marker control PDU, as specified in the subclause 6.2.3, for the QoS flow;
|
||||
@@ -155,33 +157,24 @@ nr_sdap_ul_hdr_t nr_sdap_construct_ctrl_pdu(uint8_t qfi);
|
||||
* 1.) default DRB or
|
||||
* 2.) DRB according to the stored QoS flow to DRB mapping rule
|
||||
*/
|
||||
rb_id_t nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, rb_id_t pdcp_entity, int map_type, uint8_t dl_qfi);
|
||||
int nr_sdap_map_ctrl_pdu(nr_sdap_entity_t *entity, int map_type, uint8_t dl_qfi);
|
||||
|
||||
/*
|
||||
* TS 37.324 5.3 QoS flow to DRB Mapping
|
||||
* Submit the end-marker control PDU to the lower layer.
|
||||
*/
|
||||
void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu);
|
||||
|
||||
/*
|
||||
* TS 37.324 4.4 5.1.1 SDAP entity establishment
|
||||
* Establish an SDAP entity.
|
||||
*/
|
||||
nr_sdap_entity_t *new_nr_sdap_entity(int is_gnb,
|
||||
bool has_sdap_rx,
|
||||
bool has_sdap_tx,
|
||||
ue_id_t ue_id,
|
||||
int pdusession_id,
|
||||
bool is_defaultDRB,
|
||||
uint8_t default_DRB,
|
||||
NR_QFI_t *mapped_qfi_2_add,
|
||||
uint8_t mappedQFIs2AddCount);
|
||||
void nr_sdap_submit_ctrl_pdu(ue_id_t ue_id, int sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu);
|
||||
|
||||
/* Entity Handling Related Functions */
|
||||
nr_sdap_entity_t *nr_sdap_get_entity(ue_id_t ue_id, int pdusession_id);
|
||||
|
||||
sdap_config_t nr_sdap_get_config(const int is_gnb, const NR_SDAP_Config_t *sdap_Config, const int drb_id);
|
||||
|
||||
void nr_sdap_release_drb(ue_id_t ue_id, int drb_id, int pdusession_id);
|
||||
|
||||
/** @brief Add or modify an SDAP entity based on whether it already exists */
|
||||
void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t *sdap);
|
||||
|
||||
/**
|
||||
* @brief Function to delete a single SDAP Entity based on the ue_id and pdusession_id.
|
||||
* @note 1. SDAP entities may have the same ue_id.
|
||||
@@ -200,20 +193,6 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id);
|
||||
*/
|
||||
bool nr_sdap_delete_ue_entities(ue_id_t ue_id);
|
||||
|
||||
/**
|
||||
* @brief indicates whether it is a receiving SDAP entity
|
||||
* i.e. for UE, header for DL data is present
|
||||
* for gNB, header for UL data is present
|
||||
*/
|
||||
bool is_sdap_rx(bool is_gnb, NR_SDAP_Config_t *sdap_config);
|
||||
|
||||
/**
|
||||
* @brief indicates whether it is a transmitting SDAP entity
|
||||
* i.e. for UE, header for UL data is present
|
||||
* for gNB, header for DL data is present
|
||||
*/
|
||||
bool is_sdap_tx(bool is_gnb, NR_SDAP_Config_t *sdap_config);
|
||||
|
||||
/**
|
||||
* @brief Run the SDAP reconfiguration for the DRB
|
||||
* @param[in] ue_id Unique identifier for the User Equipment. ID Range [0, 65536].
|
||||
|
||||
Reference in New Issue
Block a user